Merge branch 'i2c/for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux

Pull i2c updates from Wolfram Sang:
 "This pull request contains:

   - i2c core reorganization. One source file became too monolithic. It
     is now split up, yet we still have the same named object as the
     final output. This should ease maintenance.

   - new drivers: ZTE ZX2967 family, ASPEED 24XX/25XX

   - designware driver gained slave mode support

   - xgene-slimpro driver gained ACPI support

   - bigger overhaul for pca-platform driver

   - the algo-bit module now supports messages with enforced STOP

   - slightly bigger than usual set of driver updates and improvements

  and with much appreciated quality assurance from Andy Shevchenko"

* 'i2c/for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (51 commits)
  i2c: Provide a stub for i2c_detect_slave_mode()
  i2c: designware: Let slave adapter support be optional
  i2c: designware: Make HW init functions static
  i2c: designware: fix spelling mistakes
  i2c: pca-platform: propagate error from i2c_pca_add_numbered_bus
  i2c: pca-platform: correctly set algo_data.reset_chip
  i2c: acpi: Do not create i2c-clients for LNXVIDEO ACPI devices
  i2c: designware: enable SLAVE in platform module
  i2c: designware: add SLAVE mode functions
  i2c: zx2967: drop COMPILE_TEST dependency
  i2c: zx2967: always use the same device when printing errors
  i2c: pca-platform: use dev_warn/dev_info instead of printk
  i2c: pca-platform: use device managed allocations
  i2c: pca-platform: add devicetree awareness
  i2c: pca-platform: switch to struct gpio_desc
  dt-bindings: add bindings for i2c-pca-platform
  i2c: cadance: fix ctrl/addr reg write order
  i2c: zx2967: add i2c controller driver for ZTE's zx2967 family
  dt: bindings: add documentation for zx2967 family i2c controller
  i2c: algo-bit: add support for I2C_M_STOP
  ...
This commit is contained in:
Linus Torvalds 2017-07-12 10:04:56 -07:00
commit 235b84fc86
41 changed files with 4949 additions and 2488 deletions

View File

@ -0,0 +1,48 @@
Device tree configuration for the I2C busses on the AST24XX and AST25XX SoCs.
Required Properties:
- #address-cells : should be 1
- #size-cells : should be 0
- reg : address offset and range of bus
- compatible : should be "aspeed,ast2400-i2c-bus"
or "aspeed,ast2500-i2c-bus"
- clocks : root clock of bus, should reference the APB
clock
- interrupts : interrupt number
- interrupt-parent : interrupt controller for bus, should reference a
aspeed,ast2400-i2c-ic or aspeed,ast2500-i2c-ic
interrupt controller
Optional Properties:
- bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not
specified
- multi-master : states that there is another master active on this bus.
Example:
i2c {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x1e78a000 0x1000>;
i2c_ic: interrupt-controller@0 {
#interrupt-cells = <1>;
compatible = "aspeed,ast2400-i2c-ic";
reg = <0x0 0x40>;
interrupts = <12>;
interrupt-controller;
};
i2c0: i2c-bus@40 {
#address-cells = <1>;
#size-cells = <0>;
#interrupt-cells = <1>;
reg = <0x40 0x40>;
compatible = "aspeed,ast2400-i2c-bus";
clocks = <&clk_apb>;
bus-frequency = <100000>;
interrupts = <0>;
interrupt-parent = <&i2c_ic>;
};
};

View File

@ -20,7 +20,7 @@ Optional properties :
- i2c-sda-falling-time-ns : should contain the SDA falling time in nanoseconds. - i2c-sda-falling-time-ns : should contain the SDA falling time in nanoseconds.
This value which is by default 300ns is used to compute the tHIGH period. This value which is by default 300ns is used to compute the tHIGH period.
Example : Examples :
i2c@f0000 { i2c@f0000 {
#address-cells = <1>; #address-cells = <1>;
@ -43,3 +43,17 @@ Example :
i2c-sda-falling-time-ns = <300>; i2c-sda-falling-time-ns = <300>;
i2c-scl-falling-time-ns = <300>; i2c-scl-falling-time-ns = <300>;
}; };
i2c@1120000 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x2000 0x100>;
clock-frequency = <400000>;
clocks = <&i2cclk>;
interrupts = <0>;
eeprom@64 {
compatible = "linux,slave-24c02";
reg = <0x40000064>;
};
};

View File

@ -0,0 +1,29 @@
* NXP PCA PCA9564/PCA9665 I2C controller
The PCA9564/PCA9665 serves as an interface between most standard
parallel-bus microcontrollers/microprocessors and the serial I2C-bus
and allows the parallel bus system to communicate bi-directionally
with the I2C-bus.
Required properties :
- reg : Offset and length of the register set for the device
- compatible : one of "nxp,pca9564" or "nxp,pca9665"
Optional properties
- interrupts : the interrupt number
- interrupt-parent : the phandle for the interrupt controller.
If an interrupt is not specified polling will be used.
- reset-gpios : gpio specifier for gpio connected to RESET_N pin. As the line
is active low, it should be marked GPIO_ACTIVE_LOW.
- clock-frequency : I2C bus frequency.
Example:
i2c0: i2c@80000 {
compatible = "nxp,pca9564";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x80000 0x4>;
reset-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
clock-frequency = <100000>;
};

View File

@ -0,0 +1,22 @@
ZTE zx2967 I2C controller
Required properties:
- compatible: must be "zte,zx296718-i2c"
- reg: physical address and length of the device registers
- interrupts: a single interrupt specifier
- clocks: clock for the device
- #address-cells: should be <1>
- #size-cells: should be <0>
- clock-frequency: the desired I2C bus clock frequency.
Examples:
i2c@112000 {
compatible = "zte,zx296718-i2c";
reg = <0x00112000 0x1000>;
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&osc24m>;
#address-cells = <1>
#size-cells = <0>;
clock-frequency = <1600000>;
};

View File

@ -41,5 +41,8 @@ i2c_adapter devices which don't support those I2C operations.
.. kernel-doc:: drivers/i2c/i2c-boardinfo.c .. kernel-doc:: drivers/i2c/i2c-boardinfo.c
:functions: i2c_register_board_info :functions: i2c_register_board_info
.. kernel-doc:: drivers/i2c/i2c-core.c .. kernel-doc:: drivers/i2c/i2c-core-base.c
:export:
.. kernel-doc:: drivers/i2c/i2c-core-smbus.c
:export: :export:

View File

@ -34,6 +34,8 @@ Supported adapters:
* Intel Broxton (SOC) * Intel Broxton (SOC)
* Intel Lewisburg (PCH) * Intel Lewisburg (PCH)
* Intel Gemini Lake (SOC) * Intel Gemini Lake (SOC)
* Intel Cannon Lake-H (PCH)
* Intel Cannon Lake-LP (PCH)
Datasheets: Publicly available at the Intel website Datasheets: Publicly available at the Intel website
On Intel Patsburg and later chipsets, both the normal host SMBus controller On Intel Patsburg and later chipsets, both the normal host SMBus controller

View File

@ -191,7 +191,7 @@ checking on future transactions.)
4* Other ioctl() calls are converted to in-kernel function calls by 4* Other ioctl() calls are converted to in-kernel function calls by
i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter
functionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, which functionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, which
performs an SMBus transaction using i2c-core.c:i2c_smbus_xfer(). performs an SMBus transaction using i2c-core-smbus.c:i2c_smbus_xfer().
The i2c-dev driver is responsible for checking all the parameters that The i2c-dev driver is responsible for checking all the parameters that
come from user-space for validity. After this point, there is no come from user-space for validity. After this point, there is no
@ -200,13 +200,13 @@ and calls that would have been performed by kernel I2C chip drivers
directly. This means that I2C bus drivers don't need to implement directly. This means that I2C bus drivers don't need to implement
anything special to support access from user-space. anything special to support access from user-space.
5* These i2c-core.c/i2c.h functions are wrappers to the actual 5* These i2c.h functions are wrappers to the actual implementation of
implementation of your I2C bus driver. Each adapter must declare your I2C bus driver. Each adapter must declare callback functions
callback functions implementing these standard calls. implementing these standard calls. i2c.h:i2c_get_functionality() calls
i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(), i2c_adapter.algo->functionality(), while
while i2c-core.c:i2c_smbus_xfer() calls either i2c-core-smbus.c:i2c_smbus_xfer() calls either
adapter.algo->smbus_xfer() if it is implemented, or if not, adapter.algo->smbus_xfer() if it is implemented, or if not,
i2c-core.c:i2c_smbus_xfer_emulated() which in turn calls i2c-core-smbus.c:i2c_smbus_xfer_emulated() which in turn calls
i2c_adapter.algo->master_xfer(). i2c_adapter.algo->master_xfer().
After your I2C bus driver has processed these requests, execution runs After your I2C bus driver has processed these requests, execution runs

View File

@ -1139,6 +1139,18 @@ F: arch/arm/mach-aspeed/
F: arch/arm/boot/dts/aspeed-* F: arch/arm/boot/dts/aspeed-*
F: drivers/*/*aspeed* F: drivers/*/*aspeed*
ARM/ASPEED I2C DRIVER
M: Brendan Higgins <brendanhiggins@google.com>
R: Benjamin Herrenschmidt <benh@kernel.crashing.org>
R: Joel Stanley <joel@jms.id.au>
L: linux-i2c@vger.kernel.org
L: openbmc@lists.ozlabs.org
S: Maintained
F: drivers/irqchip/irq-aspeed-i2c-ic.c
F: drivers/i2c/busses/i2c-aspeed.c
F: Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-i2c-ic.txt
F: Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
ARM/ATMEL AT91RM9200, AT91SAM9 AND SAMA5 SOC SUPPORT ARM/ATMEL AT91RM9200, AT91SAM9 AND SAMA5 SOC SUPPORT
M: Nicolas Ferre <nicolas.ferre@microchip.com> M: Nicolas Ferre <nicolas.ferre@microchip.com>
M: Alexandre Belloni <alexandre.belloni@free-electrons.com> M: Alexandre Belloni <alexandre.belloni@free-electrons.com>
@ -6382,6 +6394,7 @@ M: Mika Westerberg <mika.westerberg@linux.intel.com>
L: linux-i2c@vger.kernel.org L: linux-i2c@vger.kernel.org
L: linux-acpi@vger.kernel.org L: linux-acpi@vger.kernel.org
S: Maintained S: Maintained
F: drivers/i2c/i2c-core-acpi.c
I2C-TAOS-EVM DRIVER I2C-TAOS-EVM DRIVER
M: Jean Delvare <jdelvare@suse.com> M: Jean Delvare <jdelvare@suse.com>

View File

@ -4,6 +4,11 @@
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C) += i2c-core.o
i2c-core-objs := i2c-core-base.o i2c-core-smbus.o
i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o
i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
i2c-core-$(CONFIG_OF) += i2c-core-of.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o
@ -12,4 +17,4 @@ obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
CFLAGS_i2c-core.o := -Wno-deprecated-declarations CFLAGS_i2c-core-base.o := -Wno-deprecated-declarations

View File

@ -553,9 +553,16 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
if (!(pmsg->flags & I2C_M_NOSTART)) { if (!(pmsg->flags & I2C_M_NOSTART)) {
if (i) { if (i) {
bit_dbg(3, &i2c_adap->dev, "emitting " if (msgs[i - 1].flags & I2C_M_STOP) {
"repeated start condition\n"); bit_dbg(3, &i2c_adap->dev,
i2c_repstart(adap); "emitting enforced stop/start condition\n");
i2c_stop(adap);
i2c_start(adap);
} else {
bit_dbg(3, &i2c_adap->dev,
"emitting repeated start condition\n");
i2c_repstart(adap);
}
} }
ret = bit_doAddress(i2c_adap, pmsg); ret = bit_doAddress(i2c_adap, pmsg);
if ((ret != 0) && !nak_ok) { if ((ret != 0) && !nak_ok) {

View File

@ -129,6 +129,8 @@ config I2C_I801
Broxton (SOC) Broxton (SOC)
Lewisburg (PCH) Lewisburg (PCH)
Gemini Lake (SOC) Gemini Lake (SOC)
Cannon Lake-H (PCH)
Cannon Lake-LP (PCH)
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-i801. will be called i2c-i801.
@ -326,6 +328,16 @@ config I2C_POWERMAC
comment "I2C system bus drivers (mostly embedded / system-on-chip)" comment "I2C system bus drivers (mostly embedded / system-on-chip)"
config I2C_ASPEED
tristate "Aspeed I2C Controller"
depends on ARCH_ASPEED || COMPILE_TEST
help
If you say yes to this option, support will be included for the
Aspeed I2C controller.
This driver can also be built as a module. If so, the module
will be called i2c-aspeed.
config I2C_AT91 config I2C_AT91
tristate "Atmel AT91 I2C Two-Wire interface (TWI)" tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
depends on ARCH_AT91 depends on ARCH_AT91
@ -474,11 +486,22 @@ config I2C_DESIGNWARE_PLATFORM
depends on (ACPI && COMMON_CLK) || !ACPI depends on (ACPI && COMMON_CLK) || !ACPI
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported. Synopsys DesignWare I2C adapter.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-designware-platform. will be called i2c-designware-platform.
config I2C_DESIGNWARE_SLAVE
bool "Synopsys DesignWare Slave"
select I2C_SLAVE
depends on I2C_DESIGNWARE_PLATFORM
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C slave adapter.
This is not a standalone module, this module compiles together with
i2c-designware-core.
config I2C_DESIGNWARE_PCI config I2C_DESIGNWARE_PCI
tristate "Synopsys DesignWare PCI" tristate "Synopsys DesignWare PCI"
depends on PCI depends on PCI
@ -1258,4 +1281,13 @@ config I2C_OPAL
This driver can also be built as a module. If so, the module will be This driver can also be built as a module. If so, the module will be
called as i2c-opal. called as i2c-opal.
config I2C_ZX2967
tristate "ZTE ZX2967 I2C support"
depends on ARCH_ZX
default y
help
Selecting this option will add ZX2967 I2C driver.
This driver can also be built as a module. If so, the module will be
called i2c-zx2967.
endmenu endmenu

View File

@ -29,6 +29,7 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers # Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
@ -40,6 +41,10 @@ obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
i2c-designware-core-objs := i2c-designware-common.o i2c-designware-master.o
ifeq ($(CONFIG_I2C_DESIGNWARE_SLAVE),y)
i2c-designware-core-objs += i2c-designware-slave.o
endif
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-platform-objs := i2c-designware-platdrv.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
@ -102,6 +107,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o
# External I2C/SMBus adapter drivers # External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o

View File

@ -0,0 +1,891 @@
/*
* Aspeed 24XX/25XX I2C Controller.
*
* Copyright (C) 2012-2017 ASPEED Technology Inc.
* Copyright 2017 IBM Corporation
* Copyright 2017 Google, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* I2C Register */
#define ASPEED_I2C_FUN_CTRL_REG 0x00
#define ASPEED_I2C_AC_TIMING_REG1 0x04
#define ASPEED_I2C_AC_TIMING_REG2 0x08
#define ASPEED_I2C_INTR_CTRL_REG 0x0c
#define ASPEED_I2C_INTR_STS_REG 0x10
#define ASPEED_I2C_CMD_REG 0x14
#define ASPEED_I2C_DEV_ADDR_REG 0x18
#define ASPEED_I2C_BYTE_BUF_REG 0x20
/* Global Register Definition */
/* 0x00 : I2C Interrupt Status Register */
/* 0x08 : I2C Interrupt Target Assignment */
/* Device Register Definition */
/* 0x00 : I2CD Function Control Register */
#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
#define ASPEED_I2CD_SLAVE_EN BIT(1)
#define ASPEED_I2CD_MASTER_EN BIT(0)
/* 0x04 : I2CD Clock and AC Timing Control Register #1 */
#define ASPEED_I2CD_TIME_SCL_HIGH_SHIFT 16
#define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16)
#define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12
#define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12)
#define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0)
#define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0)
/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
#define ASPEED_NO_TIMEOUT_CTRL 0
/* 0x0c : I2CD Interrupt Control Register &
* 0x10 : I2CD Interrupt Status Register
*
* These share bit definitions, so use the same values for the enable &
* status bits.
*/
#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
#define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4)
#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3)
#define ASPEED_I2CD_INTR_RX_DONE BIT(2)
#define ASPEED_I2CD_INTR_TX_NAK BIT(1)
#define ASPEED_I2CD_INTR_TX_ACK BIT(0)
#define ASPEED_I2CD_INTR_ALL \
(ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \
ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \
ASPEED_I2CD_INTR_SCL_TIMEOUT | \
ASPEED_I2CD_INTR_ABNORMAL | \
ASPEED_I2CD_INTR_NORMAL_STOP | \
ASPEED_I2CD_INTR_ARBIT_LOSS | \
ASPEED_I2CD_INTR_RX_DONE | \
ASPEED_I2CD_INTR_TX_NAK | \
ASPEED_I2CD_INTR_TX_ACK)
/* 0x14 : I2CD Command/Status Register */
#define ASPEED_I2CD_SCL_LINE_STS BIT(18)
#define ASPEED_I2CD_SDA_LINE_STS BIT(17)
#define ASPEED_I2CD_BUS_BUSY_STS BIT(16)
#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
/* Command Bit */
#define ASPEED_I2CD_M_STOP_CMD BIT(5)
#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4)
#define ASPEED_I2CD_M_RX_CMD BIT(3)
#define ASPEED_I2CD_S_TX_CMD BIT(2)
#define ASPEED_I2CD_M_TX_CMD BIT(1)
#define ASPEED_I2CD_M_START_CMD BIT(0)
/* 0x18 : I2CD Slave Device Address Register */
#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
enum aspeed_i2c_master_state {
ASPEED_I2C_MASTER_START,
ASPEED_I2C_MASTER_TX_FIRST,
ASPEED_I2C_MASTER_TX,
ASPEED_I2C_MASTER_RX_FIRST,
ASPEED_I2C_MASTER_RX,
ASPEED_I2C_MASTER_STOP,
ASPEED_I2C_MASTER_INACTIVE,
};
enum aspeed_i2c_slave_state {
ASPEED_I2C_SLAVE_START,
ASPEED_I2C_SLAVE_READ_REQUESTED,
ASPEED_I2C_SLAVE_READ_PROCESSED,
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
ASPEED_I2C_SLAVE_STOP,
};
struct aspeed_i2c_bus {
struct i2c_adapter adap;
struct device *dev;
void __iomem *base;
/* Synchronizes I/O mem access to base. */
spinlock_t lock;
struct completion cmd_complete;
unsigned long parent_clk_frequency;
u32 bus_frequency;
/* Transaction state. */
enum aspeed_i2c_master_state master_state;
struct i2c_msg *msgs;
size_t buf_index;
size_t msgs_index;
size_t msgs_count;
bool send_stop;
int cmd_err;
/* Protected only by i2c_lock_bus */
int master_xfer_result;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
enum aspeed_i2c_slave_state slave_state;
#endif /* CONFIG_I2C_SLAVE */
};
static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus);
static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
{
unsigned long time_left, flags;
int ret = 0;
u32 command;
spin_lock_irqsave(&bus->lock, flags);
command = readl(bus->base + ASPEED_I2C_CMD_REG);
if (command & ASPEED_I2CD_SDA_LINE_STS) {
/* Bus is idle: no recovery needed. */
if (command & ASPEED_I2CD_SCL_LINE_STS)
goto out;
dev_dbg(bus->dev, "SCL hung (state %x), attempting recovery\n",
command);
reinit_completion(&bus->cmd_complete);
writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG);
spin_unlock_irqrestore(&bus->lock, flags);
time_left = wait_for_completion_timeout(
&bus->cmd_complete, bus->adap.timeout);
spin_lock_irqsave(&bus->lock, flags);
if (time_left == 0)
goto reset_out;
else if (bus->cmd_err)
goto reset_out;
/* Recovery failed. */
else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) &
ASPEED_I2CD_SCL_LINE_STS))
goto reset_out;
/* Bus error. */
} else {
dev_dbg(bus->dev, "SDA hung (state %x), attempting recovery\n",
command);
reinit_completion(&bus->cmd_complete);
/* Writes 1 to 8 SCL clock cycles until SDA is released. */
writel(ASPEED_I2CD_BUS_RECOVER_CMD,
bus->base + ASPEED_I2C_CMD_REG);
spin_unlock_irqrestore(&bus->lock, flags);
time_left = wait_for_completion_timeout(
&bus->cmd_complete, bus->adap.timeout);
spin_lock_irqsave(&bus->lock, flags);
if (time_left == 0)
goto reset_out;
else if (bus->cmd_err)
goto reset_out;
/* Recovery failed. */
else if (!(readl(bus->base + ASPEED_I2C_CMD_REG) &
ASPEED_I2CD_SDA_LINE_STS))
goto reset_out;
}
out:
spin_unlock_irqrestore(&bus->lock, flags);
return ret;
reset_out:
spin_unlock_irqrestore(&bus->lock, flags);
return aspeed_i2c_reset(bus);
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
{
u32 command, irq_status, status_ack = 0;
struct i2c_client *slave = bus->slave;
bool irq_handled = true;
u8 value;
spin_lock(&bus->lock);
if (!slave) {
irq_handled = false;
goto out;
}
command = readl(bus->base + ASPEED_I2C_CMD_REG);
irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
/* Slave was requested, restart state machine. */
if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
bus->slave_state = ASPEED_I2C_SLAVE_START;
}
/* Slave is not currently active, irq was for someone else. */
if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
irq_handled = false;
goto out;
}
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
irq_status, command);
/* Slave was sent something. */
if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
/* Handle address frame. */
if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
if (value & 0x1)
bus->slave_state =
ASPEED_I2C_SLAVE_READ_REQUESTED;
else
bus->slave_state =
ASPEED_I2C_SLAVE_WRITE_REQUESTED;
}
status_ack |= ASPEED_I2CD_INTR_RX_DONE;
}
/* Slave was asked to stop. */
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
}
if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
}
switch (bus->slave_state) {
case ASPEED_I2C_SLAVE_READ_REQUESTED:
if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
dev_err(bus->dev, "Unexpected ACK on read request.\n");
bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_READ_PROCESSED:
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
dev_err(bus->dev,
"Expected ACK after processed read.\n");
i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_WRITE_REQUESTED:
bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
break;
case ASPEED_I2C_SLAVE_WRITE_RECEIVED:
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
break;
case ASPEED_I2C_SLAVE_STOP:
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
break;
default:
dev_err(bus->dev, "unhandled slave_state: %d\n",
bus->slave_state);
break;
}
if (status_ack != irq_status)
dev_err(bus->dev,
"irq handled != irq. expected %x, but was %x\n",
irq_status, status_ack);
writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG);
out:
spin_unlock(&bus->lock);
return irq_handled;
}
#endif /* CONFIG_I2C_SLAVE */
/* precondition: bus.lock has been acquired. */
static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
{
u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
u8 slave_addr = msg->addr << 1;
bus->master_state = ASPEED_I2C_MASTER_START;
bus->buf_index = 0;
if (msg->flags & I2C_M_RD) {
slave_addr |= 1;
command |= ASPEED_I2CD_M_RX_CMD;
/* Need to let the hardware know to NACK after RX. */
if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
}
writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(command, bus->base + ASPEED_I2C_CMD_REG);
}
/* precondition: bus.lock has been acquired. */
static void aspeed_i2c_do_stop(struct aspeed_i2c_bus *bus)
{
bus->master_state = ASPEED_I2C_MASTER_STOP;
writel(ASPEED_I2CD_M_STOP_CMD, bus->base + ASPEED_I2C_CMD_REG);
}
/* precondition: bus.lock has been acquired. */
static void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus)
{
if (bus->msgs_index + 1 < bus->msgs_count) {
bus->msgs_index++;
aspeed_i2c_do_start(bus);
} else {
aspeed_i2c_do_stop(bus);
}
}
static int aspeed_i2c_is_irq_error(u32 irq_status)
{
if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS)
return -EAGAIN;
if (irq_status & (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
ASPEED_I2CD_INTR_SCL_TIMEOUT))
return -EBUSY;
if (irq_status & (ASPEED_I2CD_INTR_ABNORMAL))
return -EPROTO;
return 0;
}
static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
{
u32 irq_status, status_ack = 0, command = 0;
struct i2c_msg *msg;
u8 recv_byte;
int ret;
spin_lock(&bus->lock);
irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
/* Ack all interrupt bits. */
writel(irq_status, bus->base + ASPEED_I2C_INTR_STS_REG);
if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) {
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
status_ack |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE;
goto out_complete;
}
/*
* We encountered an interrupt that reports an error: the hardware
* should clear the command queue effectively taking us back to the
* INACTIVE state.
*/
ret = aspeed_i2c_is_irq_error(irq_status);
if (ret < 0) {
dev_dbg(bus->dev, "received error interrupt: 0x%08x",
irq_status);
bus->cmd_err = ret;
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
goto out_complete;
}
/* We are in an invalid state; reset bus to a known state. */
if (!bus->msgs && bus->master_state != ASPEED_I2C_MASTER_STOP) {
dev_err(bus->dev, "bus in unknown state");
bus->cmd_err = -EIO;
aspeed_i2c_do_stop(bus);
goto out_no_complete;
}
msg = &bus->msgs[bus->msgs_index];
/*
* START is a special case because we still have to handle a subsequent
* TX or RX immediately after we handle it, so we handle it here and
* then update the state and handle the new state below.
*/
if (bus->master_state == ASPEED_I2C_MASTER_START) {
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
pr_devel("no slave present at %02x", msg->addr);
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
bus->cmd_err = -ENXIO;
aspeed_i2c_do_stop(bus);
goto out_no_complete;
}
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
if (msg->len == 0) { /* SMBUS_QUICK */
aspeed_i2c_do_stop(bus);
goto out_no_complete;
}
if (msg->flags & I2C_M_RD)
bus->master_state = ASPEED_I2C_MASTER_RX_FIRST;
else
bus->master_state = ASPEED_I2C_MASTER_TX_FIRST;
}
switch (bus->master_state) {
case ASPEED_I2C_MASTER_TX:
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) {
dev_dbg(bus->dev, "slave NACKed TX");
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
goto error_and_stop;
} else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
dev_err(bus->dev, "slave failed to ACK TX");
goto error_and_stop;
}
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
/* fallthrough intended */
case ASPEED_I2C_MASTER_TX_FIRST:
if (bus->buf_index < msg->len) {
bus->master_state = ASPEED_I2C_MASTER_TX;
writel(msg->buf[bus->buf_index++],
bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(ASPEED_I2CD_M_TX_CMD,
bus->base + ASPEED_I2C_CMD_REG);
} else {
aspeed_i2c_next_msg_or_stop(bus);
}
goto out_no_complete;
case ASPEED_I2C_MASTER_RX_FIRST:
/* RX may not have completed yet (only address cycle) */
if (!(irq_status & ASPEED_I2CD_INTR_RX_DONE))
goto out_no_complete;
/* fallthrough intended */
case ASPEED_I2C_MASTER_RX:
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) {
dev_err(bus->dev, "master failed to RX");
goto error_and_stop;
}
status_ack |= ASPEED_I2CD_INTR_RX_DONE;
recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
msg->buf[bus->buf_index++] = recv_byte;
if (msg->flags & I2C_M_RECV_LEN) {
if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) {
bus->cmd_err = -EPROTO;
aspeed_i2c_do_stop(bus);
goto out_no_complete;
}
msg->len = recv_byte +
((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
msg->flags &= ~I2C_M_RECV_LEN;
}
if (bus->buf_index < msg->len) {
bus->master_state = ASPEED_I2C_MASTER_RX;
command = ASPEED_I2CD_M_RX_CMD;
if (bus->buf_index + 1 == msg->len)
command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
writel(command, bus->base + ASPEED_I2C_CMD_REG);
} else {
aspeed_i2c_next_msg_or_stop(bus);
}
goto out_no_complete;
case ASPEED_I2C_MASTER_STOP:
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) {
dev_err(bus->dev, "master failed to STOP");
bus->cmd_err = -EIO;
/* Do not STOP as we have already tried. */
} else {
status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
}
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
goto out_complete;
case ASPEED_I2C_MASTER_INACTIVE:
dev_err(bus->dev,
"master received interrupt 0x%08x, but is inactive",
irq_status);
bus->cmd_err = -EIO;
/* Do not STOP as we should be inactive. */
goto out_complete;
default:
WARN(1, "unknown master state\n");
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
bus->cmd_err = -EINVAL;
goto out_complete;
}
error_and_stop:
bus->cmd_err = -EIO;
aspeed_i2c_do_stop(bus);
goto out_no_complete;
out_complete:
bus->msgs = NULL;
if (bus->cmd_err)
bus->master_xfer_result = bus->cmd_err;
else
bus->master_xfer_result = bus->msgs_index + 1;
complete(&bus->cmd_complete);
out_no_complete:
if (irq_status != status_ack)
dev_err(bus->dev,
"irq handled != irq. expected 0x%08x, but was 0x%08x\n",
irq_status, status_ack);
spin_unlock(&bus->lock);
return !!irq_status;
}
static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
{
struct aspeed_i2c_bus *bus = dev_id;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
if (aspeed_i2c_slave_irq(bus)) {
dev_dbg(bus->dev, "irq handled by slave.\n");
return IRQ_HANDLED;
}
#endif /* CONFIG_I2C_SLAVE */
return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE;
}
static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
unsigned long time_left, flags;
int ret = 0;
spin_lock_irqsave(&bus->lock, flags);
bus->cmd_err = 0;
/* If bus is busy, attempt recovery. We assume a single master
* environment.
*/
if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
spin_unlock_irqrestore(&bus->lock, flags);
ret = aspeed_i2c_recover_bus(bus);
if (ret)
return ret;
spin_lock_irqsave(&bus->lock, flags);
}
bus->cmd_err = 0;
bus->msgs = msgs;
bus->msgs_index = 0;
bus->msgs_count = num;
reinit_completion(&bus->cmd_complete);
aspeed_i2c_do_start(bus);
spin_unlock_irqrestore(&bus->lock, flags);
time_left = wait_for_completion_timeout(&bus->cmd_complete,
bus->adap.timeout);
if (time_left == 0)
return -ETIMEDOUT;
else
return bus->master_xfer_result;
}
static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* precondition: bus.lock has been acquired. */
static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
{
u32 addr_reg_val, func_ctrl_reg_val;
/* Set slave addr. */
addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG);
addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG);
/* Turn on slave mode. */
func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
}
static int aspeed_i2c_reg_slave(struct i2c_client *client)
{
struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter);
unsigned long flags;
spin_lock_irqsave(&bus->lock, flags);
if (bus->slave) {
spin_unlock_irqrestore(&bus->lock, flags);
return -EINVAL;
}
__aspeed_i2c_reg_slave(bus, client->addr);
bus->slave = client;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
spin_unlock_irqrestore(&bus->lock, flags);
return 0;
}
static int aspeed_i2c_unreg_slave(struct i2c_client *client)
{
struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter);
u32 func_ctrl_reg_val;
unsigned long flags;
spin_lock_irqsave(&bus->lock, flags);
if (!bus->slave) {
spin_unlock_irqrestore(&bus->lock, flags);
return -EINVAL;
}
/* Turn off slave mode. */
func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
bus->slave = NULL;
spin_unlock_irqrestore(&bus->lock, flags);
return 0;
}
#endif /* CONFIG_I2C_SLAVE */
static const struct i2c_algorithm aspeed_i2c_algo = {
.master_xfer = aspeed_i2c_master_xfer,
.functionality = aspeed_i2c_functionality,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
.reg_slave = aspeed_i2c_reg_slave,
.unreg_slave = aspeed_i2c_unreg_slave,
#endif /* CONFIG_I2C_SLAVE */
};
static u32 aspeed_i2c_get_clk_reg_val(u32 divisor)
{
u32 base_clk, clk_high, clk_low, tmp;
/*
* The actual clock frequency of SCL is:
* SCL_freq = APB_freq / (base_freq * (SCL_high + SCL_low))
* = APB_freq / divisor
* where base_freq is a programmable clock divider; its value is
* base_freq = 1 << base_clk
* SCL_high is the number of base_freq clock cycles that SCL stays high
* and SCL_low is the number of base_freq clock cycles that SCL stays
* low for a period of SCL.
* The actual register has a minimum SCL_high and SCL_low minimum of 1;
* thus, they start counting at zero. So
* SCL_high = clk_high + 1
* SCL_low = clk_low + 1
* Thus,
* SCL_freq = APB_freq /
* ((1 << base_clk) * (clk_high + 1 + clk_low + 1))
* The documentation recommends clk_high >= 8 and clk_low >= 7 when
* possible; this last constraint gives us the following solution:
*/
base_clk = divisor > 33 ? ilog2((divisor - 1) / 32) + 1 : 0;
tmp = divisor / (1 << base_clk);
clk_high = tmp / 2 + tmp % 2;
clk_low = tmp - clk_high;
clk_high -= 1;
clk_low -= 1;
return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT)
& ASPEED_I2CD_TIME_SCL_HIGH_MASK)
| ((clk_low << ASPEED_I2CD_TIME_SCL_LOW_SHIFT)
& ASPEED_I2CD_TIME_SCL_LOW_MASK)
| (base_clk & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK);
}
/* precondition: bus.lock has been acquired. */
static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
{
u32 divisor, clk_reg_val;
divisor = bus->parent_clk_frequency / bus->bus_frequency;
clk_reg_val = aspeed_i2c_get_clk_reg_val(divisor);
writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1);
writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2);
return 0;
}
/* precondition: bus.lock has been acquired. */
static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
struct platform_device *pdev)
{
u32 fun_ctrl_reg = ASPEED_I2CD_MASTER_EN;
int ret;
/* Disable everything. */
writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG);
ret = aspeed_i2c_init_clk(bus);
if (ret < 0)
return ret;
if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
/* Enable Master Mode */
writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg,
bus->base + ASPEED_I2C_FUN_CTRL_REG);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* If slave has already been registered, re-enable it. */
if (bus->slave)
__aspeed_i2c_reg_slave(bus, bus->slave->addr);
#endif /* CONFIG_I2C_SLAVE */
/* Set interrupt generation of I2C controller */
writel(ASPEED_I2CD_INTR_ALL, bus->base + ASPEED_I2C_INTR_CTRL_REG);
return 0;
}
static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus)
{
struct platform_device *pdev = to_platform_device(bus->dev);
unsigned long flags;
int ret;
spin_lock_irqsave(&bus->lock, flags);
/* Disable and ack all interrupts. */
writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG);
writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG);
ret = aspeed_i2c_init(bus, pdev);
spin_unlock_irqrestore(&bus->lock, flags);
return ret;
}
static int aspeed_i2c_probe_bus(struct platform_device *pdev)
{
struct aspeed_i2c_bus *bus;
struct clk *parent_clk;
struct resource *res;
int irq, ret;
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bus->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(bus->base))
return PTR_ERR(bus->base);
parent_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(parent_clk))
return PTR_ERR(parent_clk);
bus->parent_clk_frequency = clk_get_rate(parent_clk);
/* We just need the clock rate, we don't actually use the clk object. */
devm_clk_put(&pdev->dev, parent_clk);
ret = of_property_read_u32(pdev->dev.of_node,
"bus-frequency", &bus->bus_frequency);
if (ret < 0) {
dev_err(&pdev->dev,
"Could not read bus-frequency property\n");
bus->bus_frequency = 100000;
}
/* Initialize the I2C adapter */
spin_lock_init(&bus->lock);
init_completion(&bus->cmd_complete);
bus->adap.owner = THIS_MODULE;
bus->adap.retries = 0;
bus->adap.timeout = 5 * HZ;
bus->adap.algo = &aspeed_i2c_algo;
bus->adap.dev.parent = &pdev->dev;
bus->adap.dev.of_node = pdev->dev.of_node;
strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name));
i2c_set_adapdata(&bus->adap, bus);
bus->dev = &pdev->dev;
/* Clean up any left over interrupt state. */
writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG);
writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG);
/*
* bus.lock does not need to be held because the interrupt handler has
* not been enabled yet.
*/
ret = aspeed_i2c_init(bus, pdev);
if (ret < 0)
return ret;
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq,
0, dev_name(&pdev->dev), bus);
if (ret < 0)
return ret;
ret = i2c_add_adapter(&bus->adap);
if (ret < 0)
return ret;
platform_set_drvdata(pdev, bus);
dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
bus->adap.nr, irq);
return 0;
}
static int aspeed_i2c_remove_bus(struct platform_device *pdev)
{
struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
unsigned long flags;
spin_lock_irqsave(&bus->lock, flags);
/* Disable everything. */
writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG);
writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG);
spin_unlock_irqrestore(&bus->lock, flags);
i2c_del_adapter(&bus->adap);
return 0;
}
static const struct of_device_id aspeed_i2c_bus_of_table[] = {
{ .compatible = "aspeed,ast2400-i2c-bus", },
{ .compatible = "aspeed,ast2500-i2c-bus", },
{ },
};
MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
static struct platform_driver aspeed_i2c_bus_driver = {
.probe = aspeed_i2c_probe_bus,
.remove = aspeed_i2c_remove_bus,
.driver = {
.name = "aspeed-i2c-bus",
.of_match_table = aspeed_i2c_bus_of_table,
},
};
module_platform_driver(aspeed_i2c_bus_driver);
MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
MODULE_LICENSE("GPL v2");

View File

@ -274,7 +274,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
if (!dev->use_alt_cmd) if (!dev->use_alt_cmd)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len); dev_dbg(dev->dev, "wrote 0x%x, to go %zu\n", *dev->buf, dev->buf_len);
++dev->buf; ++dev->buf;
} }
@ -402,7 +402,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
dev->msg->flags &= ~I2C_M_RECV_LEN; dev->msg->flags &= ~I2C_M_RECV_LEN;
dev->buf_len += *dev->buf; dev->buf_len += *dev->buf;
dev->msg->len = dev->buf_len + 1; dev->msg->len = dev->buf_len + 1;
dev_dbg(dev->dev, "received block length %d\n", dev_dbg(dev->dev, "received block length %zu\n",
dev->buf_len); dev->buf_len);
} else { } else {
/* abort and send the stop by reading one more byte */ /* abort and send the stop by reading one more byte */
@ -415,7 +415,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
if (!dev->use_alt_cmd && dev->buf_len == 1) if (!dev->use_alt_cmd && dev->buf_len == 1)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len); dev_dbg(dev->dev, "read 0x%x, to go %zu\n", *dev->buf, dev->buf_len);
++dev->buf; ++dev->buf;
} }
@ -622,7 +622,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
* writing the corresponding bit into the Control Register. * writing the corresponding bit into the Control Register.
*/ */
dev_dbg(dev->dev, "transfer: %s %d bytes.\n", dev_dbg(dev->dev, "transfer: %s %zu bytes.\n",
(dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len); (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
reinit_completion(&dev->cmd_complete); reinit_completion(&dev->cmd_complete);
@ -1083,12 +1083,16 @@ static int at91_twi_probe(struct platform_device *pdev)
dev_err(dev->dev, "no clock defined\n"); dev_err(dev->dev, "no clock defined\n");
return -ENODEV; return -ENODEV;
} }
clk_prepare_enable(dev->clk); rc = clk_prepare_enable(dev->clk);
if (rc)
return rc;
if (dev->dev->of_node) { if (dev->dev->of_node) {
rc = at91_twi_configure_dma(dev, phy_addr); rc = at91_twi_configure_dma(dev, phy_addr);
if (rc == -EPROBE_DEFER) if (rc == -EPROBE_DEFER) {
clk_disable_unprepare(dev->clk);
return rc; return rc;
}
} }
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",

View File

@ -405,14 +405,14 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
} }
/* Set the slave address in address register - triggers operation */
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
/* Clear the bus hold flag if bytes to receive is less than FIFO size */ /* Clear the bus hold flag if bytes to receive is less than FIFO size */
if (!id->bus_hold_flag && if (!id->bus_hold_flag &&
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
(id->recv_count <= CDNS_I2C_FIFO_DEPTH)) (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
cdns_i2c_clear_bus_hold(id); 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_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
} }

View File

@ -0,0 +1,281 @@
/*
* Synopsys DesignWare I2C adapter driver.
*
* Based on the TI DAVINCI I2C adapter driver.
*
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*
* ----------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ----------------------------------------------------------------------------
*
*/
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include "i2c-designware-core.h"
static char *abort_sources[] = {
[ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
[ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
[ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] =
"data not acknowledged",
[ABRT_GCALL_NOACK] =
"no acknowledgement for a general call",
[ABRT_GCALL_READ] =
"read after general call",
[ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
[ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
[ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
[ABRT_MASTER_DIS] =
"trying to use disabled adapter",
[ARB_LOST] =
"lost arbitration",
[ABRT_SLAVE_FLUSH_TXFIFO] =
"read command so flush old data in the TX FIFO",
[ABRT_SLAVE_ARBLOST] =
"slave lost the bus while transmitting data to a remote master",
[ABRT_SLAVE_RD_INTX] =
"incorrect slave-transmitter mode configuration",
};
u32 dw_readl(struct dw_i2c_dev *dev, int offset)
{
u32 value;
if (dev->flags & ACCESS_16BIT)
value = readw_relaxed(dev->base + offset) |
(readw_relaxed(dev->base + offset + 2) << 16);
else
value = readl_relaxed(dev->base + offset);
if (dev->flags & ACCESS_SWAP)
return swab32(value);
else
return value;
}
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
{
if (dev->flags & ACCESS_SWAP)
b = swab32(b);
if (dev->flags & ACCESS_16BIT) {
writew_relaxed((u16)b, dev->base + offset);
writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
} else {
writel_relaxed(b, dev->base + offset);
}
}
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
{
/*
* DesignWare I2C core doesn't seem to have solid strategy to meet
* the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
* will result in violation of the tHD;STA spec.
*/
if (cond)
/*
* Conditional expression:
*
* IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
*
* This is based on the DW manuals, and represents an ideal
* configuration. The resulting I2C bus speed will be
* faster than any of the others.
*
* If your hardware is free from tHD;STA issue, try this one.
*/
return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
else
/*
* Conditional expression:
*
* IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
*
* This is just experimental rule; the tHD;STA period turned
* out to be proportinal to (_HCNT + 3). With this setting,
* we could meet both tHIGH and tHD;STA timing specs.
*
* If unsure, you'd better to take this alternative.
*
* 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) + 500000) / 1000000
- 3 + offset;
}
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
{
/*
* Conditional expression:
*
* IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
*
* DW I2C core starts counting the SCL CNTs for the LOW period
* of the SCL clock (tLOW) as soon as it pulls the SCL line.
* In order to meet the tLOW timing spec, we need to take into
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}
void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
{
dw_writel(dev, enable, DW_IC_ENABLE);
}
void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable)
{
int timeout = 100;
do {
__i2c_dw_enable(dev, enable);
if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable)
return;
/*
* Wait 10 times the signaling period of the highest I2C
* transfer supported by the driver (for 400KHz this is
* 25us) as described in the DesignWare I2C databook.
*/
usleep_range(25, 250);
} while (timeout--);
dev_warn(dev->dev, "timeout in %sabling adapter\n",
enable ? "en" : "dis");
}
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
{
/*
* Clock is not necessary if we got LCNT/HCNT values directly from
* the platform code.
*/
if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
return 0;
return dev->get_clk_rate_khz(dev);
}
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev)
{
int ret;
if (!dev->acquire_lock)
return 0;
ret = dev->acquire_lock(dev);
if (!ret)
return 0;
dev_err(dev->dev, "couldn't acquire bus ownership\n");
return ret;
}
void i2c_dw_release_lock(struct dw_i2c_dev *dev)
{
if (dev->release_lock)
dev->release_lock(dev);
}
/*
* Waiting for bus not busy
*/
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
}
timeout--;
usleep_range(1000, 1100);
}
return 0;
}
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
{
unsigned long abort_source = dev->abort_source;
int i;
if (abort_source & DW_IC_TX_ABRT_NOACK) {
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_dbg(dev->dev,
"%s: %s\n", __func__, abort_sources[i]);
return -EREMOTEIO;
}
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
if (abort_source & DW_IC_TX_ARB_LOST)
return -EAGAIN;
else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
return -EINVAL; /* wrong msgs[] data */
else
return -EIO;
}
u32 i2c_dw_func(struct i2c_adapter *adap)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
return dev->functionality;
}
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
/* Disable controller */
__i2c_dw_enable_and_wait(dev, false);
/* Disable all interupts */
dw_writel(dev, 0, DW_IC_INTR_MASK);
dw_readl(dev, DW_IC_CLR_INTR);
}
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
{
dw_writel(dev, 0, DW_IC_INTR_MASK);
}
u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
{
return dw_readl(dev, DW_IC_COMP_PARAM_1);
}
EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
MODULE_LICENSE("GPL");

View File

@ -1,5 +1,5 @@
/* /*
* Synopsys DesignWare I2C adapter driver (master only). * Synopsys DesignWare I2C adapter driver.
* *
* Based on the TI DAVINCI I2C adapter driver. * Based on the TI DAVINCI I2C adapter driver.
* *
@ -37,9 +37,152 @@
#define DW_IC_CON_SPEED_FAST 0x4 #define DW_IC_CON_SPEED_FAST 0x4
#define DW_IC_CON_SPEED_HIGH 0x6 #define DW_IC_CON_SPEED_HIGH 0x6
#define DW_IC_CON_SPEED_MASK 0x6 #define DW_IC_CON_SPEED_MASK 0x6
#define DW_IC_CON_10BITADDR_SLAVE 0x8
#define DW_IC_CON_10BITADDR_MASTER 0x10 #define DW_IC_CON_10BITADDR_MASTER 0x10
#define DW_IC_CON_RESTART_EN 0x20 #define DW_IC_CON_RESTART_EN 0x20
#define DW_IC_CON_SLAVE_DISABLE 0x40 #define DW_IC_CON_SLAVE_DISABLE 0x40
#define DW_IC_CON_STOP_DET_IFADDRESSED 0x80
#define DW_IC_CON_TX_EMPTY_CTRL 0x100
#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
/*
* Registers offset
*/
#define DW_IC_CON 0x0
#define DW_IC_TAR 0x4
#define DW_IC_SAR 0x8
#define DW_IC_DATA_CMD 0x10
#define DW_IC_SS_SCL_HCNT 0x14
#define DW_IC_SS_SCL_LCNT 0x18
#define DW_IC_FS_SCL_HCNT 0x1c
#define DW_IC_FS_SCL_LCNT 0x20
#define DW_IC_HS_SCL_HCNT 0x24
#define DW_IC_HS_SCL_LCNT 0x28
#define DW_IC_INTR_STAT 0x2c
#define DW_IC_INTR_MASK 0x30
#define DW_IC_RAW_INTR_STAT 0x34
#define DW_IC_RX_TL 0x38
#define DW_IC_TX_TL 0x3c
#define DW_IC_CLR_INTR 0x40
#define DW_IC_CLR_RX_UNDER 0x44
#define DW_IC_CLR_RX_OVER 0x48
#define DW_IC_CLR_TX_OVER 0x4c
#define DW_IC_CLR_RD_REQ 0x50
#define DW_IC_CLR_TX_ABRT 0x54
#define DW_IC_CLR_RX_DONE 0x58
#define DW_IC_CLR_ACTIVITY 0x5c
#define DW_IC_CLR_STOP_DET 0x60
#define DW_IC_CLR_START_DET 0x64
#define DW_IC_CLR_GEN_CALL 0x68
#define DW_IC_ENABLE 0x6c
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
#define DW_IC_RXFLR 0x78
#define DW_IC_SDA_HOLD 0x7c
#define DW_IC_TX_ABRT_SOURCE 0x80
#define DW_IC_ENABLE_STATUS 0x9c
#define DW_IC_CLR_RESTART_DET 0xa8
#define DW_IC_COMP_PARAM_1 0xf4
#define DW_IC_COMP_VERSION 0xf8
#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A
#define DW_IC_COMP_TYPE 0xfc
#define DW_IC_COMP_TYPE_VALUE 0x44570140
#define DW_IC_INTR_RX_UNDER 0x001
#define DW_IC_INTR_RX_OVER 0x002
#define DW_IC_INTR_RX_FULL 0x004
#define DW_IC_INTR_TX_OVER 0x008
#define DW_IC_INTR_TX_EMPTY 0x010
#define DW_IC_INTR_RD_REQ 0x020
#define DW_IC_INTR_TX_ABRT 0x040
#define DW_IC_INTR_RX_DONE 0x080
#define DW_IC_INTR_ACTIVITY 0x100
#define DW_IC_INTR_STOP_DET 0x200
#define DW_IC_INTR_START_DET 0x400
#define DW_IC_INTR_GEN_CALL 0x800
#define DW_IC_INTR_RESTART_DET 0x1000
#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
DW_IC_INTR_TX_ABRT | \
DW_IC_INTR_STOP_DET)
#define DW_IC_INTR_MASTER_MASK (DW_IC_INTR_DEFAULT_MASK | \
DW_IC_INTR_TX_EMPTY)
#define DW_IC_INTR_SLAVE_MASK (DW_IC_INTR_DEFAULT_MASK | \
DW_IC_INTR_RX_DONE | \
DW_IC_INTR_RX_UNDER | \
DW_IC_INTR_RD_REQ)
#define DW_IC_STATUS_ACTIVITY 0x1
#define DW_IC_STATUS_TFE BIT(2)
#define DW_IC_STATUS_MASTER_ACTIVITY BIT(5)
#define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6)
#define DW_IC_SDA_HOLD_RX_SHIFT 16
#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT)
#define DW_IC_ERR_TX_ABRT 0x1
#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2)
/*
* status codes
*/
#define STATUS_IDLE 0x0
#define STATUS_WRITE_IN_PROGRESS 0x1
#define STATUS_READ_IN_PROGRESS 0x2
#define TIMEOUT 20 /* ms */
/*
* operation modes
*/
#define DW_IC_MASTER 0
#define DW_IC_SLAVE 1
/*
* Hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
*
* Only expected abort codes are listed here
* refer to the datasheet for the full list
*/
#define ABRT_7B_ADDR_NOACK 0
#define ABRT_10ADDR1_NOACK 1
#define ABRT_10ADDR2_NOACK 2
#define ABRT_TXDATA_NOACK 3
#define ABRT_GCALL_NOACK 4
#define ABRT_GCALL_READ 5
#define ABRT_SBYTE_ACKDET 7
#define ABRT_SBYTE_NORSTRT 9
#define ABRT_10B_RD_NORSTRT 10
#define ABRT_MASTER_DIS 11
#define ARB_LOST 12
#define ABRT_SLAVE_FLUSH_TXFIFO 13
#define ABRT_SLAVE_ARBLOST 14
#define ABRT_SLAVE_RD_INTX 15
#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK)
#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK)
#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK)
#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ)
#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET)
#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT)
#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
#define DW_IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX)
#define DW_IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST)
#define DW_IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO)
#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
DW_IC_TX_ABRT_10ADDR1_NOACK | \
DW_IC_TX_ABRT_10ADDR2_NOACK | \
DW_IC_TX_ABRT_TXDATA_NOACK | \
DW_IC_TX_ABRT_GCALL_NOACK)
/** /**
@ -48,8 +191,9 @@
* @base: IO registers pointer * @base: IO registers pointer
* @cmd_complete: tx completion indicator * @cmd_complete: tx completion indicator
* @clk: input reference clock * @clk: input reference clock
* @slave: represent an I2C slave device
* @cmd_err: run time hadware error code * @cmd_err: run time hadware error code
* @msgs: points to an array of messages currently being transfered * @msgs: points to an array of messages currently being transferred
* @msgs_num: the number of elements in msgs * @msgs_num: the number of elements in msgs
* @msg_write_idx: the element index of the current tx message in the msgs * @msg_write_idx: the element index of the current tx message in the msgs
* array * array
@ -64,6 +208,7 @@
* @abort_source: copy of the TX_ABRT_SOURCE register * @abort_source: copy of the TX_ABRT_SOURCE register
* @irq: interrupt number for the i2c master * @irq: interrupt number for the i2c master
* @adapter: i2c subsystem adapter node * @adapter: i2c subsystem adapter node
* @slave_cfg: configuration for the slave device
* @tx_fifo_depth: depth of the hardware tx fifo * @tx_fifo_depth: depth of the hardware tx fifo
* @rx_fifo_depth: depth of the hardware rx fifo * @rx_fifo_depth: depth of the hardware rx fifo
* @rx_outstanding: current master-rx elements in tx fifo * @rx_outstanding: current master-rx elements in tx fifo
@ -80,6 +225,10 @@
* @acquire_lock: function to acquire a hardware lock on the bus * @acquire_lock: function to acquire a hardware lock on the bus
* @release_lock: function to release a hardware lock on the bus * @release_lock: function to release a hardware lock on the bus
* @pm_disabled: true if power-management should be disabled for this i2c-bus * @pm_disabled: true if power-management should be disabled for this i2c-bus
* @disable: function to disable the controller
* @disable_int: function to disable all interrupts
* @init: function to initialize the I2C hardware
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
* *
* HCNT and LCNT parameters can be used if the platform knows more accurate * HCNT and LCNT parameters can be used if the platform knows more accurate
* values than the one computed based only on the input clock frequency. * values than the one computed based only on the input clock frequency.
@ -91,6 +240,7 @@ struct dw_i2c_dev {
struct completion cmd_complete; struct completion cmd_complete;
struct clk *clk; struct clk *clk;
struct reset_control *rst; struct reset_control *rst;
struct i2c_client *slave;
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
struct dw_pci_controller *controller; struct dw_pci_controller *controller;
int cmd_err; int cmd_err;
@ -110,6 +260,7 @@ struct dw_i2c_dev {
struct i2c_adapter adapter; struct i2c_adapter adapter;
u32 functionality; u32 functionality;
u32 master_cfg; u32 master_cfg;
u32 slave_cfg;
unsigned int tx_fifo_depth; unsigned int tx_fifo_depth;
unsigned int rx_fifo_depth; unsigned int rx_fifo_depth;
int rx_outstanding; int rx_outstanding;
@ -129,6 +280,10 @@ struct dw_i2c_dev {
int (*acquire_lock)(struct dw_i2c_dev *dev); int (*acquire_lock)(struct dw_i2c_dev *dev);
void (*release_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev);
bool pm_disabled; bool pm_disabled;
void (*disable)(struct dw_i2c_dev *dev);
void (*disable_int)(struct dw_i2c_dev *dev);
int (*init)(struct dw_i2c_dev *dev);
int mode;
}; };
#define ACCESS_SWAP 0x00000001 #define ACCESS_SWAP 0x00000001
@ -137,11 +292,28 @@ struct dw_i2c_dev {
#define MODEL_CHERRYTRAIL 0x00000100 #define MODEL_CHERRYTRAIL 0x00000100
extern int i2c_dw_init(struct dw_i2c_dev *dev); u32 dw_readl(struct dw_i2c_dev *dev, int offset);
extern void i2c_dw_disable(struct dw_i2c_dev *dev); void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable);
void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable);
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
void i2c_dw_release_lock(struct dw_i2c_dev *dev);
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev);
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
u32 i2c_dw_func(struct i2c_adapter *adap);
void i2c_dw_disable(struct dw_i2c_dev *dev);
void i2c_dw_disable_int(struct dw_i2c_dev *dev);
extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev); extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
extern int i2c_dw_probe(struct dw_i2c_dev *dev); extern int i2c_dw_probe(struct dw_i2c_dev *dev);
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_SLAVE)
extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev);
#else
static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; }
#endif
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev); extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev);

View File

@ -21,311 +21,37 @@
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* *
*/ */
#include <linux/export.h> #include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h>
#include "i2c-designware-core.h" #include "i2c-designware-core.h"
/* static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
* Registers offset
*/
#define DW_IC_CON 0x0
#define DW_IC_TAR 0x4
#define DW_IC_DATA_CMD 0x10
#define DW_IC_SS_SCL_HCNT 0x14
#define DW_IC_SS_SCL_LCNT 0x18
#define DW_IC_FS_SCL_HCNT 0x1c
#define DW_IC_FS_SCL_LCNT 0x20
#define DW_IC_HS_SCL_HCNT 0x24
#define DW_IC_HS_SCL_LCNT 0x28
#define DW_IC_INTR_STAT 0x2c
#define DW_IC_INTR_MASK 0x30
#define DW_IC_RAW_INTR_STAT 0x34
#define DW_IC_RX_TL 0x38
#define DW_IC_TX_TL 0x3c
#define DW_IC_CLR_INTR 0x40
#define DW_IC_CLR_RX_UNDER 0x44
#define DW_IC_CLR_RX_OVER 0x48
#define DW_IC_CLR_TX_OVER 0x4c
#define DW_IC_CLR_RD_REQ 0x50
#define DW_IC_CLR_TX_ABRT 0x54
#define DW_IC_CLR_RX_DONE 0x58
#define DW_IC_CLR_ACTIVITY 0x5c
#define DW_IC_CLR_STOP_DET 0x60
#define DW_IC_CLR_START_DET 0x64
#define DW_IC_CLR_GEN_CALL 0x68
#define DW_IC_ENABLE 0x6c
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
#define DW_IC_RXFLR 0x78
#define DW_IC_SDA_HOLD 0x7c
#define DW_IC_TX_ABRT_SOURCE 0x80
#define DW_IC_ENABLE_STATUS 0x9c
#define DW_IC_COMP_PARAM_1 0xf4
#define DW_IC_COMP_VERSION 0xf8
#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A
#define DW_IC_COMP_TYPE 0xfc
#define DW_IC_COMP_TYPE_VALUE 0x44570140
#define DW_IC_INTR_RX_UNDER 0x001
#define DW_IC_INTR_RX_OVER 0x002
#define DW_IC_INTR_RX_FULL 0x004
#define DW_IC_INTR_TX_OVER 0x008
#define DW_IC_INTR_TX_EMPTY 0x010
#define DW_IC_INTR_RD_REQ 0x020
#define DW_IC_INTR_TX_ABRT 0x040
#define DW_IC_INTR_RX_DONE 0x080
#define DW_IC_INTR_ACTIVITY 0x100
#define DW_IC_INTR_STOP_DET 0x200
#define DW_IC_INTR_START_DET 0x400
#define DW_IC_INTR_GEN_CALL 0x800
#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
DW_IC_INTR_TX_EMPTY | \
DW_IC_INTR_TX_ABRT | \
DW_IC_INTR_STOP_DET)
#define DW_IC_STATUS_ACTIVITY 0x1
#define DW_IC_SDA_HOLD_RX_SHIFT 16
#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT)
#define DW_IC_ERR_TX_ABRT 0x1
#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2)
/*
* status codes
*/
#define STATUS_IDLE 0x0
#define STATUS_WRITE_IN_PROGRESS 0x1
#define STATUS_READ_IN_PROGRESS 0x2
#define TIMEOUT 20 /* ms */
/*
* hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
*
* only expected abort codes are listed here
* refer to the datasheet for the full list
*/
#define ABRT_7B_ADDR_NOACK 0
#define ABRT_10ADDR1_NOACK 1
#define ABRT_10ADDR2_NOACK 2
#define ABRT_TXDATA_NOACK 3
#define ABRT_GCALL_NOACK 4
#define ABRT_GCALL_READ 5
#define ABRT_SBYTE_ACKDET 7
#define ABRT_SBYTE_NORSTRT 9
#define ABRT_10B_RD_NORSTRT 10
#define ABRT_MASTER_DIS 11
#define ARB_LOST 12
#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK)
#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK)
#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK)
#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ)
#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET)
#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT)
#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
DW_IC_TX_ABRT_10ADDR1_NOACK | \
DW_IC_TX_ABRT_10ADDR2_NOACK | \
DW_IC_TX_ABRT_TXDATA_NOACK | \
DW_IC_TX_ABRT_GCALL_NOACK)
static char *abort_sources[] = {
[ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
[ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
[ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] =
"data not acknowledged",
[ABRT_GCALL_NOACK] =
"no acknowledgement for a general call",
[ABRT_GCALL_READ] =
"read after general call",
[ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
[ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
[ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
[ABRT_MASTER_DIS] =
"trying to use disabled adapter",
[ARB_LOST] =
"lost arbitration",
};
static u32 dw_readl(struct dw_i2c_dev *dev, int offset)
{ {
u32 value; /* Configure Tx/Rx FIFO threshold levels */
dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);
if (dev->flags & ACCESS_16BIT) /* Configure the I2C master */
value = readw_relaxed(dev->base + offset) | dw_writel(dev, dev->master_cfg, DW_IC_CON);
(readw_relaxed(dev->base + offset + 2) << 16);
else
value = readl_relaxed(dev->base + offset);
if (dev->flags & ACCESS_SWAP)
return swab32(value);
else
return value;
}
static void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
{
if (dev->flags & ACCESS_SWAP)
b = swab32(b);
if (dev->flags & ACCESS_16BIT) {
writew_relaxed((u16)b, dev->base + offset);
writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
} else {
writel_relaxed(b, dev->base + offset);
}
}
static u32
i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
{
/*
* DesignWare I2C core doesn't seem to have solid strategy to meet
* the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
* will result in violation of the tHD;STA spec.
*/
if (cond)
/*
* Conditional expression:
*
* IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
*
* This is based on the DW manuals, and represents an ideal
* configuration. The resulting I2C bus speed will be
* faster than any of the others.
*
* If your hardware is free from tHD;STA issue, try this one.
*/
return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
else
/*
* Conditional expression:
*
* IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
*
* This is just experimental rule; the tHD;STA period turned
* out to be proportinal to (_HCNT + 3). With this setting,
* we could meet both tHIGH and tHD;STA timing specs.
*
* If unsure, you'd better to take this alternative.
*
* 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) + 500000) / 1000000
- 3 + offset;
}
static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
{
/*
* Conditional expression:
*
* IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
*
* DW I2C core starts counting the SCL CNTs for the LOW period
* of the SCL clock (tLOW) as soon as it pulls the SCL line.
* In order to meet the tLOW timing spec, we need to take into
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}
static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
{
dw_writel(dev, enable, DW_IC_ENABLE);
}
static void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable)
{
int timeout = 100;
do {
__i2c_dw_enable(dev, enable);
if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable)
return;
/*
* Wait 10 times the signaling period of the highest I2C
* transfer supported by the driver (for 400KHz this is
* 25us) as described in the DesignWare I2C databook.
*/
usleep_range(25, 250);
} while (timeout--);
dev_warn(dev->dev, "timeout in %sabling adapter\n",
enable ? "en" : "dis");
}
static unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
{
/*
* Clock is not necessary if we got LCNT/HCNT values directly from
* the platform code.
*/
if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
return 0;
return dev->get_clk_rate_khz(dev);
}
static int i2c_dw_acquire_lock(struct dw_i2c_dev *dev)
{
int ret;
if (!dev->acquire_lock)
return 0;
ret = dev->acquire_lock(dev);
if (!ret)
return 0;
dev_err(dev->dev, "couldn't acquire bus ownership\n");
return ret;
}
static void i2c_dw_release_lock(struct dw_i2c_dev *dev)
{
if (dev->release_lock)
dev->release_lock(dev);
} }
/** /**
* i2c_dw_init() - initialize the designware i2c master hardware * i2c_dw_init() - Initialize the designware I2C master hardware
* @dev: device private data * @dev: device private data
* *
* This functions configures and enables the I2C master. * This functions configures and enables the I2C master.
* This function is called during I2C init function, and in case of timeout at * This function is called during I2C init function, and in case of timeout at
* run time. * run time.
*/ */
int i2c_dw_init(struct dw_i2c_dev *dev) static int i2c_dw_init_master(struct dw_i2c_dev *dev)
{ {
u32 hcnt, lcnt; u32 hcnt, lcnt;
u32 reg, comp_param1; u32 reg, comp_param1;
@ -344,8 +70,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
/* Configure register access mode 16bit */ /* Configure register access mode 16bit */
dev->flags |= ACCESS_16BIT; dev->flags |= ACCESS_16BIT;
} else if (reg != DW_IC_COMP_TYPE_VALUE) { } else if (reg != DW_IC_COMP_TYPE_VALUE) {
dev_err(dev->dev, "Unknown Synopsys component type: " dev_err(dev->dev,
"0x%08x\n", reg); "Unknown Synopsys component type: 0x%08x\n", reg);
i2c_dw_release_lock(dev); i2c_dw_release_lock(dev);
return -ENODEV; return -ENODEV;
} }
@ -355,7 +81,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
/* Disable the adapter */ /* Disable the adapter */
__i2c_dw_enable_and_wait(dev, false); __i2c_dw_enable_and_wait(dev, false);
/* set standard and fast speed deviders for high/low periods */ /* Set standard and fast speed deviders for high/low periods */
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */ sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */ scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
@ -440,37 +166,11 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
"Hardware too old to adjust SDA hold time.\n"); "Hardware too old to adjust SDA hold time.\n");
} }
/* Configure Tx/Rx FIFO threshold levels */ i2c_dw_configure_fifo_master(dev);
dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);
/* configure the i2c master */
dw_writel(dev, dev->master_cfg , DW_IC_CON);
i2c_dw_release_lock(dev); i2c_dw_release_lock(dev);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(i2c_dw_init);
/*
* Waiting for bus not busy
*/
static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
}
timeout--;
usleep_range(1000, 1100);
}
return 0;
}
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{ {
@ -480,7 +180,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* Disable the adapter */ /* Disable the adapter */
__i2c_dw_enable_and_wait(dev, false); __i2c_dw_enable_and_wait(dev, false);
/* if the slave address is ten bit address, enable 10BITADDR */ /* If the slave address is ten bit address, enable 10BITADDR */
ic_con = dw_readl(dev, DW_IC_CON); ic_con = dw_readl(dev, DW_IC_CON);
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
ic_con |= DW_IC_CON_10BITADDR_MASTER; ic_con |= DW_IC_CON_10BITADDR_MASTER;
@ -503,7 +203,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
*/ */
dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR); dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
/* enforce disabled interrupts (due to HW issues) */ /* Enforce disabled interrupts (due to HW issues) */
i2c_dw_disable_int(dev); i2c_dw_disable_int(dev);
/* Enable the adapter */ /* Enable the adapter */
@ -511,7 +211,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* Clear and enable interrupts */ /* Clear and enable interrupts */
dw_readl(dev, DW_IC_CLR_INTR); dw_readl(dev, DW_IC_CLR_INTR);
dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK); dw_writel(dev, DW_IC_INTR_MASTER_MASK, DW_IC_INTR_MASK);
} }
/* /*
@ -531,15 +231,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
u8 *buf = dev->tx_buf; u8 *buf = dev->tx_buf;
bool need_restart = false; bool need_restart = false;
intr_mask = DW_IC_INTR_DEFAULT_MASK; intr_mask = DW_IC_INTR_MASTER_MASK;
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
u32 flags = msgs[dev->msg_write_idx].flags; u32 flags = msgs[dev->msg_write_idx].flags;
/* /*
* if target address has changed, we need to * If target address has changed, we need to
* reprogram the target address in the i2c * reprogram the target address in the I2C
* adapter when we are done with this transfer * adapter when we are done with this transfer.
*/ */
if (msgs[dev->msg_write_idx].addr != addr) { if (msgs[dev->msg_write_idx].addr != addr) {
dev_err(dev->dev, dev_err(dev->dev,
@ -583,7 +283,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
*/ */
/* /*
* i2c-core.c always sets the buffer length of * i2c-core always sets the buffer length of
* I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will * I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will
* be adjusted when receiving the first byte. * be adjusted when receiving the first byte.
* Thus we can't stop the transaction here. * Thus we can't stop the transaction here.
@ -599,7 +299,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
/* avoid rx buffer overrun */ /* Avoid rx buffer overrun */
if (dev->rx_outstanding >= dev->rx_fifo_depth) if (dev->rx_outstanding >= dev->rx_fifo_depth)
break; break;
@ -704,31 +404,8 @@ i2c_dw_read(struct dw_i2c_dev *dev)
} }
} }
static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
{
unsigned long abort_source = dev->abort_source;
int i;
if (abort_source & DW_IC_TX_ABRT_NOACK) {
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_dbg(dev->dev,
"%s: %s\n", __func__, abort_sources[i]);
return -EREMOTEIO;
}
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
if (abort_source & DW_IC_TX_ARB_LOST)
return -EAGAIN;
else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
return -EINVAL; /* wrong msgs[] data */
else
return -EIO;
}
/* /*
* Prepare controller for a transaction and call i2c_dw_xfer_msg * Prepare controller for a transaction and call i2c_dw_xfer_msg.
*/ */
static int static int
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
@ -759,14 +436,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (ret < 0) if (ret < 0)
goto done; goto done;
/* start the transfers */ /* Start the transfers */
i2c_dw_xfer_init(dev); i2c_dw_xfer_init(dev);
/* wait for tx to complete */ /* Wait for tx to complete */
if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
dev_err(dev->dev, "controller timed out\n"); dev_err(dev->dev, "controller timed out\n");
/* i2c_dw_init implicitly disables the adapter */ /* i2c_dw_init implicitly disables the adapter */
i2c_dw_init(dev); i2c_dw_init_master(dev);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto done; goto done;
} }
@ -786,7 +463,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
goto done; goto done;
} }
/* no error */ /* No error */
if (likely(!dev->cmd_err && !dev->status)) { if (likely(!dev->cmd_err && !dev->status)) {
ret = num; ret = num;
goto done; goto done;
@ -814,15 +491,9 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
return ret; return ret;
} }
static u32 i2c_dw_func(struct i2c_adapter *adap)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
return dev->functionality;
}
static const struct i2c_algorithm i2c_dw_algo = { static const struct i2c_algorithm i2c_dw_algo = {
.master_xfer = i2c_dw_xfer, .master_xfer = i2c_dw_xfer,
.functionality = i2c_dw_func, .functionality = i2c_dw_func,
}; };
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
@ -881,29 +552,21 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
} }
/* /*
* Interrupt service routine. This gets called whenever an I2C interrupt * Interrupt service routine. This gets called whenever an I2C master interrupt
* occurs. * occurs.
*/ */
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
{ {
struct dw_i2c_dev *dev = dev_id; u32 stat;
u32 stat, enabled;
enabled = dw_readl(dev, DW_IC_ENABLE);
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat);
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
return IRQ_NONE;
stat = i2c_dw_read_clear_intrbits(dev); stat = i2c_dw_read_clear_intrbits(dev);
if (stat & DW_IC_INTR_TX_ABRT) { if (stat & DW_IC_INTR_TX_ABRT) {
dev->cmd_err |= DW_IC_ERR_TX_ABRT; dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE; dev->status = STATUS_IDLE;
/* /*
* Anytime TX_ABRT is set, the contents of the tx/rx * Anytime TX_ABRT is set, the contents of the tx/rx
* buffers are flushed. Make sure to skip them. * buffers are flushed. Make sure to skip them.
*/ */
dw_writel(dev, 0, DW_IC_INTR_MASK); dw_writel(dev, 0, DW_IC_INTR_MASK);
goto tx_aborted; goto tx_aborted;
@ -925,49 +588,46 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete); complete(&dev->cmd_complete);
else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
/* workaround to trigger pending interrupt */ /* Workaround to trigger pending interrupt */
stat = dw_readl(dev, DW_IC_INTR_MASK); stat = dw_readl(dev, DW_IC_INTR_MASK);
i2c_dw_disable_int(dev); i2c_dw_disable_int(dev);
dw_writel(dev, stat, DW_IC_INTR_MASK); dw_writel(dev, stat, DW_IC_INTR_MASK);
} }
return 0;
}
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
u32 stat, enabled;
enabled = dw_readl(dev, DW_IC_ENABLE);
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
return IRQ_NONE;
i2c_dw_irq_handler_master(dev);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
/* Disable controller */
__i2c_dw_enable_and_wait(dev, false);
/* Disable all interupts */
dw_writel(dev, 0, DW_IC_INTR_MASK);
dw_readl(dev, DW_IC_CLR_INTR);
}
EXPORT_SYMBOL_GPL(i2c_dw_disable);
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
{
dw_writel(dev, 0, DW_IC_INTR_MASK);
}
EXPORT_SYMBOL_GPL(i2c_dw_disable_int);
u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
{
return dw_readl(dev, DW_IC_COMP_PARAM_1);
}
EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
int i2c_dw_probe(struct dw_i2c_dev *dev) int i2c_dw_probe(struct dw_i2c_dev *dev)
{ {
struct i2c_adapter *adap = &dev->adapter; struct i2c_adapter *adap = &dev->adapter;
unsigned long irq_flags; unsigned long irq_flags;
int r; int ret;
init_completion(&dev->cmd_complete); init_completion(&dev->cmd_complete);
r = i2c_dw_init(dev); dev->init = i2c_dw_init_master;
if (r) dev->disable = i2c_dw_disable;
return r; dev->disable_int = i2c_dw_disable_int;
ret = dev->init(dev);
if (ret)
return ret;
snprintf(adap->name, sizeof(adap->name), snprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C adapter"); "Synopsys DesignWare I2C adapter");
@ -984,12 +644,12 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
} }
i2c_dw_disable_int(dev); i2c_dw_disable_int(dev);
r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
dev_name(dev->dev), dev); dev_name(dev->dev), dev);
if (r) { if (ret) {
dev_err(dev->dev, "failure requesting irq %i: %d\n", dev_err(dev->dev, "failure requesting irq %i: %d\n",
dev->irq, r); dev->irq, ret);
return r; return ret;
} }
/* /*
@ -999,14 +659,14 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
* registered I2C slaves that do I2C transfers in their probe. * registered I2C slaves that do I2C transfers in their probe.
*/ */
pm_runtime_get_noresume(dev->dev); pm_runtime_get_noresume(dev->dev);
r = i2c_add_numbered_adapter(adap); ret = i2c_add_numbered_adapter(adap);
if (r) if (ret)
dev_err(dev->dev, "failure adding adapter: %d\n", r); dev_err(dev->dev, "failure adding adapter: %d\n", ret);
pm_runtime_put_noidle(dev->dev); pm_runtime_put_noidle(dev->dev);
return r; return ret;
} }
EXPORT_SYMBOL_GPL(i2c_dw_probe); EXPORT_SYMBOL_GPL(i2c_dw_probe);
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus master adapter");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -187,16 +187,19 @@ static struct dw_pci_controller dw_pci_controllers[] = {
static int i2c_dw_pci_suspend(struct device *dev) static int i2c_dw_pci_suspend(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
i_dev->disable(i_dev);
i2c_dw_disable(pci_get_drvdata(pdev));
return 0; return 0;
} }
static int i2c_dw_pci_resume(struct device *dev) static int i2c_dw_pci_resume(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
return i2c_dw_init(pci_get_drvdata(pdev)); return i_dev->init(i_dev);
} }
#endif #endif
@ -296,7 +299,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
{ {
struct dw_i2c_dev *dev = pci_get_drvdata(pdev); struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
i2c_dw_disable(dev); dev->disable(dev);
pm_runtime_forbid(&pdev->dev); pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev); pm_runtime_get_noresume(&pdev->dev);

View File

@ -1,5 +1,5 @@
/* /*
* Synopsys DesignWare I2C adapter driver (master only). * Synopsys DesignWare I2C adapter driver.
* *
* Based on the TI DAVINCI I2C adapter driver. * Based on the TI DAVINCI I2C adapter driver.
* *
@ -21,27 +21,28 @@
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* *
*/ */
#include <linux/kernel.h> #include <linux/acpi.h>
#include <linux/module.h> #include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_data/i2c-designware.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/io.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/platform_data/i2c-designware.h>
#include "i2c-designware-core.h" #include "i2c-designware-core.h"
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
@ -171,6 +172,49 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
} }
#endif #endif
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN;
dev->mode = DW_IC_MASTER;
switch (dev->clk_freq) {
case 100000:
dev->master_cfg |= DW_IC_CON_SPEED_STD;
break;
case 3400000:
dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
break;
default:
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
}
}
static void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
{
dev->functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY;
dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED |
DW_IC_CON_SPEED_FAST;
dev->mode = DW_IC_SLAVE;
switch (dev->clk_freq) {
case 100000:
dev->slave_cfg |= DW_IC_CON_SPEED_STD;
break;
case 3400000:
dev->slave_cfg |= DW_IC_CON_SPEED_HIGH;
break;
default:
dev->slave_cfg |= DW_IC_CON_SPEED_FAST;
}
}
static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare) static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare)
{ {
if (IS_ERR(i_dev->clk)) if (IS_ERR(i_dev->clk))
@ -209,11 +253,11 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
static int dw_i2c_plat_probe(struct platform_device *pdev) static int dw_i2c_plat_probe(struct platform_device *pdev)
{ {
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev); struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dw_i2c_dev *dev;
struct i2c_adapter *adap; struct i2c_adapter *adap;
struct resource *mem; struct dw_i2c_dev *dev;
int irq, r;
u32 acpi_speed, ht = 0; u32 acpi_speed, ht = 0;
struct resource *mem;
int irq, ret;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
@ -276,29 +320,18 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
&& dev->clk_freq != 1000000 && dev->clk_freq != 3400000) { && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Only 100kHz, 400kHz, 1MHz and 3.4MHz supported"); "Only 100kHz, 400kHz, 1MHz and 3.4MHz supported");
r = -EINVAL; ret = -EINVAL;
goto exit_reset; goto exit_reset;
} }
r = i2c_dw_probe_lock_support(dev); ret = i2c_dw_probe_lock_support(dev);
if (r) if (ret)
goto exit_reset; goto exit_reset;
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; if (i2c_detect_slave_mode(&pdev->dev))
i2c_dw_configure_slave(dev);
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | else
DW_IC_CON_RESTART_EN; i2c_dw_configure_master(dev);
switch (dev->clk_freq) {
case 100000:
dev->master_cfg |= DW_IC_CON_SPEED_STD;
break;
case 3400000:
dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
break;
default:
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
}
dev->clk = devm_clk_get(&pdev->dev, NULL); dev->clk = devm_clk_get(&pdev->dev, NULL);
if (!i2c_dw_plat_prepare_clk(dev, true)) { if (!i2c_dw_plat_prepare_clk(dev, true)) {
@ -327,11 +360,15 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
} }
r = i2c_dw_probe(dev); if (dev->mode == DW_IC_SLAVE)
if (r) ret = i2c_dw_probe_slave(dev);
else
ret = i2c_dw_probe(dev);
if (ret)
goto exit_probe; goto exit_probe;
return r; return ret;
exit_probe: exit_probe:
if (!dev->pm_disabled) if (!dev->pm_disabled)
@ -339,7 +376,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
exit_reset: exit_reset:
if (!IS_ERR_OR_NULL(dev->rst)) if (!IS_ERR_OR_NULL(dev->rst))
reset_control_assert(dev->rst); reset_control_assert(dev->rst);
return r; return ret;
} }
static int dw_i2c_plat_remove(struct platform_device *pdev) static int dw_i2c_plat_remove(struct platform_device *pdev)
@ -350,7 +387,7 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter); i2c_del_adapter(&dev->adapter);
i2c_dw_disable(dev); dev->disable(dev);
pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
@ -394,7 +431,7 @@ static int dw_i2c_plat_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
i2c_dw_disable(i_dev); i_dev->disable(i_dev);
i2c_dw_plat_prepare_clk(i_dev, false); i2c_dw_plat_prepare_clk(i_dev, false);
return 0; return 0;
@ -406,7 +443,7 @@ static int dw_i2c_plat_resume(struct device *dev)
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
i2c_dw_plat_prepare_clk(i_dev, true); i2c_dw_plat_prepare_clk(i_dev, true);
i2c_dw_init(i_dev); i_dev->init(i_dev);
return 0; return 0;
} }
@ -423,7 +460,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
#define DW_I2C_DEV_PMOPS NULL #define DW_I2C_DEV_PMOPS NULL
#endif #endif
/* work with hotplug and coldplug */ /* Work with hotplug and coldplug */
MODULE_ALIAS("platform:i2c_designware"); MODULE_ALIAS("platform:i2c_designware");
static struct platform_driver dw_i2c_driver = { static struct platform_driver dw_i2c_driver = {

View File

@ -0,0 +1,393 @@
/*
* Synopsys DesignWare I2C adapter driver (slave only).
*
* Based on the Synopsys DesignWare I2C adapter driver (master).
*
* Copyright (C) 2016 Synopsys Inc.
*
* ----------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This 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/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include "i2c-designware-core.h"
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
{
/* Configure Tx/Rx FIFO threshold levels. */
dw_writel(dev, 0, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);
/* Configure the I2C slave. */
dw_writel(dev, dev->slave_cfg, DW_IC_CON);
dw_writel(dev, DW_IC_INTR_SLAVE_MASK, DW_IC_INTR_MASK);
}
/**
* i2c_dw_init_slave() - Initialize the designware i2c slave hardware
* @dev: device private data
*
* This function configures and enables the I2C in slave mode.
* This function is called during I2C init function, and in case of timeout at
* run time.
*/
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
{
u32 sda_falling_time, scl_falling_time;
u32 reg, comp_param1;
u32 hcnt, lcnt;
int ret;
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
reg = dw_readl(dev, DW_IC_COMP_TYPE);
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
/* Configure register endianness access. */
dev->flags |= ACCESS_SWAP;
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
/* Configure register access mode 16bit. */
dev->flags |= ACCESS_16BIT;
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
dev_err(dev->dev,
"Unknown Synopsys component type: 0x%08x\n", reg);
i2c_dw_release_lock(dev);
return -ENODEV;
}
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
/* Disable the adapter. */
__i2c_dw_enable_and_wait(dev, false);
/* 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 */
/* Set SCL timing parameters for standard-mode. */
if (dev->ss_hcnt && dev->ss_lcnt) {
hcnt = dev->ss_hcnt;
lcnt = dev->ss_lcnt;
} else {
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
4000, /* tHD;STA = tHIGH = 4.0 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
4700, /* tLOW = 4.7 us */
scl_falling_time,
0); /* No offset */
}
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
/* Set SCL timing parameters for fast-mode or fast-mode plus. */
if ((dev->clk_freq == 1000000) && dev->fp_hcnt && dev->fp_lcnt) {
hcnt = dev->fp_hcnt;
lcnt = dev->fp_lcnt;
} else if (dev->fs_hcnt && dev->fs_lcnt) {
hcnt = dev->fs_hcnt;
lcnt = dev->fs_lcnt;
} else {
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
600, /* tHD;STA = tHIGH = 0.6 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
1300, /* tLOW = 1.3 us */
scl_falling_time,
0); /* No offset */
}
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
if ((dev->slave_cfg & DW_IC_CON_SPEED_MASK) ==
DW_IC_CON_SPEED_HIGH) {
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
dev_err(dev->dev, "High Speed not supported!\n");
dev->slave_cfg &= ~DW_IC_CON_SPEED_MASK;
dev->slave_cfg |= DW_IC_CON_SPEED_FAST;
} else if (dev->hs_hcnt && dev->hs_lcnt) {
hcnt = dev->hs_hcnt;
lcnt = dev->hs_lcnt;
dw_writel(dev, hcnt, DW_IC_HS_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_HS_SCL_LCNT);
dev_dbg(dev->dev, "HighSpeed-mode HCNT:LCNT = %d:%d\n",
hcnt, lcnt);
}
}
/* Configure SDA Hold Time if required. */
reg = dw_readl(dev, DW_IC_COMP_VERSION);
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
if (!dev->sda_hold_time) {
/* Keep previous hold time setting if no one set it. */
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
}
/*
* Workaround for avoiding TX arbitration lost in case I2C
* slave pulls SDA down "too quickly" after falling egde of
* SCL by enabling non-zero SDA RX hold. Specification says it
* extends incoming SDA low to high transition while SCL is
* high but it apprears to help also above issue.
*/
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
} else {
dev_warn(dev->dev,
"Hardware too old to adjust SDA hold time.\n");
}
i2c_dw_configure_fifo_slave(dev);
i2c_dw_release_lock(dev);
return 0;
}
static int i2c_dw_reg_slave(struct i2c_client *slave)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
if (dev->slave)
return -EBUSY;
if (slave->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
/*
* Set slave address in the IC_SAR register,
* the address to which the DW_apb_i2c responds.
*/
__i2c_dw_enable(dev, false);
dw_writel(dev, slave->addr, DW_IC_SAR);
dev->slave = slave;
__i2c_dw_enable(dev, true);
dev->cmd_err = 0;
dev->msg_write_idx = 0;
dev->msg_read_idx = 0;
dev->msg_err = 0;
dev->status = STATUS_IDLE;
dev->abort_source = 0;
dev->rx_outstanding = 0;
return 0;
}
static int i2c_dw_unreg_slave(struct i2c_client *slave)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
dev->disable_int(dev);
dev->disable(dev);
dev->slave = NULL;
return 0;
}
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
{
u32 stat;
/*
* The IC_INTR_STAT register just indicates "enabled" interrupts.
* Ths unmasked raw version of interrupt status bits are available
* in the IC_RAW_INTR_STAT register.
*
* That is,
* stat = dw_readl(IC_INTR_STAT);
* equals to,
* stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
*
* The raw version might be useful for debugging purposes.
*/
stat = dw_readl(dev, DW_IC_INTR_STAT);
/*
* Do not use the IC_CLR_INTR register to clear interrupts, or
* you'll miss some interrupts, triggered during the period from
* dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
*
* Instead, use the separately-prepared IC_CLR_* registers.
*/
if (stat & DW_IC_INTR_TX_ABRT)
dw_readl(dev, DW_IC_CLR_TX_ABRT);
if (stat & DW_IC_INTR_RX_UNDER)
dw_readl(dev, DW_IC_CLR_RX_UNDER);
if (stat & DW_IC_INTR_RX_OVER)
dw_readl(dev, DW_IC_CLR_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
dw_readl(dev, DW_IC_CLR_TX_OVER);
if (stat & DW_IC_INTR_RX_DONE)
dw_readl(dev, DW_IC_CLR_RX_DONE);
if (stat & DW_IC_INTR_ACTIVITY)
dw_readl(dev, DW_IC_CLR_ACTIVITY);
if (stat & DW_IC_INTR_STOP_DET)
dw_readl(dev, DW_IC_CLR_STOP_DET);
if (stat & DW_IC_INTR_START_DET)
dw_readl(dev, DW_IC_CLR_START_DET);
if (stat & DW_IC_INTR_GEN_CALL)
dw_readl(dev, DW_IC_CLR_GEN_CALL);
return stat;
}
/*
* Interrupt service routine. This gets called whenever an I2C slave interrupt
* occurs.
*/
static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
{
u32 raw_stat, stat, enabled;
u8 val, slave_activity;
stat = dw_readl(dev, DW_IC_INTR_STAT);
enabled = dw_readl(dev, DW_IC_ENABLE);
raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
slave_activity = ((dw_readl(dev, DW_IC_STATUS) &
DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);
if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY))
return 0;
dev_dbg(dev->dev,
"%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",
enabled, slave_activity, raw_stat, stat);
if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET))
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
if (stat & DW_IC_INTR_RD_REQ) {
if (slave_activity) {
if (stat & DW_IC_INTR_RX_FULL) {
val = dw_readl(dev, DW_IC_DATA_CMD);
if (!i2c_slave_event(dev->slave,
I2C_SLAVE_WRITE_RECEIVED,
&val)) {
dev_vdbg(dev->dev, "Byte %X acked!",
val);
}
dw_readl(dev, DW_IC_CLR_RD_REQ);
stat = i2c_dw_read_clear_intrbits_slave(dev);
} else {
dw_readl(dev, DW_IC_CLR_RD_REQ);
dw_readl(dev, DW_IC_CLR_RX_UNDER);
stat = i2c_dw_read_clear_intrbits_slave(dev);
}
if (!i2c_slave_event(dev->slave,
I2C_SLAVE_READ_REQUESTED,
&val))
dw_writel(dev, val, DW_IC_DATA_CMD);
}
}
if (stat & DW_IC_INTR_RX_DONE) {
if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
&val))
dw_readl(dev, DW_IC_CLR_RX_DONE);
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
stat = i2c_dw_read_clear_intrbits_slave(dev);
return 1;
}
if (stat & DW_IC_INTR_RX_FULL) {
val = dw_readl(dev, DW_IC_DATA_CMD);
if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
&val))
dev_vdbg(dev->dev, "Byte %X acked!", val);
} else {
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
stat = i2c_dw_read_clear_intrbits_slave(dev);
}
return 1;
}
static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
int ret;
i2c_dw_read_clear_intrbits_slave(dev);
ret = i2c_dw_irq_handler_slave(dev);
if (ret > 0)
complete(&dev->cmd_complete);
return IRQ_RETVAL(ret);
}
static struct i2c_algorithm i2c_dw_algo = {
.functionality = i2c_dw_func,
.reg_slave = i2c_dw_reg_slave,
.unreg_slave = i2c_dw_unreg_slave,
};
int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
int ret;
init_completion(&dev->cmd_complete);
dev->init = i2c_dw_init_slave;
dev->disable = i2c_dw_disable;
dev->disable_int = i2c_dw_disable_int;
ret = dev->init(dev);
if (ret)
return ret;
snprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C Slave adapter");
adap->retries = 3;
adap->algo = &i2c_dw_algo;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr_slave,
IRQF_SHARED, dev_name(dev->dev), dev);
if (ret) {
dev_err(dev->dev, "failure requesting irq %i: %d\n",
dev->irq, ret);
return ret;
}
ret = i2c_add_numbered_adapter(adap);
if (ret)
dev_err(dev->dev, "failure adding adapter: %d\n", ret);
pm_runtime_put_noidle(dev->dev);
return ret;
}
EXPORT_SYMBOL_GPL(i2c_dw_probe_slave);
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");
MODULE_LICENSE("GPL v2");

View File

@ -375,7 +375,9 @@ static int em_i2c_probe(struct platform_device *pdev)
if (IS_ERR(priv->sclk)) if (IS_ERR(priv->sclk))
return PTR_ERR(priv->sclk); return PTR_ERR(priv->sclk);
clk_prepare_enable(priv->sclk); ret = clk_prepare_enable(priv->sclk);
if (ret)
return ret;
priv->adap.timeout = msecs_to_jiffies(100); priv->adap.timeout = msecs_to_jiffies(100);
priv->adap.retries = 5; priv->adap.retries = 5;

View File

@ -66,6 +66,8 @@
* Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes * Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes
* Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes * Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes
* Gemini Lake (SOC) 0x31d4 32 hard yes yes yes * Gemini Lake (SOC) 0x31d4 32 hard yes yes yes
* Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
* *
* Features supported by this driver: * Features supported by this driver:
* Software PEC no * Software PEC no
@ -226,10 +228,12 @@
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS 0x9da3
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS 0xa1a3 #define PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS 0xa1a3
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223 #define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223
#define PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS 0xa2a3 #define PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS 0xa2a3
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS 0xa323
struct i801_mux_config { struct i801_mux_config {
char *gpio_chip; char *gpio_chip;
@ -1026,6 +1030,8 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
{ 0, } { 0, }
}; };
@ -1499,6 +1505,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
switch (dev->device) { switch (dev->device) {
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS: case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS: case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
case PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS:
case PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS:
case PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS: case PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS:
case PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS: case PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS:
case PCI_DEVICE_ID_INTEL_DNV_SMBUS: case PCI_DEVICE_ID_INTEL_DNV_SMBUS:

View File

@ -419,7 +419,7 @@ static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
ret = mxs_i2c_pio_wait_xfer_end(i2c); ret = mxs_i2c_pio_wait_xfer_end(i2c);
if (ret) { if (ret) {
dev_err(i2c->dev, dev_dbg(i2c->dev,
"PIO: Failed to send SELECT command!\n"); "PIO: Failed to send SELECT command!\n");
goto cleanup; goto cleanup;
} }
@ -431,7 +431,7 @@ static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
ret = mxs_i2c_pio_wait_xfer_end(i2c); ret = mxs_i2c_pio_wait_xfer_end(i2c);
if (ret) { if (ret) {
dev_err(i2c->dev, dev_dbg(i2c->dev,
"PIO: Failed to send READ command!\n"); "PIO: Failed to send READ command!\n");
goto cleanup; goto cleanup;
} }
@ -528,7 +528,7 @@ static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
/* Wait for the end of the transfer. */ /* Wait for the end of the transfer. */
ret = mxs_i2c_pio_wait_xfer_end(i2c); ret = mxs_i2c_pio_wait_xfer_end(i2c);
if (ret) { if (ret) {
dev_err(i2c->dev, dev_dbg(i2c->dev,
"PIO: Failed to finish WRITE cmd!\n"); "PIO: Failed to finish WRITE cmd!\n");
break; break;
} }

View File

@ -22,14 +22,17 @@
#include <linux/i2c-algo-pca.h> #include <linux/i2c-algo-pca.h>
#include <linux/i2c-pca-platform.h> #include <linux/i2c-pca-platform.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/irq.h> #include <asm/irq.h>
struct i2c_pca_pf_data { struct i2c_pca_pf_data {
void __iomem *reg_base; void __iomem *reg_base;
int irq; /* if 0, use polling */ int irq; /* if 0, use polling */
int gpio; struct gpio_desc *gpio;
wait_queue_head_t wait; wait_queue_head_t wait;
struct i2c_adapter adap; struct i2c_adapter adap;
struct i2c_algo_pca_data algo_data; struct i2c_algo_pca_data algo_data;
@ -104,17 +107,17 @@ static int i2c_pca_pf_waitforcompletion(void *pd)
static void i2c_pca_pf_dummyreset(void *pd) static void i2c_pca_pf_dummyreset(void *pd)
{ {
struct i2c_pca_pf_data *i2c = pd; struct i2c_pca_pf_data *i2c = pd;
printk(KERN_WARNING "%s: No reset-pin found. Chip may get stuck!\n",
i2c->adap.name); dev_warn(&i2c->adap.dev, "No reset-pin found. Chip may get stuck!\n");
} }
static void i2c_pca_pf_resetchip(void *pd) static void i2c_pca_pf_resetchip(void *pd)
{ {
struct i2c_pca_pf_data *i2c = pd; struct i2c_pca_pf_data *i2c = pd;
gpio_set_value(i2c->gpio, 0); gpiod_set_value(i2c->gpio, 1);
ndelay(100); ndelay(100);
gpio_set_value(i2c->gpio, 1); gpiod_set_value(i2c->gpio, 0);
} }
static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id)
@ -136,36 +139,27 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
struct i2c_pca9564_pf_platform_data *platform_data = struct i2c_pca9564_pf_platform_data *platform_data =
dev_get_platdata(&pdev->dev); dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
int ret = 0; int ret = 0;
int irq; int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
/* If irq is 0, we do polling. */ /* If irq is 0, we do polling. */
if (irq < 0)
irq = 0;
if (res == NULL) { i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
ret = -ENODEV; if (!i2c)
goto e_print; return -ENOMEM;
}
if (!request_mem_region(res->start, resource_size(res), res->name)) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ret = -ENOMEM; i2c->reg_base = devm_ioremap_resource(&pdev->dev, res);
goto e_print; if (IS_ERR(i2c->reg_base))
} return PTR_ERR(i2c->reg_base);
i2c = kzalloc(sizeof(struct i2c_pca_pf_data), GFP_KERNEL);
if (!i2c) {
ret = -ENOMEM;
goto e_alloc;
}
init_waitqueue_head(&i2c->wait); init_waitqueue_head(&i2c->wait);
i2c->reg_base = ioremap(res->start, resource_size(res));
if (!i2c->reg_base) {
ret = -ENOMEM;
goto e_remap;
}
i2c->io_base = res->start; i2c->io_base = res->start;
i2c->io_size = resource_size(res); i2c->io_size = resource_size(res);
i2c->irq = irq; i2c->irq = irq;
@ -177,20 +171,43 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
(unsigned long) res->start); (unsigned long) res->start);
i2c->adap.algo_data = &i2c->algo_data; i2c->adap.algo_data = &i2c->algo_data;
i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = np;
if (platform_data) { if (platform_data) {
i2c->adap.timeout = platform_data->timeout; i2c->adap.timeout = platform_data->timeout;
i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed; i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed;
i2c->gpio = platform_data->gpio; if (gpio_is_valid(platform_data->gpio)) {
ret = devm_gpio_request_one(&pdev->dev,
platform_data->gpio,
GPIOF_ACTIVE_LOW,
i2c->adap.name);
if (ret == 0) {
i2c->gpio = gpio_to_desc(platform_data->gpio);
gpiod_direction_output(i2c->gpio, 0);
} else {
dev_warn(&pdev->dev, "Registering gpio failed!\n");
i2c->gpio = NULL;
}
}
} else if (np) {
i2c->adap.timeout = HZ;
i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
if (IS_ERR(i2c->gpio))
return PTR_ERR(i2c->gpio);
of_property_read_u32_index(np, "clock-frequency", 0,
&i2c->algo_data.i2c_clock);
} else { } else {
i2c->adap.timeout = HZ; i2c->adap.timeout = HZ;
i2c->algo_data.i2c_clock = 59000; i2c->algo_data.i2c_clock = 59000;
i2c->gpio = -1; i2c->gpio = NULL;
} }
i2c->algo_data.data = i2c; i2c->algo_data.data = i2c;
i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion; i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion;
i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset; if (i2c->gpio)
i2c->algo_data.reset_chip = i2c_pca_pf_resetchip;
else
i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset;
switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_32BIT: case IORESOURCE_MEM_32BIT:
@ -208,52 +225,22 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
break; break;
} }
/* Use gpio_is_valid() when in mainline */
if (i2c->gpio > -1) {
ret = gpio_request(i2c->gpio, i2c->adap.name);
if (ret == 0) {
gpio_direction_output(i2c->gpio, 1);
i2c->algo_data.reset_chip = i2c_pca_pf_resetchip;
} else {
printk(KERN_WARNING "%s: Registering gpio failed!\n",
i2c->adap.name);
i2c->gpio = ret;
}
}
if (irq) { if (irq) {
ret = request_irq(irq, i2c_pca_pf_handler, ret = devm_request_irq(&pdev->dev, irq, i2c_pca_pf_handler,
IRQF_TRIGGER_FALLING, pdev->name, i2c); IRQF_TRIGGER_FALLING, pdev->name, i2c);
if (ret) if (ret)
goto e_reqirq; return ret;
} }
if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) { ret = i2c_pca_add_numbered_bus(&i2c->adap);
ret = -ENODEV; if (ret)
goto e_adapt; return ret;
}
platform_set_drvdata(pdev, i2c); platform_set_drvdata(pdev, i2c);
printk(KERN_INFO "%s registered.\n", i2c->adap.name); dev_info(&pdev->dev, "registered.\n");
return 0; return 0;
e_adapt:
if (irq)
free_irq(irq, i2c);
e_reqirq:
if (i2c->gpio > -1)
gpio_free(i2c->gpio);
iounmap(i2c->reg_base);
e_remap:
kfree(i2c);
e_alloc:
release_mem_region(res->start, resource_size(res));
e_print:
printk(KERN_ERR "Registering PCA9564/PCA9665 FAILED! (%d)\n", ret);
return ret;
} }
static int i2c_pca_pf_remove(struct platform_device *pdev) static int i2c_pca_pf_remove(struct platform_device *pdev)
@ -262,24 +249,24 @@ static int i2c_pca_pf_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c->adap); i2c_del_adapter(&i2c->adap);
if (i2c->irq)
free_irq(i2c->irq, i2c);
if (i2c->gpio > -1)
gpio_free(i2c->gpio);
iounmap(i2c->reg_base);
release_mem_region(i2c->io_base, i2c->io_size);
kfree(i2c);
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id i2c_pca_of_match_table[] = {
{ .compatible = "nxp,pca9564" },
{ .compatible = "nxp,pca9665" },
{},
};
MODULE_DEVICE_TABLE(of, i2c_pca_of_match_table);
#endif
static struct platform_driver i2c_pca_pf_driver = { static struct platform_driver i2c_pca_pf_driver = {
.probe = i2c_pca_pf_probe, .probe = i2c_pca_pf_probe,
.remove = i2c_pca_pf_remove, .remove = i2c_pca_pf_remove,
.driver = { .driver = {
.name = "i2c-pca-platform", .name = "i2c-pca-platform",
.of_match_table = of_match_ptr(i2c_pca_of_match_table),
}, },
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Driver for the Renesas RCar I2C unit * Driver for the Renesas R-Car I2C unit
* *
* Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com> * Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
* Copyright (C) 2011-2015 Renesas Electronics Corporation * Copyright (C) 2011-2015 Renesas Electronics Corporation
@ -783,7 +783,12 @@ static int rcar_unreg_slave(struct i2c_client *slave)
static u32 rcar_i2c_func(struct i2c_adapter *adap) static u32 rcar_i2c_func(struct i2c_adapter *adap)
{ {
/* This HW can't do SMBUS_QUICK and NOSTART */ /*
* This HW can't do:
* I2C_SMBUS_QUICK (setting FSB during START didn't work)
* I2C_M_NOSTART (automatically sends address after START)
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
*/
return I2C_FUNC_I2C | I2C_FUNC_SLAVE | return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
} }

View File

@ -24,7 +24,6 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c/i2c-sh_mobile.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
@ -879,10 +878,10 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile
static int sh_mobile_i2c_probe(struct platform_device *dev) static int sh_mobile_i2c_probe(struct platform_device *dev)
{ {
struct i2c_sh_mobile_platform_data *pdata = dev_get_platdata(&dev->dev);
struct sh_mobile_i2c_data *pd; struct sh_mobile_i2c_data *pd;
struct i2c_adapter *adap; struct i2c_adapter *adap;
struct resource *res; struct resource *res;
const struct of_device_id *match;
int ret; int ret;
u32 bus_speed; u32 bus_speed;
@ -910,30 +909,18 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
if (IS_ERR(pd->reg)) if (IS_ERR(pd->reg))
return PTR_ERR(pd->reg); return PTR_ERR(pd->reg);
/* Use platform data bus speed or STANDARD_MODE */
ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed); ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed);
pd->bus_speed = ret ? STANDARD_MODE : bus_speed; pd->bus_speed = ret ? STANDARD_MODE : bus_speed;
pd->clks_per_count = 1; pd->clks_per_count = 1;
if (dev->dev.of_node) { match = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev);
const struct of_device_id *match; if (match) {
const struct sh_mobile_dt_config *config = match->data;
match = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev); pd->clks_per_count = config->clks_per_count;
if (match) {
const struct sh_mobile_dt_config *config;
config = match->data; if (config->setup)
pd->clks_per_count = config->clks_per_count; config->setup(pd);
if (config->setup)
config->setup(pd);
}
} else {
if (pdata && pdata->bus_speed)
pd->bus_speed = pdata->bus_speed;
if (pdata && pdata->clks_per_count)
pd->clks_per_count = pdata->clks_per_count;
} }
/* The IIC blocks on SH-Mobile ARM processors /* The IIC blocks on SH-Mobile ARM processors

View File

@ -22,10 +22,12 @@
* using the APM X-Gene SLIMpro mailbox driver. * using the APM X-Gene SLIMpro mailbox driver.
* *
*/ */
#include <acpi/pcc.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_client.h> #include <linux/mailbox_client.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
@ -89,6 +91,8 @@
((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_MASK) | \ ((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_MASK) | \
((datalen << SLIMPRO_IIC_DATALEN_SHIFT) & SLIMPRO_IIC_DATALEN_MASK)) ((datalen << SLIMPRO_IIC_DATALEN_SHIFT) & SLIMPRO_IIC_DATALEN_MASK))
#define SLIMPRO_MSG_TYPE(v) (((v) & 0xF0000000) >> 28)
/* /*
* Encode for upper address for block data * Encode for upper address for block data
*/ */
@ -99,19 +103,47 @@
& 0x3FF00000)) & 0x3FF00000))
#define SLIMPRO_IIC_ENCODE_ADDR(a) ((a) & 0x000FFFFF) #define SLIMPRO_IIC_ENCODE_ADDR(a) ((a) & 0x000FFFFF)
#define SLIMPRO_IIC_MSG_DWORD_COUNT 3
/* PCC related defines */
#define PCC_SIGNATURE 0x50424300
#define PCC_STS_CMD_COMPLETE BIT(0)
#define PCC_STS_SCI_DOORBELL BIT(1)
#define PCC_STS_ERR BIT(2)
#define PCC_STS_PLAT_NOTIFY BIT(3)
#define PCC_CMD_GENERATE_DB_INT BIT(15)
struct slimpro_i2c_dev { struct slimpro_i2c_dev {
struct i2c_adapter adapter; struct i2c_adapter adapter;
struct device *dev; struct device *dev;
struct mbox_chan *mbox_chan; struct mbox_chan *mbox_chan;
struct mbox_client mbox_client; struct mbox_client mbox_client;
int mbox_idx;
struct completion rd_complete; struct completion rd_complete;
u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */
u32 *resp_msg; u32 *resp_msg;
phys_addr_t comm_base_addr;
void *pcc_comm_addr;
}; };
#define to_slimpro_i2c_dev(cl) \ #define to_slimpro_i2c_dev(cl) \
container_of(cl, struct slimpro_i2c_dev, mbox_client) container_of(cl, struct slimpro_i2c_dev, mbox_client)
/*
* This function tests and clears a bitmask then returns its old value
*/
static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
{
u16 ret, val;
val = le16_to_cpu(READ_ONCE(*addr));
ret = val & mask;
val &= ~mask;
WRITE_ONCE(*addr, cpu_to_le16(val));
return ret;
}
static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg) static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg)
{ {
struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl); struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl);
@ -129,9 +161,53 @@ static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg)
complete(&ctx->rd_complete); complete(&ctx->rd_complete);
} }
static void slimpro_i2c_pcc_rx_cb(struct mbox_client *cl, void *msg)
{
struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl);
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
/* Check if platform sends interrupt */
if (!xgene_word_tst_and_clr(&generic_comm_base->status,
PCC_STS_SCI_DOORBELL))
return;
if (xgene_word_tst_and_clr(&generic_comm_base->status,
PCC_STS_CMD_COMPLETE)) {
msg = generic_comm_base + 1;
/* Response message msg[1] contains the return value. */
if (ctx->resp_msg)
*ctx->resp_msg = ((u32 *)msg)[1];
complete(&ctx->rd_complete);
}
}
static void slimpro_i2c_pcc_tx_prepare(struct slimpro_i2c_dev *ctx, u32 *msg)
{
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
u32 *ptr = (void *)(generic_comm_base + 1);
u16 status;
int i;
WRITE_ONCE(generic_comm_base->signature,
cpu_to_le32(PCC_SIGNATURE | ctx->mbox_idx));
WRITE_ONCE(generic_comm_base->command,
cpu_to_le16(SLIMPRO_MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INT));
status = le16_to_cpu(READ_ONCE(generic_comm_base->status));
status &= ~PCC_STS_CMD_COMPLETE;
WRITE_ONCE(generic_comm_base->status, cpu_to_le16(status));
/* Copy the message to the PCC comm space */
for (i = 0; i < SLIMPRO_IIC_MSG_DWORD_COUNT; i++)
WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
}
static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx) static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx)
{ {
if (ctx->mbox_client.tx_block) { if (ctx->mbox_client.tx_block || !acpi_disabled) {
if (!wait_for_completion_timeout(&ctx->rd_complete, if (!wait_for_completion_timeout(&ctx->rd_complete,
msecs_to_jiffies(MAILBOX_OP_TIMEOUT))) msecs_to_jiffies(MAILBOX_OP_TIMEOUT)))
return -ETIMEDOUT; return -ETIMEDOUT;
@ -144,26 +220,46 @@ static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx)
return 0; return 0;
} }
static int slimpro_i2c_send_msg(struct slimpro_i2c_dev *ctx,
u32 *msg,
u32 *data)
{
int rc;
ctx->resp_msg = data;
if (!acpi_disabled) {
reinit_completion(&ctx->rd_complete);
slimpro_i2c_pcc_tx_prepare(ctx, msg);
}
rc = mbox_send_message(ctx->mbox_chan, msg);
if (rc < 0)
goto err;
rc = start_i2c_msg_xfer(ctx);
err:
if (!acpi_disabled)
mbox_chan_txdone(ctx->mbox_chan, 0);
ctx->resp_msg = NULL;
return rc;
}
static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip, static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip,
u32 addr, u32 addrlen, u32 protocol, u32 addr, u32 addrlen, u32 protocol,
u32 readlen, u32 *data) u32 readlen, u32 *data)
{ {
u32 msg[3]; u32 msg[3];
int rc;
msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip,
SLIMPRO_IIC_READ, protocol, addrlen, readlen); SLIMPRO_IIC_READ, protocol, addrlen, readlen);
msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr);
msg[2] = 0; msg[2] = 0;
ctx->resp_msg = data;
rc = mbox_send_message(ctx->mbox_chan, &msg);
if (rc < 0)
goto err;
rc = start_i2c_msg_xfer(ctx); return slimpro_i2c_send_msg(ctx, msg, data);
err:
ctx->resp_msg = NULL;
return rc;
} }
static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip, static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip,
@ -171,22 +267,13 @@ static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip,
u32 data) u32 data)
{ {
u32 msg[3]; u32 msg[3];
int rc;
msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip,
SLIMPRO_IIC_WRITE, protocol, addrlen, writelen); SLIMPRO_IIC_WRITE, protocol, addrlen, writelen);
msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr);
msg[2] = data; msg[2] = data;
ctx->resp_msg = msg;
rc = mbox_send_message(ctx->mbox_chan, &msg); return slimpro_i2c_send_msg(ctx, msg, msg);
if (rc < 0)
goto err;
rc = start_i2c_msg_xfer(ctx);
err:
ctx->resp_msg = NULL;
return rc;
} }
static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr, static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr,
@ -201,8 +288,7 @@ static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr,
if (dma_mapping_error(ctx->dev, paddr)) { if (dma_mapping_error(ctx->dev, paddr)) {
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
ctx->dma_buffer); ctx->dma_buffer);
rc = -ENOMEM; return -ENOMEM;
goto err;
} }
msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ, msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ,
@ -212,21 +298,13 @@ static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr,
SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) |
SLIMPRO_IIC_ENCODE_ADDR(addr); SLIMPRO_IIC_ENCODE_ADDR(addr);
msg[2] = (u32)paddr; msg[2] = (u32)paddr;
ctx->resp_msg = msg;
rc = mbox_send_message(ctx->mbox_chan, &msg); rc = slimpro_i2c_send_msg(ctx, msg, msg);
if (rc < 0)
goto err_unmap;
rc = start_i2c_msg_xfer(ctx);
/* Copy to destination */ /* Copy to destination */
memcpy(data, ctx->dma_buffer, readlen); memcpy(data, ctx->dma_buffer, readlen);
err_unmap:
dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE); dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE);
err:
ctx->resp_msg = NULL;
return rc; return rc;
} }
@ -244,8 +322,7 @@ static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip,
if (dma_mapping_error(ctx->dev, paddr)) { if (dma_mapping_error(ctx->dev, paddr)) {
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
ctx->dma_buffer); ctx->dma_buffer);
rc = -ENOMEM; return -ENOMEM;
goto err;
} }
msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE, msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE,
@ -254,21 +331,13 @@ static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip,
SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) |
SLIMPRO_IIC_ENCODE_ADDR(addr); SLIMPRO_IIC_ENCODE_ADDR(addr);
msg[2] = (u32)paddr; msg[2] = (u32)paddr;
ctx->resp_msg = msg;
if (ctx->mbox_client.tx_block) if (ctx->mbox_client.tx_block)
reinit_completion(&ctx->rd_complete); reinit_completion(&ctx->rd_complete);
rc = mbox_send_message(ctx->mbox_chan, &msg); rc = slimpro_i2c_send_msg(ctx, msg, msg);
if (rc < 0)
goto err_unmap;
rc = start_i2c_msg_xfer(ctx);
err_unmap:
dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE); dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE);
err:
ctx->resp_msg = NULL;
return rc; return rc;
} }
@ -394,17 +463,73 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
/* Request mailbox channel */ /* Request mailbox channel */
cl->dev = &pdev->dev; cl->dev = &pdev->dev;
cl->rx_callback = slimpro_i2c_rx_cb;
cl->tx_block = true;
init_completion(&ctx->rd_complete); init_completion(&ctx->rd_complete);
cl->tx_tout = MAILBOX_OP_TIMEOUT; cl->tx_tout = MAILBOX_OP_TIMEOUT;
cl->knows_txdone = false; cl->knows_txdone = false;
ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX); if (acpi_disabled) {
if (IS_ERR(ctx->mbox_chan)) { cl->tx_block = true;
dev_err(&pdev->dev, "i2c mailbox channel request failed\n"); cl->rx_callback = slimpro_i2c_rx_cb;
return PTR_ERR(ctx->mbox_chan); ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX);
} if (IS_ERR(ctx->mbox_chan)) {
dev_err(&pdev->dev, "i2c mailbox channel request failed\n");
return PTR_ERR(ctx->mbox_chan);
}
} else {
struct acpi_pcct_hw_reduced *cppc_ss;
if (device_property_read_u32(&pdev->dev, "pcc-channel",
&ctx->mbox_idx))
ctx->mbox_idx = MAILBOX_I2C_INDEX;
cl->tx_block = false;
cl->rx_callback = slimpro_i2c_pcc_rx_cb;
ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
if (IS_ERR(ctx->mbox_chan)) {
dev_err(&pdev->dev, "PCC mailbox channel request failed\n");
return PTR_ERR(ctx->mbox_chan);
}
/*
* The PCC mailbox controller driver should
* have parsed the PCCT (global table of all
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
cppc_ss = ctx->mbox_chan->con_priv;
if (!cppc_ss) {
dev_err(&pdev->dev, "PPC subspace not found\n");
rc = -ENOENT;
goto mbox_err;
}
if (!ctx->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ not supported\n");
rc = -ENOENT;
goto mbox_err;
}
/*
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
ctx->comm_base_addr = cppc_ss->base_address;
if (ctx->comm_base_addr) {
ctx->pcc_comm_addr = memremap(ctx->comm_base_addr,
cppc_ss->length,
MEMREMAP_WB);
} else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
rc = -ENOENT;
goto mbox_err;
}
if (!ctx->pcc_comm_addr) {
dev_err(&pdev->dev,
"Failed to ioremap PCC comm region\n");
rc = -ENOMEM;
goto mbox_err;
}
}
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc) if (rc)
dev_warn(&pdev->dev, "Unable to set dma mask\n"); dev_warn(&pdev->dev, "Unable to set dma mask\n");
@ -419,13 +544,19 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev)); ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev));
i2c_set_adapdata(adapter, ctx); i2c_set_adapdata(adapter, ctx);
rc = i2c_add_adapter(adapter); rc = i2c_add_adapter(adapter);
if (rc) { if (rc)
mbox_free_channel(ctx->mbox_chan); goto mbox_err;
return rc;
}
dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n"); dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n");
return 0; return 0;
mbox_err:
if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
return rc;
} }
static int xgene_slimpro_i2c_remove(struct platform_device *pdev) static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
@ -434,7 +565,10 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&ctx->adapter); i2c_del_adapter(&ctx->adapter);
mbox_free_channel(ctx->mbox_chan); if (acpi_disabled)
mbox_free_channel(ctx->mbox_chan);
else
pcc_mbox_free_channel(ctx->mbox_chan);
return 0; return 0;
} }

View File

@ -393,6 +393,7 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev)
init_completion(&priv->msg_complete); init_completion(&priv->msg_complete);
priv->adapter.dev.parent = &pdev->dev; priv->adapter.dev.parent = &pdev->dev;
priv->adapter.algo = &xlp9xx_i2c_algo; priv->adapter.algo = &xlp9xx_i2c_algo;
priv->adapter.class = I2C_CLASS_HWMON;
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev));
priv->adapter.dev.of_node = pdev->dev.of_node; priv->adapter.dev.of_node = pdev->dev.of_node;
priv->dev = &pdev->dev; priv->dev = &pdev->dev;

View File

@ -0,0 +1,609 @@
/*
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
* Copyright 2017 Linaro Ltd.
*
* Author: Baoyou Xie <baoyou.xie@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define REG_CMD 0x04
#define REG_DEVADDR_H 0x0C
#define REG_DEVADDR_L 0x10
#define REG_CLK_DIV_FS 0x14
#define REG_CLK_DIV_HS 0x18
#define REG_WRCONF 0x1C
#define REG_RDCONF 0x20
#define REG_DATA 0x24
#define REG_STAT 0x28
#define I2C_STOP 0
#define I2C_MASTER BIT(0)
#define I2C_ADDR_MODE_TEN BIT(1)
#define I2C_IRQ_MSK_ENABLE BIT(3)
#define I2C_RW_READ BIT(4)
#define I2C_CMB_RW_EN BIT(5)
#define I2C_START BIT(6)
#define I2C_ADDR_LOW_MASK GENMASK(6, 0)
#define I2C_ADDR_LOW_SHIFT 0
#define I2C_ADDR_HI_MASK GENMASK(2, 0)
#define I2C_ADDR_HI_SHIFT 7
#define I2C_WFIFO_RESET BIT(7)
#define I2C_RFIFO_RESET BIT(7)
#define I2C_IRQ_ACK_CLEAR BIT(7)
#define I2C_INT_MASK GENMASK(6, 0)
#define I2C_TRANS_DONE BIT(0)
#define I2C_SR_EDEVICE BIT(1)
#define I2C_SR_EDATA BIT(2)
#define I2C_FIFO_MAX 16
#define I2C_TIMEOUT msecs_to_jiffies(1000)
#define DEV(i2c) ((i2c)->adap.dev.parent)
struct zx2967_i2c {
struct i2c_adapter adap;
struct clk *clk;
struct completion complete;
u32 clk_freq;
void __iomem *reg_base;
size_t residue;
int irq;
int msg_rd;
u8 *cur_trans;
u8 access_cnt;
bool is_suspended;
int error;
};
static void zx2967_i2c_writel(struct zx2967_i2c *i2c,
u32 val, unsigned long reg)
{
writel_relaxed(val, i2c->reg_base + reg);
}
static u32 zx2967_i2c_readl(struct zx2967_i2c *i2c, unsigned long reg)
{
return readl_relaxed(i2c->reg_base + reg);
}
static void zx2967_i2c_writesb(struct zx2967_i2c *i2c,
void *data, unsigned long reg, int len)
{
writesb(i2c->reg_base + reg, data, len);
}
static void zx2967_i2c_readsb(struct zx2967_i2c *i2c,
void *data, unsigned long reg, int len)
{
readsb(i2c->reg_base + reg, data, len);
}
static void zx2967_i2c_start_ctrl(struct zx2967_i2c *i2c)
{
u32 status;
u32 ctl;
status = zx2967_i2c_readl(i2c, REG_STAT);
status |= I2C_IRQ_ACK_CLEAR;
zx2967_i2c_writel(i2c, status, REG_STAT);
ctl = zx2967_i2c_readl(i2c, REG_CMD);
if (i2c->msg_rd)
ctl |= I2C_RW_READ;
else
ctl &= ~I2C_RW_READ;
ctl &= ~I2C_CMB_RW_EN;
ctl |= I2C_START;
zx2967_i2c_writel(i2c, ctl, REG_CMD);
}
static void zx2967_i2c_flush_fifos(struct zx2967_i2c *i2c)
{
u32 offset;
u32 val;
if (i2c->msg_rd) {
offset = REG_RDCONF;
val = I2C_RFIFO_RESET;
} else {
offset = REG_WRCONF;
val = I2C_WFIFO_RESET;
}
val |= zx2967_i2c_readl(i2c, offset);
zx2967_i2c_writel(i2c, val, offset);
}
static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c *i2c, u32 size)
{
u8 val[I2C_FIFO_MAX] = {0};
int i;
if (size > I2C_FIFO_MAX) {
dev_err(DEV(i2c), "fifo size %d over the max value %d\n",
size, I2C_FIFO_MAX);
return -EINVAL;
}
zx2967_i2c_readsb(i2c, val, REG_DATA, size);
for (i = 0; i < size; i++) {
*i2c->cur_trans++ = val[i];
i2c->residue--;
}
barrier();
return 0;
}
static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c *i2c)
{
size_t residue = i2c->residue;
u8 *buf = i2c->cur_trans;
if (residue == 0) {
dev_err(DEV(i2c), "residue is %d\n", (int)residue);
return -EINVAL;
}
if (residue <= I2C_FIFO_MAX) {
zx2967_i2c_writesb(i2c, buf, REG_DATA, residue);
/* Again update before writing to FIFO to make sure isr sees. */
i2c->residue = 0;
i2c->cur_trans = NULL;
} else {
zx2967_i2c_writesb(i2c, buf, REG_DATA, I2C_FIFO_MAX);
i2c->residue -= I2C_FIFO_MAX;
i2c->cur_trans += I2C_FIFO_MAX;
}
barrier();
return 0;
}
static int zx2967_i2c_reset_hardware(struct zx2967_i2c *i2c)
{
u32 val;
u32 clk_div;
val = I2C_MASTER | I2C_IRQ_MSK_ENABLE;
zx2967_i2c_writel(i2c, val, REG_CMD);
clk_div = clk_get_rate(i2c->clk) / i2c->clk_freq - 1;
zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_FS);
zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_HS);
zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_WRCONF);
zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_RDCONF);
zx2967_i2c_writel(i2c, 1, REG_RDCONF);
zx2967_i2c_flush_fifos(i2c);
return 0;
}
static void zx2967_i2c_isr_clr(struct zx2967_i2c *i2c)
{
u32 status;
status = zx2967_i2c_readl(i2c, REG_STAT);
status |= I2C_IRQ_ACK_CLEAR;
zx2967_i2c_writel(i2c, status, REG_STAT);
}
static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id)
{
u32 status;
struct zx2967_i2c *i2c = (struct zx2967_i2c *)dev_id;
status = zx2967_i2c_readl(i2c, REG_STAT) & I2C_INT_MASK;
zx2967_i2c_isr_clr(i2c);
if (status & I2C_SR_EDEVICE)
i2c->error = -ENXIO;
else if (status & I2C_SR_EDATA)
i2c->error = -EIO;
else if (status & I2C_TRANS_DONE)
i2c->error = 0;
else
goto done;
complete(&i2c->complete);
done:
return IRQ_HANDLED;
}
static void zx2967_set_addr(struct zx2967_i2c *i2c, u16 addr)
{
u16 val;
val = (addr >> I2C_ADDR_LOW_SHIFT) & I2C_ADDR_LOW_MASK;
zx2967_i2c_writel(i2c, val, REG_DEVADDR_L);
val = (addr >> I2C_ADDR_HI_SHIFT) & I2C_ADDR_HI_MASK;
zx2967_i2c_writel(i2c, val, REG_DEVADDR_H);
if (val)
val = zx2967_i2c_readl(i2c, REG_CMD) | I2C_ADDR_MODE_TEN;
else
val = zx2967_i2c_readl(i2c, REG_CMD) & ~I2C_ADDR_MODE_TEN;
zx2967_i2c_writel(i2c, val, REG_CMD);
}
static int zx2967_i2c_xfer_bytes(struct zx2967_i2c *i2c, u32 bytes)
{
unsigned long time_left;
int rd = i2c->msg_rd;
int ret;
reinit_completion(&i2c->complete);
if (rd) {
zx2967_i2c_writel(i2c, bytes - 1, REG_RDCONF);
} else {
ret = zx2967_i2c_fill_tx_fifo(i2c);
if (ret)
return ret;
}
zx2967_i2c_start_ctrl(i2c);
time_left = wait_for_completion_timeout(&i2c->complete,
I2C_TIMEOUT);
if (time_left == 0)
return -ETIMEDOUT;
if (i2c->error)
return i2c->error;
return rd ? zx2967_i2c_empty_rx_fifo(i2c, bytes) : 0;
}
static int zx2967_i2c_xfer_msg(struct zx2967_i2c *i2c,
struct i2c_msg *msg)
{
int ret;
int i;
if (msg->len == 0)
return -EINVAL;
zx2967_i2c_flush_fifos(i2c);
i2c->cur_trans = msg->buf;
i2c->residue = msg->len;
i2c->access_cnt = msg->len / I2C_FIFO_MAX;
i2c->msg_rd = msg->flags & I2C_M_RD;
for (i = 0; i < i2c->access_cnt; i++) {
ret = zx2967_i2c_xfer_bytes(i2c, I2C_FIFO_MAX);
if (ret)
return ret;
}
if (i2c->residue > 0) {
ret = zx2967_i2c_xfer_bytes(i2c, i2c->residue);
if (ret)
return ret;
}
i2c->residue = 0;
i2c->access_cnt = 0;
return 0;
}
static int zx2967_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
int ret;
int i;
if (i2c->is_suspended)
return -EBUSY;
zx2967_set_addr(i2c, msgs->addr);
for (i = 0; i < num; i++) {
ret = zx2967_i2c_xfer_msg(i2c, &msgs[i]);
if (ret)
return ret;
}
return num;
}
static void
zx2967_smbus_xfer_prepare(struct zx2967_i2c *i2c, u16 addr,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
u32 val;
val = zx2967_i2c_readl(i2c, REG_RDCONF);
val |= I2C_RFIFO_RESET;
zx2967_i2c_writel(i2c, val, REG_RDCONF);
zx2967_set_addr(i2c, addr);
val = zx2967_i2c_readl(i2c, REG_CMD);
val &= ~I2C_RW_READ;
zx2967_i2c_writel(i2c, val, REG_CMD);
switch (size) {
case I2C_SMBUS_BYTE:
zx2967_i2c_writel(i2c, command, REG_DATA);
break;
case I2C_SMBUS_BYTE_DATA:
zx2967_i2c_writel(i2c, command, REG_DATA);
if (read_write == I2C_SMBUS_WRITE)
zx2967_i2c_writel(i2c, data->byte, REG_DATA);
break;
case I2C_SMBUS_WORD_DATA:
zx2967_i2c_writel(i2c, command, REG_DATA);
if (read_write == I2C_SMBUS_WRITE) {
zx2967_i2c_writel(i2c, (data->word >> 8), REG_DATA);
zx2967_i2c_writel(i2c, (data->word & 0xff),
REG_DATA);
}
break;
}
}
static int zx2967_smbus_xfer_read(struct zx2967_i2c *i2c, int size,
union i2c_smbus_data *data)
{
unsigned long time_left;
u8 buf[2];
u32 val;
reinit_completion(&i2c->complete);
val = zx2967_i2c_readl(i2c, REG_CMD);
val |= I2C_CMB_RW_EN;
zx2967_i2c_writel(i2c, val, REG_CMD);
val = zx2967_i2c_readl(i2c, REG_CMD);
val |= I2C_START;
zx2967_i2c_writel(i2c, val, REG_CMD);
time_left = wait_for_completion_timeout(&i2c->complete,
I2C_TIMEOUT);
if (time_left == 0)
return -ETIMEDOUT;
if (i2c->error)
return i2c->error;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
val = zx2967_i2c_readl(i2c, REG_DATA);
data->byte = val;
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
buf[0] = zx2967_i2c_readl(i2c, REG_DATA);
buf[1] = zx2967_i2c_readl(i2c, REG_DATA);
data->word = (buf[0] << 8) | buf[1];
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int zx2967_smbus_xfer_write(struct zx2967_i2c *i2c)
{
unsigned long time_left;
u32 val;
reinit_completion(&i2c->complete);
val = zx2967_i2c_readl(i2c, REG_CMD);
val |= I2C_START;
zx2967_i2c_writel(i2c, val, REG_CMD);
time_left = wait_for_completion_timeout(&i2c->complete,
I2C_TIMEOUT);
if (time_left == 0)
return -ETIMEDOUT;
if (i2c->error)
return i2c->error;
return 0;
}
static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
if (size == I2C_SMBUS_QUICK)
read_write = I2C_SMBUS_WRITE;
switch (size) {
case I2C_SMBUS_QUICK:
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
case I2C_SMBUS_WORD_DATA:
zx2967_smbus_xfer_prepare(i2c, addr, read_write,
command, size, data);
break;
default:
return -EOPNOTSUPP;
}
if (read_write == I2C_SMBUS_READ)
return zx2967_smbus_xfer_read(i2c, size, data);
return zx2967_smbus_xfer_write(i2c);
}
static u32 zx2967_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C |
I2C_FUNC_SMBUS_QUICK |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_I2C_BLOCK;
}
static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
{
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
i2c->is_suspended = true;
clk_disable_unprepare(i2c->clk);
return 0;
}
static int __maybe_unused zx2967_i2c_resume(struct device *dev)
{
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
i2c->is_suspended = false;
clk_prepare_enable(i2c->clk);
return 0;
}
static SIMPLE_DEV_PM_OPS(zx2967_i2c_dev_pm_ops,
zx2967_i2c_suspend, zx2967_i2c_resume);
static const struct i2c_algorithm zx2967_i2c_algo = {
.master_xfer = zx2967_i2c_xfer,
.smbus_xfer = zx2967_smbus_xfer,
.functionality = zx2967_i2c_func,
};
static const struct of_device_id zx2967_i2c_of_match[] = {
{ .compatible = "zte,zx296718-i2c", },
{ },
};
MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match);
static int zx2967_i2c_probe(struct platform_device *pdev)
{
struct zx2967_i2c *i2c;
void __iomem *reg_base;
struct resource *res;
struct clk *clk;
int ret;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "missing controller clock");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable i2c_clk\n");
return ret;
}
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
&i2c->clk_freq);
if (ret) {
dev_err(&pdev->dev, "missing clock-frequency");
return ret;
}
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
i2c->irq = ret;
i2c->reg_base = reg_base;
i2c->clk = clk;
init_completion(&i2c->complete);
platform_set_drvdata(pdev, i2c);
ret = zx2967_i2c_reset_hardware(i2c);
if (ret) {
dev_err(&pdev->dev, "failed to initialize i2c controller\n");
goto err_clk_unprepare;
}
ret = devm_request_irq(&pdev->dev, i2c->irq,
zx2967_i2c_isr, 0, dev_name(&pdev->dev), i2c);
if (ret) {
dev_err(&pdev->dev, "failed to request irq %i\n", i2c->irq);
goto err_clk_unprepare;
}
i2c_set_adapdata(&i2c->adap, i2c);
strlcpy(i2c->adap.name, "zx2967 i2c adapter",
sizeof(i2c->adap.name));
i2c->adap.algo = &zx2967_i2c_algo;
i2c->adap.nr = pdev->id;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret)
goto err_clk_unprepare;
return 0;
err_clk_unprepare:
clk_disable_unprepare(i2c->clk);
return ret;
}
static int zx2967_i2c_remove(struct platform_device *pdev)
{
struct zx2967_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
clk_disable_unprepare(i2c->clk);
return 0;
}
static struct platform_driver zx2967_i2c_driver = {
.probe = zx2967_i2c_probe,
.remove = zx2967_i2c_remove,
.driver = {
.name = "zx2967_i2c",
.of_match_table = zx2967_i2c_of_match,
.pm = &zx2967_i2c_dev_pm_ops,
},
};
module_platform_driver(zx2967_i2c_driver);
MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
MODULE_DESCRIPTION("ZTE ZX2967 I2C Bus Controller driver");
MODULE_LICENSE("GPL v2");

665
drivers/i2c/i2c-core-acpi.c Normal file
View File

@ -0,0 +1,665 @@
/*
* Linux I2C core ACPI support code
*
* Copyright (C) 2014 Intel Corp, Author: Lan Tianyu <tianyu.lan@intel.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "i2c-core.h"
struct i2c_acpi_handler_data {
struct acpi_connection_info info;
struct i2c_adapter *adapter;
};
struct gsb_buffer {
u8 status;
u8 len;
union {
u16 wdata;
u8 bdata;
u8 data[0];
};
} __packed;
struct i2c_acpi_lookup {
struct i2c_board_info *info;
acpi_handle adapter_handle;
acpi_handle device_handle;
acpi_handle search_handle;
int n;
int index;
u32 speed;
u32 min_speed;
};
static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
{
struct i2c_acpi_lookup *lookup = data;
struct i2c_board_info *info = lookup->info;
struct acpi_resource_i2c_serialbus *sb;
acpi_status status;
if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
return 1;
sb = &ares->data.i2c_serial_bus;
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)
return 1;
if (lookup->index != -1 && lookup->n++ != lookup->index)
return 1;
status = acpi_get_handle(lookup->device_handle,
sb->resource_source.string_ptr,
&lookup->adapter_handle);
if (!ACPI_SUCCESS(status))
return 1;
info->addr = sb->slave_address;
lookup->speed = sb->connection_speed;
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
info->flags |= I2C_CLIENT_TEN;
return 1;
}
static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
/*
* ACPI video acpi_devices, which are handled by the acpi-video driver
* sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these.
*/
{ ACPI_VIDEO_HID, 0 },
{}
};
static int i2c_acpi_do_lookup(struct acpi_device *adev,
struct i2c_acpi_lookup *lookup)
{
struct i2c_board_info *info = lookup->info;
struct list_head resource_list;
int ret;
if (acpi_bus_get_status(adev) || !adev->status.present ||
acpi_device_enumerated(adev))
return -EINVAL;
if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0)
return -ENODEV;
memset(info, 0, sizeof(*info));
lookup->device_handle = acpi_device_handle(adev);
/* Look up for I2cSerialBus resource */
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
i2c_acpi_fill_info, lookup);
acpi_dev_free_resource_list(&resource_list);
if (ret < 0 || !info->addr)
return -EINVAL;
return 0;
}
static int i2c_acpi_get_info(struct acpi_device *adev,
struct i2c_board_info *info,
struct i2c_adapter *adapter,
acpi_handle *adapter_handle)
{
struct list_head resource_list;
struct resource_entry *entry;
struct i2c_acpi_lookup lookup;
int ret;
memset(&lookup, 0, sizeof(lookup));
lookup.info = info;
lookup.index = -1;
ret = i2c_acpi_do_lookup(adev, &lookup);
if (ret)
return ret;
if (adapter) {
/* The adapter must match the one in I2cSerialBus() connector */
if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle)
return -ENODEV;
} else {
struct acpi_device *adapter_adev;
/* The adapter must be present */
if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev))
return -ENODEV;
if (acpi_bus_get_status(adapter_adev) ||
!adapter_adev->status.present)
return -ENODEV;
}
info->fwnode = acpi_fwnode_handle(adev);
if (adapter_handle)
*adapter_handle = lookup.adapter_handle;
/* Then fill IRQ number if any */
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
if (ret < 0)
return -EINVAL;
resource_list_for_each_entry(entry, &resource_list) {
if (resource_type(entry->res) == IORESOURCE_IRQ) {
info->irq = entry->res->start;
break;
}
}
acpi_dev_free_resource_list(&resource_list);
acpi_set_modalias(adev, dev_name(&adev->dev), info->type,
sizeof(info->type));
return 0;
}
static void i2c_acpi_register_device(struct i2c_adapter *adapter,
struct acpi_device *adev,
struct i2c_board_info *info)
{
adev->power.flags.ignore_parent = true;
acpi_device_set_enumerated(adev);
if (!i2c_new_device(adapter, info)) {
adev->power.flags.ignore_parent = false;
dev_err(&adapter->dev,
"failed to add I2C device %s from ACPI\n",
dev_name(&adev->dev));
}
}
static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_adapter *adapter = data;
struct acpi_device *adev;
struct i2c_board_info info;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (i2c_acpi_get_info(adev, &info, adapter, NULL))
return AE_OK;
i2c_acpi_register_device(adapter, adev, &info);
return AE_OK;
}
#define I2C_ACPI_MAX_SCAN_DEPTH 32
/**
* i2c_acpi_register_devices - enumerate I2C slave devices behind adapter
* @adap: pointer to adapter
*
* Enumerate all I2C slave devices behind this adapter by walking the ACPI
* namespace. When a device is found it will be added to the Linux device
* model and bound to the corresponding ACPI handle.
*/
void i2c_acpi_register_devices(struct i2c_adapter *adap)
{
acpi_status status;
if (!has_acpi_companion(&adap->dev))
return;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
I2C_ACPI_MAX_SCAN_DEPTH,
i2c_acpi_add_device, NULL,
adap, NULL);
if (ACPI_FAILURE(status))
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
}
static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_acpi_lookup *lookup = data;
struct acpi_device *adev;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (i2c_acpi_do_lookup(adev, lookup))
return AE_OK;
if (lookup->search_handle != lookup->adapter_handle)
return AE_OK;
if (lookup->speed <= lookup->min_speed)
lookup->min_speed = lookup->speed;
return AE_OK;
}
/**
* i2c_acpi_find_bus_speed - find I2C bus speed from ACPI
* @dev: The device owning the bus
*
* Find the I2C bus speed by walking the ACPI namespace for all I2C slaves
* devices connected to this bus and use the speed of slowest device.
*
* Returns the speed in Hz or zero
*/
u32 i2c_acpi_find_bus_speed(struct device *dev)
{
struct i2c_acpi_lookup lookup;
struct i2c_board_info dummy;
acpi_status status;
if (!has_acpi_companion(dev))
return 0;
memset(&lookup, 0, sizeof(lookup));
lookup.search_handle = ACPI_HANDLE(dev);
lookup.min_speed = UINT_MAX;
lookup.info = &dummy;
lookup.index = -1;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
I2C_ACPI_MAX_SCAN_DEPTH,
i2c_acpi_lookup_speed, NULL,
&lookup, NULL);
if (ACPI_FAILURE(status)) {
dev_warn(dev, "unable to find I2C bus speed from ACPI\n");
return 0;
}
return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
}
EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);
static int i2c_acpi_match_adapter(struct device *dev, void *data)
{
struct i2c_adapter *adapter = i2c_verify_adapter(dev);
if (!adapter)
return 0;
return ACPI_HANDLE(dev) == (acpi_handle)data;
}
static int i2c_acpi_match_device(struct device *dev, void *data)
{
return ACPI_COMPANION(dev) == data;
}
static struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle)
{
struct device *dev;
dev = bus_find_device(&i2c_bus_type, NULL, handle,
i2c_acpi_match_adapter);
return dev ? i2c_verify_adapter(dev) : NULL;
}
static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev)
{
struct device *dev;
dev = bus_find_device(&i2c_bus_type, NULL, adev, i2c_acpi_match_device);
return dev ? i2c_verify_client(dev) : NULL;
}
static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value,
void *arg)
{
struct acpi_device *adev = arg;
struct i2c_board_info info;
acpi_handle adapter_handle;
struct i2c_adapter *adapter;
struct i2c_client *client;
switch (value) {
case ACPI_RECONFIG_DEVICE_ADD:
if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle))
break;
adapter = i2c_acpi_find_adapter_by_handle(adapter_handle);
if (!adapter)
break;
i2c_acpi_register_device(adapter, adev, &info);
break;
case ACPI_RECONFIG_DEVICE_REMOVE:
if (!acpi_device_enumerated(adev))
break;
client = i2c_acpi_find_client_by_adev(adev);
if (!client)
break;
i2c_unregister_device(client);
put_device(&client->dev);
break;
}
return NOTIFY_OK;
}
struct notifier_block i2c_acpi_notifier = {
.notifier_call = i2c_acpi_notify,
};
/**
* i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource
* @dev: Device owning the ACPI resources to get the client from
* @index: Index of ACPI resource to get
* @info: describes the I2C device; note this is modified (addr gets set)
* Context: can sleep
*
* By default the i2c subsys creates an i2c-client for the first I2cSerialBus
* resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus
* resources, in that case this function can be used to create an i2c-client
* for other I2cSerialBus resources in the Current Resource Settings table.
*
* Also see i2c_new_device, which this function calls to create the i2c-client.
*
* Returns a pointer to the new i2c-client, or NULL if the adapter is not found.
*/
struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
struct i2c_board_info *info)
{
struct i2c_acpi_lookup lookup;
struct i2c_adapter *adapter;
struct acpi_device *adev;
LIST_HEAD(resource_list);
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return NULL;
memset(&lookup, 0, sizeof(lookup));
lookup.info = info;
lookup.device_handle = acpi_device_handle(adev);
lookup.index = index;
ret = acpi_dev_get_resources(adev, &resource_list,
i2c_acpi_fill_info, &lookup);
acpi_dev_free_resource_list(&resource_list);
if (ret < 0 || !info->addr)
return NULL;
adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle);
if (!adapter)
return NULL;
return i2c_new_device(adapter, info);
}
EXPORT_SYMBOL_GPL(i2c_acpi_new_device);
#ifdef CONFIG_ACPI_I2C_OPREGION
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
u8 cmd, u8 *data, u8 data_len)
{
struct i2c_msg msgs[2];
int ret;
u8 *buffer;
buffer = kzalloc(data_len, GFP_KERNEL);
if (!buffer)
return AE_NO_MEMORY;
msgs[0].addr = client->addr;
msgs[0].flags = client->flags;
msgs[0].len = 1;
msgs[0].buf = &cmd;
msgs[1].addr = client->addr;
msgs[1].flags = client->flags | I2C_M_RD;
msgs[1].len = data_len;
msgs[1].buf = buffer;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
dev_err(&client->adapter->dev, "i2c read failed\n");
else
memcpy(data, buffer, data_len);
kfree(buffer);
return ret;
}
static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
u8 cmd, u8 *data, u8 data_len)
{
struct i2c_msg msgs[1];
u8 *buffer;
int ret = AE_OK;
buffer = kzalloc(data_len + 1, GFP_KERNEL);
if (!buffer)
return AE_NO_MEMORY;
buffer[0] = cmd;
memcpy(buffer + 1, data, data_len);
msgs[0].addr = client->addr;
msgs[0].flags = client->flags;
msgs[0].len = data_len + 1;
msgs[0].buf = buffer;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
dev_err(&client->adapter->dev, "i2c write failed\n");
kfree(buffer);
return ret;
}
static acpi_status
i2c_acpi_space_handler(u32 function, acpi_physical_address command,
u32 bits, u64 *value64,
void *handler_context, void *region_context)
{
struct gsb_buffer *gsb = (struct gsb_buffer *)value64;
struct i2c_acpi_handler_data *data = handler_context;
struct acpi_connection_info *info = &data->info;
struct acpi_resource_i2c_serialbus *sb;
struct i2c_adapter *adapter = data->adapter;
struct i2c_client *client;
struct acpi_resource *ares;
u32 accessor_type = function >> 16;
u8 action = function & ACPI_IO_MASK;
acpi_status ret;
int status;
ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
if (ACPI_FAILURE(ret))
return ret;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
ret = AE_NO_MEMORY;
goto err;
}
if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
ret = AE_BAD_PARAMETER;
goto err;
}
sb = &ares->data.i2c_serial_bus;
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
ret = AE_BAD_PARAMETER;
goto err;
}
client->adapter = adapter;
client->addr = sb->slave_address;
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
client->flags |= I2C_CLIENT_TEN;
switch (accessor_type) {
case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV:
if (action == ACPI_READ) {
status = i2c_smbus_read_byte(client);
if (status >= 0) {
gsb->bdata = status;
status = 0;
}
} else {
status = i2c_smbus_write_byte(client, gsb->bdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_BYTE:
if (action == ACPI_READ) {
status = i2c_smbus_read_byte_data(client, command);
if (status >= 0) {
gsb->bdata = status;
status = 0;
}
} else {
status = i2c_smbus_write_byte_data(client, command,
gsb->bdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_WORD:
if (action == ACPI_READ) {
status = i2c_smbus_read_word_data(client, command);
if (status >= 0) {
gsb->wdata = status;
status = 0;
}
} else {
status = i2c_smbus_write_word_data(client, command,
gsb->wdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_BLOCK:
if (action == ACPI_READ) {
status = i2c_smbus_read_block_data(client, command,
gsb->data);
if (status >= 0) {
gsb->len = status;
status = 0;
}
} else {
status = i2c_smbus_write_block_data(client, command,
gsb->len, gsb->data);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE:
if (action == ACPI_READ) {
status = acpi_gsb_i2c_read_bytes(client, command,
gsb->data, info->access_length);
if (status > 0)
status = 0;
} else {
status = acpi_gsb_i2c_write_bytes(client, command,
gsb->data, info->access_length);
}
break;
default:
dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
accessor_type, client->addr);
ret = AE_BAD_PARAMETER;
goto err;
}
gsb->status = status;
err:
kfree(client);
ACPI_FREE(ares);
return ret;
}
int i2c_acpi_install_space_handler(struct i2c_adapter *adapter)
{
acpi_handle handle;
struct i2c_acpi_handler_data *data;
acpi_status status;
if (!adapter->dev.parent)
return -ENODEV;
handle = ACPI_HANDLE(adapter->dev.parent);
if (!handle)
return -ENODEV;
data = kzalloc(sizeof(struct i2c_acpi_handler_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
data->adapter = adapter;
status = acpi_bus_attach_private_data(handle, (void *)data);
if (ACPI_FAILURE(status)) {
kfree(data);
return -ENOMEM;
}
status = acpi_install_address_space_handler(handle,
ACPI_ADR_SPACE_GSBUS,
&i2c_acpi_space_handler,
NULL,
data);
if (ACPI_FAILURE(status)) {
dev_err(&adapter->dev, "Error installing i2c space handler\n");
acpi_bus_detach_private_data(handle);
kfree(data);
return -ENOMEM;
}
acpi_walk_dep_device_list(handle);
return 0;
}
void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter)
{
acpi_handle handle;
struct i2c_acpi_handler_data *data;
acpi_status status;
if (!adapter->dev.parent)
return;
handle = ACPI_HANDLE(adapter->dev.parent);
if (!handle)
return;
acpi_remove_address_space_handler(handle,
ACPI_ADR_SPACE_GSBUS,
&i2c_acpi_space_handler);
status = acpi_bus_get_private_data(handle, (void **)&data);
if (ACPI_SUCCESS(status))
kfree(data);
acpi_bus_detach_private_data(handle);
}
#endif /* CONFIG_ACPI_I2C_OPREGION */

File diff suppressed because it is too large Load Diff

276
drivers/i2c/i2c-core-of.c Normal file
View File

@ -0,0 +1,276 @@
/*
* Linux I2C core OF support code
*
* Copyright (C) 2008 Jochen Friedrich <jochen@scram.de>
* based on a previous patch from Jon Smirl <jonsmirl@gmail.com>
*
* Copyright (C) 2013 Wolfram Sang <wsa@the-dreams.de>
*
* 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 <dt-bindings/i2c/i2c.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "i2c-core.h"
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr_be;
u32 addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
addr_be = of_get_property(node, "reg", &len);
if (!addr_be || (len < sizeof(*addr_be))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info.flags |= I2C_CLIENT_SLAVE;
}
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
addr, node->full_name);
return ERR_PTR(-EINVAL);
}
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_property_read_bool(node, "host-notify"))
info.flags |= I2C_CLIENT_HOST_NOTIFY;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *bus, *node;
struct i2c_client *client;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
if (!bus)
bus = of_node_get(adap->dev.of_node);
for_each_available_child_of_node(bus, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
dev_warn(&adap->dev,
"Failed to create I2C device for %s\n",
node->full_name);
of_node_clear_flag(node, OF_POPULATED);
}
}
of_node_put(bus);
}
static int of_dev_node_match(struct device *dev, void *data)
{
return dev->of_node == data;
}
/* must call put_device() when done with returned i2c_client device */
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
struct device *dev;
struct i2c_client *client;
dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
if (!dev)
return NULL;
client = i2c_verify_client(dev);
if (!client)
put_device(dev);
return client;
}
EXPORT_SYMBOL(of_find_i2c_device_by_node);
/* must call put_device() when done with returned i2c_adapter device */
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{
struct device *dev;
struct i2c_adapter *adapter;
dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
if (!dev)
return NULL;
adapter = i2c_verify_adapter(dev);
if (!adapter)
put_device(dev);
return adapter;
}
EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node)
{
struct i2c_adapter *adapter;
adapter = of_find_i2c_adapter_by_node(node);
if (!adapter)
return NULL;
if (!try_module_get(adapter->owner)) {
put_device(&adapter->dev);
adapter = NULL;
}
return adapter;
}
EXPORT_SYMBOL(of_get_i2c_adapter_by_node);
static const struct of_device_id*
i2c_of_match_device_sysfs(const struct of_device_id *matches,
struct i2c_client *client)
{
const char *name;
for (; matches->compatible[0]; matches++) {
/*
* Adding devices through the i2c sysfs interface provides us
* a string to match which may be compatible with the device
* tree compatible strings, however with no actual of_node the
* of_match_device() will not match
*/
if (sysfs_streq(client->name, matches->compatible))
return matches;
name = strchr(matches->compatible, ',');
if (!name)
name = matches->compatible;
else
name++;
if (sysfs_streq(client->name, name))
return matches;
}
return NULL;
}
const struct of_device_id
*i2c_of_match_device(const struct of_device_id *matches,
struct i2c_client *client)
{
const struct of_device_id *match;
if (!(client && matches))
return NULL;
match = of_match_device(matches, &client->dev);
if (match)
return match;
return i2c_of_match_device_sysfs(matches, client);
}
EXPORT_SYMBOL_GPL(i2c_of_match_device);
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
void *arg)
{
struct of_reconfig_data *rd = arg;
struct i2c_adapter *adap;
struct i2c_client *client;
switch (of_reconfig_get_state_change(action, rd)) {
case OF_RECONFIG_CHANGE_ADD:
adap = of_find_i2c_adapter_by_node(rd->dn->parent);
if (adap == NULL)
return NOTIFY_OK; /* not for us */
if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
put_device(&adap->dev);
return NOTIFY_OK;
}
client = of_i2c_register_device(adap, rd->dn);
put_device(&adap->dev);
if (IS_ERR(client)) {
dev_err(&adap->dev, "failed to create client for '%s'\n",
rd->dn->full_name);
of_node_clear_flag(rd->dn, OF_POPULATED);
return notifier_from_errno(PTR_ERR(client));
}
break;
case OF_RECONFIG_CHANGE_REMOVE:
/* already depopulated? */
if (!of_node_check_flag(rd->dn, OF_POPULATED))
return NOTIFY_OK;
/* find our device by node */
client = of_find_i2c_device_by_node(rd->dn);
if (client == NULL)
return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */
i2c_unregister_device(client);
/* and put the reference of the find */
put_device(&client->dev);
break;
}
return NOTIFY_OK;
}
struct notifier_block i2c_of_notifier = {
.notifier_call = of_i2c_notify,
};
#endif /* CONFIG_OF_DYNAMIC */

View File

@ -0,0 +1,115 @@
/*
* Linux I2C core slave support code
*
* Copyright (C) 2014 by Wolfram Sang <wsa@sang-engineering.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <dt-bindings/i2c/i2c.h>
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include "i2c-core.h"
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
{
int ret;
if (!client || !slave_cb) {
WARN(1, "insufficient data\n");
return -EINVAL;
}
if (!(client->flags & I2C_CLIENT_SLAVE))
dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n",
__func__);
if (!(client->flags & I2C_CLIENT_TEN)) {
/* Enforce stricter address checking */
ret = i2c_check_7bit_addr_validity_strict(client->addr);
if (ret) {
dev_err(&client->dev, "%s: invalid address\n", __func__);
return ret;
}
}
if (!client->adapter->algo->reg_slave) {
dev_err(&client->dev, "%s: not supported by adapter\n", __func__);
return -EOPNOTSUPP;
}
client->slave_cb = slave_cb;
i2c_lock_adapter(client->adapter);
ret = client->adapter->algo->reg_slave(client);
i2c_unlock_adapter(client->adapter);
if (ret) {
client->slave_cb = NULL;
dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret);
}
return ret;
}
EXPORT_SYMBOL_GPL(i2c_slave_register);
int i2c_slave_unregister(struct i2c_client *client)
{
int ret;
if (!client->adapter->algo->unreg_slave) {
dev_err(&client->dev, "%s: not supported by adapter\n", __func__);
return -EOPNOTSUPP;
}
i2c_lock_adapter(client->adapter);
ret = client->adapter->algo->unreg_slave(client);
i2c_unlock_adapter(client->adapter);
if (ret == 0)
client->slave_cb = NULL;
else
dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
/**
* i2c_detect_slave_mode - detect operation mode
* @dev: The device owning the bus
*
* This checks the device nodes for an I2C slave by checking the address
* used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS
* flag this means the device is configured to act as a I2C slave and it will
* be listening at that address.
*
* Returns true if an I2C own slave address is detected, otherwise returns
* false.
*/
bool i2c_detect_slave_mode(struct device *dev)
{
if (IS_BUILTIN(CONFIG_OF) && dev->of_node) {
struct device_node *child;
u32 reg;
for_each_child_of_node(dev->of_node, child) {
of_property_read_u32(child, "reg", &reg);
if (reg & I2C_OWN_SLAVE_ADDRESS) {
of_node_put(child);
return true;
}
}
} else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) {
dev_dbg(dev, "ACPI slave is not supported yet\n");
}
return false;
}
EXPORT_SYMBOL_GPL(i2c_detect_slave_mode);

View File

@ -0,0 +1,594 @@
/*
* Linux I2C core SMBus and SMBus emulation code
*
* This file contains the SMBus functions which are always included in the I2C
* core because they can be emulated via I2C. SMBus specific extensions
* (e.g. smbalert) are handled in a seperate i2c-smbus module.
*
* All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
* SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
* Jean Delvare <jdelvare@suse.de>
*
* 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/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#define CREATE_TRACE_POINTS
#include <trace/events/smbus.h>
/* The SMBus parts */
#define POLY (0x1070U << 3)
static u8 crc8(u16 data)
{
int i;
for (i = 0; i < 8; i++) {
if (data & 0x8000)
data = data ^ POLY;
data = data << 1;
}
return (u8)(data >> 8);
}
/* Incremental CRC8 over count bytes in the array pointed to by p */
static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
{
int i;
for (i = 0; i < count; i++)
crc = crc8((crc ^ p[i]) << 8);
return crc;
}
/* Assume a 7-bit address, which is reasonable for SMBus */
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
{
/* The address will be sent first */
u8 addr = i2c_8bit_addr_from_msg(msg);
pec = i2c_smbus_pec(pec, &addr, 1);
/* The data buffer follows */
return i2c_smbus_pec(pec, msg->buf, msg->len);
}
/* Used for write only transactions */
static inline void i2c_smbus_add_pec(struct i2c_msg *msg)
{
msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);
msg->len++;
}
/* Return <0 on CRC error
If there was a write before this read (most cases) we need to take the
partial CRC from the write part into account.
Note that this function does modify the message (we need to decrease the
message length to hide the CRC byte from the caller). */
static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
{
u8 rpec = msg->buf[--msg->len];
cpec = i2c_smbus_msg_pec(cpec, msg);
if (rpec != cpec) {
pr_debug("Bad PEC 0x%02x vs. 0x%02x\n",
rpec, cpec);
return -EBADMSG;
}
return 0;
}
/**
* i2c_smbus_read_byte - SMBus "receive byte" protocol
* @client: Handle to slave device
*
* This executes the SMBus "receive byte" protocol, returning negative errno
* else the byte received from the device.
*/
s32 i2c_smbus_read_byte(const struct i2c_client *client)
{
union i2c_smbus_data data;
int status;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE, &data);
return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte);
/**
* i2c_smbus_write_byte - SMBus "send byte" protocol
* @client: Handle to slave device
* @value: Byte to be sent
*
* This executes the SMBus "send byte" protocol, returning negative errno
* else zero on success.
*/
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}
EXPORT_SYMBOL(i2c_smbus_write_byte);
/**
* i2c_smbus_read_byte_data - SMBus "read byte" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
*
* This executes the SMBus "read byte" protocol, returning negative errno
* else a data byte received from the device.
*/
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
int status;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BYTE_DATA, &data);
return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte_data);
/**
* i2c_smbus_write_byte_data - SMBus "write byte" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @value: Byte being written
*
* This executes the SMBus "write byte" protocol, returning negative errno
* else zero on success.
*/
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command,
u8 value)
{
union i2c_smbus_data data;
data.byte = value;
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_BYTE_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_byte_data);
/**
* i2c_smbus_read_word_data - SMBus "read word" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
*
* This executes the SMBus "read word" protocol, returning negative errno
* else a 16-bit unsigned "word" received from the device.
*/
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
int status;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_WORD_DATA, &data);
return (status < 0) ? status : data.word;
}
EXPORT_SYMBOL(i2c_smbus_read_word_data);
/**
* i2c_smbus_write_word_data - SMBus "write word" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @value: 16-bit "word" being written
*
* This executes the SMBus "write word" protocol, returning negative errno
* else zero on success.
*/
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
u16 value)
{
union i2c_smbus_data data;
data.word = value;
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_WORD_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_word_data);
/**
* i2c_smbus_read_block_data - SMBus "block read" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @values: Byte array into which data will be read; big enough to hold
* the data returned by the slave. SMBus allows at most 32 bytes.
*
* This executes the SMBus "block read" protocol, returning negative errno
* else the number of data bytes in the slave's response.
*
* Note that using this function requires that the client's adapter support
* the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers
* support this; its emulation through I2C messaging relies on a specific
* mechanism (I2C_M_RECV_LEN) which may not be implemented.
*/
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
u8 *values)
{
union i2c_smbus_data data;
int status;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BLOCK_DATA, &data);
if (status)
return status;
memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
}
EXPORT_SYMBOL(i2c_smbus_read_block_data);
/**
* i2c_smbus_write_block_data - SMBus "block write" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @length: Size of data block; SMBus allows at most 32 bytes
* @values: Byte array which will be written.
*
* This executes the SMBus "block write" protocol, returning negative errno
* else zero on success.
*/
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,
u8 length, const u8 *values)
{
union i2c_smbus_data data;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
memcpy(&data.block[1], values, length);
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_BLOCK_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_block_data);
/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command,
u8 length, u8 *values)
{
union i2c_smbus_data data;
int status;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
if (status < 0)
return status;
memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
}
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
u8 length, const u8 *values)
{
union i2c_smbus_data data;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
memcpy(data.block + 1, values, length);
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
/* Simulate a SMBus command using the i2c protocol
No checking of parameters is done! */
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
/* So we need to generate a series of msgs. In the case of writing, we
need to use only one message; when reading, we need two. We initialize
most things with sane defaults, to keep the code below somewhat
simpler. */
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
int i;
u8 partial_pec = 0;
int status;
struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = flags,
.len = 1,
.buf = msgbuf0,
}, {
.addr = addr,
.flags = flags | I2C_M_RD,
.len = 0,
.buf = msgbuf1,
},
};
msgbuf0[0] = command;
switch (size) {
case I2C_SMBUS_QUICK:
msg[0].len = 0;
/* Special case: The read/write field is used as data */
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
I2C_M_RD : 0);
num = 1;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
/* Special case: only a read! */
msg[0].flags = I2C_M_RD | flags;
num = 1;
}
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 1;
else {
msg[0].len = 2;
msgbuf0[1] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 2;
else {
msg[0].len = 3;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = data->word >> 8;
}
break;
case I2C_SMBUS_PROC_CALL:
num = 2; /* Special case */
read_write = I2C_SMBUS_READ;
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = data->word >> 8;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by
the underlying bus driver */
} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
num = 2; /* Another special case */
read_write = I2C_SMBUS_READ;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
msg[0].len = data->block[0] + 2;
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by
the underlying bus driver */
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].len = data->block[0];
} else {
msg[0].len = data->block[0] + 1;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
for (i = 1; i <= data->block[0]; i++)
msgbuf0[i] = data->block[i];
}
break;
default:
dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}
i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
if (i) {
/* Compute PEC if first message is a write */
if (!(msg[0].flags & I2C_M_RD)) {
if (num == 1) /* Write only */
i2c_smbus_add_pec(&msg[0]);
else /* Write followed by read */
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
}
/* Ask for PEC if last message is a read */
if (msg[num-1].flags & I2C_M_RD)
msg[num-1].len++;
}
status = i2c_transfer(adapter, msg, num);
if (status < 0)
return status;
/* Check PEC if last message is a read */
if (i && (msg[num-1].flags & I2C_M_RD)) {
status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
if (status < 0)
return status;
}
if (read_write == I2C_SMBUS_READ)
switch (size) {
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < data->block[0]; i++)
data->block[i+1] = msgbuf1[i];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
for (i = 0; i < msgbuf1[0] + 1; i++)
data->block[i] = msgbuf1[i];
break;
}
return 0;
}
/**
* i2c_smbus_xfer - execute SMBus protocol operations
* @adapter: Handle to I2C bus
* @addr: Address of SMBus slave on that bus
* @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
* @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
* @command: Byte interpreted by slave, for protocols which use such bytes
* @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
* @data: Data to be read or written
*
* This executes an SMBus protocol operation, and returns a negative
* errno code else zero on success.
*/
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
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) {
i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
goto trace;
/*
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
* implement native support for the SMBus operation.
*/
}
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);
/**
* i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes
* @values: Byte array into which data will be read; big enough to hold
* the data returned by the slave. SMBus allows at most
* I2C_SMBUS_BLOCK_MAX bytes.
*
* This executes the SMBus "block read" protocol if supported by the adapter.
* If block read is not supported, it emulates it using either word or byte
* read protocols depending on availability.
*
* The addresses of the I2C slave device that are accessed with this function
* must be mapped to a linear region, so that a block read will have the same
* effect as a byte read. Before using this function you must double-check
* if the I2C slave does support exchanging a block transfer with a byte
* transfer.
*/
s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
u8 command, u8 length, u8 *values)
{
u8 i = 0;
int status;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
return i2c_smbus_read_i2c_block_data(client, command, length, values);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA))
return -EOPNOTSUPP;
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
while ((i + 2) <= length) {
status = i2c_smbus_read_word_data(client, command + i);
if (status < 0)
return status;
values[i] = status & 0xff;
values[i + 1] = status >> 8;
i += 2;
}
}
while (i < length) {
status = i2c_smbus_read_byte_data(client, command + i);
if (status < 0)
return status;
values[i] = status;
i++;
}
return i;
}
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);

View File

@ -27,3 +27,27 @@ extern struct rw_semaphore __i2c_board_lock;
extern struct list_head __i2c_board_list; extern struct list_head __i2c_board_list;
extern int __i2c_first_dynamic_bus_num; extern int __i2c_first_dynamic_bus_num;
int i2c_check_addr_validity(unsigned addr, unsigned short flags);
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
#ifdef CONFIG_ACPI
void i2c_acpi_register_devices(struct i2c_adapter *adap);
#else /* CONFIG_ACPI */
static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { }
#endif /* CONFIG_ACPI */
extern struct notifier_block i2c_acpi_notifier;
#ifdef CONFIG_ACPI_I2C_OPREGION
int i2c_acpi_install_space_handler(struct i2c_adapter *adapter);
void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter);
#else /* CONFIG_ACPI_I2C_OPREGION */
static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { return 0; }
static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { }
#endif /* CONFIG_ACPI_I2C_OPREGION */
#ifdef CONFIG_OF
void of_i2c_register_devices(struct i2c_adapter *adap);
#else
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
#endif
extern struct notifier_block i2c_of_notifier;

View File

@ -16,6 +16,7 @@
*/ */
#define DEBUG 1 #define DEBUG 1
#define pr_fmt(fmt) "i2c-stub: " fmt
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/i2c.h> #include <linux/i2c.h>
@ -342,7 +343,7 @@ static int __init i2c_stub_allocate_banks(int i)
if (!chip->bank_words) if (!chip->bank_words)
return -ENOMEM; return -ENOMEM;
pr_debug("i2c-stub: Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n", pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n",
chip->bank_mask, chip->bank_size, chip->bank_start, chip->bank_mask, chip->bank_size, chip->bank_start,
chip->bank_end); chip->bank_end);
@ -363,28 +364,27 @@ static int __init i2c_stub_init(void)
int i, ret; int i, ret;
if (!chip_addr[0]) { if (!chip_addr[0]) {
pr_err("i2c-stub: Please specify a chip address\n"); pr_err("Please specify a chip address\n");
return -ENODEV; return -ENODEV;
} }
for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) {
pr_err("i2c-stub: Invalid chip address 0x%02x\n", pr_err("Invalid chip address 0x%02x\n",
chip_addr[i]); chip_addr[i]);
return -EINVAL; return -EINVAL;
} }
pr_info("i2c-stub: Virtual chip at 0x%02x\n", chip_addr[i]); pr_info("Virtual chip at 0x%02x\n", chip_addr[i]);
} }
/* Allocate memory for all chips at once */ /* Allocate memory for all chips at once */
stub_chips_nr = i; stub_chips_nr = i;
stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip), stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip),
GFP_KERNEL); GFP_KERNEL);
if (!stub_chips) { if (!stub_chips)
pr_err("i2c-stub: Out of memory\n");
return -ENOMEM; return -ENOMEM;
}
for (i = 0; i < stub_chips_nr; i++) { for (i = 0; i < stub_chips_nr; i++) {
INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); INIT_LIST_HEAD(&stub_chips[i].smbus_blocks);

View File

@ -295,6 +295,8 @@ static inline int i2c_slave_event(struct i2c_client *client,
{ {
return client->slave_cb(client, event, val); return client->slave_cb(client, event, val);
} }
#else
static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
#endif #endif
/** /**

View File

@ -1,11 +0,0 @@
#ifndef __I2C_SH_MOBILE_H__
#define __I2C_SH_MOBILE_H__
#include <linux/platform_device.h>
struct i2c_sh_mobile_platform_data {
unsigned long bus_speed;
unsigned int clks_per_count;
};
#endif /* __I2C_SH_MOBILE_H__ */

View File

@ -1,4 +1,4 @@
/* I2C and SMBUS message transfer tracepoints /* I2C message transfer tracepoints
* *
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
@ -18,7 +18,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
/* /*
* drivers/i2c/i2c-core.c * drivers/i2c/i2c-core-base.c
*/ */
extern int i2c_transfer_trace_reg(void); extern int i2c_transfer_trace_reg(void);
extern void i2c_transfer_trace_unreg(void); extern void i2c_transfer_trace_unreg(void);
@ -144,228 +144,6 @@ TRACE_EVENT_FN(i2c_result,
i2c_transfer_trace_reg, i2c_transfer_trace_reg,
i2c_transfer_trace_unreg); 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 */ #endif /* _TRACE_I2C_H */
/* This part must be outside protection */ /* This part must be outside protection */

View File

@ -0,0 +1,249 @@
/* 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 smbus
#if !defined(_TRACE_SMBUS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SMBUS_H
#include <linux/i2c.h>
#include <linux/tracepoint.h>
/*
* drivers/i2c/i2c-core-smbus.c
*/
/*
* 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_SMBUS_H */
/* This part must be outside protection */
#include <trace/define_trace.h>