diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt new file mode 100644 index 000000000000..bd6480b19535 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt @@ -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>; + }; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-designware.txt b/Documentation/devicetree/bindings/i2c/i2c-designware.txt index fee26dc3e858..fbb0a6d8b964 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-designware.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-designware.txt @@ -20,7 +20,7 @@ Optional properties : - 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. -Example : +Examples : i2c@f0000 { #address-cells = <1>; @@ -43,3 +43,17 @@ Example : i2c-sda-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>; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-pca-platform.txt b/Documentation/devicetree/bindings/i2c/i2c-pca-platform.txt new file mode 100644 index 000000000000..f1f3876bb8e8 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-pca-platform.txt @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt new file mode 100644 index 000000000000..cb806d1ae4c9 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-zx2967.txt @@ -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 = ; + clocks = <&osc24m>; + #address-cells = <1> + #size-cells = <0>; + clock-frequency = <1600000>; + }; diff --git a/Documentation/driver-api/i2c.rst b/Documentation/driver-api/i2c.rst index 0bf86a445d01..7582c079d747 100644 --- a/Documentation/driver-api/i2c.rst +++ b/Documentation/driver-api/i2c.rst @@ -41,5 +41,8 @@ i2c_adapter devices which don't support those I2C operations. .. kernel-doc:: drivers/i2c/i2c-boardinfo.c :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: diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 820d9040de16..0500193434cb 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -34,6 +34,8 @@ Supported adapters: * Intel Broxton (SOC) * Intel Lewisburg (PCH) * Intel Gemini Lake (SOC) + * Intel Cannon Lake-H (PCH) + * Intel Cannon Lake-LP (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/Documentation/i2c/dev-interface b/Documentation/i2c/dev-interface index bcf919d8625c..5ff19447ac44 100644 --- a/Documentation/i2c/dev-interface +++ b/Documentation/i2c/dev-interface @@ -191,7 +191,7 @@ checking on future transactions.) 4* Other ioctl() calls are converted to in-kernel function calls by i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter 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 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 anything special to support access from user-space. -5* These i2c-core.c/i2c.h functions are wrappers to the actual -implementation of your I2C bus driver. Each adapter must declare -callback functions implementing these standard calls. -i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(), -while i2c-core.c:i2c_smbus_xfer() calls either +5* These i2c.h functions are wrappers to the actual implementation of +your I2C bus driver. Each adapter must declare callback functions +implementing these standard calls. i2c.h:i2c_get_functionality() calls +i2c_adapter.algo->functionality(), while +i2c-core-smbus.c:i2c_smbus_xfer() calls either 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(). After your I2C bus driver has processed these requests, execution runs diff --git a/MAINTAINERS b/MAINTAINERS index f6ec1be48609..1347726cf9d5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1139,6 +1139,18 @@ F: arch/arm/mach-aspeed/ F: arch/arm/boot/dts/aspeed-* F: drivers/*/*aspeed* +ARM/ASPEED I2C DRIVER +M: Brendan Higgins +R: Benjamin Herrenschmidt +R: Joel Stanley +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 M: Nicolas Ferre M: Alexandre Belloni @@ -6382,6 +6394,7 @@ M: Mika Westerberg L: linux-i2c@vger.kernel.org L: linux-acpi@vger.kernel.org S: Maintained +F: drivers/i2c/i2c-core-acpi.c I2C-TAOS-EVM DRIVER M: Jean Delvare diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 45095b3d16a9..7bb65a4369e1 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -4,6 +4,11 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.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_CHARDEV) += i2c-dev.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 ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG -CFLAGS_i2c-core.o := -Wno-deprecated-declarations +CFLAGS_i2c-core-base.o := -Wno-deprecated-declarations diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index a8e89df665b9..1147bddb8b2c 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -553,9 +553,16 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; if (!(pmsg->flags & I2C_M_NOSTART)) { if (i) { - bit_dbg(3, &i2c_adap->dev, "emitting " - "repeated start condition\n"); - i2c_repstart(adap); + if (msgs[i - 1].flags & I2C_M_STOP) { + bit_dbg(3, &i2c_adap->dev, + "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); if ((ret != 0) && !nak_ok) { diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 144cbadc7c72..1006b230b236 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -129,6 +129,8 @@ config I2C_I801 Broxton (SOC) Lewisburg (PCH) Gemini Lake (SOC) + Cannon Lake-H (PCH) + Cannon Lake-LP (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -326,6 +328,16 @@ config I2C_POWERMAC 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 tristate "Atmel AT91 I2C Two-Wire interface (TWI)" depends on ARCH_AT91 @@ -474,11 +486,22 @@ config I2C_DESIGNWARE_PLATFORM depends on (ACPI && COMMON_CLK) || !ACPI help 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 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 tristate "Synopsys DesignWare 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 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 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 30b60855fbcd..1b2fc815a4d8 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers +obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.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_DAVINCI) += i2c-davinci.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 i2c-designware-platform-objs := i2c-designware-platdrv.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_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o +obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c new file mode 100644 index 000000000000..f19348328a71 --- /dev/null +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index fabbb9e49161..38dd61d621df 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -274,7 +274,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev) if (!dev->use_alt_cmd) 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; } @@ -402,7 +402,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) dev->msg->flags &= ~I2C_M_RECV_LEN; dev->buf_len += *dev->buf; 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); } else { /* 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) 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; } @@ -622,7 +622,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) * 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); 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"); return -ENODEV; } - clk_prepare_enable(dev->clk); + rc = clk_prepare_enable(dev->clk); + if (rc) + return rc; if (dev->dev->of_node) { rc = at91_twi_configure_dma(dev, phy_addr); - if (rc == -EPROBE_DEFER) + if (rc == -EPROBE_DEFER) { + clk_disable_unprepare(dev->clk); return rc; + } } if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 45d6771fac8c..75d80161931f 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -405,14 +405,14 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id) 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 */ if (!id->bus_hold_flag && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) cdns_i2c_clear_bus_hold(id); - /* Set the slave address in address register - triggers operation */ - cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, - CDNS_I2C_ADDR_OFFSET); cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET); } diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c new file mode 100644 index 000000000000..d1a69372432f --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index a7cf429daf60..9fee4c054d3d 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -1,5 +1,5 @@ /* - * Synopsys DesignWare I2C adapter driver (master only). + * Synopsys DesignWare 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_HIGH 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_RESTART_EN 0x20 #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 * @cmd_complete: tx completion indicator * @clk: input reference clock + * @slave: represent an I2C slave device * @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 * @msg_write_idx: the element index of the current tx message in the msgs * array @@ -64,6 +208,7 @@ * @abort_source: copy of the TX_ABRT_SOURCE register * @irq: interrupt number for the i2c master * @adapter: i2c subsystem adapter node + * @slave_cfg: configuration for the slave device * @tx_fifo_depth: depth of the hardware tx fifo * @rx_fifo_depth: depth of the hardware rx 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 * @release_lock: function to release a hardware lock on the 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 * 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 clk *clk; struct reset_control *rst; + struct i2c_client *slave; u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); struct dw_pci_controller *controller; int cmd_err; @@ -110,6 +260,7 @@ struct dw_i2c_dev { struct i2c_adapter adapter; u32 functionality; u32 master_cfg; + u32 slave_cfg; unsigned int tx_fifo_depth; unsigned int rx_fifo_depth; int rx_outstanding; @@ -129,6 +280,10 @@ struct dw_i2c_dev { int (*acquire_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev); 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 @@ -137,11 +292,28 @@ struct dw_i2c_dev { #define MODEL_CHERRYTRAIL 0x00000100 -extern int i2c_dw_init(struct dw_i2c_dev *dev); -extern void i2c_dw_disable(struct dw_i2c_dev *dev); -extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); +u32 dw_readl(struct dw_i2c_dev *dev, int offset); +void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset); +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 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) extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-master.c similarity index 59% rename from drivers/i2c/busses/i2c-designware-core.c rename to drivers/i2c/busses/i2c-designware-master.c index c453717b753b..418c233075d3 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -21,311 +21,37 @@ * ---------------------------------------------------------------------------- * */ -#include -#include +#include #include +#include +#include #include #include #include -#include -#include #include +#include + #include "i2c-designware-core.h" -/* - * 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) +static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) { - 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) - 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; -} - -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); + /* Configure the I2C master */ + dw_writel(dev, dev->master_cfg, DW_IC_CON); } /** - * i2c_dw_init() - initialize the designware i2c master hardware + * i2c_dw_init() - Initialize the designware I2C master hardware * @dev: device private data * * This functions configures and enables the I2C master. * This function is called during I2C init function, and in case of timeout at * 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 reg, comp_param1; @@ -344,8 +70,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev) /* 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); + dev_err(dev->dev, + "Unknown Synopsys component type: 0x%08x\n", reg); i2c_dw_release_lock(dev); return -ENODEV; } @@ -355,7 +81,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev) /* Disable the adapter */ __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 */ 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"); } - /* 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); - - /* configure the i2c master */ - dw_writel(dev, dev->master_cfg , DW_IC_CON); - + i2c_dw_configure_fifo_master(dev); i2c_dw_release_lock(dev); 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) { @@ -480,7 +180,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) /* Disable the adapter */ __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); if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { 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); - /* enforce disabled interrupts (due to HW issues) */ + /* Enforce disabled interrupts (due to HW issues) */ i2c_dw_disable_int(dev); /* Enable the adapter */ @@ -511,7 +211,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) /* Clear and enable interrupts */ 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; 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++) { u32 flags = msgs[dev->msg_write_idx].flags; /* - * if target address has changed, we need to - * reprogram the target address in the i2c - * adapter when we are done with this transfer + * If target address has changed, we need to + * reprogram the target address in the I2C + * adapter when we are done with this transfer. */ if (msgs[dev->msg_write_idx].addr != addr) { 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 * be adjusted when receiving the first byte. * 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) { - /* avoid rx buffer overrun */ + /* Avoid rx buffer overrun */ if (dev->rx_outstanding >= dev->rx_fifo_depth) 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 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) goto done; - /* start the transfers */ + /* Start the transfers */ 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)) { dev_err(dev->dev, "controller timed out\n"); /* i2c_dw_init implicitly disables the adapter */ - i2c_dw_init(dev); + i2c_dw_init_master(dev); ret = -ETIMEDOUT; goto done; } @@ -786,7 +463,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) goto done; } - /* no error */ + /* No error */ if (likely(!dev->cmd_err && !dev->status)) { ret = num; goto done; @@ -814,15 +491,9 @@ done_nolock: 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 = { - .master_xfer = i2c_dw_xfer, - .functionality = i2c_dw_func, + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, }; 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. */ -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, 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; + u32 stat; stat = i2c_dw_read_clear_intrbits(dev); - if (stat & DW_IC_INTR_TX_ABRT) { dev->cmd_err |= DW_IC_ERR_TX_ABRT; dev->status = STATUS_IDLE; /* * 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); goto tx_aborted; @@ -925,49 +588,46 @@ tx_aborted: if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) complete(&dev->cmd_complete); 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); i2c_dw_disable_int(dev); 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; } -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) { struct i2c_adapter *adap = &dev->adapter; unsigned long irq_flags; - int r; + int ret; init_completion(&dev->cmd_complete); - r = i2c_dw_init(dev); - if (r) - return r; + dev->init = i2c_dw_init_master; + 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 adapter"); @@ -984,12 +644,12 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) } i2c_dw_disable_int(dev); - r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, - dev_name(dev->dev), dev); - if (r) { + ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, + dev_name(dev->dev), dev); + if (ret) { dev_err(dev->dev, "failure requesting irq %i: %d\n", - dev->irq, r); - return r; + dev->irq, ret); + 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. */ pm_runtime_get_noresume(dev->dev); - r = i2c_add_numbered_adapter(adap); - if (r) - dev_err(dev->dev, "failure adding adapter: %d\n", r); + 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 r; + return ret; } 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"); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index ed485b69b449..86e1bd0b82e9 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -187,16 +187,19 @@ static struct dw_pci_controller dw_pci_controllers[] = { static int i2c_dw_pci_suspend(struct device *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; } static int i2c_dw_pci_resume(struct device *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 @@ -296,7 +299,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) { struct dw_i2c_dev *dev = pci_get_drvdata(pdev); - i2c_dw_disable(dev); + dev->disable(dev); pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index d1263b82d646..2ea6d0d25a01 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -1,5 +1,5 @@ /* - * Synopsys DesignWare I2C adapter driver (master only). + * Synopsys DesignWare I2C adapter driver. * * Based on the TI DAVINCI I2C adapter driver. * @@ -21,27 +21,28 @@ * ---------------------------------------------------------------------------- * */ -#include -#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include +#include +#include #include +#include +#include +#include #include +#include #include #include #include #include -#include #include +#include #include -#include -#include + #include "i2c-designware-core.h" 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 +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) { 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) { struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct dw_i2c_dev *dev; struct i2c_adapter *adap; - struct resource *mem; - int irq, r; + struct dw_i2c_dev *dev; u32 acpi_speed, ht = 0; + struct resource *mem; + int irq, ret; irq = platform_get_irq(pdev, 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_err(&pdev->dev, "Only 100kHz, 400kHz, 1MHz and 3.4MHz supported"); - r = -EINVAL; + ret = -EINVAL; goto exit_reset; } - r = i2c_dw_probe_lock_support(dev); - if (r) + ret = i2c_dw_probe_lock_support(dev); + if (ret) goto exit_reset; - 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; - - 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; - } + if (i2c_detect_slave_mode(&pdev->dev)) + i2c_dw_configure_slave(dev); + else + i2c_dw_configure_master(dev); dev->clk = devm_clk_get(&pdev->dev, NULL); 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); } - r = i2c_dw_probe(dev); - if (r) + if (dev->mode == DW_IC_SLAVE) + ret = i2c_dw_probe_slave(dev); + else + ret = i2c_dw_probe(dev); + + if (ret) goto exit_probe; - return r; + return ret; exit_probe: if (!dev->pm_disabled) @@ -339,7 +376,7 @@ exit_probe: exit_reset: if (!IS_ERR_OR_NULL(dev->rst)) reset_control_assert(dev->rst); - return r; + return ret; } 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_dw_disable(dev); + dev->disable(dev); pm_runtime_dont_use_autosuspend(&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 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); 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); i2c_dw_plat_prepare_clk(i_dev, true); - i2c_dw_init(i_dev); + i_dev->init(i_dev); return 0; } @@ -423,7 +460,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = { #define DW_I2C_DEV_PMOPS NULL #endif -/* work with hotplug and coldplug */ +/* Work with hotplug and coldplug */ MODULE_ALIAS("platform:i2c_designware"); static struct platform_driver dw_i2c_driver = { diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c new file mode 100644 index 000000000000..0548c7ea578c --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 312912708854..d2e84480fbe9 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -375,7 +375,9 @@ static int em_i2c_probe(struct platform_device *pdev) if (IS_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.retries = 5; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 6484fa6dbb84..c9536e17d6ff 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -66,6 +66,8 @@ * Lewisburg Supersku (PCH) 0xa223 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 + * 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: * Software PEC no @@ -226,10 +228,12 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2 #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_LEWISBURG_SMBUS 0xa1a3 #define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223 #define PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS 0xa2a3 +#define PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS 0xa323 struct i801_mux_config { 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_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_CANNONLAKE_H_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) }, { 0, } }; @@ -1499,6 +1505,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) switch (dev->device) { case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_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_SSKU_SMBUS: case PCI_DEVICE_ID_INTEL_DNV_SMBUS: diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 5738556b6aac..d4e8f1954f23 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -419,7 +419,7 @@ static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap, ret = mxs_i2c_pio_wait_xfer_end(i2c); if (ret) { - dev_err(i2c->dev, + dev_dbg(i2c->dev, "PIO: Failed to send SELECT command!\n"); 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); if (ret) { - dev_err(i2c->dev, + dev_dbg(i2c->dev, "PIO: Failed to send READ command!\n"); goto cleanup; } @@ -528,7 +528,7 @@ static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap, /* Wait for the end of the transfer. */ ret = mxs_i2c_pio_wait_xfer_end(i2c); if (ret) { - dev_err(i2c->dev, + dev_dbg(i2c->dev, "PIO: Failed to finish WRITE cmd!\n"); break; } diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c index 3bd2e7d06e4b..853a2abedb05 100644 --- a/drivers/i2c/busses/i2c-pca-platform.c +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -22,14 +22,17 @@ #include #include #include +#include #include +#include +#include #include struct i2c_pca_pf_data { void __iomem *reg_base; int irq; /* if 0, use polling */ - int gpio; + struct gpio_desc *gpio; wait_queue_head_t wait; struct i2c_adapter adap; 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) { 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) { struct i2c_pca_pf_data *i2c = pd; - gpio_set_value(i2c->gpio, 0); + gpiod_set_value(i2c->gpio, 1); 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) @@ -136,36 +139,27 @@ static int i2c_pca_pf_probe(struct platform_device *pdev) struct resource *res; struct i2c_pca9564_pf_platform_data *platform_data = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; int ret = 0; int irq; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); /* If irq is 0, we do polling. */ + if (irq < 0) + irq = 0; - if (res == NULL) { - ret = -ENODEV; - goto e_print; - } + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; - if (!request_mem_region(res->start, resource_size(res), res->name)) { - ret = -ENOMEM; - goto e_print; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->reg_base = devm_ioremap_resource(&pdev->dev, res); + 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); - 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_size = resource_size(res); i2c->irq = irq; @@ -177,20 +171,43 @@ static int i2c_pca_pf_probe(struct platform_device *pdev) (unsigned long) res->start); i2c->adap.algo_data = &i2c->algo_data; i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; if (platform_data) { i2c->adap.timeout = platform_data->timeout; 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 { i2c->adap.timeout = HZ; i2c->algo_data.i2c_clock = 59000; - i2c->gpio = -1; + i2c->gpio = NULL; } i2c->algo_data.data = i2c; 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) { case IORESOURCE_MEM_32BIT: @@ -208,52 +225,22 @@ static int i2c_pca_pf_probe(struct platform_device *pdev) 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) { - 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); if (ret) - goto e_reqirq; + return ret; } - if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) { - ret = -ENODEV; - goto e_adapt; - } + ret = i2c_pca_add_numbered_bus(&i2c->adap); + if (ret) + return ret; platform_set_drvdata(pdev, i2c); - printk(KERN_INFO "%s registered.\n", i2c->adap.name); + dev_info(&pdev->dev, "registered.\n"); 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) @@ -262,24 +249,24 @@ static int i2c_pca_pf_remove(struct platform_device *pdev) 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; } +#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 = { .probe = i2c_pca_pf_probe, .remove = i2c_pca_pf_remove, .driver = { .name = "i2c-pca-platform", + .of_match_table = of_match_ptr(i2c_pca_of_match_table), }, }; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 8be3e6cb8fe6..93c1a54981df 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -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 * 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) { - /* 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 | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 3d7559348745..2e097d97d258 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -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) { - struct i2c_sh_mobile_platform_data *pdata = dev_get_platdata(&dev->dev); struct sh_mobile_i2c_data *pd; struct i2c_adapter *adap; struct resource *res; + const struct of_device_id *match; int ret; u32 bus_speed; @@ -910,30 +909,18 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) if (IS_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); pd->bus_speed = ret ? STANDARD_MODE : bus_speed; - pd->clks_per_count = 1; - if (dev->dev.of_node) { - const struct of_device_id *match; + match = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev); + if (match) { + const struct sh_mobile_dt_config *config = match->data; - match = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev); - if (match) { - const struct sh_mobile_dt_config *config; + pd->clks_per_count = config->clks_per_count; - config = match->data; - pd->clks_per_count = config->clks_per_count; - - 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; + if (config->setup) + config->setup(pd); } /* The IIC blocks on SH-Mobile ARM processors diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 6ba6c83ca8f1..7e89ba6fcf6f 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -22,10 +22,12 @@ * using the APM X-Gene SLIMpro mailbox driver. * */ +#include #include #include #include #include +#include #include #include #include @@ -89,6 +91,8 @@ ((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_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 */ @@ -99,19 +103,47 @@ & 0x3FF00000)) #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 i2c_adapter adapter; struct device *dev; struct mbox_chan *mbox_chan; struct mbox_client mbox_client; + int mbox_idx; struct completion rd_complete; u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */ u32 *resp_msg; + phys_addr_t comm_base_addr; + void *pcc_comm_addr; }; #define to_slimpro_i2c_dev(cl) \ 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) { 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); } +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) { - if (ctx->mbox_client.tx_block) { + if (ctx->mbox_client.tx_block || !acpi_disabled) { if (!wait_for_completion_timeout(&ctx->rd_complete, msecs_to_jiffies(MAILBOX_OP_TIMEOUT))) return -ETIMEDOUT; @@ -144,26 +220,46 @@ static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx) 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, u32 addr, u32 addrlen, u32 protocol, u32 readlen, u32 *data) { u32 msg[3]; - int rc; msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ, protocol, addrlen, readlen); msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); 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); -err: - ctx->resp_msg = NULL; - return rc; + return slimpro_i2c_send_msg(ctx, msg, data); } 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 msg[3]; - int rc; msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE, protocol, addrlen, writelen); msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); msg[2] = data; - ctx->resp_msg = msg; - rc = mbox_send_message(ctx->mbox_chan, &msg); - if (rc < 0) - goto err; - - rc = start_i2c_msg_xfer(ctx); -err: - ctx->resp_msg = NULL; - return rc; + return slimpro_i2c_send_msg(ctx, msg, msg); } 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)) { dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", ctx->dma_buffer); - rc = -ENOMEM; - goto err; + return -ENOMEM; } 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_ADDR(addr); msg[2] = (u32)paddr; - ctx->resp_msg = msg; - rc = mbox_send_message(ctx->mbox_chan, &msg); - if (rc < 0) - goto err_unmap; - - rc = start_i2c_msg_xfer(ctx); + rc = slimpro_i2c_send_msg(ctx, msg, msg); /* Copy to destination */ memcpy(data, ctx->dma_buffer, readlen); -err_unmap: dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE); -err: - ctx->resp_msg = NULL; 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)) { dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", ctx->dma_buffer); - rc = -ENOMEM; - goto err; + return -ENOMEM; } 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_ADDR(addr); msg[2] = (u32)paddr; - ctx->resp_msg = msg; if (ctx->mbox_client.tx_block) reinit_completion(&ctx->rd_complete); - rc = mbox_send_message(ctx->mbox_chan, &msg); - if (rc < 0) - goto err_unmap; + rc = slimpro_i2c_send_msg(ctx, msg, msg); - rc = start_i2c_msg_xfer(ctx); - -err_unmap: dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE); -err: - ctx->resp_msg = NULL; return rc; } @@ -394,17 +463,73 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev) /* Request mailbox channel */ cl->dev = &pdev->dev; - cl->rx_callback = slimpro_i2c_rx_cb; - cl->tx_block = true; init_completion(&ctx->rd_complete); cl->tx_tout = MAILBOX_OP_TIMEOUT; cl->knows_txdone = false; - 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); - } + if (acpi_disabled) { + cl->tx_block = true; + cl->rx_callback = slimpro_i2c_rx_cb; + 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)); if (rc) 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)); i2c_set_adapdata(adapter, ctx); rc = i2c_add_adapter(adapter); - if (rc) { - mbox_free_channel(ctx->mbox_chan); - return rc; - } + if (rc) + goto mbox_err; dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n"); 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) @@ -434,7 +565,10 @@ static int xgene_slimpro_i2c_remove(struct platform_device *pdev) 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; } diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index ae80228104e9..6b106e94bc09 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -393,6 +393,7 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev) init_completion(&priv->msg_complete); priv->adapter.dev.parent = &pdev->dev; priv->adapter.algo = &xlp9xx_i2c_algo; + priv->adapter.class = I2C_CLASS_HWMON; ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); priv->adapter.dev.of_node = pdev->dev.of_node; priv->dev = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c new file mode 100644 index 000000000000..48281c1b30c6 --- /dev/null +++ b/drivers/i2c/busses/i2c-zx2967.c @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2017 Sanechips Technology Co., Ltd. + * Copyright 2017 Linaro Ltd. + * + * Author: Baoyou Xie + * + * 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 +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("ZTE ZX2967 I2C Bus Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c new file mode 100644 index 000000000000..4842ec3a5451 --- /dev/null +++ b/drivers/i2c/i2c-core-acpi.c @@ -0,0 +1,665 @@ +/* + * Linux I2C core ACPI support code + * + * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core-base.c similarity index 58% rename from drivers/i2c/i2c-core.c rename to drivers/i2c/i2c-core-base.c index 82576aaccc90..c89dac7fd2e7 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core-base.c @@ -1,36 +1,26 @@ -/* i2c-core.c - a device driver for the iic-bus interface */ -/* ------------------------------------------------------------------------- */ -/* Copyright (C) 1995-99 Simon G. Vogl - - 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. */ -/* ------------------------------------------------------------------------- */ - -/* With some changes from Kyösti Mälkki . - All SMBus-related things are written by Frodo Looijaard - SMBus 2.0 support by Mark Studebaker and - Jean Delvare - Mux support by Rodolfo Giometti and - Michael Lawnick - OF support is copyright (c) 2008 Jochen Friedrich - (based on a previous patch from Jon Smirl ) and - (c) 2013 Wolfram Sang - I2C ACPI code Copyright (C) 2014 Intel Corp - Author: Lan Tianyu - I2C slave support (c) 2014 by Wolfram Sang +/* + * Linux I2C core + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki + * Mux support by Rodolfo Giometti and + * Michael Lawnick + * + * Copyright (C) 2013-2017 Wolfram Sang + * + * 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. */ #define pr_fmt(fmt) "i2c-core: " fmt #include -#include #include #include #include @@ -38,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -68,9 +57,10 @@ #define I2C_ADDR_7BITS_MAX 0x77 #define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1) -/* core_lock protects i2c_adapter_idr, and guarantees - that device detection, deletion of detected devices, and attach_adapter - calls are serialized */ +/* + * core_lock protects i2c_adapter_idr, and guarantees that device detection, + * deletion of detected devices, and attach_adapter calls are serialized + */ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); @@ -90,652 +80,6 @@ void i2c_transfer_trace_unreg(void) static_key_slow_dec(&i2c_trace_msg); } -#if defined(CONFIG_ACPI) -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 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; - - 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. - */ -static 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; -} - -static 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); -#else /* CONFIG_ACPI */ -static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } -extern struct notifier_block i2c_acpi_notifier; -#endif /* CONFIG_ACPI */ - -#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; -} - - -static 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; -} - -static 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); -} -#else /* CONFIG_ACPI_I2C_OPREGION */ -static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) -{ } - -static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) -{ return 0; } -#endif /* CONFIG_ACPI_I2C_OPREGION */ - -/* ------------------------------------------------------------------------- */ - const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { @@ -1195,7 +539,7 @@ static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client) /* This is a permissive address validity check, I2C address map constraints * are purposely not enforced, except for the general call address. */ -static int i2c_check_addr_validity(unsigned addr, unsigned short flags) +int i2c_check_addr_validity(unsigned addr, unsigned short flags) { if (flags & I2C_CLIENT_TEN) { /* 10-bit address, all values are valid */ @@ -1213,7 +557,7 @@ static int i2c_check_addr_validity(unsigned addr, unsigned short flags) * device uses a reserved address, then it shouldn't be probed. 7-bit * addressing is assumed, 10-bit address devices are rare and should be * explicitly enumerated. */ -static int i2c_check_7bit_addr_validity_strict(unsigned short addr) +int i2c_check_7bit_addr_validity_strict(unsigned short addr) { /* * Reserved addresses per I2C specification: @@ -1764,210 +1108,6 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) up_read(&__i2c_board_lock); } -/* OF support code */ - -#if IS_ENABLED(CONFIG_OF) -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; -} - -static 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); -#else -static void of_i2c_register_devices(struct i2c_adapter *adap) { } -#endif /* CONFIG_OF */ - static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { @@ -2562,62 +1702,6 @@ void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) } EXPORT_SYMBOL(i2c_clients_command); -#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; -} -static struct notifier_block i2c_of_notifier = { - .notifier_call = of_i2c_notify, -}; -#else -extern struct notifier_block i2c_of_notifier; -#endif /* CONFIG_OF_DYNAMIC */ - static int __init i2c_init(void) { int retval; @@ -3156,676 +2240,6 @@ void i2c_put_adapter(struct i2c_adapter *adap) } EXPORT_SYMBOL(i2c_put_adapter); -/* 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); - -#if IS_ENABLED(CONFIG_I2C_SLAVE) -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", ®); - 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); - -#endif - MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus main module"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c new file mode 100644 index 000000000000..ccf82fdbcd8e --- /dev/null +++ b/drivers/i2c/i2c-core-of.c @@ -0,0 +1,276 @@ +/* + * Linux I2C core OF support code + * + * Copyright (C) 2008 Jochen Friedrich + * based on a previous patch from Jon Smirl + * + * Copyright (C) 2013 Wolfram Sang + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c new file mode 100644 index 000000000000..4a78c65e9971 --- /dev/null +++ b/drivers/i2c/i2c-core-slave.c @@ -0,0 +1,115 @@ +/* + * Linux I2C core slave support code + * + * Copyright (C) 2014 by Wolfram Sang + * + * 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 +#include +#include +#include +#include +#include + +#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", ®); + 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); diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c new file mode 100644 index 000000000000..10f00a82ec9d --- /dev/null +++ b/drivers/i2c/i2c-core-smbus.c @@ -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 + * SMBus 2.0 support by Mark Studebaker and + * Jean Delvare + * + * 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 +#include +#include + +#define CREATE_TRACE_POINTS +#include + + +/* 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); diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 17700bfddcf5..3b63f5e5b89c 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -27,3 +27,27 @@ extern struct rw_semaphore __i2c_board_lock; extern struct list_head __i2c_board_list; 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; diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c index 06af583d5101..4a9ad91c5ba3 100644 --- a/drivers/i2c/i2c-stub.c +++ b/drivers/i2c/i2c-stub.c @@ -16,6 +16,7 @@ */ #define DEBUG 1 +#define pr_fmt(fmt) "i2c-stub: " fmt #include #include @@ -342,7 +343,7 @@ static int __init i2c_stub_allocate_banks(int i) if (!chip->bank_words) 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_end); @@ -363,28 +364,27 @@ static int __init i2c_stub_init(void) int i, ret; if (!chip_addr[0]) { - pr_err("i2c-stub: Please specify a chip address\n"); + pr_err("Please specify a chip address\n"); return -ENODEV; } for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { 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]); 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 */ stub_chips_nr = i; stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip), GFP_KERNEL); - if (!stub_chips) { - pr_err("i2c-stub: Out of memory\n"); + if (!stub_chips) return -ENOMEM; - } + for (i = 0; i < stub_chips_nr; i++) { INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 72d0ece70ed3..00ca5b86a753 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -295,6 +295,8 @@ static inline int i2c_slave_event(struct i2c_client *client, { return client->slave_cb(client, event, val); } +#else +static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } #endif /** diff --git a/include/linux/i2c/i2c-sh_mobile.h b/include/linux/i2c/i2c-sh_mobile.h deleted file mode 100644 index 06e3089795fb..000000000000 --- a/include/linux/i2c/i2c-sh_mobile.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __I2C_SH_MOBILE_H__ -#define __I2C_SH_MOBILE_H__ - -#include - -struct i2c_sh_mobile_platform_data { - unsigned long bus_speed; - unsigned int clks_per_count; -}; - -#endif /* __I2C_SH_MOBILE_H__ */ diff --git a/include/trace/events/i2c.h b/include/trace/events/i2c.h index 4abb8eab34d3..86a401190df9 100644 --- a/include/trace/events/i2c.h +++ b/include/trace/events/i2c.h @@ -1,4 +1,4 @@ -/* I2C and SMBUS message transfer tracepoints +/* I2C message transfer tracepoints * * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -18,7 +18,7 @@ #include /* - * drivers/i2c/i2c-core.c + * drivers/i2c/i2c-core-base.c */ extern int i2c_transfer_trace_reg(void); extern void i2c_transfer_trace_unreg(void); @@ -144,228 +144,6 @@ TRACE_EVENT_FN(i2c_result, i2c_transfer_trace_reg, i2c_transfer_trace_unreg); -/* - * i2c_smbus_xfer() write data or procedure call request - */ -TRACE_EVENT_CONDITION(smbus_write, - TP_PROTO(const struct i2c_adapter *adap, - u16 addr, unsigned short flags, - char read_write, u8 command, int protocol, - const union i2c_smbus_data *data), - TP_ARGS(adap, addr, flags, read_write, command, protocol, data), - TP_CONDITION(read_write == I2C_SMBUS_WRITE || - protocol == I2C_SMBUS_PROC_CALL || - protocol == I2C_SMBUS_BLOCK_PROC_CALL), - TP_STRUCT__entry( - __field(int, adapter_nr ) - __field(__u16, addr ) - __field(__u16, flags ) - __field(__u8, command ) - __field(__u8, len ) - __field(__u32, protocol ) - __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ), - TP_fast_assign( - __entry->adapter_nr = adap->nr; - __entry->addr = addr; - __entry->flags = flags; - __entry->command = command; - __entry->protocol = protocol; - - switch (protocol) { - case I2C_SMBUS_BYTE_DATA: - __entry->len = 1; - goto copy; - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - __entry->len = 2; - goto copy; - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - case I2C_SMBUS_I2C_BLOCK_DATA: - __entry->len = data->block[0] + 1; - copy: - memcpy(__entry->buf, data->block, __entry->len); - break; - case I2C_SMBUS_QUICK: - case I2C_SMBUS_BYTE: - case I2C_SMBUS_I2C_BLOCK_BROKEN: - default: - __entry->len = 0; - } - ), - TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]", - __entry->adapter_nr, - __entry->addr, - __entry->flags, - __entry->command, - __print_symbolic(__entry->protocol, - { I2C_SMBUS_QUICK, "QUICK" }, - { I2C_SMBUS_BYTE, "BYTE" }, - { I2C_SMBUS_BYTE_DATA, "BYTE_DATA" }, - { I2C_SMBUS_WORD_DATA, "WORD_DATA" }, - { I2C_SMBUS_PROC_CALL, "PROC_CALL" }, - { I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" }, - { I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" }, - { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" }, - { I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }), - __entry->len, - __entry->len, __entry->buf - )); - -/* - * i2c_smbus_xfer() read data request - */ -TRACE_EVENT_CONDITION(smbus_read, - TP_PROTO(const struct i2c_adapter *adap, - u16 addr, unsigned short flags, - char read_write, u8 command, int protocol), - TP_ARGS(adap, addr, flags, read_write, command, protocol), - TP_CONDITION(!(read_write == I2C_SMBUS_WRITE || - protocol == I2C_SMBUS_PROC_CALL || - protocol == I2C_SMBUS_BLOCK_PROC_CALL)), - TP_STRUCT__entry( - __field(int, adapter_nr ) - __field(__u16, flags ) - __field(__u16, addr ) - __field(__u8, command ) - __field(__u32, protocol ) - __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ), - TP_fast_assign( - __entry->adapter_nr = adap->nr; - __entry->addr = addr; - __entry->flags = flags; - __entry->command = command; - __entry->protocol = protocol; - ), - TP_printk("i2c-%d a=%03x f=%04x c=%x %s", - __entry->adapter_nr, - __entry->addr, - __entry->flags, - __entry->command, - __print_symbolic(__entry->protocol, - { I2C_SMBUS_QUICK, "QUICK" }, - { I2C_SMBUS_BYTE, "BYTE" }, - { I2C_SMBUS_BYTE_DATA, "BYTE_DATA" }, - { I2C_SMBUS_WORD_DATA, "WORD_DATA" }, - { I2C_SMBUS_PROC_CALL, "PROC_CALL" }, - { I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" }, - { I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" }, - { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" }, - { I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }) - )); - -/* - * i2c_smbus_xfer() read data or procedure call reply - */ -TRACE_EVENT_CONDITION(smbus_reply, - TP_PROTO(const struct i2c_adapter *adap, - u16 addr, unsigned short flags, - char read_write, u8 command, int protocol, - const union i2c_smbus_data *data), - TP_ARGS(adap, addr, flags, read_write, command, protocol, data), - TP_CONDITION(read_write == I2C_SMBUS_READ), - TP_STRUCT__entry( - __field(int, adapter_nr ) - __field(__u16, addr ) - __field(__u16, flags ) - __field(__u8, command ) - __field(__u8, len ) - __field(__u32, protocol ) - __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ), - TP_fast_assign( - __entry->adapter_nr = adap->nr; - __entry->addr = addr; - __entry->flags = flags; - __entry->command = command; - __entry->protocol = protocol; - - switch (protocol) { - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - __entry->len = 1; - goto copy; - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - __entry->len = 2; - goto copy; - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - case I2C_SMBUS_I2C_BLOCK_DATA: - __entry->len = data->block[0] + 1; - copy: - memcpy(__entry->buf, data->block, __entry->len); - break; - case I2C_SMBUS_QUICK: - case I2C_SMBUS_I2C_BLOCK_BROKEN: - default: - __entry->len = 0; - } - ), - TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]", - __entry->adapter_nr, - __entry->addr, - __entry->flags, - __entry->command, - __print_symbolic(__entry->protocol, - { I2C_SMBUS_QUICK, "QUICK" }, - { I2C_SMBUS_BYTE, "BYTE" }, - { I2C_SMBUS_BYTE_DATA, "BYTE_DATA" }, - { I2C_SMBUS_WORD_DATA, "WORD_DATA" }, - { I2C_SMBUS_PROC_CALL, "PROC_CALL" }, - { I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" }, - { I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" }, - { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" }, - { I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }), - __entry->len, - __entry->len, __entry->buf - )); - -/* - * i2c_smbus_xfer() result - */ -TRACE_EVENT(smbus_result, - TP_PROTO(const struct i2c_adapter *adap, - u16 addr, unsigned short flags, - char read_write, u8 command, int protocol, - int res), - TP_ARGS(adap, addr, flags, read_write, command, protocol, res), - TP_STRUCT__entry( - __field(int, adapter_nr ) - __field(__u16, addr ) - __field(__u16, flags ) - __field(__u8, read_write ) - __field(__u8, command ) - __field(__s16, res ) - __field(__u32, protocol ) - ), - TP_fast_assign( - __entry->adapter_nr = adap->nr; - __entry->addr = addr; - __entry->flags = flags; - __entry->read_write = read_write; - __entry->command = command; - __entry->protocol = protocol; - __entry->res = res; - ), - TP_printk("i2c-%d a=%03x f=%04x c=%x %s %s res=%d", - __entry->adapter_nr, - __entry->addr, - __entry->flags, - __entry->command, - __print_symbolic(__entry->protocol, - { I2C_SMBUS_QUICK, "QUICK" }, - { I2C_SMBUS_BYTE, "BYTE" }, - { I2C_SMBUS_BYTE_DATA, "BYTE_DATA" }, - { I2C_SMBUS_WORD_DATA, "WORD_DATA" }, - { I2C_SMBUS_PROC_CALL, "PROC_CALL" }, - { I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" }, - { I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" }, - { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" }, - { I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }), - __entry->read_write == I2C_SMBUS_WRITE ? "wr" : "rd", - __entry->res - )); - #endif /* _TRACE_I2C_H */ /* This part must be outside protection */ diff --git a/include/trace/events/smbus.h b/include/trace/events/smbus.h new file mode 100644 index 000000000000..d2fb6e1d3e10 --- /dev/null +++ b/include/trace/events/smbus.h @@ -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 +#include + +/* + * 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