diff options
Diffstat (limited to 'drivers/i2c')
83 files changed, 7863 insertions, 2564 deletions
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..45a3f3ca29b3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -129,6 +129,9 @@ config I2C_I801 Broxton (SOC) Lewisburg (PCH) Gemini Lake (SOC) + Cannon Lake-H (PCH) + Cannon Lake-LP (PCH) + Cedar Fork (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -187,6 +190,14 @@ config I2C_PIIX4 This driver can also be built as a module. If so, the module will be called i2c-piix4. +config I2C_CHT_WC + tristate "Intel Cherry Trail Whiskey Cove PMIC smbus controller" + depends on INTEL_SOC_PMIC_CHTWC + help + If you say yes to this option, support will be included for the + SMBus controller found in the Intel Cherry Trail Whiskey Cove PMIC + found on some Intel Cherry Trail systems. + config I2C_NFORCE2 tristate "Nvidia nForce2, nForce3 and nForce4" depends on PCI @@ -326,6 +337,26 @@ config I2C_POWERMAC comment "I2C system bus drivers (mostly embedded / system-on-chip)" +config I2C_ALTERA + tristate "Altera Soft IP I2C" + depends on (ARCH_SOCFPGA || NIOS2) && OF + help + If you say yes to this option, support will be included for the + Altera Soft IP I2C interfaces on SoCFPGA and Nios2 architectures. + + This driver can also be built as a module. If so, the module + will be called i2c-altera. + +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 +505,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 @@ -877,6 +919,13 @@ config I2C_SIRF This driver can also be built as a module. If so, the module will be called i2c-sirf. +config I2C_SPRD + bool "Spreadtrum I2C interface" + depends on I2C=y && ARCH_SPRD + help + If you say yes to this option, support will be included for the + Spreadtrum I2C interface. + config I2C_ST tristate "STMicroelectronics SSC I2C support" depends on ARCH_STI @@ -897,6 +946,16 @@ config I2C_STM32F4 This driver can also be built as module. If so, the module will be called i2c-stm32f4. +config I2C_STM32F7 + tristate "STMicroelectronics STM32F7 I2C support" + depends on ARCH_STM32 || COMPILE_TEST + help + Enable this option to add support for STM32 I2C controller embedded + in STM32F7 SoCs. + + This driver can also be built as module. If so, the module + will be called i2c-stm32f7. + config I2C_STU300 tristate "ST Microelectronics DDC I2C interface" depends on MACH_U300 @@ -960,7 +1019,7 @@ config I2C_UNIPHIER_F config I2C_VERSATILE tristate "ARM Versatile/Realview I2C bus support" - depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST + depends on ARCH_MPS2 || ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST select I2C_ALGOBIT help Say yes if you want to support the I2C serial bus on ARMs Versatile @@ -1258,4 +1317,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..47f3ac9a695a 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_CHT_WC) += i2c-cht-wc.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o @@ -29,6 +30,8 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers +obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o +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 +43,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 @@ -84,8 +91,10 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o +obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o obj-$(CONFIG_I2C_ST) += i2c-st.o obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o +obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o @@ -102,6 +111,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-altera.c b/drivers/i2c/busses/i2c-altera.c new file mode 100644 index 000000000000..f5e1941e65b5 --- /dev/null +++ b/drivers/i2c/busses/i2c-altera.c @@ -0,0 +1,511 @@ +/* + * Copyright Intel Corporation (C) 2017. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + * + * Based on the i2c-axxia.c driver. + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/iopoll.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#define ALTR_I2C_TFR_CMD 0x00 /* Transfer Command register */ +#define ALTR_I2C_TFR_CMD_STA BIT(9) /* send START before byte */ +#define ALTR_I2C_TFR_CMD_STO BIT(8) /* send STOP after byte */ +#define ALTR_I2C_TFR_CMD_RW_D BIT(0) /* Direction of transfer */ +#define ALTR_I2C_RX_DATA 0x04 /* RX data FIFO register */ +#define ALTR_I2C_CTRL 0x08 /* Control register */ +#define ALTR_I2C_CTRL_RXT_SHFT 4 /* RX FIFO Threshold */ +#define ALTR_I2C_CTRL_TCT_SHFT 2 /* TFER CMD FIFO Threshold */ +#define ALTR_I2C_CTRL_BSPEED BIT(1) /* Bus Speed (1=Fast) */ +#define ALTR_I2C_CTRL_EN BIT(0) /* Enable Core (1=Enable) */ +#define ALTR_I2C_ISER 0x0C /* Interrupt Status Enable register */ +#define ALTR_I2C_ISER_RXOF_EN BIT(4) /* Enable RX OVERFLOW IRQ */ +#define ALTR_I2C_ISER_ARB_EN BIT(3) /* Enable ARB LOST IRQ */ +#define ALTR_I2C_ISER_NACK_EN BIT(2) /* Enable NACK DET IRQ */ +#define ALTR_I2C_ISER_RXRDY_EN BIT(1) /* Enable RX Ready IRQ */ +#define ALTR_I2C_ISER_TXRDY_EN BIT(0) /* Enable TX Ready IRQ */ +#define ALTR_I2C_ISR 0x10 /* Interrupt Status register */ +#define ALTR_I2C_ISR_RXOF BIT(4) /* RX OVERFLOW IRQ */ +#define ALTR_I2C_ISR_ARB BIT(3) /* ARB LOST IRQ */ +#define ALTR_I2C_ISR_NACK BIT(2) /* NACK DET IRQ */ +#define ALTR_I2C_ISR_RXRDY BIT(1) /* RX Ready IRQ */ +#define ALTR_I2C_ISR_TXRDY BIT(0) /* TX Ready IRQ */ +#define ALTR_I2C_STATUS 0x14 /* Status register */ +#define ALTR_I2C_STAT_CORE BIT(0) /* Core Status (0=idle) */ +#define ALTR_I2C_TC_FIFO_LVL 0x18 /* Transfer FIFO LVL register */ +#define ALTR_I2C_RX_FIFO_LVL 0x1C /* Receive FIFO LVL register */ +#define ALTR_I2C_SCL_LOW 0x20 /* SCL low count register */ +#define ALTR_I2C_SCL_HIGH 0x24 /* SCL high count register */ +#define ALTR_I2C_SDA_HOLD 0x28 /* SDA hold count register */ + +#define ALTR_I2C_ALL_IRQ (ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \ + ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \ + ALTR_I2C_ISR_TXRDY) + +#define ALTR_I2C_THRESHOLD 0 /* IRQ Threshold at 1 element */ +#define ALTR_I2C_DFLT_FIFO_SZ 4 +#define ALTR_I2C_TIMEOUT 100000 /* 100ms */ +#define ALTR_I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) + +/** + * altr_i2c_dev - I2C device context + * @base: pointer to register struct + * @msg: pointer to current message + * @msg_len: number of bytes transferred in msg + * @msg_err: error code for completed message + * @msg_complete: xfer completion object + * @dev: device reference + * @adapter: core i2c abstraction + * @i2c_clk: clock reference for i2c input clock + * @bus_clk_rate: current i2c bus clock rate + * @buf: ptr to msg buffer for easier use. + * @fifo_size: size of the FIFO passed in. + * @isr_mask: cached copy of local ISR enables. + * @isr_status: cached copy of local ISR status. + * @lock: spinlock for IRQ synchronization. + */ +struct altr_i2c_dev { + void __iomem *base; + struct i2c_msg *msg; + size_t msg_len; + int msg_err; + struct completion msg_complete; + struct device *dev; + struct i2c_adapter adapter; + struct clk *i2c_clk; + u32 bus_clk_rate; + u8 *buf; + u32 fifo_size; + u32 isr_mask; + u32 isr_status; + spinlock_t lock; /* IRQ synchronization */ +}; + +static void +altr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable) +{ + unsigned long flags; + u32 int_en; + + spin_lock_irqsave(&idev->lock, flags); + + int_en = readl(idev->base + ALTR_I2C_ISER); + if (enable) + idev->isr_mask = int_en | mask; + else + idev->isr_mask = int_en & ~mask; + + writel(idev->isr_mask, idev->base + ALTR_I2C_ISER); + + spin_unlock_irqrestore(&idev->lock, flags); +} + +static void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask) +{ + u32 int_en = readl(idev->base + ALTR_I2C_ISR); + + writel(int_en | mask, idev->base + ALTR_I2C_ISR); +} + +static void altr_i2c_core_disable(struct altr_i2c_dev *idev) +{ + u32 tmp = readl(idev->base + ALTR_I2C_CTRL); + + writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); +} + +static void altr_i2c_core_enable(struct altr_i2c_dev *idev) +{ + u32 tmp = readl(idev->base + ALTR_I2C_CTRL); + + writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL); +} + +static void altr_i2c_reset(struct altr_i2c_dev *idev) +{ + altr_i2c_core_disable(idev); + altr_i2c_core_enable(idev); +} + +static inline void altr_i2c_stop(struct altr_i2c_dev *idev) +{ + writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD); +} + +static void altr_i2c_init(struct altr_i2c_dev *idev) +{ + u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate; + u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000; + u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) | + (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT); + u32 t_high, t_low; + + if (idev->bus_clk_rate <= 100000) { + tmp &= ~ALTR_I2C_CTRL_BSPEED; + /* Standard mode SCL 50/50 */ + t_high = divisor * 1 / 2; + t_low = divisor * 1 / 2; + } else { + tmp |= ALTR_I2C_CTRL_BSPEED; + /* Fast mode SCL 33/66 */ + t_high = divisor * 1 / 3; + t_low = divisor * 2 / 3; + } + writel(tmp, idev->base + ALTR_I2C_CTRL); + + dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", + idev->bus_clk_rate, clk_mhz, divisor); + + /* Reset controller */ + altr_i2c_reset(idev); + + /* SCL High Time */ + writel(t_high, idev->base + ALTR_I2C_SCL_HIGH); + /* SCL Low Time */ + writel(t_low, idev->base + ALTR_I2C_SCL_LOW); + /* SDA Hold Time, 300ns */ + writel(div_u64(300 * clk_mhz, 1000), idev->base + ALTR_I2C_SDA_HOLD); + + /* Mask all master interrupt bits */ + altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); +} + +/** + * altr_i2c_transfer - On the last byte to be transmitted, send + * a Stop bit on the last byte. + */ +static void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data) +{ + /* On the last byte to be transmitted, send STOP */ + if (idev->msg_len == 1) + data |= ALTR_I2C_TFR_CMD_STO; + if (idev->msg_len > 0) + writel(data, idev->base + ALTR_I2C_TFR_CMD); +} + +/** + * altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of + * transfer. Send a Stop bit on the last byte. + */ +static void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev) +{ + size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL); + int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len); + + while (bytes_to_transfer-- > 0) { + *idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA); + idev->msg_len--; + altr_i2c_transfer(idev, 0); + } +} + +/** + * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer. + * @return: Number of bytes left to transfer. + */ +static int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev) +{ + size_t tx_fifo_avail = idev->fifo_size - readl(idev->base + + ALTR_I2C_TC_FIFO_LVL); + int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len); + int ret = idev->msg_len - bytes_to_transfer; + + while (bytes_to_transfer-- > 0) { + altr_i2c_transfer(idev, *idev->buf++); + idev->msg_len--; + } + + return ret; +} + +static irqreturn_t altr_i2c_isr_quick(int irq, void *_dev) +{ + struct altr_i2c_dev *idev = _dev; + irqreturn_t ret = IRQ_HANDLED; + + /* Read IRQ status but only interested in Enabled IRQs. */ + idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask; + if (idev->isr_status) + ret = IRQ_WAKE_THREAD; + + return ret; +} + +static irqreturn_t altr_i2c_isr(int irq, void *_dev) +{ + int ret; + bool read, finish = false; + struct altr_i2c_dev *idev = _dev; + u32 status = idev->isr_status; + + if (!idev->msg) { + dev_warn(idev->dev, "unexpected interrupt\n"); + altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); + return IRQ_HANDLED; + } + read = (idev->msg->flags & I2C_M_RD) != 0; + + /* handle Lost Arbitration */ + if (unlikely(status & ALTR_I2C_ISR_ARB)) { + altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB); + idev->msg_err = -EAGAIN; + finish = true; + } else if (unlikely(status & ALTR_I2C_ISR_NACK)) { + dev_dbg(idev->dev, "Could not get ACK\n"); + idev->msg_err = -ENXIO; + altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK); + altr_i2c_stop(idev); + finish = true; + } else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) { + /* handle RX FIFO Overflow */ + altr_i2c_empty_rx_fifo(idev); + altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); + altr_i2c_stop(idev); + dev_err(idev->dev, "RX FIFO Overflow\n"); + finish = true; + } else if (read && (status & ALTR_I2C_ISR_RXRDY)) { + /* RX FIFO needs service? */ + altr_i2c_empty_rx_fifo(idev); + altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY); + if (!idev->msg_len) + finish = true; + } else if (!read && (status & ALTR_I2C_ISR_TXRDY)) { + /* TX FIFO needs service? */ + altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY); + if (idev->msg_len > 0) + altr_i2c_fill_tx_fifo(idev); + else + finish = true; + } else { + dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status); + altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); + } + + if (finish) { + /* Wait for the Core to finish */ + ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS, + status, + !(status & ALTR_I2C_STAT_CORE), + 1, ALTR_I2C_TIMEOUT); + if (ret) + dev_err(idev->dev, "message timeout\n"); + altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); + altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ); + complete(&idev->msg_complete); + dev_dbg(idev->dev, "Message Complete\n"); + } + + return IRQ_HANDLED; +} + +static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg) +{ + u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK; + unsigned long time_left; + u32 value; + u8 addr = i2c_8bit_addr_from_msg(msg); + + idev->msg = msg; + idev->msg_len = msg->len; + idev->buf = msg->buf; + idev->msg_err = 0; + reinit_completion(&idev->msg_complete); + altr_i2c_core_enable(idev); + + /* Make sure RX FIFO is empty */ + do { + readl(idev->base + ALTR_I2C_RX_DATA); + } while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL)); + + writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD); + + if ((msg->flags & I2C_M_RD) != 0) { + imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN; + altr_i2c_int_enable(idev, imask, true); + /* write the first byte to start the RX */ + altr_i2c_transfer(idev, 0); + } else { + imask |= ALTR_I2C_ISR_TXRDY; + altr_i2c_int_enable(idev, imask, true); + altr_i2c_fill_tx_fifo(idev); + } + + time_left = wait_for_completion_timeout(&idev->msg_complete, + ALTR_I2C_XFER_TIMEOUT); + altr_i2c_int_enable(idev, imask, false); + + value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE; + if (value) + dev_err(idev->dev, "Core Status not IDLE...\n"); + + if (time_left == 0) { + idev->msg_err = -ETIMEDOUT; + dev_dbg(idev->dev, "Transaction timed out.\n"); + } + + altr_i2c_core_disable(idev); + + return idev->msg_err; +} + +static int +altr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct altr_i2c_dev *idev = i2c_get_adapdata(adap); + int i, ret; + + for (i = 0; i < num; i++) { + ret = altr_i2c_xfer_msg(idev, msgs++); + if (ret) + return ret; + } + return num; +} + +static u32 altr_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm altr_i2c_algo = { + .master_xfer = altr_i2c_xfer, + .functionality = altr_i2c_func, +}; + +static int altr_i2c_probe(struct platform_device *pdev) +{ + struct altr_i2c_dev *idev = NULL; + struct resource *res; + int irq, ret; + u32 val; + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + idev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(idev->base)) + return PTR_ERR(idev->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing interrupt resource\n"); + return irq; + } + + idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(idev->i2c_clk)) { + dev_err(&pdev->dev, "missing clock\n"); + return PTR_ERR(idev->i2c_clk); + } + + idev->dev = &pdev->dev; + init_completion(&idev->msg_complete); + spin_lock_init(&idev->lock); + + val = device_property_read_u32(idev->dev, "fifo-size", + &idev->fifo_size); + if (val) { + dev_err(&pdev->dev, "FIFO size set to default of %d\n", + ALTR_I2C_DFLT_FIFO_SZ); + idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ; + } + + val = device_property_read_u32(idev->dev, "clock-frequency", + &idev->bus_clk_rate); + if (val) { + dev_err(&pdev->dev, "Default to 100kHz\n"); + idev->bus_clk_rate = 100000; /* default clock rate */ + } + + if (idev->bus_clk_rate > 400000) { + dev_err(&pdev->dev, "invalid clock-frequency %d\n", + idev->bus_clk_rate); + return -EINVAL; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick, + altr_i2c_isr, IRQF_ONESHOT, + pdev->name, idev); + if (ret) { + dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq); + return ret; + } + + ret = clk_prepare_enable(idev->i2c_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + return ret; + } + + altr_i2c_init(idev); + + i2c_set_adapdata(&idev->adapter, idev); + strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); + idev->adapter.owner = THIS_MODULE; + idev->adapter.algo = &altr_i2c_algo; + idev->adapter.dev.parent = &pdev->dev; + idev->adapter.dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, idev); + + ret = i2c_add_adapter(&idev->adapter); + if (ret) { + clk_disable_unprepare(idev->i2c_clk); + return ret; + } + dev_info(&pdev->dev, "Altera SoftIP I2C Probe Complete\n"); + + return 0; +} + +static int altr_i2c_remove(struct platform_device *pdev) +{ + struct altr_i2c_dev *idev = platform_get_drvdata(pdev); + + clk_disable_unprepare(idev->i2c_clk); + i2c_del_adapter(&idev->adapter); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id altr_i2c_of_match[] = { + { .compatible = "altr,softip-i2c-v1.0" }, + {}, +}; +MODULE_DEVICE_TABLE(of, altr_i2c_of_match); + +static struct platform_driver altr_i2c_driver = { + .probe = altr_i2c_probe, + .remove = altr_i2c_remove, + .driver = { + .name = "altera-i2c", + .of_match_table = altr_i2c_of_match, + }, +}; + +module_platform_driver(altr_i2c_driver); + +MODULE_DESCRIPTION("Altera Soft IP I2C bus driver"); +MODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c new file mode 100644 index 000000000000..284f8670dbeb --- /dev/null +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -0,0 +1,937 @@ +/* + * Aspeed 24XX/25XX I2C Controller. + * + * Copyright (C) 2012-2017 ASPEED Technology Inc. + * Copyright 2017 IBM Corporation + * Copyright 2017 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* I2C Register */ +#define ASPEED_I2C_FUN_CTRL_REG 0x00 +#define ASPEED_I2C_AC_TIMING_REG1 0x04 +#define ASPEED_I2C_AC_TIMING_REG2 0x08 +#define ASPEED_I2C_INTR_CTRL_REG 0x0c +#define ASPEED_I2C_INTR_STS_REG 0x10 +#define ASPEED_I2C_CMD_REG 0x14 +#define ASPEED_I2C_DEV_ADDR_REG 0x18 +#define ASPEED_I2C_BYTE_BUF_REG 0x20 + +/* Global Register Definition */ +/* 0x00 : I2C Interrupt Status Register */ +/* 0x08 : I2C Interrupt Target Assignment */ + +/* Device Register Definition */ +/* 0x00 : I2CD Function Control Register */ +#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15) +#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) +#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) +#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) +#define ASPEED_I2CD_SLAVE_EN BIT(1) +#define ASPEED_I2CD_MASTER_EN BIT(0) + +/* 0x04 : I2CD Clock and AC Timing Control Register #1 */ +#define ASPEED_I2CD_TIME_TBUF_MASK GENMASK(31, 28) +#define ASPEED_I2CD_TIME_THDSTA_MASK GENMASK(27, 24) +#define ASPEED_I2CD_TIME_TACST_MASK GENMASK(23, 20) +#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; + u32 (*get_clk_reg_val)(u32 divisor); + 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) { + dev_err(bus->dev, "bus in unknown state"); + bus->cmd_err = -EIO; + if (bus->master_state != ASPEED_I2C_MASTER_STOP) + 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 clk_high_low_max, 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 >= clk_high_max / 2 and + * clk_low >= clk_low_max / 2 - 1 when possible; this last constraint + * gives us the following solution: + */ + base_clk = divisor > clk_high_low_max ? + ilog2((divisor - 1) / clk_high_low_max) + 1 : 0; + tmp = (divisor + (1 << base_clk) - 1) >> base_clk; + clk_low = tmp / 2; + clk_high = tmp - clk_low; + + if (clk_high) + clk_high--; + + if (clk_low) + clk_low--; + + + 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); +} + +static u32 aspeed_i2c_24xx_get_clk_reg_val(u32 divisor) +{ + /* + * clk_high and clk_low are each 3 bits wide, so each can hold a max + * value of 8 giving a clk_high_low_max of 16. + */ + return aspeed_i2c_get_clk_reg_val(16, divisor); +} + +static u32 aspeed_i2c_25xx_get_clk_reg_val(u32 divisor) +{ + /* + * clk_high and clk_low are each 4 bits wide, so each can hold a max + * value of 16 giving a clk_high_low_max of 32. + */ + return aspeed_i2c_get_clk_reg_val(32, divisor); +} + +/* precondition: bus.lock has been acquired. */ +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) +{ + u32 divisor, clk_reg_val; + + divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); + clk_reg_val = readl(bus->base + ASPEED_I2C_AC_TIMING_REG1); + clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK | + ASPEED_I2CD_TIME_THDSTA_MASK | + ASPEED_I2CD_TIME_TACST_MASK); + clk_reg_val |= bus->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 const struct of_device_id aspeed_i2c_bus_of_table[] = { + { + .compatible = "aspeed,ast2400-i2c-bus", + .data = aspeed_i2c_24xx_get_clk_reg_val, + }, + { + .compatible = "aspeed,ast2500-i2c-bus", + .data = aspeed_i2c_25xx_get_clk_reg_val, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); + +static int aspeed_i2c_probe_bus(struct platform_device *pdev) +{ + const struct of_device_id *match; + 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; + } + + match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node); + if (!match) + bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val; + else + bus->get_clk_reg_val = match->data; + + /* 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 struct platform_driver aspeed_i2c_bus_driver = { + .probe = aspeed_i2c_probe_bus, + .remove = aspeed_i2c_remove_bus, + .driver = { + .name = "aspeed-i2c-bus", + .of_match_table = aspeed_i2c_bus_of_table, + }, +}; +module_platform_driver(aspeed_i2c_bus_driver); + +MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>"); +MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index fabbb9e49161..bfd1fdff64a9 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); @@ -809,7 +809,7 @@ out: * The hardware can handle at most two messages concatenated by a * repeated start via it's internal address feature. */ -static struct i2c_adapter_quirks at91_twi_quirks = { +static const struct i2c_adapter_quirks at91_twi_quirks = { .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, .max_comb_1st_msg_len = 3, }; @@ -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-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 318df559adc5..4c8c3bc4669c 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -510,8 +510,7 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev) static int bcm_iproc_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); /* make sure there's no pending interrupt when we go into suspend */ writel(0, iproc_i2c->base + IE_OFFSET); @@ -526,8 +525,7 @@ static int bcm_iproc_i2c_suspend(struct device *dev) static int bcm_iproc_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); int ret; u32 val; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 9fe942b8c610..ff3343186a82 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/delay.h> -#include <linux/i2c/bfin_twi.h> #include <asm/irq.h> #include <asm/portmux.h> diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 45d6771fac8c..b13605718291 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); } @@ -826,8 +826,7 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long */ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cdns_i2c *xi2c = platform_get_drvdata(pdev); + struct cdns_i2c *xi2c = dev_get_drvdata(dev); clk_disable(xi2c->clk); @@ -844,8 +843,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) */ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cdns_i2c *xi2c = platform_get_drvdata(pdev); + struct cdns_i2c *xi2c = dev_get_drvdata(dev); int ret; ret = clk_enable(xi2c->clk); diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c new file mode 100644 index 000000000000..190bbbc7bfee --- /dev/null +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -0,0 +1,363 @@ +/* + * Intel CHT Whiskey Cove PMIC I2C Master driver + * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> + * + * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: + * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved. + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CHT_WC_I2C_CTRL 0x5e24 +#define CHT_WC_I2C_CTRL_WR BIT(0) +#define CHT_WC_I2C_CTRL_RD BIT(1) +#define CHT_WC_I2C_CLIENT_ADDR 0x5e25 +#define CHT_WC_I2C_REG_OFFSET 0x5e26 +#define CHT_WC_I2C_WRDATA 0x5e27 +#define CHT_WC_I2C_RDDATA 0x5e28 + +#define CHT_WC_EXTCHGRIRQ 0x6e0a +#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ BIT(0) +#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ BIT(1) +#define CHT_WC_EXTCHGRIRQ_READ_IRQ BIT(2) +#define CHT_WC_EXTCHGRIRQ_NACK_IRQ BIT(3) +#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK ((u8)GENMASK(3, 1)) +#define CHT_WC_EXTCHGRIRQ_MSK 0x6e17 + +struct cht_wc_i2c_adap { + struct i2c_adapter adapter; + wait_queue_head_t wait; + struct irq_chip irqchip; + struct mutex adap_lock; + struct mutex irqchip_lock; + struct regmap *regmap; + struct irq_domain *irq_domain; + struct i2c_client *client; + int client_irq; + u8 irq_mask; + u8 old_irq_mask; + int read_data; + bool io_error; + bool done; +}; + +static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data) +{ + struct cht_wc_i2c_adap *adap = data; + int ret, reg; + + mutex_lock(&adap->adap_lock); + + /* Read IRQs */ + ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, ®); + if (ret) { + dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n"); + mutex_unlock(&adap->adap_lock); + return IRQ_NONE; + } + + reg &= ~adap->irq_mask; + + /* Reads must be acked after reading the received data. */ + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data); + if (ret) + adap->io_error = true; + + /* + * Immediately ack IRQs, so that if new IRQs arrives while we're + * handling the previous ones our irq will re-trigger when we're done. + */ + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg); + if (ret) + dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n"); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) { + adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ); + adap->done = true; + } + + mutex_unlock(&adap->adap_lock); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) + wake_up(&adap->wait); + + /* + * Do NOT use handle_nested_irq here, the client irq handler will + * likely want to do i2c transfers and the i2c controller uses this + * interrupt handler as well, so running the client irq handler from + * this thread will cause things to lock up. + */ + if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) { + /* + * generic_handle_irq expects local IRQs to be disabled + * as normally it is called from interrupt context. + */ + local_irq_disable(); + generic_handle_irq(adap->client_irq); + local_irq_enable(); + } + + return IRQ_HANDLED; +} + +static u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap) +{ + /* This i2c adapter only supports SMBUS byte transfers */ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap); + int ret; + + mutex_lock(&adap->adap_lock); + adap->io_error = false; + adap->done = false; + mutex_unlock(&adap->adap_lock); + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr); + if (ret) + return ret; + + if (read_write == I2C_SMBUS_WRITE) { + ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte); + if (ret) + return ret; + } + + ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL, + (read_write == I2C_SMBUS_WRITE) ? + CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD); + if (ret) + return ret; + + ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30)); + if (ret == 0) { + /* + * The CHT GPIO controller serializes all IRQs, sometimes + * causing significant delays, check status manually. + */ + cht_wc_i2c_adap_thread_handler(0, adap); + if (!adap->done) + return -ETIMEDOUT; + } + + ret = 0; + mutex_lock(&adap->adap_lock); + if (adap->io_error) + ret = -EIO; + else if (read_write == I2C_SMBUS_READ) + data->byte = adap->read_data; + mutex_unlock(&adap->adap_lock); + + return ret; +} + +static const struct i2c_algorithm cht_wc_i2c_adap_algo = { + .functionality = cht_wc_i2c_adap_master_func, + .smbus_xfer = cht_wc_i2c_adap_smbus_xfer, +}; + +/**** irqchip for the client connected to the extchgr i2c adapter ****/ +static void cht_wc_i2c_irq_lock(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + mutex_lock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_sync_unlock(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + int ret; + + if (adap->irq_mask != adap->old_irq_mask) { + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, + adap->irq_mask); + if (ret == 0) + adap->old_irq_mask = adap->irq_mask; + else + dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n"); + } + + mutex_unlock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_enable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static void cht_wc_i2c_irq_disable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static const struct irq_chip cht_wc_i2c_irq_chip = { + .irq_bus_lock = cht_wc_i2c_irq_lock, + .irq_bus_sync_unlock = cht_wc_i2c_irq_sync_unlock, + .irq_disable = cht_wc_i2c_irq_disable, + .irq_enable = cht_wc_i2c_irq_enable, + .name = "cht_wc_ext_chrg_irq_chip", +}; + +static const struct property_entry bq24190_props[] = { + PROPERTY_ENTRY_STRING("extcon-name", "cht_wcove_pwrsrc"), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct cht_wc_i2c_adap *adap; + struct i2c_board_info board_info = { + .type = "bq24190", + .addr = 0x6b, + .properties = bq24190_props, + }; + int ret, reg, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Error missing irq resource\n"); + return -EINVAL; + } + + adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + init_waitqueue_head(&adap->wait); + mutex_init(&adap->adap_lock); + mutex_init(&adap->irqchip_lock); + adap->irqchip = cht_wc_i2c_irq_chip; + adap->regmap = pmic->regmap; + adap->adapter.owner = THIS_MODULE; + adap->adapter.class = I2C_CLASS_HWMON; + adap->adapter.algo = &cht_wc_i2c_adap_algo; + strlcpy(adap->adapter.name, "PMIC I2C Adapter", + sizeof(adap->adapter.name)); + adap->adapter.dev.parent = &pdev->dev; + + /* Clear and activate i2c-adapter interrupts, disable client IRQ */ + adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK; + + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, ®); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask); + if (ret) + return ret; + + /* Alloc and register client IRQ */ + adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1, + &irq_domain_simple_ops, NULL); + if (!adap->irq_domain) + return -ENOMEM; + + adap->client_irq = irq_create_mapping(adap->irq_domain, 0); + if (!adap->client_irq) { + ret = -ENOMEM; + goto remove_irq_domain; + } + + irq_set_chip_data(adap->client_irq, adap); + irq_set_chip_and_handler(adap->client_irq, &adap->irqchip, + handle_simple_irq); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + cht_wc_i2c_adap_thread_handler, + IRQF_ONESHOT, "PMIC I2C Adapter", adap); + if (ret) + goto remove_irq_domain; + + i2c_set_adapdata(&adap->adapter, adap); + ret = i2c_add_adapter(&adap->adapter); + if (ret) + goto remove_irq_domain; + + board_info.irq = adap->client_irq; + adap->client = i2c_new_device(&adap->adapter, &board_info); + if (!adap->client) { + ret = -ENOMEM; + goto del_adapter; + } + + platform_set_drvdata(pdev, adap); + return 0; + +del_adapter: + i2c_del_adapter(&adap->adapter); +remove_irq_domain: + irq_domain_remove(adap->irq_domain); + return ret; +} + +static int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev) +{ + struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev); + + i2c_unregister_device(adap->client); + i2c_del_adapter(&adap->adapter); + irq_domain_remove(adap->irq_domain); + + return 0; +} + +static struct platform_device_id cht_wc_i2c_adap_id_table[] = { + { .name = "cht_wcove_ext_chgr" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table); + +static struct platform_driver cht_wc_i2c_adap_driver = { + .probe = cht_wc_i2c_adap_i2c_probe, + .remove = cht_wc_i2c_adap_i2c_remove, + .driver = { + .name = "cht_wcove_ext_chgr", + }, + .id_table = cht_wc_i2c_adap_id_table, +}; +module_platform_driver(cht_wc_i2c_adap_driver); + +MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index d89bde2c5da2..8a8ca945561b 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -413,7 +413,7 @@ static const struct i2c_algorithm cpm_i2c_algo = { }; /* CPM_MAX_READ is also limiting writes according to the code! */ -static struct i2c_adapter_quirks cpm_i2c_quirks = { +static const struct i2c_adapter_quirks cpm_i2c_quirks = { .max_num_msgs = CPM_MAXBD, .max_read_len = CPM_MAX_READ, .max_write_len = CPM_MAX_READ, diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 9e7ef5cf5d49..b8c43535f16c 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -733,7 +733,7 @@ static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) } #endif -static struct i2c_algorithm i2c_davinci_algo = { +static const struct i2c_algorithm i2c_davinci_algo = { .master_xfer = i2c_davinci_xfer, .functionality = i2c_davinci_func, }; @@ -801,7 +801,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) - return -ENODEV; + return PTR_ERR(dev->clk); clk_prepare_enable(dev->clk); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -876,8 +876,7 @@ static int davinci_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int davinci_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); /* put I2C into reset */ davinci_i2c_reset_ctrl(i2c_dev, 0); @@ -888,8 +887,7 @@ static int davinci_i2c_suspend(struct device *dev) static int davinci_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); clk_prepare_enable(i2c_dev->clk); /* take I2C out of reset */ 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 <linux/delay.h> +#include <linux/export.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include "i2c-designware-core.h" + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", + [ABRT_SLAVE_FLUSH_TXFIFO] = + "read command so flush old data in the TX FIFO", + [ABRT_SLAVE_ARBLOST] = + "slave lost the bus while transmitting data to a remote master", + [ABRT_SLAVE_RD_INTX] = + "incorrect slave-transmitter mode configuration", +}; + +u32 dw_readl(struct dw_i2c_dev *dev, int offset) +{ + u32 value; + + if (dev->flags & ACCESS_16BIT) + value = readw_relaxed(dev->base + offset) | + (readw_relaxed(dev->base + offset + 2) << 16); + else + value = readl_relaxed(dev->base + offset); + + if (dev->flags & ACCESS_SWAP) + return swab32(value); + else + return value; +} + +void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset) +{ + if (dev->flags & ACCESS_SWAP) + b = swab32(b); + + if (dev->flags & ACCESS_16BIT) { + writew_relaxed((u16)b, dev->base + offset); + writew_relaxed((u16)(b >> 16), dev->base + offset + 2); + } else { + writel_relaxed(b, dev->base + offset); + } +} + +u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 + - 3 + offset; +} + +u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset; +} + +void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable) +{ + dw_writel(dev, enable, DW_IC_ENABLE); +} + +void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable) +{ + int timeout = 100; + + do { + __i2c_dw_enable(dev, enable); + if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable) + return; + + /* + * Wait 10 times the signaling period of the highest I2C + * transfer supported by the driver (for 400KHz this is + * 25us) as described in the DesignWare I2C databook. + */ + usleep_range(25, 250); + } while (timeout--); + + dev_warn(dev->dev, "timeout in %sabling adapter\n", + enable ? "en" : "dis"); +} + +unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev) +{ + /* + * Clock is not necessary if we got LCNT/HCNT values directly from + * the platform code. + */ + if (WARN_ON_ONCE(!dev->get_clk_rate_khz)) + return 0; + return dev->get_clk_rate_khz(dev); +} + +int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) +{ + int ret; + + if (!dev->acquire_lock) + return 0; + + ret = dev->acquire_lock(dev); + if (!ret) + return 0; + + dev_err(dev->dev, "couldn't acquire bus ownership\n"); + + return ret; +} + +void i2c_dw_release_lock(struct dw_i2c_dev *dev) +{ + if (dev->release_lock) + dev->release_lock(dev); +} + +/* + * Waiting for bus not busy + */ +int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) +{ + int timeout = TIMEOUT; + + while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + if (timeout <= 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + timeout--; + usleep_range(1000, 1100); + } + + return 0; +} + +int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & DW_IC_TX_ARB_LOST) + return -EAGAIN; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ + else + return -EIO; +} + +u32 i2c_dw_func(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + return dev->functionality; +} + +void i2c_dw_disable(struct dw_i2c_dev *dev) +{ + /* Disable controller */ + __i2c_dw_enable_and_wait(dev, false); + + /* Disable all interupts */ + dw_writel(dev, 0, DW_IC_INTR_MASK); + dw_readl(dev, DW_IC_CLR_INTR); +} + +void i2c_dw_disable_int(struct dw_i2c_dev *dev) +{ + dw_writel(dev, 0, DW_IC_INTR_MASK); +} + +u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev) +{ + return dw_readl(dev, DW_IC_COMP_PARAM_1); +} +EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param); + +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); +MODULE_LICENSE("GPL"); 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 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 <linux/export.h> -#include <linux/errno.h> +#include <linux/delay.h> #include <linux/err.h> +#include <linux/errno.h> +#include <linux/export.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/pm_runtime.h> -#include <linux/delay.h> #include <linux/module.h> -#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) -{ - 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; -} - -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"); -} +#include <linux/pm_runtime.h> -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); -} +#include "i2c-designware-core.h" -static int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) +static void i2c_dw_configure_fifo_master(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; -} + /* 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); -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 IRQ_HANDLED; + return 0; } -void i2c_dw_disable(struct dw_i2c_dev *dev) +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) { - /* Disable controller */ - __i2c_dw_enable_and_wait(dev, false); + struct dw_i2c_dev *dev = dev_id; + u32 stat, enabled; - /* Disable all interupts */ - dw_writel(dev, 0, DW_IC_INTR_MASK); - dw_readl(dev, DW_IC_CLR_INTR); -} -EXPORT_SYMBOL_GPL(i2c_dw_disable); + 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; -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); + i2c_dw_irq_handler_master(dev); -u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev) -{ - return dw_readl(dev, DW_IC_COMP_PARAM_1); + return IRQ_HANDLED; } -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..0e65b97842b4 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 <linux/kernel.h> -#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/clk-provider.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/dmi.h> -#include <linux/i2c.h> -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/errno.h> -#include <linux/sched.h> #include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> +#include <linux/platform_data/i2c-designware.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> -#include <linux/io.h> #include <linux/reset.h> +#include <linux/sched.h> #include <linux/slab.h> -#include <linux/acpi.h> -#include <linux/platform_data/i2c-designware.h> + #include "i2c-designware-core.h" static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) @@ -171,6 +172,48 @@ 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; + + 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 +252,12 @@ 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 i, irq, ret; + const int supported_speeds[] = { 0, 100000, 400000, 1000000, 3400000 }; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -255,6 +299,16 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); /* + * Some DSTDs use a non standard speed, round down to the lowest + * standard speed. + */ + for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) { + if (acpi_speed < supported_speeds[i]) + break; + } + acpi_speed = supported_speeds[i - 1]; + + /* * Find bus speed from the "clock-frequency" device property, ACPI * or by using fast mode if neither is set. */ @@ -275,30 +329,20 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) if (dev->clk_freq != 100000 && dev->clk_freq != 400000 && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) { dev_err(&pdev->dev, - "Only 100kHz, 400kHz, 1MHz and 3.4MHz supported"); - r = -EINVAL; + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n", + dev->clk_freq); + 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 +371,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 +387,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 +398,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); @@ -389,12 +437,11 @@ static void dw_i2c_plat_complete(struct device *dev) #endif #ifdef CONFIG_PM -static int dw_i2c_plat_suspend(struct device *dev) +static int dw_i2c_plat_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i2c_dw_disable(i_dev); + i_dev->disable(i_dev); i2c_dw_plat_prepare_clk(i_dev, false); return 0; @@ -402,20 +449,29 @@ static int dw_i2c_plat_suspend(struct device *dev) static int dw_i2c_plat_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); i2c_dw_plat_prepare_clk(i_dev, true); - i2c_dw_init(i_dev); + i_dev->init(i_dev); return 0; } +#ifdef CONFIG_PM_SLEEP +static int dw_i2c_plat_suspend(struct device *dev) +{ + pm_runtime_resume(dev); + return dw_i2c_plat_runtime_suspend(dev); +} +#endif + static const struct dev_pm_ops dw_i2c_dev_pm_ops = { .prepare = dw_i2c_plat_prepare, .complete = dw_i2c_plat_complete, SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL) + SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, + dw_i2c_plat_resume, + NULL) }; #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) @@ -423,7 +479,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..ea9578ab19a1 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -0,0 +1,395 @@ +/* + * Synopsys DesignWare I2C adapter driver (slave only). + * + * Based on the Synopsys DesignWare I2C adapter driver (master). + * + * Copyright (C) 2016 Synopsys Inc. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ---------------------------------------------------------------------------- + * + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include "i2c-designware-core.h" + +static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev) +{ + /* Configure Tx/Rx FIFO threshold levels. */ + dw_writel(dev, 0, DW_IC_TX_TL); + dw_writel(dev, 0, DW_IC_RX_TL); + + /* Configure the I2C slave. */ + dw_writel(dev, dev->slave_cfg, DW_IC_CON); + dw_writel(dev, DW_IC_INTR_SLAVE_MASK, DW_IC_INTR_MASK); +} + +/** + * i2c_dw_init_slave() - Initialize the designware i2c slave hardware + * @dev: device private data + * + * This function configures and enables the I2C in slave mode. + * This function is called during I2C init function, and in case of timeout at + * run time. + */ +static int i2c_dw_init_slave(struct dw_i2c_dev *dev) +{ + u32 sda_falling_time, scl_falling_time; + u32 reg, comp_param1; + u32 hcnt, lcnt; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + reg = dw_readl(dev, DW_IC_COMP_TYPE); + if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) { + /* Configure register endianness access. */ + dev->flags |= ACCESS_SWAP; + } else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) { + /* Configure register access mode 16bit. */ + dev->flags |= ACCESS_16BIT; + } else if (reg != DW_IC_COMP_TYPE_VALUE) { + dev_err(dev->dev, + "Unknown Synopsys component type: 0x%08x\n", reg); + i2c_dw_release_lock(dev); + return -ENODEV; + } + + comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1); + + /* Disable the adapter. */ + __i2c_dw_enable_and_wait(dev, false); + + /* Set standard and fast speed deviders for high/low periods. */ + sda_falling_time = dev->sda_falling_time ?: 300; /* ns */ + scl_falling_time = dev->scl_falling_time ?: 300; /* ns */ + + /* Set SCL timing parameters for standard-mode. */ + if (dev->ss_hcnt && dev->ss_lcnt) { + hcnt = dev->ss_hcnt; + lcnt = dev->ss_lcnt; + } else { + hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev), + 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev), + 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + } + dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT); + dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Set SCL timing parameters for fast-mode or fast-mode plus. */ + if ((dev->clk_freq == 1000000) && dev->fp_hcnt && dev->fp_lcnt) { + hcnt = dev->fp_hcnt; + lcnt = dev->fp_lcnt; + } else if (dev->fs_hcnt && dev->fs_lcnt) { + hcnt = dev->fs_hcnt; + lcnt = dev->fs_lcnt; + } else { + hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev), + 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev), + 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + } + dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT); + dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + if ((dev->slave_cfg & DW_IC_CON_SPEED_MASK) == + DW_IC_CON_SPEED_HIGH) { + if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK) + != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) { + dev_err(dev->dev, "High Speed not supported!\n"); + dev->slave_cfg &= ~DW_IC_CON_SPEED_MASK; + dev->slave_cfg |= DW_IC_CON_SPEED_FAST; + } else if (dev->hs_hcnt && dev->hs_lcnt) { + hcnt = dev->hs_hcnt; + lcnt = dev->hs_lcnt; + dw_writel(dev, hcnt, DW_IC_HS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_HS_SCL_LCNT); + dev_dbg(dev->dev, "HighSpeed-mode HCNT:LCNT = %d:%d\n", + hcnt, lcnt); + } + } + + /* Configure SDA Hold Time if required. */ + reg = dw_readl(dev, DW_IC_COMP_VERSION); + if (reg >= DW_IC_SDA_HOLD_MIN_VERS) { + if (!dev->sda_hold_time) { + /* Keep previous hold time setting if no one set it. */ + dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD); + } + /* + * Workaround for avoiding TX arbitration lost in case I2C + * slave pulls SDA down "too quickly" after falling egde of + * SCL by enabling non-zero SDA RX hold. Specification says it + * extends incoming SDA low to high transition while SCL is + * high but it apprears to help also above issue. + */ + if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK)) + dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT; + dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD); + } else { + dev_warn(dev->dev, + "Hardware too old to adjust SDA hold time.\n"); + } + + i2c_dw_configure_fifo_slave(dev); + i2c_dw_release_lock(dev); + + return 0; +} + +static int i2c_dw_reg_slave(struct i2c_client *slave) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + if (dev->slave) + return -EBUSY; + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + pm_runtime_get_sync(dev->dev); + + /* + * 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; + pm_runtime_put(dev->dev); + + 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) || !dev->slave) + 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 const 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); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_dw_probe_slave); + +MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter"); +MODULE_LICENSE("GPL v2"); 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-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 23ed4d67ecad..3855e0b11877 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -803,8 +803,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int exynos5_i2c_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + struct exynos5_i2c *i2c = dev_get_drvdata(dev); i2c->suspended = 1; @@ -815,8 +814,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) static int exynos5_i2c_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + struct exynos5_i2c *i2c = dev_get_drvdata(dev); int ret = 0; ret = clk_prepare_enable(i2c->clk); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 34cfc0ebdcb9..0ef8fcc6ac3a 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -98,8 +98,8 @@ static int of_i2c_gpio_get_pins(struct device_node *np, return -EPROBE_DEFER; if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) { - pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", - np->full_name, *sda_pin, *scl_pin); + pr_err("%pOF: invalid GPIO pins, sda=%d/scl=%d\n", + np, *sda_pin, *scl_pin); return -ENODEV; } diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c index ae7f3180f7e8..bb68957d3da5 100644 --- a/drivers/i2c/busses/i2c-hix5hd2.c +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -505,8 +505,7 @@ static int hix5hd2_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int hix5hd2_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct hix5hd2_i2c_priv *priv = platform_get_drvdata(pdev); + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); clk_disable_unprepare(priv->clk); @@ -515,8 +514,7 @@ static int hix5hd2_i2c_runtime_suspend(struct device *dev) static int hix5hd2_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct hix5hd2_i2c_priv *priv = platform_get_drvdata(pdev); + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); clk_prepare_enable(priv->clk); hix5hd2_i2c_init(priv); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 6484fa6dbb84..9e12a53ef7b8 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -66,6 +66,9 @@ * 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 + * Cedar Fork (PCH) 0x18df 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -202,6 +205,7 @@ /* Older devices have their ID defined in <linux/pci_ids.h> */ #define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12 +#define PCI_DEVICE_ID_INTEL_CDF_SMBUS 0x18df #define PCI_DEVICE_ID_INTEL_DNV_SMBUS 0x19df #define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 @@ -226,10 +230,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; @@ -1021,11 +1027,14 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROXTON_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_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, } }; @@ -1326,6 +1335,7 @@ static void i801_add_tco(struct i801_priv *priv) u32 tco_base, tco_ctl; u32 base_addr, ctrl_val; u64 base64_addr; + u8 hidden; if (!(priv->features & FEATURE_TCO)) return; @@ -1370,8 +1380,10 @@ static void i801_add_tco(struct i801_priv *priv) devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1); - /* Unhide the P2SB device */ - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0); + /* Unhide the P2SB device, if it is hidden */ + pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden); + if (hidden) + pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0); pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr); base64_addr = base_addr & 0xfffffff0; @@ -1379,8 +1391,9 @@ static void i801_add_tco(struct i801_priv *priv) pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr); base64_addr |= (u64)base_addr << 32; - /* Hide the P2SB device */ - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x1); + /* Hide the P2SB device, if it was hidden before */ + if (hidden) + pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden); spin_unlock(&p2sb_spinlock); res = &tco_res[ICH_RES_MEM_OFF]; @@ -1499,8 +1512,11 @@ 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_CDF_SMBUS: case PCI_DEVICE_ID_INTEL_DNV_SMBUS: case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS: priv->features |= FEATURE_I2C_BLOCK_READ; diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 84fb35f6837f..eb1d91b986fd 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1459,6 +1459,6 @@ static struct platform_driver img_scb_i2c_driver = { }; module_platform_driver(img_scb_i2c_driver); -MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>"); +MODULE_AUTHOR("James Hogan <jhogan@kernel.org>"); MODULE_DESCRIPTION("IMG host I2C driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 54a47b40546f..f96830ffd9f1 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1021,7 +1021,7 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx, } dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n", - rinfo->sda_gpio, rinfo->scl_gpio); + rinfo->scl_gpio, rinfo->sda_gpio); rinfo->prepare_recovery = i2c_imx_prepare_recovery; rinfo->unprepare_recovery = i2c_imx_unprepare_recovery; @@ -1100,7 +1100,7 @@ static int i2c_imx_probe(struct platform_device *pdev) } /* Request IRQ */ - ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, + ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED, pdev->name, i2c_imx); if (ret) { dev_err(&pdev->dev, "can't claim irq %d\n", irq); diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index e98e44e584a4..b51adffa4841 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -340,6 +340,11 @@ static int ismt_process_desc(const struct ismt_desc *desc, data->word = dma_buffer[0] | (dma_buffer[1] << 8); break; case I2C_SMBUS_BLOCK_DATA: + if (desc->rxbytes != dma_buffer[0] + 1) + return -EMSGSIZE; + + memcpy(data->block, dma_buffer, desc->rxbytes); + break; case I2C_SMBUS_I2C_BLOCK_DATA: memcpy(&data->block[1], dma_buffer, desc->rxbytes); data->block[0] = desc->rxbytes; diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c index 25993d2e64bf..e879190b5d1d 100644 --- a/drivers/i2c/busses/i2c-kempld.c +++ b/drivers/i2c/busses/i2c-kempld.c @@ -289,7 +289,7 @@ static const struct i2c_algorithm kempld_i2c_algorithm = { .functionality = kempld_i2c_func, }; -static struct i2c_adapter kempld_i2c_adapter = { +static const struct i2c_adapter kempld_i2c_adapter = { .owner = THIS_MODULE, .name = "i2c-kempld", .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c index 9b1fef455a89..59167c018ae7 100644 --- a/drivers/i2c/busses/i2c-lpc2k.c +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -457,8 +457,7 @@ static int i2c_lpc2k_remove(struct platform_device *dev) #ifdef CONFIG_PM static int i2c_lpc2k_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct lpc2k_i2c *i2c = platform_get_drvdata(pdev); + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -467,8 +466,7 @@ static int i2c_lpc2k_suspend(struct device *dev) static int i2c_lpc2k_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct lpc2k_i2c *i2c = platform_get_drvdata(pdev); + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); clk_enable(i2c->clk); i2c_lpc2k_reset(i2c); diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c index d271e6a0954c..4c28fa28ce76 100644 --- a/drivers/i2c/busses/i2c-mlxcpld.c +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -433,7 +433,7 @@ static const struct i2c_algorithm mlxcpld_i2c_algo = { .functionality = mlxcpld_i2c_func }; -static struct i2c_adapter_quirks mlxcpld_i2c_quirks = { +static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = { .flags = I2C_AQ_COMB_WRITE_THEN_READ, .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 45d61714c81b..09d288ce0ddb 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -50,7 +50,6 @@ #define I2C_FS_START_CON 0x1800 #define I2C_TIME_CLR_VALUE 0x0000 #define I2C_TIME_DEFAULT_VALUE 0x0003 -#define I2C_FS_TIME_INIT_VALUE 0x1303 #define I2C_WRRD_TRANAC_VALUE 0x0002 #define I2C_RD_TRANAC_VALUE 0x0001 @@ -154,6 +153,7 @@ struct mtk_i2c { bool use_push_pull; /* IO config push-pull mode */ u16 irq_stat; /* interrupt status */ + unsigned int clk_src_div; unsigned int speed_hz; /* The speed in transfer */ enum mtk_trans_op op; u16 timing_reg; @@ -172,6 +172,10 @@ static const struct i2c_adapter_quirks mt6577_i2c_quirks = { .max_comb_2nd_msg_len = 31, }; +static const struct i2c_adapter_quirks mt7622_i2c_quirks = { + .max_num_msgs = 255, +}; + static const struct mtk_i2c_compatible mt6577_compat = { .quirks = &mt6577_i2c_quirks, .pmic_i2c = 0, @@ -190,6 +194,15 @@ static const struct mtk_i2c_compatible mt6589_compat = { .support_33bits = 0, }; +static const struct mtk_i2c_compatible mt7622_compat = { + .quirks = &mt7622_i2c_quirks, + .pmic_i2c = 0, + .dcm = 1, + .auto_restart = 1, + .aux_len_reg = 1, + .support_33bits = 0, +}; + static const struct mtk_i2c_compatible mt8173_compat = { .pmic_i2c = 0, .dcm = 1, @@ -201,6 +214,7 @@ static const struct mtk_i2c_compatible mt8173_compat = { static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, {} }; @@ -285,23 +299,20 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) * less than or equal to i2c->speed_hz. The calculation try to get * sample_cnt and step_cn */ -static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, - unsigned int clock_div) +static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, + unsigned int target_speed, + unsigned int *timing_step_cnt, + unsigned int *timing_sample_cnt) { - unsigned int clk_src; unsigned int step_cnt; unsigned int sample_cnt; unsigned int max_step_cnt; - unsigned int target_speed; unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV; unsigned int base_step_cnt; unsigned int opt_div; unsigned int best_mul; unsigned int cnt_mul; - clk_src = parent_clk / clock_div; - target_speed = i2c->speed_hz; - if (target_speed > MAX_HS_MODE_SPEED) target_speed = MAX_HS_MODE_SPEED; @@ -347,16 +358,48 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, return -EINVAL; } - step_cnt--; - sample_cnt--; + *timing_step_cnt = step_cnt - 1; + *timing_sample_cnt = sample_cnt - 1; + + return 0; +} + +static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) +{ + unsigned int clk_src; + unsigned int step_cnt; + unsigned int sample_cnt; + unsigned int target_speed; + int ret; + + clk_src = parent_clk / i2c->clk_src_div; + target_speed = i2c->speed_hz; if (target_speed > MAX_FS_MODE_SPEED) { + /* Set master code speed register */ + ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + + i2c->timing_reg = (sample_cnt << 8) | step_cnt; + /* Set the high speed mode register */ - i2c->timing_reg = I2C_FS_TIME_INIT_VALUE; + ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE | (sample_cnt << 12) | (step_cnt << 8); } else { - i2c->timing_reg = (sample_cnt << 8) | (step_cnt << 0); + ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + + i2c->timing_reg = (sample_cnt << 8) | step_cnt; + /* Disable the high speed transaction */ i2c->high_speed_reg = I2C_TIME_CLR_VALUE; } @@ -647,8 +690,7 @@ static const struct i2c_algorithm mtk_i2c_algorithm = { .functionality = mtk_i2c_functionality, }; -static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, - unsigned int *clk_src_div) +static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) { int ret; @@ -656,11 +698,11 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, if (ret < 0) i2c->speed_hz = I2C_DEFAULT_SPEED; - ret = of_property_read_u32(np, "clock-div", clk_src_div); + ret = of_property_read_u32(np, "clock-div", &i2c->clk_src_div); if (ret < 0) return ret; - if (*clk_src_div == 0) + if (i2c->clk_src_div == 0) return -EINVAL; i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic"); @@ -676,7 +718,6 @@ static int mtk_i2c_probe(struct platform_device *pdev) int ret = 0; struct mtk_i2c *i2c; struct clk *clk; - unsigned int clk_src_div; struct resource *res; int irq; @@ -684,7 +725,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; - ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div); + ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c); if (ret) return -EINVAL; @@ -745,7 +786,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); - ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div); + ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk)); if (ret) { dev_err(&pdev->dev, "Failed to set the speed.\n"); return -EINVAL; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 5c4db65c5019..a832c45276a4 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -820,7 +820,7 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, goto out; } - drv_data->rstc = devm_reset_control_get_optional(dev, NULL); + drv_data->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(drv_data->rstc)) { rc = PTR_ERR(drv_data->rstc); goto out; @@ -975,8 +975,7 @@ mv64xxx_i2c_remove(struct platform_device *dev) #ifdef CONFIG_PM static int mv64xxx_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pdev); + struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev); mv64xxx_i2c_hw_init(drv_data); 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-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index da6609d62848..49c7c0c91486 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -1088,7 +1088,7 @@ static struct i2c_vendor_data vendor_db8500 = { .fifodepth = 32, /* Guessed from TFTR/RFTR = 15 */ }; -static struct amba_id nmk_i2c_ids[] = { +static const struct amba_id nmk_i2c_ids[] = { { .id = 0x00180024, .mask = 0x00ffffff, diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 34f1889a4073..8c42ca7107b2 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -276,7 +276,7 @@ static const struct i2c_algorithm ocores_algorithm = { .functionality = ocores_func, }; -static struct i2c_adapter ocores_adapter = { +static const struct i2c_adapter ocores_adapter = { .owner = THIS_MODULE, .name = "i2c-ocores", .class = I2C_CLASS_DEPRECATED, diff --git a/drivers/i2c/busses/i2c-octeon-platdrv.c b/drivers/i2c/busses/i2c-octeon-platdrv.c index 917524ce6890..64bda83e65ac 100644 --- a/drivers/i2c/busses/i2c-octeon-platdrv.c +++ b/drivers/i2c/busses/i2c-octeon-platdrv.c @@ -126,7 +126,7 @@ static const struct i2c_algorithm octeon_i2c_algo = { .functionality = octeon_i2c_functionality, }; -static struct i2c_adapter octeon_i2c_ops = { +static const struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = &octeon_i2c_algo, diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 1ebb5e947e0b..23c2ea2baedc 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -360,6 +360,7 @@ static int omap_i2c_init(struct omap_i2c_dev *omap) unsigned long fclk_rate = 12000000; unsigned long internal_clk = 0; struct clk *fclk; + int error; if (omap->rev >= OMAP_I2C_REV_ON_3430_3530) { /* @@ -378,6 +379,13 @@ static int omap_i2c_init(struct omap_i2c_dev *omap) * do this bit unconditionally. */ fclk = clk_get(omap->dev, "fck"); + if (IS_ERR(fclk)) { + error = PTR_ERR(fclk); + dev_err(omap->dev, "could not get fck: %i\n", error); + + return error; + } + fclk_rate = clk_get_rate(fclk); clk_put(fclk); @@ -410,6 +418,12 @@ static int omap_i2c_init(struct omap_i2c_dev *omap) else internal_clk = 4000; fclk = clk_get(omap->dev, "fck"); + if (IS_ERR(fclk)) { + error = PTR_ERR(fclk); + dev_err(omap->dev, "could not get fck: %i\n", error); + + return error; + } fclk_rate = clk_get_rate(fclk) / 1000; clk_put(fclk); diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c index 11e2a1fc10e9..0aabb7eca0c5 100644 --- a/drivers/i2c/busses/i2c-opal.c +++ b/drivers/i2c/busses/i2c-opal.c @@ -204,7 +204,7 @@ static const struct i2c_algorithm i2c_opal_algo = { * For two messages, we basically support simple smbus transactions of a * write-then-anything. */ -static struct i2c_adapter_quirks i2c_opal_quirks = { +static const struct i2c_adapter_quirks i2c_opal_quirks = { .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, .max_comb_1st_msg_len = 4, }; 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 <linux/i2c-algo-pca.h> #include <linux/i2c-pca-platform.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/irq.h> 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-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 0ecdb47a23ab..174579d32e5f 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -85,6 +85,9 @@ /* SB800 constants */ #define SB800_PIIX4_SMB_IDX 0xcd6 +#define KERNCZ_IMC_IDX 0x3e +#define KERNCZ_IMC_DATA 0x3f + /* * SB800 port is selected by bits 2:1 of the smb_en register (0x2c) * or the smb_sel register (0x2e), depending on bit 0 of register 0x2f. @@ -94,6 +97,12 @@ #define SB800_PIIX4_PORT_IDX_ALT 0x2e #define SB800_PIIX4_PORT_IDX_SEL 0x2f #define SB800_PIIX4_PORT_IDX_MASK 0x06 +#define SB800_PIIX4_PORT_IDX_SHIFT 1 + +/* On kerncz, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ +#define SB800_PIIX4_PORT_IDX_KERNCZ 0x02 +#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 +#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 /* insmod parameters */ @@ -149,6 +158,8 @@ static const struct dmi_system_id piix4_dmi_ibm[] = { */ static DEFINE_MUTEX(piix4_mutex_sb800); static u8 piix4_port_sel_sb800; +static u8 piix4_port_mask_sb800; +static u8 piix4_port_shift_sb800; static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { " port 0", " port 2", " port 3", " port 4" }; @@ -159,6 +170,7 @@ struct i2c_piix4_adapdata { /* SB800 */ bool sb800_main; + bool notify_imc; u8 port; /* Port number, shifted */ }; @@ -347,7 +359,19 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, /* Find which register is used for port selection */ if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) { - piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; + switch (PIIX4_dev->device) { + case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS: + piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; + piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ; + piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ; + break; + case PCI_DEVICE_ID_AMD_HUDSON2_SMBUS: + default: + piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; + piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; + piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; + break; + } } else { mutex_lock(&piix4_mutex_sb800); outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); @@ -355,6 +379,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_port_sel_sb800 = (port_sel & 0x01) ? SB800_PIIX4_PORT_IDX_ALT : SB800_PIIX4_PORT_IDX; + piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; + piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; mutex_unlock(&piix4_mutex_sb800); } @@ -572,6 +598,67 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr, return 0; } +static uint8_t piix4_imc_read(uint8_t idx) +{ + outb_p(idx, KERNCZ_IMC_IDX); + return inb_p(KERNCZ_IMC_DATA); +} + +static void piix4_imc_write(uint8_t idx, uint8_t value) +{ + outb_p(idx, KERNCZ_IMC_IDX); + outb_p(value, KERNCZ_IMC_DATA); +} + +static int piix4_imc_sleep(void) +{ + int timeout = MAX_TIMEOUT; + + if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) + return -EBUSY; + + /* clear response register */ + piix4_imc_write(0x82, 0x00); + /* request ownership flag */ + piix4_imc_write(0x83, 0xB4); + /* kick off IMC Mailbox command 96 */ + piix4_imc_write(0x80, 0x96); + + while (timeout--) { + if (piix4_imc_read(0x82) == 0xfa) { + release_region(KERNCZ_IMC_IDX, 2); + return 0; + } + usleep_range(1000, 2000); + } + + release_region(KERNCZ_IMC_IDX, 2); + return -ETIMEDOUT; +} + +static void piix4_imc_wakeup(void) +{ + int timeout = MAX_TIMEOUT; + + if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) + return; + + /* clear response register */ + piix4_imc_write(0x82, 0x00); + /* release ownership flag */ + piix4_imc_write(0x83, 0xB5); + /* kick off IMC Mailbox command 96 */ + piix4_imc_write(0x80, 0x96); + + while (timeout--) { + if (piix4_imc_read(0x82) == 0xfa) + break; + usleep_range(1000, 2000); + } + + release_region(KERNCZ_IMC_IDX, 2); +} + /* * Handles access to multiple SMBus ports on the SB800. * The port is selected by bits 2:1 of the smb_en register (0x2c). @@ -612,12 +699,47 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, return -EBUSY; } + /* + * Notify the IMC (Integrated Micro Controller) if required. + * Among other responsibilities, the IMC is in charge of monitoring + * the System fans and temperature sensors, and act accordingly. + * All this is done through SMBus and can/will collide + * with our transactions if they are long (BLOCK_DATA). + * Therefore we need to request the ownership flag during those + * transactions. + */ + if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) { + int ret; + + ret = piix4_imc_sleep(); + switch (ret) { + case -EBUSY: + dev_warn(&adap->dev, + "IMC base address index region 0x%x already in use.\n", + KERNCZ_IMC_IDX); + break; + case -ETIMEDOUT: + dev_warn(&adap->dev, + "Failed to communicate with the IMC.\n"); + break; + default: + break; + } + + /* If IMC communication fails do not retry */ + if (ret) { + dev_warn(&adap->dev, + "Continuing without IMC notification.\n"); + adapdata->notify_imc = false; + } + } + outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); port = adapdata->port; - if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != port) - outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | port, + if ((smba_en_lo & piix4_port_mask_sb800) != port) + outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, SB800_PIIX4_SMB_IDX + 1); retval = piix4_access(adap, addr, flags, read_write, @@ -628,6 +750,9 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, /* Release the semaphore */ outb_p(smbslvcnt | 0x20, SMBSLVCNT); + if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) + piix4_imc_wakeup(); + mutex_unlock(&piix4_mutex_sb800); return retval; @@ -679,7 +804,7 @@ static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; static struct i2c_adapter *piix4_aux_adapter; static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, - bool sb800_main, u8 port, + bool sb800_main, u8 port, bool notify_imc, const char *name, struct i2c_adapter **padap) { struct i2c_adapter *adap; @@ -706,7 +831,8 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, adapdata->smba = smba; adapdata->sb800_main = sb800_main; - adapdata->port = port << 1; + adapdata->port = port << piix4_port_shift_sb800; + adapdata->notify_imc = notify_imc; /* set up the sysfs linkage to our parent device */ adap->dev.parent = &dev->dev; @@ -728,14 +854,15 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, return 0; } -static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba) +static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, + bool notify_imc) { struct i2c_piix4_adapdata *adapdata; int port; int retval; for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) { - retval = piix4_add_adapter(dev, smba, true, port, + retval = piix4_add_adapter(dev, smba, true, port, notify_imc, piix4_main_port_names_sb800[port], &piix4_main_adapters[port]); if (retval < 0) @@ -769,6 +896,7 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->revision >= 0x40) || dev->vendor == PCI_VENDOR_ID_AMD) { + bool notify_imc = false; is_sb800 = true; if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) { @@ -778,6 +906,20 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) return -EBUSY; } + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { + u8 imc; + + /* + * Detect if IMC is active or not, this method is + * described on coreboot's AMD IMC notes + */ + pci_bus_read_config_byte(dev->bus, PCI_DEVFN(0x14, 3), + 0x40, &imc); + if (imc & 0x80) + notify_imc = true; + } + /* base address location etc changed in SB800 */ retval = piix4_setup_sb800(dev, id, 0); if (retval < 0) { @@ -789,7 +931,7 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) * Try to register multiplexed main SMBus adapter, * give up if we can't */ - retval = piix4_add_adapters_sb800(dev, retval); + retval = piix4_add_adapters_sb800(dev, retval, notify_imc); if (retval < 0) { release_region(SB800_PIIX4_SMB_IDX, 2); return retval; @@ -800,7 +942,7 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) return retval; /* Try to register main SMBus adapter, give up if we can't */ - retval = piix4_add_adapter(dev, retval, false, 0, "", + retval = piix4_add_adapter(dev, retval, false, 0, false, "", &piix4_main_adapters[0]); if (retval < 0) return retval; @@ -827,7 +969,7 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) if (retval > 0) { /* Try to add the aux adapter if it exists, * piix4_add_adapter will clean up if this fails */ - piix4_add_adapter(dev, retval, false, 0, + piix4_add_adapter(dev, retval, false, 0, false, is_sb800 ? piix4_aux_port_name_sb800 : "", &piix4_aux_adapter); } diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index 217c78711d65..2aa0e83174c5 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -577,7 +577,7 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL; } -static struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { +static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { .flags = I2C_AQ_COMB_WRITE_THEN_READ, .max_write_len = MSP_MAX_BYTES_PER_RW, .max_read_len = MSP_MAX_BYTES_PER_RW, @@ -587,7 +587,7 @@ static struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { /* -- Initialization -- */ -static struct i2c_algorithm pmcmsptwi_algo = { +static const struct i2c_algorithm pmcmsptwi_algo = { .master_xfer = pmcmsptwi_master_xfer, .functionality = pmcmsptwi_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index fd5f9d2bf6d9..42d6b3a226f8 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -590,7 +590,7 @@ static u32 i2c_pnx_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm pnx_algorithm = { +static const struct i2c_algorithm pnx_algorithm = { .master_xfer = i2c_pnx_xfer, .functionality = i2c_pnx_func, }; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index b0d9dee14a7e..f2a2067525ef 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -197,7 +197,7 @@ static const struct i2c_algorithm i2c_powermac_algorithm = { .functionality = i2c_powermac_func, }; -static struct i2c_adapter_quirks i2c_powermac_quirks = { +static const struct i2c_adapter_quirks i2c_powermac_quirks = { .max_num_msgs = 1, }; @@ -234,7 +234,7 @@ static u32 i2c_powermac_get_addr(struct i2c_adapter *adap, else if (!strcmp(node->name, "deq")) return 0x34; - dev_warn(&adap->dev, "No i2c address for %s\n", node->full_name); + dev_warn(&adap->dev, "No i2c address for %pOF\n", node); return 0xffffffff; } @@ -315,8 +315,7 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap, } } - dev_err(&adap->dev, "i2c-powermac: modalias failure" - " on %s\n", node->full_name); + dev_err(&adap->dev, "i2c-powermac: modalias failure on %pOF\n", node); return false; } @@ -348,8 +347,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap, if (!pmac_i2c_match_adapter(node, adap)) continue; - dev_dbg(&adap->dev, "i2c-powermac: register %s\n", - node->full_name); + dev_dbg(&adap->dev, "i2c-powermac: register %pOF\n", node); /* * Keep track of some device existence to handle @@ -372,7 +370,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap, newdev = i2c_new_device(adap, &info); if (!newdev) { dev_err(&adap->dev, "i2c-powermac: Failure to register" - " %s\n", node->full_name); + " %pOF\n", node); of_node_put(node); /* We do not dispose of the interrupt mapping on * purpose. It's not necessary (interrupt cannot be diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c index 0c8b1571886d..287088b8c4c8 100644 --- a/drivers/i2c/busses/i2c-puv3.c +++ b/drivers/i2c/busses/i2c-puv3.c @@ -175,7 +175,7 @@ static u32 puv3_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm puv3_i2c_algorithm = { +static const struct i2c_algorithm puv3_i2c_algorithm = { .master_xfer = puv3_i2c_xfer, .functionality = puv3_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 6cf333ecc8b8..600d264e080c 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1346,8 +1346,7 @@ static int i2c_pxa_remove(struct platform_device *dev) #ifdef CONFIG_PM static int i2c_pxa_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct pxa_i2c *i2c = platform_get_drvdata(pdev); + struct pxa_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -1356,8 +1355,7 @@ static int i2c_pxa_suspend_noirq(struct device *dev) static int i2c_pxa_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct pxa_i2c *i2c = platform_get_drvdata(pdev); + struct pxa_i2c *i2c = dev_get_drvdata(dev); clk_enable(i2c->clk); i2c_pxa_reset(i2c); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 1902d8ac9753..08f8e0107642 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1396,7 +1396,7 @@ static const struct i2c_algorithm qup_i2c_algo_v2 = { * the end of the read, the length of the read is specified as one byte * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. */ -static struct i2c_adapter_quirks qup_i2c_quirks = { +static const struct i2c_adapter_quirks qup_i2c_quirks = { .max_read_len = QUP_READ_LIMIT, }; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 8be3e6cb8fe6..15d764afec3b 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 <wsa@sang-engineering.com> * Copyright (C) 2011-2015 Renesas Electronics Corporation @@ -625,9 +625,8 @@ static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev, chan = dma_request_chan(dev, chan_name); if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", - chan_name, ret); + dev_dbg(dev, "request_channel failed for %s (%ld)\n", + chan_name, PTR_ERR(chan)); return chan; } @@ -783,7 +782,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-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index df220666d627..fe234578380a 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -1131,6 +1131,11 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = { .functionality = rk3x_i2c_func, }; +static const struct rk3x_i2c_soc_data rv1108_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + static const struct rk3x_i2c_soc_data rk3066_soc_data = { .grf_offset = 0x154, .calc_timings = rk3x_i2c_v0_calc_timings, @@ -1158,6 +1163,10 @@ static const struct rk3x_i2c_soc_data rk3399_soc_data = { static const struct of_device_id rk3x_i2c_match[] = { { + .compatible = "rockchip,rv1108-i2c", + .data = (void *)&rv1108_soc_data + }, + { .compatible = "rockchip,rk3066-i2c", .data = (void *)&rk3066_soc_data }, diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 499af26e736e..5d97510ee48b 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1246,8 +1246,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int s3c24xx_i2c_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); i2c->suspended = 1; @@ -1259,8 +1258,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev) static int s3c24xx_i2c_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); int ret; if (!IS_ERR(i2c->sysreg)) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 3d7559348745..6f2aaeb7c4fa 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -24,7 +24,6 @@ #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/i2c.h> -#include <linux/i2c/i2c-sh_mobile.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -562,8 +561,8 @@ static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, chan = dma_request_slave_channel_reason(dev, chan_name); if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); + dev_dbg(dev, "request_channel failed for %s (%ld)\n", chan_name, + PTR_ERR(chan)); return chan; } @@ -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 = of_match_device(sh_mobile_i2c_dt_ids, &dev->dev); + if (match) { + const struct sh_mobile_dt_config *config = match->data; - config = match->data; - pd->clks_per_count = config->clks_per_count; + 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-simtec.c b/drivers/i2c/busses/i2c-simtec.c index b4685bb9b5d7..adca51a99487 100644 --- a/drivers/i2c/busses/i2c-simtec.c +++ b/drivers/i2c/busses/i2c-simtec.c @@ -127,8 +127,7 @@ static int simtec_i2c_probe(struct platform_device *dev) iounmap(pd->reg); err_res: - release_resource(pd->ioarea); - kfree(pd->ioarea); + release_mem_region(pd->ioarea->start, size); err: kfree(pd); @@ -142,8 +141,7 @@ static int simtec_i2c_remove(struct platform_device *dev) i2c_del_adapter(&pd->adap); iounmap(pd->reg); - release_resource(pd->ioarea); - kfree(pd->ioarea); + release_mem_region(pd->ioarea->start, resource_size(pd->ioarea)); kfree(pd); return 0; diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 95e81d0f72b4..2fd8b6d00391 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -421,8 +421,7 @@ static int i2c_sirfsoc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int i2c_sirfsoc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct i2c_adapter *adapter = dev_get_drvdata(dev); struct sirfsoc_i2c *siic = adapter->algo_data; clk_enable(siic->clk); @@ -434,8 +433,7 @@ static int i2c_sirfsoc_suspend(struct device *dev) static int i2c_sirfsoc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct i2c_adapter *adapter = dev_get_drvdata(dev); struct sirfsoc_i2c *siic = adapter->algo_data; clk_enable(siic->clk); diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c new file mode 100644 index 000000000000..25fcc3c1e32b --- /dev/null +++ b/drivers/i2c/busses/i2c-sprd.c @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + * SPDX-License-Identifier: (GPL-2.0+ OR MIT) + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#define I2C_CTL 0x00 +#define I2C_ADDR_CFG 0x04 +#define I2C_COUNT 0x08 +#define I2C_RX 0x0c +#define I2C_TX 0x10 +#define I2C_STATUS 0x14 +#define I2C_HSMODE_CFG 0x18 +#define I2C_VERSION 0x1c +#define ADDR_DVD0 0x20 +#define ADDR_DVD1 0x24 +#define ADDR_STA0_DVD 0x28 +#define ADDR_RST 0x2c + +/* I2C_CTL */ +#define STP_EN BIT(20) +#define FIFO_AF_LVL_MASK GENMASK(19, 16) +#define FIFO_AF_LVL 16 +#define FIFO_AE_LVL_MASK GENMASK(15, 12) +#define FIFO_AE_LVL 12 +#define I2C_DMA_EN BIT(11) +#define FULL_INTEN BIT(10) +#define EMPTY_INTEN BIT(9) +#define I2C_DVD_OPT BIT(8) +#define I2C_OUT_OPT BIT(7) +#define I2C_TRIM_OPT BIT(6) +#define I2C_HS_MODE BIT(4) +#define I2C_MODE BIT(3) +#define I2C_EN BIT(2) +#define I2C_INT_EN BIT(1) +#define I2C_START BIT(0) + +/* I2C_STATUS */ +#define SDA_IN BIT(21) +#define SCL_IN BIT(20) +#define FIFO_FULL BIT(4) +#define FIFO_EMPTY BIT(3) +#define I2C_INT BIT(2) +#define I2C_RX_ACK BIT(1) +#define I2C_BUSY BIT(0) + +/* ADDR_RST */ +#define I2C_RST BIT(0) + +#define I2C_FIFO_DEEP 12 +#define I2C_FIFO_FULL_THLD 15 +#define I2C_FIFO_EMPTY_THLD 4 +#define I2C_DATA_STEP 8 +#define I2C_ADDR_DVD0_CALC(high, low) \ + ((((high) & GENMASK(15, 0)) << 16) | ((low) & GENMASK(15, 0))) +#define I2C_ADDR_DVD1_CALC(high, low) \ + (((high) & GENMASK(31, 16)) | (((low) & GENMASK(31, 16)) >> 16)) + +/* timeout (ms) for pm runtime autosuspend */ +#define SPRD_I2C_PM_TIMEOUT 1000 + +/* SPRD i2c data structure */ +struct sprd_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct i2c_msg *msg; + struct clk *clk; + u32 src_clk; + u32 bus_freq; + struct completion complete; + u8 *buf; + u32 count; + int irq; + int err; +}; + +static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count) +{ + writel(count, i2c_dev->base + I2C_COUNT); +} + +static void sprd_i2c_send_stop(struct sprd_i2c *i2c_dev, int stop) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (stop) + writel(tmp & ~STP_EN, i2c_dev->base + I2C_CTL); + else + writel(tmp | STP_EN, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp & ~I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_ack(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_RX_ACK, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_clear_irq(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_INT, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_reset_fifo(struct sprd_i2c *i2c_dev) +{ + writel(I2C_RST, i2c_dev->base + ADDR_RST); +} + +static void sprd_i2c_set_devaddr(struct sprd_i2c *i2c_dev, struct i2c_msg *m) +{ + writel(m->addr << 1, i2c_dev->base + I2C_ADDR_CFG); +} + +static void sprd_i2c_write_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + writeb(buf[i], i2c_dev->base + I2C_TX); +} + +static void sprd_i2c_read_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + buf[i] = readb(i2c_dev->base + I2C_RX); +} + +static void sprd_i2c_set_full_thld(struct sprd_i2c *i2c_dev, u32 full_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AF_LVL_MASK; + tmp |= full_thld << FIFO_AF_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_empty_thld(struct sprd_i2c *i2c_dev, u32 empty_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AE_LVL_MASK; + tmp |= empty_thld << FIFO_AE_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_full_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= FULL_INTEN; + else + tmp &= ~FULL_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_empty_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= EMPTY_INTEN; + else + tmp &= ~EMPTY_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_opt_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp | I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_opt_mode(struct sprd_i2c *i2c_dev, int rw) +{ + u32 cmd = readl(i2c_dev->base + I2C_CTL) & ~I2C_MODE; + + writel(cmd | rw << 3, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_data_transfer(struct sprd_i2c *i2c_dev) +{ + u32 i2c_count = i2c_dev->count; + u32 need_tran = i2c_count <= I2C_FIFO_DEEP ? i2c_count : I2C_FIFO_DEEP; + struct i2c_msg *msg = i2c_dev->msg; + + if (msg->flags & I2C_M_RD) { + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, I2C_FIFO_FULL_THLD); + i2c_dev->count -= I2C_FIFO_FULL_THLD; + i2c_dev->buf += I2C_FIFO_FULL_THLD; + + /* + * If the read data count is larger than rx fifo full threshold, + * we should enable the rx fifo full interrupt to read data + * again. + */ + if (i2c_dev->count >= I2C_FIFO_FULL_THLD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + } else { + sprd_i2c_write_bytes(i2c_dev, i2c_dev->buf, need_tran); + i2c_dev->buf += need_tran; + i2c_dev->count -= need_tran; + + /* + * If the write data count is arger than tx fifo depth which + * means we can not write all data in one time, then we should + * enable the tx fifo empty interrupt to write again. + */ + if (i2c_count > I2C_FIFO_DEEP) + sprd_i2c_set_fifo_empty_int(i2c_dev, 1); + } +} + +static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, bool is_last_msg) +{ + struct sprd_i2c *i2c_dev = i2c_adap->algo_data; + + i2c_dev->msg = msg; + i2c_dev->buf = msg->buf; + i2c_dev->count = msg->len; + + reinit_completion(&i2c_dev->complete); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_set_devaddr(i2c_dev, msg); + sprd_i2c_set_count(i2c_dev, msg->len); + + if (msg->flags & I2C_M_RD) { + sprd_i2c_opt_mode(i2c_dev, 1); + sprd_i2c_send_stop(i2c_dev, 1); + } else { + sprd_i2c_opt_mode(i2c_dev, 0); + sprd_i2c_send_stop(i2c_dev, !!is_last_msg); + } + + /* + * We should enable rx fifo full interrupt to get data when receiving + * full data. + */ + if (msg->flags & I2C_M_RD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + else + sprd_i2c_data_transfer(i2c_dev); + + sprd_i2c_opt_start(i2c_dev); + + wait_for_completion(&i2c_dev->complete); + + return i2c_dev->err; +} + +static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct sprd_i2c *i2c_dev = i2c_adap->algo_data; + int im, ret; + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + + for (im = 0; im < num - 1; im++) { + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im], 0); + if (ret) + goto err_msg; + } + + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im++], 1); + +err_msg: + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + + return ret < 0 ? ret : im; +} + +static u32 sprd_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sprd_i2c_algo = { + .master_xfer = sprd_i2c_master_xfer, + .functionality = sprd_i2c_func, +}; + +static void sprd_i2c_set_clk(struct sprd_i2c *i2c_dev, u32 freq) +{ + u32 apb_clk = i2c_dev->src_clk; + /* + * From I2C databook, the prescale calculation formula: + * prescale = freq_i2c / (4 * freq_scl) - 1; + */ + u32 i2c_dvd = apb_clk / (4 * freq) - 1; + /* + * From I2C databook, the high period of SCL clock is recommended as + * 40% (2/5), and the low period of SCL clock is recommended as 60% + * (3/5), then the formula should be: + * high = (prescale * 2 * 2) / 5 + * low = (prescale * 2 * 3) / 5 + */ + u32 high = ((i2c_dvd << 1) * 2) / 5; + u32 low = ((i2c_dvd << 1) * 3) / 5; + u32 div0 = I2C_ADDR_DVD0_CALC(high, low); + u32 div1 = I2C_ADDR_DVD1_CALC(high, low); + + writel(div0, i2c_dev->base + ADDR_DVD0); + writel(div1, i2c_dev->base + ADDR_DVD1); + + /* Start hold timing = hold time(us) * source clock */ + if (freq == 400000) + writel((6 * apb_clk) / 10000000, i2c_dev->base + ADDR_STA0_DVD); + else if (freq == 100000) + writel((4 * apb_clk) / 1000000, i2c_dev->base + ADDR_STA0_DVD); +} + +static void sprd_i2c_enable(struct sprd_i2c *i2c_dev) +{ + u32 tmp = I2C_DVD_OPT; + + writel(tmp, i2c_dev->base + I2C_CTL); + + sprd_i2c_set_full_thld(i2c_dev, I2C_FIFO_FULL_THLD); + sprd_i2c_set_empty_thld(i2c_dev, I2C_FIFO_EMPTY_THLD); + + sprd_i2c_set_clk(i2c_dev, i2c_dev->bus_freq); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + + tmp = readl(i2c_dev->base + I2C_CTL); + writel(tmp | I2C_EN | I2C_INT_EN, i2c_dev->base + I2C_CTL); +} + +static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_count = readl(i2c_dev->base + I2C_COUNT); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_count; + + /* + * If we got one ACK from slave when writing data, and we did not + * finish this transmission (i2c_tran is not zero), then we should + * continue to write data. + * + * For reading data, ack is always true, if i2c_tran is not 0 which + * means we still need to contine to read data from slave. + */ + if (i2c_tran && ack) { + sprd_i2c_data_transfer(i2c_dev); + return IRQ_HANDLED; + } + + i2c_dev->err = 0; + + /* + * If we did not get one ACK from slave when writing data, we should + * return -EIO to notify users. + */ + if (!ack) + i2c_dev->err = -EIO; + else if (msg->flags & I2C_M_RD && i2c_dev->count) + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, i2c_dev->count); + + /* Transmission is done and clear ack and start operation */ + sprd_i2c_clear_ack(i2c_dev); + sprd_i2c_clear_start(i2c_dev); + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +static irqreturn_t sprd_i2c_isr(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + u32 i2c_count = readl(i2c_dev->base + I2C_COUNT); + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_count; + + /* + * If we did not get one ACK from slave when writing data, then we + * should finish this transmission since we got some errors. + * + * When writing data, if i2c_tran == 0 which means we have writen + * done all data, then we can finish this transmission. + * + * When reading data, if conut < rx fifo full threshold, which + * means we can read all data in one time, then we can finish this + * transmission too. + */ + if (!i2c_tran || !ack) { + sprd_i2c_clear_start(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + } + + sprd_i2c_set_fifo_empty_int(i2c_dev, 0); + sprd_i2c_set_fifo_full_int(i2c_dev, 0); + + return IRQ_WAKE_THREAD; +} + +static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev) +{ + struct clk *clk_i2c, *clk_parent; + + clk_i2c = devm_clk_get(i2c_dev->dev, "i2c"); + if (IS_ERR(clk_i2c)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the i2c clock\n", + i2c_dev->adap.nr); + clk_i2c = NULL; + } + + clk_parent = devm_clk_get(i2c_dev->dev, "source"); + if (IS_ERR(clk_parent)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the source clock\n", + i2c_dev->adap.nr); + clk_parent = NULL; + } + + if (clk_set_parent(clk_i2c, clk_parent)) + i2c_dev->src_clk = clk_get_rate(clk_i2c); + else + i2c_dev->src_clk = 26000000; + + dev_dbg(i2c_dev->dev, "i2c%d set source clock is %d\n", + i2c_dev->adap.nr, i2c_dev->src_clk); + + i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable"); + if (IS_ERR(i2c_dev->clk)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the enable clock\n", + i2c_dev->adap.nr); + i2c_dev->clk = NULL; + } + + return 0; +} + +static int sprd_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sprd_i2c *i2c_dev; + struct resource *res; + u32 prop; + int ret; + + pdev->id = of_alias_get_id(dev->of_node, "i2c"); + + i2c_dev = devm_kzalloc(dev, sizeof(struct sprd_i2c), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c_dev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + i2c_dev->irq = platform_get_irq(pdev, 0); + if (i2c_dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + return i2c_dev->irq; + } + + i2c_set_adapdata(&i2c_dev->adap, i2c_dev); + init_completion(&i2c_dev->complete); + snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name), + "%s", "sprd-i2c"); + + i2c_dev->bus_freq = 100000; + i2c_dev->adap.owner = THIS_MODULE; + i2c_dev->dev = dev; + i2c_dev->adap.retries = 3; + i2c_dev->adap.algo = &sprd_i2c_algo; + i2c_dev->adap.algo_data = i2c_dev; + i2c_dev->adap.dev.parent = dev; + i2c_dev->adap.nr = pdev->id; + i2c_dev->adap.dev.of_node = dev->of_node; + + if (!of_property_read_u32(dev->of_node, "clock-frequency", &prop)) + i2c_dev->bus_freq = prop; + + /* We only support 100k and 400k now, otherwise will return error. */ + if (i2c_dev->bus_freq != 100000 && i2c_dev->bus_freq != 400000) + return -EINVAL; + + sprd_i2c_clk_init(i2c_dev); + platform_set_drvdata(pdev, i2c_dev); + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + pm_runtime_set_autosuspend_delay(i2c_dev->dev, SPRD_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + goto err_rpm_put; + + ret = devm_request_threaded_irq(dev, i2c_dev->irq, + sprd_i2c_isr, sprd_i2c_isr_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %d\n", i2c_dev->irq); + goto err_rpm_put; + } + + ret = i2c_add_numbered_adapter(&i2c_dev->adap); + if (ret) { + dev_err(&pdev->dev, "add adapter failed\n"); + goto err_rpm_put; + } + + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; + +err_rpm_put: + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + clk_disable_unprepare(i2c_dev->clk); + return ret; +} + +static int sprd_i2c_remove(struct platform_device *pdev) +{ + struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); + int ret; + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + + i2c_del_adapter(&i2c_dev->adap); + clk_disable_unprepare(i2c_dev->clk); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + + return 0; +} + +static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev) +{ + return pm_runtime_force_suspend(pdev); +} + +static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev) +{ + return pm_runtime_force_resume(pdev); +} + +static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + + clk_disable_unprepare(i2c_dev->clk); + + return 0; +} + +static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + return 0; +} + +static const struct dev_pm_ops sprd_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(sprd_i2c_runtime_suspend, + sprd_i2c_runtime_resume, NULL) + + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sprd_i2c_suspend_noirq, + sprd_i2c_resume_noirq) +}; + +static const struct of_device_id sprd_i2c_of_match[] = { + { .compatible = "sprd,sc9860-i2c", }, + {}, +}; + +static struct platform_driver sprd_i2c_driver = { + .probe = sprd_i2c_probe, + .remove = sprd_i2c_remove, + .driver = { + .name = "sprd-i2c", + .of_match_table = sprd_i2c_of_match, + .pm = &sprd_i2c_pm_ops, + }, +}; + +static int sprd_i2c_init(void) +{ + return platform_driver_register(&sprd_i2c_driver); +} +arch_initcall_sync(sprd_i2c_init); diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 1eb9fa82dcfd..9e62f893958a 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -745,8 +745,7 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap, #ifdef CONFIG_PM_SLEEP static int st_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct st_i2c_dev *i2c_dev = dev_get_drvdata(dev); if (i2c_dev->busy) return -EBUSY; diff --git a/drivers/i2c/busses/i2c-stm32.h b/drivers/i2c/busses/i2c-stm32.h new file mode 100644 index 000000000000..dab51761f8c5 --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32.h @@ -0,0 +1,20 @@ +/* + * i2c-stm32.h + * + * Copyright (C) M'boumba Cedric Madianga 2017 + * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com> + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _I2C_STM32_H +#define _I2C_STM32_H + +enum stm32_i2c_speed { + STM32_I2C_SPEED_STANDARD, /* 100 kHz */ + STM32_I2C_SPEED_FAST, /* 400 kHz */ + STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */ + STM32_I2C_SPEED_END, +}; + +#endif /* _I2C_STM32_H */ diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c index f9dd7e86b861..4ec108496f15 100644 --- a/drivers/i2c/busses/i2c-stm32f4.c +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -27,6 +27,8 @@ #include <linux/platform_device.h> #include <linux/reset.h> +#include "i2c-stm32.h" + /* STM32F4 I2C offset registers */ #define STM32F4_I2C_CR1 0x00 #define STM32F4_I2C_CR2 0x04 @@ -90,12 +92,6 @@ #define STM32F4_I2C_MAX_FREQ 46U #define HZ_TO_MHZ 1000000 -enum stm32f4_i2c_speed { - STM32F4_I2C_SPEED_STANDARD, /* 100 kHz */ - STM32F4_I2C_SPEED_FAST, /* 400 kHz */ - STM32F4_I2C_SPEED_END, -}; - /** * struct stm32f4_i2c_msg - client specific data * @addr: 8-bit slave addr, including r/w bit @@ -159,7 +155,7 @@ static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) i2c_dev->parent_rate = clk_get_rate(i2c_dev->clk); freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ); - if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) { + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) { /* * To reach 100 kHz, the parent clk frequency should be between * a minimum value of 2 MHz and a maximum value of 46 MHz due @@ -216,7 +212,7 @@ static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev) * is not higher than 46 MHz . As a result trise is at most 4 bits wide * and so fits into the TRISE bits [5:0]. */ - if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) trise = freq + 1; else trise = freq * 3 / 10 + 1; @@ -230,7 +226,7 @@ static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev) u32 val; u32 ccr = 0; - if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) { + if (i2c_dev->speed == STM32_I2C_SPEED_STANDARD) { /* * In standard mode: * t_scl_high = t_scl_low = CCR * I2C parent clk period @@ -751,7 +747,7 @@ static u32 stm32f4_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm stm32f4_i2c_algo = { +static const struct i2c_algorithm stm32f4_i2c_algo = { .master_xfer = stm32f4_i2c_xfer, .functionality = stm32f4_i2c_func, }; @@ -798,7 +794,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) return ret; } - rst = devm_reset_control_get(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) { dev_err(&pdev->dev, "Error: Missing controller reset\n"); ret = PTR_ERR(rst); @@ -808,10 +804,10 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) udelay(2); reset_control_deassert(rst); - i2c_dev->speed = STM32F4_I2C_SPEED_STANDARD; + i2c_dev->speed = STM32_I2C_SPEED_STANDARD; ret = of_property_read_u32(np, "clock-frequency", &clk_rate); if (!ret && clk_rate >= 400000) - i2c_dev->speed = STM32F4_I2C_SPEED_FAST; + i2c_dev->speed = STM32_I2C_SPEED_FAST; i2c_dev->dev = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c new file mode 100644 index 000000000000..d4a6e9c2e9aa --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -0,0 +1,969 @@ +/* + * Driver for STMicroelectronics STM32F7 I2C controller + * + * This I2C controller is described in the STM32F75xxx and STM32F74xxx Soc + * reference manual. + * Please see below a link to the documentation: + * http://www.st.com/resource/en/reference_manual/dm00124865.pdf + * + * Copyright (C) M'boumba Cedric Madianga 2017 + * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com> + * + * This driver is based on i2c-stm32f4.c + * + * License terms: GNU General Public License (GPL), version 2 + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include "i2c-stm32.h" + +/* STM32F7 I2C registers */ +#define STM32F7_I2C_CR1 0x00 +#define STM32F7_I2C_CR2 0x04 +#define STM32F7_I2C_TIMINGR 0x10 +#define STM32F7_I2C_ISR 0x18 +#define STM32F7_I2C_ICR 0x1C +#define STM32F7_I2C_RXDR 0x24 +#define STM32F7_I2C_TXDR 0x28 + +/* STM32F7 I2C control 1 */ +#define STM32F7_I2C_CR1_ANFOFF BIT(12) +#define STM32F7_I2C_CR1_ERRIE BIT(7) +#define STM32F7_I2C_CR1_TCIE BIT(6) +#define STM32F7_I2C_CR1_STOPIE BIT(5) +#define STM32F7_I2C_CR1_NACKIE BIT(4) +#define STM32F7_I2C_CR1_ADDRIE BIT(3) +#define STM32F7_I2C_CR1_RXIE BIT(2) +#define STM32F7_I2C_CR1_TXIE BIT(1) +#define STM32F7_I2C_CR1_PE BIT(0) +#define STM32F7_I2C_ALL_IRQ_MASK (STM32F7_I2C_CR1_ERRIE \ + | STM32F7_I2C_CR1_TCIE \ + | STM32F7_I2C_CR1_STOPIE \ + | STM32F7_I2C_CR1_NACKIE \ + | STM32F7_I2C_CR1_RXIE \ + | STM32F7_I2C_CR1_TXIE) + +/* STM32F7 I2C control 2 */ +#define STM32F7_I2C_CR2_RELOAD BIT(24) +#define STM32F7_I2C_CR2_NBYTES_MASK GENMASK(23, 16) +#define STM32F7_I2C_CR2_NBYTES(n) (((n) & 0xff) << 16) +#define STM32F7_I2C_CR2_NACK BIT(15) +#define STM32F7_I2C_CR2_STOP BIT(14) +#define STM32F7_I2C_CR2_START BIT(13) +#define STM32F7_I2C_CR2_RD_WRN BIT(10) +#define STM32F7_I2C_CR2_SADD7_MASK GENMASK(7, 1) +#define STM32F7_I2C_CR2_SADD7(n) (((n) & 0x7f) << 1) + +/* STM32F7 I2C Interrupt Status */ +#define STM32F7_I2C_ISR_BUSY BIT(15) +#define STM32F7_I2C_ISR_ARLO BIT(9) +#define STM32F7_I2C_ISR_BERR BIT(8) +#define STM32F7_I2C_ISR_TCR BIT(7) +#define STM32F7_I2C_ISR_TC BIT(6) +#define STM32F7_I2C_ISR_STOPF BIT(5) +#define STM32F7_I2C_ISR_NACKF BIT(4) +#define STM32F7_I2C_ISR_RXNE BIT(2) +#define STM32F7_I2C_ISR_TXIS BIT(1) + +/* STM32F7 I2C Interrupt Clear */ +#define STM32F7_I2C_ICR_ARLOCF BIT(9) +#define STM32F7_I2C_ICR_BERRCF BIT(8) +#define STM32F7_I2C_ICR_STOPCF BIT(5) +#define STM32F7_I2C_ICR_NACKCF BIT(4) + +/* STM32F7 I2C Timing */ +#define STM32F7_I2C_TIMINGR_PRESC(n) (((n) & 0xf) << 28) +#define STM32F7_I2C_TIMINGR_SCLDEL(n) (((n) & 0xf) << 20) +#define STM32F7_I2C_TIMINGR_SDADEL(n) (((n) & 0xf) << 16) +#define STM32F7_I2C_TIMINGR_SCLH(n) (((n) & 0xff) << 8) +#define STM32F7_I2C_TIMINGR_SCLL(n) ((n) & 0xff) + +#define STM32F7_I2C_MAX_LEN 0xff + +#define STM32F7_I2C_DNF_DEFAULT 0 +#define STM32F7_I2C_DNF_MAX 16 + +#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1 +#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */ +#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */ + +#define STM32F7_I2C_RISE_TIME_DEFAULT 25 /* ns */ +#define STM32F7_I2C_FALL_TIME_DEFAULT 10 /* ns */ + +#define STM32F7_PRESC_MAX BIT(4) +#define STM32F7_SCLDEL_MAX BIT(4) +#define STM32F7_SDADEL_MAX BIT(4) +#define STM32F7_SCLH_MAX BIT(8) +#define STM32F7_SCLL_MAX BIT(8) + +/** + * struct stm32f7_i2c_spec - private i2c specification timing + * @rate: I2C bus speed (Hz) + * @rate_min: 80% of I2C bus speed (Hz) + * @rate_max: 100% of I2C bus speed (Hz) + * @fall_max: Max fall time of both SDA and SCL signals (ns) + * @rise_max: Max rise time of both SDA and SCL signals (ns) + * @hddat_min: Min data hold time (ns) + * @vddat_max: Max data valid time (ns) + * @sudat_min: Min data setup time (ns) + * @l_min: Min low period of the SCL clock (ns) + * @h_min: Min high period of the SCL clock (ns) + */ +struct stm32f7_i2c_spec { + u32 rate; + u32 rate_min; + u32 rate_max; + u32 fall_max; + u32 rise_max; + u32 hddat_min; + u32 vddat_max; + u32 sudat_min; + u32 l_min; + u32 h_min; +}; + +/** + * struct stm32f7_i2c_setup - private I2C timing setup parameters + * @speed: I2C speed mode (standard, Fast Plus) + * @speed_freq: I2C speed frequency (Hz) + * @clock_src: I2C clock source frequency (Hz) + * @rise_time: Rise time (ns) + * @fall_time: Fall time (ns) + * @dnf: Digital filter coefficient (0-16) + * @analog_filter: Analog filter delay (On/Off) + */ +struct stm32f7_i2c_setup { + enum stm32_i2c_speed speed; + u32 speed_freq; + u32 clock_src; + u32 rise_time; + u32 fall_time; + u8 dnf; + bool analog_filter; +}; + +/** + * struct stm32f7_i2c_timings - private I2C output parameters + * @prec: Prescaler value + * @scldel: Data setup time + * @sdadel: Data hold time + * @sclh: SCL high period (master mode) + * @sclh: SCL low period (master mode) + */ +struct stm32f7_i2c_timings { + struct list_head node; + u8 presc; + u8 scldel; + u8 sdadel; + u8 sclh; + u8 scll; +}; + +/** + * struct stm32f7_i2c_msg - client specific data + * @addr: 8-bit slave addr, including r/w bit + * @count: number of bytes to be transferred + * @buf: data buffer + * @result: result of the transfer + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct stm32f7_i2c_msg { + u8 addr; + u32 count; + u8 *buf; + int result; + bool stop; +}; + +/** + * struct stm32f7_i2c_dev - private data of the controller + * @adap: I2C adapter for this controller + * @dev: device for this controller + * @base: virtual memory area + * @complete: completion of I2C message + * @clk: hw i2c clock + * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+ + * @msg: Pointer to data to be written + * @msg_num: number of I2C messages to be executed + * @msg_id: message identifiant + * @f7_msg: customized i2c msg for driver usage + * @setup: I2C timing input setup + * @timing: I2C computed timings + */ +struct stm32f7_i2c_dev { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct completion complete; + struct clk *clk; + int speed; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_id; + struct stm32f7_i2c_msg f7_msg; + struct stm32f7_i2c_setup setup; + struct stm32f7_i2c_timings timing; +}; + +/** + * All these values are coming from I2C Specification, Version 6.0, 4th of + * April 2014. + * + * Table10. Characteristics of the SDA and SCL bus lines for Standard, Fast, + * and Fast-mode Plus I2C-bus devices + */ +static struct stm32f7_i2c_spec i2c_specs[] = { + [STM32_I2C_SPEED_STANDARD] = { + .rate = 100000, + .rate_min = 80000, + .rate_max = 100000, + .fall_max = 300, + .rise_max = 1000, + .hddat_min = 0, + .vddat_max = 3450, + .sudat_min = 250, + .l_min = 4700, + .h_min = 4000, + }, + [STM32_I2C_SPEED_FAST] = { + .rate = 400000, + .rate_min = 320000, + .rate_max = 400000, + .fall_max = 300, + .rise_max = 300, + .hddat_min = 0, + .vddat_max = 900, + .sudat_min = 100, + .l_min = 1300, + .h_min = 600, + }, + [STM32_I2C_SPEED_FAST_PLUS] = { + .rate = 1000000, + .rate_min = 800000, + .rate_max = 1000000, + .fall_max = 100, + .rise_max = 120, + .hddat_min = 0, + .vddat_max = 450, + .sudat_min = 50, + .l_min = 500, + .h_min = 260, + }, +}; + +static const struct stm32f7_i2c_setup stm32f7_setup = { + .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, + .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, + .dnf = STM32F7_I2C_DNF_DEFAULT, + .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE, +}; + +static inline void stm32f7_i2c_set_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) | mask, reg); +} + +static inline void stm32f7_i2c_clr_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) & ~mask, reg); +} + +static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, + struct stm32f7_i2c_setup *setup, + struct stm32f7_i2c_timings *output) +{ + u32 p_prev = STM32F7_PRESC_MAX; + u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, + setup->clock_src); + u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, + setup->speed_freq); + u32 clk_error_prev = i2cbus; + u32 tsync; + u32 af_delay_min, af_delay_max; + u32 dnf_delay; + u32 clk_min, clk_max; + int sdadel_min, sdadel_max; + int scldel_min; + struct stm32f7_i2c_timings *v, *_v, *s; + struct list_head solutions; + u16 p, l, a, h; + int ret = 0; + + if (setup->speed >= STM32_I2C_SPEED_END) { + dev_err(i2c_dev->dev, "speed out of bound {%d/%d}\n", + setup->speed, STM32_I2C_SPEED_END - 1); + return -EINVAL; + } + + if ((setup->rise_time > i2c_specs[setup->speed].rise_max) || + (setup->fall_time > i2c_specs[setup->speed].fall_max)) { + dev_err(i2c_dev->dev, + "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", + setup->rise_time, i2c_specs[setup->speed].rise_max, + setup->fall_time, i2c_specs[setup->speed].fall_max); + return -EINVAL; + } + + if (setup->dnf > STM32F7_I2C_DNF_MAX) { + dev_err(i2c_dev->dev, + "DNF out of bound %d/%d\n", + setup->dnf, STM32F7_I2C_DNF_MAX); + return -EINVAL; + } + + if (setup->speed_freq > i2c_specs[setup->speed].rate) { + dev_err(i2c_dev->dev, "ERROR: Freq {%d/%d}\n", + setup->speed_freq, i2c_specs[setup->speed].rate); + return -EINVAL; + } + + /* Analog and Digital Filters */ + af_delay_min = + (setup->analog_filter ? + STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0); + af_delay_max = + (setup->analog_filter ? + STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); + dnf_delay = setup->dnf * i2cclk; + + sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min - + af_delay_min - (setup->dnf + 3) * i2cclk; + + sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time - + af_delay_max - (setup->dnf + 4) * i2cclk; + + scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min; + + if (sdadel_min < 0) + sdadel_min = 0; + if (sdadel_max < 0) + sdadel_max = 0; + + dev_dbg(i2c_dev->dev, "SDADEL(min/max): %i/%i, SCLDEL(Min): %i\n", + sdadel_min, sdadel_max, scldel_min); + + INIT_LIST_HEAD(&solutions); + /* Compute possible values for PRESC, SCLDEL and SDADEL */ + for (p = 0; p < STM32F7_PRESC_MAX; p++) { + for (l = 0; l < STM32F7_SCLDEL_MAX; l++) { + u32 scldel = (l + 1) * (p + 1) * i2cclk; + + if (scldel < scldel_min) + continue; + + for (a = 0; a < STM32F7_SDADEL_MAX; a++) { + u32 sdadel = (a * (p + 1) + 1) * i2cclk; + + if (((sdadel >= sdadel_min) && + (sdadel <= sdadel_max)) && + (p != p_prev)) { + v = kmalloc(sizeof(*v), GFP_KERNEL); + if (!v) { + ret = -ENOMEM; + goto exit; + } + + v->presc = p; + v->scldel = l; + v->sdadel = a; + p_prev = p; + + list_add_tail(&v->node, + &solutions); + } + } + } + } + + if (list_empty(&solutions)) { + dev_err(i2c_dev->dev, "no Prescaler solution\n"); + ret = -EPERM; + goto exit; + } + + tsync = af_delay_min + dnf_delay + (2 * i2cclk); + s = NULL; + clk_max = NSEC_PER_SEC / i2c_specs[setup->speed].rate_min; + clk_min = NSEC_PER_SEC / i2c_specs[setup->speed].rate_max; + + /* + * Among Prescaler possibilities discovered above figures out SCL Low + * and High Period. Provided: + * - SCL Low Period has to be higher than SCL Clock Low Period + * defined by I2C Specification. I2C Clock has to be lower than + * (SCL Low Period - Analog/Digital filters) / 4. + * - SCL High Period has to be lower than SCL Clock High Period + * defined by I2C Specification + * - I2C Clock has to be lower than SCL High Period + */ + list_for_each_entry(v, &solutions, node) { + u32 prescaler = (v->presc + 1) * i2cclk; + + for (l = 0; l < STM32F7_SCLL_MAX; l++) { + u32 tscl_l = (l + 1) * prescaler + tsync; + + if ((tscl_l < i2c_specs[setup->speed].l_min) || + (i2cclk >= + ((tscl_l - af_delay_min - dnf_delay) / 4))) { + continue; + } + + for (h = 0; h < STM32F7_SCLH_MAX; h++) { + u32 tscl_h = (h + 1) * prescaler + tsync; + u32 tscl = tscl_l + tscl_h + + setup->rise_time + setup->fall_time; + + if ((tscl >= clk_min) && (tscl <= clk_max) && + (tscl_h >= i2c_specs[setup->speed].h_min) && + (i2cclk < tscl_h)) { + int clk_error = tscl - i2cbus; + + if (clk_error < 0) + clk_error = -clk_error; + + if (clk_error < clk_error_prev) { + clk_error_prev = clk_error; + v->scll = l; + v->sclh = h; + s = v; + } + } + } + } + } + + if (!s) { + dev_err(i2c_dev->dev, "no solution at all\n"); + ret = -EPERM; + goto exit; + } + + output->presc = s->presc; + output->scldel = s->scldel; + output->sdadel = s->sdadel; + output->scll = s->scll; + output->sclh = s->sclh; + + dev_dbg(i2c_dev->dev, + "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n", + output->presc, + output->scldel, output->sdadel, + output->scll, output->sclh); + +exit: + /* Release list and memory */ + list_for_each_entry_safe(v, _v, &solutions, node) { + list_del(&v->node); + kfree(v); + } + + return ret; +} + +static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, + struct stm32f7_i2c_setup *setup) +{ + int ret = 0; + + setup->speed = i2c_dev->speed; + setup->speed_freq = i2c_specs[setup->speed].rate; + setup->clock_src = clk_get_rate(i2c_dev->clk); + + if (!setup->clock_src) { + dev_err(i2c_dev->dev, "clock rate is 0\n"); + return -EINVAL; + } + + do { + ret = stm32f7_i2c_compute_timing(i2c_dev, setup, + &i2c_dev->timing); + if (ret) { + dev_err(i2c_dev->dev, + "failed to compute I2C timings.\n"); + if (i2c_dev->speed > STM32_I2C_SPEED_STANDARD) { + i2c_dev->speed--; + setup->speed = i2c_dev->speed; + setup->speed_freq = + i2c_specs[setup->speed].rate; + dev_warn(i2c_dev->dev, + "downgrade I2C Speed Freq to (%i)\n", + i2c_specs[setup->speed].rate); + } else { + break; + } + } + } while (ret); + + if (ret) { + dev_err(i2c_dev->dev, "Impossible to compute I2C timings.\n"); + return ret; + } + + dev_dbg(i2c_dev->dev, "I2C Speed(%i), Freq(%i), Clk Source(%i)\n", + setup->speed, setup->speed_freq, setup->clock_src); + dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", + setup->rise_time, setup->fall_time); + dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n", + (setup->analog_filter ? "On" : "Off"), setup->dnf); + + return 0; +} + +static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_timings *t = &i2c_dev->timing; + u32 timing = 0; + + /* Timing settings */ + timing |= STM32F7_I2C_TIMINGR_PRESC(t->presc); + timing |= STM32F7_I2C_TIMINGR_SCLDEL(t->scldel); + timing |= STM32F7_I2C_TIMINGR_SDADEL(t->sdadel); + timing |= STM32F7_I2C_TIMINGR_SCLH(t->sclh); + timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll); + writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR); + + /* Enable I2C */ + if (i2c_dev->setup.analog_filter) + stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_ANFOFF); + else + stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_ANFOFF); + stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_PE); +} + +static void stm32f7_i2c_write_tx_data(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + + if (f7_msg->count) { + writeb_relaxed(*f7_msg->buf++, base + STM32F7_I2C_TXDR); + f7_msg->count--; + } +} + +static void stm32f7_i2c_read_rx_data(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + + if (f7_msg->count) { + *f7_msg->buf++ = readb_relaxed(base + STM32F7_I2C_RXDR); + f7_msg->count--; + } +} + +static void stm32f7_i2c_reload(struct stm32f7_i2c_dev *i2c_dev) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + u32 cr2; + + cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); + + cr2 &= ~STM32F7_I2C_CR2_NBYTES_MASK; + if (f7_msg->count > STM32F7_I2C_MAX_LEN) { + cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN); + } else { + cr2 &= ~STM32F7_I2C_CR2_RELOAD; + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + } + + writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2); +} + +static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev) +{ + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F7_I2C_ISR, + status, + !(status & STM32F7_I2C_ISR_BUSY), + 10, 1000); + if (ret) { + dev_dbg(i2c_dev->dev, "bus busy\n"); + ret = -EBUSY; + } + + return ret; +} + +static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, + struct i2c_msg *msg) +{ + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + u32 cr1, cr2; + + f7_msg->addr = msg->addr; + f7_msg->buf = msg->buf; + f7_msg->count = msg->len; + f7_msg->result = 0; + f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1); + + reinit_completion(&i2c_dev->complete); + + cr1 = readl_relaxed(base + STM32F7_I2C_CR1); + cr2 = readl_relaxed(base + STM32F7_I2C_CR2); + + /* Set transfer direction */ + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + if (msg->flags & I2C_M_RD) + cr2 |= STM32F7_I2C_CR2_RD_WRN; + + /* Set slave address */ + cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK; + cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr); + + /* Set nb bytes to transfer and reload if needed */ + cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD); + if (f7_msg->count > STM32F7_I2C_MAX_LEN) { + cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN); + cr2 |= STM32F7_I2C_CR2_RELOAD; + } else { + cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count); + } + + /* Enable NACK, STOP, error and transfer complete interrupts */ + cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE | + STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE; + + /* Clear TX/RX interrupt */ + cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE); + + /* Enable RX/TX interrupt according to msg direction */ + if (msg->flags & I2C_M_RD) + cr1 |= STM32F7_I2C_CR1_RXIE; + else + cr1 |= STM32F7_I2C_CR1_TXIE; + + /* Configure Start/Repeated Start */ + cr2 |= STM32F7_I2C_CR2_START; + + /* Write configurations registers */ + writel_relaxed(cr1, base + STM32F7_I2C_CR1); + writel_relaxed(cr2, base + STM32F7_I2C_CR2); +} + +static void stm32f7_i2c_disable_irq(struct stm32f7_i2c_dev *i2c_dev, u32 mask) +{ + stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, mask); +} + +static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) +{ + struct stm32f7_i2c_dev *i2c_dev = data; + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + u32 status, mask; + + status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + + /* Tx empty */ + if (status & STM32F7_I2C_ISR_TXIS) + stm32f7_i2c_write_tx_data(i2c_dev); + + /* RX not empty */ + if (status & STM32F7_I2C_ISR_RXNE) + stm32f7_i2c_read_rx_data(i2c_dev); + + /* NACK received */ + if (status & STM32F7_I2C_ISR_NACKF) { + dev_dbg(i2c_dev->dev, "<%s>: Receive NACK\n", __func__); + writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR); + f7_msg->result = -ENXIO; + } + + /* STOP detection flag */ + if (status & STM32F7_I2C_ISR_STOPF) { + /* Disable interrupts */ + stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); + + /* Clear STOP flag */ + writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR); + + complete(&i2c_dev->complete); + } + + /* Transfer complete */ + if (status & STM32F7_I2C_ISR_TC) { + if (f7_msg->stop) { + mask = STM32F7_I2C_CR2_STOP; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask); + } else { + i2c_dev->msg_id++; + i2c_dev->msg++; + stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg); + } + } + + /* + * Transfer Complete Reload: 255 data bytes have been transferred + * We have to prepare the I2C controller to transfer the remaining + * data. + */ + if (status & STM32F7_I2C_ISR_TCR) + stm32f7_i2c_reload(i2c_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) +{ + struct stm32f7_i2c_dev *i2c_dev = data; + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + void __iomem *base = i2c_dev->base; + struct device *dev = i2c_dev->dev; + u32 status; + + status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + + /* Bus error */ + if (status & STM32F7_I2C_ISR_BERR) { + dev_err(dev, "<%s>: Bus error\n", __func__); + writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); + f7_msg->result = -EIO; + } + + /* Arbitration loss */ + if (status & STM32F7_I2C_ISR_ARLO) { + dev_dbg(dev, "<%s>: Arbitration loss\n", __func__); + writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR); + f7_msg->result = -EAGAIN; + } + + stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); + + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + unsigned long time_left; + int ret; + + i2c_dev->msg = msgs; + i2c_dev->msg_num = num; + i2c_dev->msg_id = 0; + + ret = clk_enable(i2c_dev->clk); + if (ret) { + dev_err(i2c_dev->dev, "Failed to enable clock\n"); + return ret; + } + + ret = stm32f7_i2c_wait_free_bus(i2c_dev); + if (ret) + goto clk_free; + + stm32f7_i2c_xfer_msg(i2c_dev, msgs); + + time_left = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + ret = f7_msg->result; + + if (!time_left) { + dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n", + i2c_dev->msg->addr); + ret = -ETIMEDOUT; + } + +clk_free: + clk_disable(i2c_dev->clk); + + return (ret < 0) ? ret : num; +} + +static u32 stm32f7_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm stm32f7_i2c_algo = { + .master_xfer = stm32f7_i2c_xfer, + .functionality = stm32f7_i2c_func, +}; + +static int stm32f7_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct stm32f7_i2c_dev *i2c_dev; + const struct stm32f7_i2c_setup *setup; + struct resource *res; + u32 irq_error, irq_event, clk_rate, rise_time, fall_time; + struct i2c_adapter *adap; + struct reset_control *rst; + int ret; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c_dev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + irq_event = irq_of_parse_and_map(np, 0); + if (!irq_event) { + dev_err(&pdev->dev, "IRQ event missing or invalid\n"); + return -EINVAL; + } + + irq_error = irq_of_parse_and_map(np, 1); + if (!irq_error) { + dev_err(&pdev->dev, "IRQ error missing or invalid\n"); + return -EINVAL; + } + + i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "Error: Missing controller clock\n"); + return PTR_ERR(i2c_dev->clk); + } + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to prepare_enable clock\n"); + return ret; + } + + i2c_dev->speed = STM32_I2C_SPEED_STANDARD; + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &clk_rate); + if (!ret && clk_rate >= 1000000) + i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS; + else if (!ret && clk_rate >= 400000) + i2c_dev->speed = STM32_I2C_SPEED_FAST; + else if (!ret && clk_rate >= 100000) + i2c_dev->speed = STM32_I2C_SPEED_STANDARD; + + rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(rst)) { + dev_err(&pdev->dev, "Error: Missing controller reset\n"); + ret = PTR_ERR(rst); + goto clk_free; + } + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + + i2c_dev->dev = &pdev->dev; + + ret = devm_request_irq(&pdev->dev, irq_event, stm32f7_i2c_isr_event, 0, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq event %i\n", + irq_event); + goto clk_free; + } + + ret = devm_request_irq(&pdev->dev, irq_error, stm32f7_i2c_isr_error, 0, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq error %i\n", + irq_error); + goto clk_free; + } + + setup = of_device_get_match_data(&pdev->dev); + i2c_dev->setup = *setup; + + ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-rising-time-ns", + &rise_time); + if (!ret) + i2c_dev->setup.rise_time = rise_time; + + ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-falling-time-ns", + &fall_time); + if (!ret) + i2c_dev->setup.fall_time = fall_time; + + ret = stm32f7_i2c_setup_timing(i2c_dev, &i2c_dev->setup); + if (ret) + goto clk_free; + + stm32f7_i2c_hw_config(i2c_dev); + + adap = &i2c_dev->adap; + i2c_set_adapdata(adap, i2c_dev); + snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", + &res->start); + adap->owner = THIS_MODULE; + adap->timeout = 2 * HZ; + adap->retries = 3; + adap->algo = &stm32f7_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + ret = i2c_add_adapter(adap); + if (ret) + goto clk_free; + + platform_set_drvdata(pdev, i2c_dev); + + clk_disable(i2c_dev->clk); + + dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); + + return 0; + +clk_free: + clk_disable_unprepare(i2c_dev->clk); + + return ret; +} + +static int stm32f7_i2c_remove(struct platform_device *pdev) +{ + struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c_dev->adap); + + clk_unprepare(i2c_dev->clk); + + return 0; +} + +static const struct of_device_id stm32f7_i2c_match[] = { + { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32f7_i2c_match); + +static struct platform_driver stm32f7_i2c_driver = { + .driver = { + .name = "stm32f7-i2c", + .of_match_table = stm32f7_i2c_match, + }, + .probe = stm32f7_i2c_probe, + .remove = stm32f7_i2c_remove, +}; + +module_platform_driver(stm32f7_i2c_driver); + +MODULE_AUTHOR("M'boumba Cedric Madianga <cedric.madianga@gmail.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32F7 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c index 7668e2e9b8fd..7c07ce116e38 100644 --- a/drivers/i2c/busses/i2c-sun6i-p2wi.c +++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c @@ -223,8 +223,8 @@ static int p2wi_probe(struct platform_device *pdev) if (childnp) { ret = of_property_read_u32(childnp, "reg", &slave_addr); if (ret) { - dev_err(dev, "invalid slave address on node %s\n", - childnp->full_name); + dev_err(dev, "invalid slave address on node %pOF\n", + childnp); return -EINVAL; } @@ -258,7 +258,7 @@ static int p2wi_probe(struct platform_device *pdev) parent_clk_freq = clk_get_rate(p2wi->clk); - p2wi->rstc = devm_reset_control_get(dev, NULL); + p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(p2wi->rstc)) { ret = PTR_ERR(p2wi->rstc); dev_err(dev, "failed to retrieve reset controller: %d\n", ret); diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c index 210ca82f8aa0..addd90a8cb59 100644 --- a/drivers/i2c/busses/i2c-taos-evm.c +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -291,7 +291,7 @@ static void taos_disconnect(struct serio *serio) dev_info(&serio->dev, "Disconnected from TAOS EVM\n"); } -static struct serio_device_id taos_serio_ids[] = { +static const struct serio_device_id taos_serio_ids[] = { { .type = SERIO_RS232, .proto = SERIO_TAOSEVM, diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 4af9bbae20df..60292d243e24 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -793,7 +793,7 @@ static const struct i2c_algorithm tegra_i2c_algo = { }; /* payload size is only 12 bit */ -static struct i2c_adapter_quirks tegra_i2c_quirks = { +static const struct i2c_adapter_quirks tegra_i2c_quirks = { .max_read_len = 4096, .max_write_len = 4096, }; @@ -911,7 +911,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; - i2c_dev->rst = devm_reset_control_get(&pdev->dev, "i2c"); + i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c"); if (IS_ERR(i2c_dev->rst)) { dev_err(&pdev->dev, "missing controller reset\n"); return PTR_ERR(i2c_dev->rst); diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index ea35a895b568..df0976f4432a 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -75,7 +75,7 @@ static const struct i2c_algorithm thunderx_i2c_algo = { .functionality = thunderx_i2c_functionality, }; -static struct i2c_adapter thunderx_i2c_ops = { +static const struct i2c_adapter thunderx_i2c_ops = { .owner = THIS_MODULE, .name = "ThunderX adapter", .algo = &thunderx_i2c_algo, diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c index beee31892295..9918bdd81619 100644 --- a/drivers/i2c/busses/i2c-uniphier-f.c +++ b/drivers/i2c/busses/i2c-uniphier-f.c @@ -97,6 +97,7 @@ struct uniphier_fi2c_priv { int error; unsigned int flags; unsigned int busy_cnt; + unsigned int clk_cycle; }; static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv, @@ -461,9 +462,9 @@ static struct i2c_bus_recovery_info uniphier_fi2c_bus_recovery_info = { .unprepare_recovery = uniphier_fi2c_unprepare_recovery, }; -static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv, - u32 bus_speed, unsigned long clk_rate) +static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv) { + unsigned int cyc = priv->clk_cycle; u32 tmp; tmp = readl(priv->membase + UNIPHIER_FI2C_CR); @@ -472,12 +473,10 @@ static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv, uniphier_fi2c_reset(priv); - tmp = clk_rate / bus_speed; - - writel(tmp, priv->membase + UNIPHIER_FI2C_CYC); - writel(tmp / 2, priv->membase + UNIPHIER_FI2C_LCTL); - writel(tmp / 2, priv->membase + UNIPHIER_FI2C_SSUT); - writel(tmp / 16, priv->membase + UNIPHIER_FI2C_DSUT); + writel(cyc, priv->membase + UNIPHIER_FI2C_CYC); + writel(cyc / 2, priv->membase + UNIPHIER_FI2C_LCTL); + writel(cyc / 2, priv->membase + UNIPHIER_FI2C_SSUT); + writel(cyc / 16, priv->membase + UNIPHIER_FI2C_DSUT); uniphier_fi2c_prepare_operation(priv); } @@ -531,6 +530,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) goto disable_clk; } + priv->clk_cycle = clk_rate / bus_speed; init_completion(&priv->comp); priv->adap.owner = THIS_MODULE; priv->adap.algo = &uniphier_fi2c_algo; @@ -541,7 +541,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adap, priv); platform_set_drvdata(pdev, priv); - uniphier_fi2c_hw_init(priv, bus_speed, clk_rate); + uniphier_fi2c_hw_init(priv); ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0, pdev->name, priv); @@ -568,6 +568,33 @@ static int uniphier_fi2c_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused uniphier_fi2c_suspend(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_fi2c_resume(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_fi2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_fi2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_fi2c_suspend, uniphier_fi2c_resume) +}; + static const struct of_device_id uniphier_fi2c_match[] = { { .compatible = "socionext,uniphier-fi2c" }, { /* sentinel */ } @@ -580,6 +607,7 @@ static struct platform_driver uniphier_fi2c_drv = { .driver = { .name = "uniphier-fi2c", .of_match_table = uniphier_fi2c_match, + .pm = &uniphier_fi2c_pm_ops, }, }; module_platform_driver(uniphier_fi2c_drv); diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c index 777c0fe93653..bb181b088291 100644 --- a/drivers/i2c/busses/i2c-uniphier.c +++ b/drivers/i2c/busses/i2c-uniphier.c @@ -53,6 +53,7 @@ struct uniphier_i2c_priv { void __iomem *membase; struct clk *clk; unsigned int busy_cnt; + unsigned int clk_cycle; }; static irqreturn_t uniphier_i2c_interrupt(int irq, void *dev_id) @@ -316,13 +317,13 @@ static struct i2c_bus_recovery_info uniphier_i2c_bus_recovery_info = { .unprepare_recovery = uniphier_i2c_unprepare_recovery, }; -static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv, - u32 bus_speed, unsigned long clk_rate) +static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv) { + unsigned int cyc = priv->clk_cycle; + uniphier_i2c_reset(priv, true); - writel((clk_rate / bus_speed / 2 << 16) | (clk_rate / bus_speed), - priv->membase + UNIPHIER_I2C_CLK); + writel((cyc / 2 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK); uniphier_i2c_reset(priv, false); } @@ -376,6 +377,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev) goto disable_clk; } + priv->clk_cycle = clk_rate / bus_speed; init_completion(&priv->comp); priv->adap.owner = THIS_MODULE; priv->adap.algo = &uniphier_i2c_algo; @@ -386,7 +388,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adap, priv); platform_set_drvdata(pdev, priv); - uniphier_i2c_hw_init(priv, bus_speed, clk_rate); + uniphier_i2c_hw_init(priv); ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name, priv); @@ -413,6 +415,33 @@ static int uniphier_i2c_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused uniphier_i2c_suspend(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_i2c_resume(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_i2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_i2c_suspend, uniphier_i2c_resume) +}; + static const struct of_device_id uniphier_i2c_match[] = { { .compatible = "socionext,uniphier-i2c" }, { /* sentinel */ } @@ -425,6 +454,7 @@ static struct platform_driver uniphier_i2c_drv = { .driver = { .name = "uniphier-i2c", .of_match_table = uniphier_i2c_match, + .pm = &uniphier_i2c_pm_ops, }, }; module_platform_driver(uniphier_i2c_drv); diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c index c73d2d22009e..f1ab2a637ec0 100644 --- a/drivers/i2c/busses/i2c-versatile.c +++ b/drivers/i2c/busses/i2c-versatile.c @@ -55,7 +55,7 @@ static int i2c_versatile_getscl(void *data) return !!(readl(i2c->base + I2C_CONTROL) & SCL); } -static struct i2c_algo_bit_data i2c_versatile_algo = { +static const struct i2c_algo_bit_data i2c_versatile_algo = { .setsda = i2c_versatile_setsda, .setscl = i2c_versatile_setscl, .getsda = i2c_versatile_getsda, 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 <acpi/pcc.h> #include <linux/acpi.h> #include <linux/dma-mapping.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of.h> @@ -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,49 +220,60 @@ static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx) return 0; } -static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip, - u32 addr, u32 addrlen, u32 protocol, - u32 readlen, u32 *data) +static int slimpro_i2c_send_msg(struct slimpro_i2c_dev *ctx, + u32 *msg, + 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 (!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]; + + 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; + + return slimpro_i2c_send_msg(ctx, msg, data); +} + static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr, u32 addrlen, u32 protocol, u32 writelen, 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-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 66bce3b311a1..ae6ed254e01d 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -721,7 +721,7 @@ static const struct i2c_algorithm xiic_algorithm = { .functionality = xiic_func, }; -static struct i2c_adapter xiic_adapter = { +static const struct i2c_adapter xiic_adapter = { .owner = THIS_MODULE, .name = DRIVER_NAME, .class = I2C_CLASS_DEPRECATED, @@ -853,8 +853,7 @@ MODULE_DEVICE_TABLE(of, xiic_of_match); static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xiic_i2c *i2c = platform_get_drvdata(pdev); + struct xiic_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -863,8 +862,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xiic_i2c *i2c = platform_get_drvdata(pdev); + struct xiic_i2c *i2c = dev_get_drvdata(dev); int ret; ret = clk_enable(i2c->clk); 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 <baoyou.xie@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define REG_CMD 0x04 +#define REG_DEVADDR_H 0x0C +#define REG_DEVADDR_L 0x10 +#define REG_CLK_DIV_FS 0x14 +#define REG_CLK_DIV_HS 0x18 +#define REG_WRCONF 0x1C +#define REG_RDCONF 0x20 +#define REG_DATA 0x24 +#define REG_STAT 0x28 + +#define I2C_STOP 0 +#define I2C_MASTER BIT(0) +#define I2C_ADDR_MODE_TEN BIT(1) +#define I2C_IRQ_MSK_ENABLE BIT(3) +#define I2C_RW_READ BIT(4) +#define I2C_CMB_RW_EN BIT(5) +#define I2C_START BIT(6) + +#define I2C_ADDR_LOW_MASK GENMASK(6, 0) +#define I2C_ADDR_LOW_SHIFT 0 +#define I2C_ADDR_HI_MASK GENMASK(2, 0) +#define I2C_ADDR_HI_SHIFT 7 + +#define I2C_WFIFO_RESET BIT(7) +#define I2C_RFIFO_RESET BIT(7) + +#define I2C_IRQ_ACK_CLEAR BIT(7) +#define I2C_INT_MASK GENMASK(6, 0) + +#define I2C_TRANS_DONE BIT(0) +#define I2C_SR_EDEVICE BIT(1) +#define I2C_SR_EDATA BIT(2) + +#define I2C_FIFO_MAX 16 + +#define I2C_TIMEOUT msecs_to_jiffies(1000) + +#define DEV(i2c) ((i2c)->adap.dev.parent) + +struct zx2967_i2c { + struct i2c_adapter adap; + struct clk *clk; + struct completion complete; + u32 clk_freq; + void __iomem *reg_base; + size_t residue; + int irq; + int msg_rd; + u8 *cur_trans; + u8 access_cnt; + bool is_suspended; + int error; +}; + +static void zx2967_i2c_writel(struct zx2967_i2c *i2c, + u32 val, unsigned long reg) +{ + writel_relaxed(val, i2c->reg_base + reg); +} + +static u32 zx2967_i2c_readl(struct zx2967_i2c *i2c, unsigned long reg) +{ + return readl_relaxed(i2c->reg_base + reg); +} + +static void zx2967_i2c_writesb(struct zx2967_i2c *i2c, + void *data, unsigned long reg, int len) +{ + writesb(i2c->reg_base + reg, data, len); +} + +static void zx2967_i2c_readsb(struct zx2967_i2c *i2c, + void *data, unsigned long reg, int len) +{ + readsb(i2c->reg_base + reg, data, len); +} + +static void zx2967_i2c_start_ctrl(struct zx2967_i2c *i2c) +{ + u32 status; + u32 ctl; + + status = zx2967_i2c_readl(i2c, REG_STAT); + status |= I2C_IRQ_ACK_CLEAR; + zx2967_i2c_writel(i2c, status, REG_STAT); + + ctl = zx2967_i2c_readl(i2c, REG_CMD); + if (i2c->msg_rd) + ctl |= I2C_RW_READ; + else + ctl &= ~I2C_RW_READ; + ctl &= ~I2C_CMB_RW_EN; + ctl |= I2C_START; + zx2967_i2c_writel(i2c, ctl, REG_CMD); +} + +static void zx2967_i2c_flush_fifos(struct zx2967_i2c *i2c) +{ + u32 offset; + u32 val; + + if (i2c->msg_rd) { + offset = REG_RDCONF; + val = I2C_RFIFO_RESET; + } else { + offset = REG_WRCONF; + val = I2C_WFIFO_RESET; + } + + val |= zx2967_i2c_readl(i2c, offset); + zx2967_i2c_writel(i2c, val, offset); +} + +static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c *i2c, u32 size) +{ + u8 val[I2C_FIFO_MAX] = {0}; + int i; + + if (size > I2C_FIFO_MAX) { + dev_err(DEV(i2c), "fifo size %d over the max value %d\n", + size, I2C_FIFO_MAX); + return -EINVAL; + } + + zx2967_i2c_readsb(i2c, val, REG_DATA, size); + for (i = 0; i < size; i++) { + *i2c->cur_trans++ = val[i]; + i2c->residue--; + } + + barrier(); + + return 0; +} + +static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c *i2c) +{ + size_t residue = i2c->residue; + u8 *buf = i2c->cur_trans; + + if (residue == 0) { + dev_err(DEV(i2c), "residue is %d\n", (int)residue); + return -EINVAL; + } + + if (residue <= I2C_FIFO_MAX) { + zx2967_i2c_writesb(i2c, buf, REG_DATA, residue); + + /* Again update before writing to FIFO to make sure isr sees. */ + i2c->residue = 0; + i2c->cur_trans = NULL; + } else { + zx2967_i2c_writesb(i2c, buf, REG_DATA, I2C_FIFO_MAX); + i2c->residue -= I2C_FIFO_MAX; + i2c->cur_trans += I2C_FIFO_MAX; + } + + barrier(); + + return 0; +} + +static int zx2967_i2c_reset_hardware(struct zx2967_i2c *i2c) +{ + u32 val; + u32 clk_div; + + val = I2C_MASTER | I2C_IRQ_MSK_ENABLE; + zx2967_i2c_writel(i2c, val, REG_CMD); + + clk_div = clk_get_rate(i2c->clk) / i2c->clk_freq - 1; + zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_FS); + zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_HS); + + zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_WRCONF); + zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_RDCONF); + zx2967_i2c_writel(i2c, 1, REG_RDCONF); + + zx2967_i2c_flush_fifos(i2c); + + return 0; +} + +static void zx2967_i2c_isr_clr(struct zx2967_i2c *i2c) +{ + u32 status; + + status = zx2967_i2c_readl(i2c, REG_STAT); + status |= I2C_IRQ_ACK_CLEAR; + zx2967_i2c_writel(i2c, status, REG_STAT); +} + +static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id) +{ + u32 status; + struct zx2967_i2c *i2c = (struct zx2967_i2c *)dev_id; + + status = zx2967_i2c_readl(i2c, REG_STAT) & I2C_INT_MASK; + zx2967_i2c_isr_clr(i2c); + + if (status & I2C_SR_EDEVICE) + i2c->error = -ENXIO; + else if (status & I2C_SR_EDATA) + i2c->error = -EIO; + else if (status & I2C_TRANS_DONE) + i2c->error = 0; + else + goto done; + + complete(&i2c->complete); +done: + return IRQ_HANDLED; +} + +static void zx2967_set_addr(struct zx2967_i2c *i2c, u16 addr) +{ + u16 val; + + val = (addr >> I2C_ADDR_LOW_SHIFT) & I2C_ADDR_LOW_MASK; + zx2967_i2c_writel(i2c, val, REG_DEVADDR_L); + + val = (addr >> I2C_ADDR_HI_SHIFT) & I2C_ADDR_HI_MASK; + zx2967_i2c_writel(i2c, val, REG_DEVADDR_H); + if (val) + val = zx2967_i2c_readl(i2c, REG_CMD) | I2C_ADDR_MODE_TEN; + else + val = zx2967_i2c_readl(i2c, REG_CMD) & ~I2C_ADDR_MODE_TEN; + zx2967_i2c_writel(i2c, val, REG_CMD); +} + +static int zx2967_i2c_xfer_bytes(struct zx2967_i2c *i2c, u32 bytes) +{ + unsigned long time_left; + int rd = i2c->msg_rd; + int ret; + + reinit_completion(&i2c->complete); + + if (rd) { + zx2967_i2c_writel(i2c, bytes - 1, REG_RDCONF); + } else { + ret = zx2967_i2c_fill_tx_fifo(i2c); + if (ret) + return ret; + } + + zx2967_i2c_start_ctrl(i2c); + + time_left = wait_for_completion_timeout(&i2c->complete, + I2C_TIMEOUT); + if (time_left == 0) + return -ETIMEDOUT; + + if (i2c->error) + return i2c->error; + + return rd ? zx2967_i2c_empty_rx_fifo(i2c, bytes) : 0; +} + +static int zx2967_i2c_xfer_msg(struct zx2967_i2c *i2c, + struct i2c_msg *msg) +{ + int ret; + int i; + + if (msg->len == 0) + return -EINVAL; + + zx2967_i2c_flush_fifos(i2c); + + i2c->cur_trans = msg->buf; + i2c->residue = msg->len; + i2c->access_cnt = msg->len / I2C_FIFO_MAX; + i2c->msg_rd = msg->flags & I2C_M_RD; + + for (i = 0; i < i2c->access_cnt; i++) { + ret = zx2967_i2c_xfer_bytes(i2c, I2C_FIFO_MAX); + if (ret) + return ret; + } + + if (i2c->residue > 0) { + ret = zx2967_i2c_xfer_bytes(i2c, i2c->residue); + if (ret) + return ret; + } + + i2c->residue = 0; + i2c->access_cnt = 0; + + return 0; +} + +static int zx2967_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct zx2967_i2c *i2c = i2c_get_adapdata(adap); + int ret; + int i; + + if (i2c->is_suspended) + return -EBUSY; + + zx2967_set_addr(i2c, msgs->addr); + + for (i = 0; i < num; i++) { + ret = zx2967_i2c_xfer_msg(i2c, &msgs[i]); + if (ret) + return ret; + } + + return num; +} + +static void +zx2967_smbus_xfer_prepare(struct zx2967_i2c *i2c, u16 addr, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + u32 val; + + val = zx2967_i2c_readl(i2c, REG_RDCONF); + val |= I2C_RFIFO_RESET; + zx2967_i2c_writel(i2c, val, REG_RDCONF); + zx2967_set_addr(i2c, addr); + val = zx2967_i2c_readl(i2c, REG_CMD); + val &= ~I2C_RW_READ; + zx2967_i2c_writel(i2c, val, REG_CMD); + + switch (size) { + case I2C_SMBUS_BYTE: + zx2967_i2c_writel(i2c, command, REG_DATA); + break; + case I2C_SMBUS_BYTE_DATA: + zx2967_i2c_writel(i2c, command, REG_DATA); + if (read_write == I2C_SMBUS_WRITE) + zx2967_i2c_writel(i2c, data->byte, REG_DATA); + break; + case I2C_SMBUS_WORD_DATA: + zx2967_i2c_writel(i2c, command, REG_DATA); + if (read_write == I2C_SMBUS_WRITE) { + zx2967_i2c_writel(i2c, (data->word >> 8), REG_DATA); + zx2967_i2c_writel(i2c, (data->word & 0xff), + REG_DATA); + } + break; + } +} + +static int zx2967_smbus_xfer_read(struct zx2967_i2c *i2c, int size, + union i2c_smbus_data *data) +{ + unsigned long time_left; + u8 buf[2]; + u32 val; + + reinit_completion(&i2c->complete); + + val = zx2967_i2c_readl(i2c, REG_CMD); + val |= I2C_CMB_RW_EN; + zx2967_i2c_writel(i2c, val, REG_CMD); + + val = zx2967_i2c_readl(i2c, REG_CMD); + val |= I2C_START; + zx2967_i2c_writel(i2c, val, REG_CMD); + + time_left = wait_for_completion_timeout(&i2c->complete, + I2C_TIMEOUT); + if (time_left == 0) + return -ETIMEDOUT; + + if (i2c->error) + return i2c->error; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + val = zx2967_i2c_readl(i2c, REG_DATA); + data->byte = val; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + buf[0] = zx2967_i2c_readl(i2c, REG_DATA); + buf[1] = zx2967_i2c_readl(i2c, REG_DATA); + data->word = (buf[0] << 8) | buf[1]; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int zx2967_smbus_xfer_write(struct zx2967_i2c *i2c) +{ + unsigned long time_left; + u32 val; + + reinit_completion(&i2c->complete); + val = zx2967_i2c_readl(i2c, REG_CMD); + val |= I2C_START; + zx2967_i2c_writel(i2c, val, REG_CMD); + + time_left = wait_for_completion_timeout(&i2c->complete, + I2C_TIMEOUT); + if (time_left == 0) + return -ETIMEDOUT; + + if (i2c->error) + return i2c->error; + + return 0; +} + +static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct zx2967_i2c *i2c = i2c_get_adapdata(adap); + + if (size == I2C_SMBUS_QUICK) + read_write = I2C_SMBUS_WRITE; + + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + zx2967_smbus_xfer_prepare(i2c, addr, read_write, + command, size, data); + break; + default: + return -EOPNOTSUPP; + } + + if (read_write == I2C_SMBUS_READ) + return zx2967_smbus_xfer_read(i2c, size, data); + + return zx2967_smbus_xfer_write(i2c); +} + +static u32 zx2967_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static int __maybe_unused zx2967_i2c_suspend(struct device *dev) +{ + struct zx2967_i2c *i2c = dev_get_drvdata(dev); + + i2c->is_suspended = true; + clk_disable_unprepare(i2c->clk); + + return 0; +} + +static int __maybe_unused zx2967_i2c_resume(struct device *dev) +{ + struct zx2967_i2c *i2c = dev_get_drvdata(dev); + + i2c->is_suspended = false; + clk_prepare_enable(i2c->clk); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(zx2967_i2c_dev_pm_ops, + zx2967_i2c_suspend, zx2967_i2c_resume); + +static const struct i2c_algorithm zx2967_i2c_algo = { + .master_xfer = zx2967_i2c_xfer, + .smbus_xfer = zx2967_smbus_xfer, + .functionality = zx2967_i2c_func, +}; + +static const struct of_device_id zx2967_i2c_of_match[] = { + { .compatible = "zte,zx296718-i2c", }, + { }, +}; +MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match); + +static int zx2967_i2c_probe(struct platform_device *pdev) +{ + struct zx2967_i2c *i2c; + void __iomem *reg_base; + struct resource *res; + struct clk *clk; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "missing controller clock"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable i2c_clk\n"); + return ret; + } + + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &i2c->clk_freq); + if (ret) { + dev_err(&pdev->dev, "missing clock-frequency"); + return ret; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + i2c->irq = ret; + i2c->reg_base = reg_base; + i2c->clk = clk; + + init_completion(&i2c->complete); + platform_set_drvdata(pdev, i2c); + + ret = zx2967_i2c_reset_hardware(i2c); + if (ret) { + dev_err(&pdev->dev, "failed to initialize i2c controller\n"); + goto err_clk_unprepare; + } + + ret = devm_request_irq(&pdev->dev, i2c->irq, + zx2967_i2c_isr, 0, dev_name(&pdev->dev), i2c); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %i\n", i2c->irq); + goto err_clk_unprepare; + } + + i2c_set_adapdata(&i2c->adap, i2c); + strlcpy(i2c->adap.name, "zx2967 i2c adapter", + sizeof(i2c->adap.name)); + i2c->adap.algo = &zx2967_i2c_algo; + i2c->adap.nr = pdev->id; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret) + goto err_clk_unprepare; + + return 0; + +err_clk_unprepare: + clk_disable_unprepare(i2c->clk); + return ret; +} + +static int zx2967_i2c_remove(struct platform_device *pdev) +{ + struct zx2967_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + clk_disable_unprepare(i2c->clk); + + return 0; +} + +static struct platform_driver zx2967_i2c_driver = { + .probe = zx2967_i2c_probe, + .remove = zx2967_i2c_remove, + .driver = { + .name = "zx2967_i2c", + .of_match_table = zx2967_i2c_of_match, + .pm = &zx2967_i2c_dev_pm_ops, + }, +}; +module_platform_driver(zx2967_i2c_driver); + +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); +MODULE_DESCRIPTION("ZTE ZX2967 I2C Bus Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c new file mode 100644 index 000000000000..a9126b3cda61 --- /dev/null +++ b/drivers/i2c/i2c-core-acpi.c @@ -0,0 +1,676 @@ +/* + * Linux I2C core ACPI support code + * + * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu <tianyu.lan@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "i2c-core.h" + +struct i2c_acpi_handler_data { + struct acpi_connection_info info; + struct i2c_adapter *adapter; +}; + +struct gsb_buffer { + u8 status; + u8 len; + union { + u16 wdata; + u8 bdata; + u8 data[0]; + }; +} __packed; + +struct i2c_acpi_lookup { + struct i2c_board_info *info; + acpi_handle adapter_handle; + acpi_handle device_handle; + acpi_handle search_handle; + int n; + int index; + u32 speed; + u32 min_speed; +}; + +static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) +{ + struct i2c_acpi_lookup *lookup = data; + struct i2c_board_info *info = lookup->info; + struct acpi_resource_i2c_serialbus *sb; + acpi_status status; + + if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; + + sb = &ares->data.i2c_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) + return 1; + + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + + status = acpi_get_handle(lookup->device_handle, + sb->resource_source.string_ptr, + &lookup->adapter_handle); + if (!ACPI_SUCCESS(status)) + return 1; + + info->addr = sb->slave_address; + lookup->speed = sb->connection_speed; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + + return 1; +} + +static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { + /* + * ACPI video acpi_devices, which are handled by the acpi-video driver + * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. + */ + { ACPI_VIDEO_HID, 0 }, + {} +}; + +static int i2c_acpi_do_lookup(struct acpi_device *adev, + struct i2c_acpi_lookup *lookup) +{ + struct i2c_board_info *info = lookup->info; + struct list_head resource_list; + int ret; + + if (acpi_bus_get_status(adev) || !adev->status.present || + acpi_device_enumerated(adev)) + return -EINVAL; + + if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) + return -ENODEV; + + memset(info, 0, sizeof(*info)); + lookup->device_handle = acpi_device_handle(adev); + + /* Look up for I2cSerialBus resource */ + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, lookup); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info->addr) + return -EINVAL; + + return 0; +} + +static int i2c_acpi_get_info(struct acpi_device *adev, + struct i2c_board_info *info, + struct i2c_adapter *adapter, + acpi_handle *adapter_handle) +{ + struct list_head resource_list; + struct resource_entry *entry; + struct i2c_acpi_lookup lookup; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + lookup.index = -1; + + ret = i2c_acpi_do_lookup(adev, &lookup); + if (ret) + return ret; + + if (adapter) { + /* The adapter must match the one in I2cSerialBus() connector */ + if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) + return -ENODEV; + } else { + struct acpi_device *adapter_adev; + + /* The adapter must be present */ + if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev)) + return -ENODEV; + if (acpi_bus_get_status(adapter_adev) || + !adapter_adev->status.present) + return -ENODEV; + } + + info->fwnode = acpi_fwnode_handle(adev); + if (adapter_handle) + *adapter_handle = lookup.adapter_handle; + + /* Then fill IRQ number if any */ + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (ret < 0) + return -EINVAL; + + resource_list_for_each_entry(entry, &resource_list) { + if (resource_type(entry->res) == IORESOURCE_IRQ) { + info->irq = entry->res->start; + break; + } + } + + acpi_dev_free_resource_list(&resource_list); + + acpi_set_modalias(adev, dev_name(&adev->dev), info->type, + sizeof(info->type)); + + return 0; +} + +static void i2c_acpi_register_device(struct i2c_adapter *adapter, + struct acpi_device *adev, + struct i2c_board_info *info) +{ + adev->power.flags.ignore_parent = true; + acpi_device_set_enumerated(adev); + + if (!i2c_new_device(adapter, info)) { + adev->power.flags.ignore_parent = false; + dev_err(&adapter->dev, + "failed to add I2C device %s from ACPI\n", + dev_name(&adev->dev)); + } +} + +static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct acpi_device *adev; + struct i2c_board_info info; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (i2c_acpi_get_info(adev, &info, adapter, NULL)) + return AE_OK; + + i2c_acpi_register_device(adapter, adev, &info); + + return AE_OK; +} + +#define I2C_ACPI_MAX_SCAN_DEPTH 32 + +/** + * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter + * @adap: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +void i2c_acpi_register_devices(struct i2c_adapter *adap) +{ + acpi_status status; + + if (!has_acpi_companion(&adap->dev)) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_add_device, NULL, + adap, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); +} + +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + if (!(client && matches)) + return NULL; + + return acpi_match_device(matches, &client->dev); +} + +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_find_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_find_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_find_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_find_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 index 82576aaccc90..56e46581b84b 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 <kmalkki@cc.hut.fi>. - All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> - SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and - Jean Delvare <jdelvare@suse.de> - Mux support by Rodolfo Giometti <giometti@enneenne.com> and - Michael Lawnick <michael.lawnick.ext@nsn.com> - OF support is copyright (c) 2008 Jochen Friedrich <jochen@scram.de> - (based on a previous patch from Jon Smirl <jonsmirl@gmail.com>) and - (c) 2013 Wolfram Sang <wsa@the-dreams.de> - I2C ACPI code Copyright (C) 2014 Intel Corp - Author: Lan Tianyu <tianyu.lan@intel.com> - I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com> +/* + * Linux I2C core + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> + * Mux support by Rodolfo Giometti <giometti@enneenne.com> and + * Michael Lawnick <michael.lawnick.ext@nsn.com> + * + * Copyright (C) 2013-2017 Wolfram Sang <wsa@the-dreams.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * 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 <dt-bindings/i2c/i2c.h> -#include <linux/uaccess.h> #include <linux/acpi.h> #include <linux/clk/clk-conf.h> #include <linux/completion.h> @@ -38,7 +28,6 @@ #include <linux/err.h> #include <linux/errno.h> #include <linux/gpio.h> -#include <linux/hardirq.h> #include <linux/i2c.h> #include <linux/idr.h> #include <linux/init.h> @@ -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) { @@ -1009,10 +353,11 @@ static int i2c_device_probe(struct device *dev) } /* - * An I2C ID table is not mandatory, if and only if, a suitable Device - * Tree match table entry is supplied for the probing device. + * An I2C ID table is not mandatory, if and only if, a suitable OF + * or ACPI ID table is supplied for the probing device. */ if (!driver->id_table && + !i2c_acpi_match_device(dev->driver->acpi_match_table, client) && !i2c_of_match_device(dev->driver->of_match_table, client)) return -ENODEV; @@ -1195,7 +540,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 +558,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 +1109,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 +1703,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 +2241,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 <simon@tk.uni-linz.ac.at>"); 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..8d474bb1dc15 --- /dev/null +++ b/drivers/i2c/i2c-core-of.c @@ -0,0 +1,274 @@ +/* + * Linux I2C core OF support code + * + * Copyright (C) 2008 Jochen Friedrich <jochen@scram.de> + * based on a previous patch from Jon Smirl <jonsmirl@gmail.com> + * + * Copyright (C) 2013 Wolfram Sang <wsa@the-dreams.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <dt-bindings/i2c/i2c.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include "i2c-core.h" + +static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, + struct device_node *node) +{ + struct i2c_client *result; + struct i2c_board_info info = {}; + struct dev_archdata dev_ad = {}; + const __be32 *addr_be; + u32 addr; + int len; + + dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); + + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { + dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n", + node); + 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 %pOF\n", node); + 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 %pOF\n", + addr, node); + 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 %pOF\n", node); + 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 %pOF\n", + node); + 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 '%pOF'\n", + rd->dn); + 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 <wsa@sang-engineering.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <dt-bindings/i2c/i2c.h> +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/of.h> + +#include "i2c-core.h" + +int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) +{ + int ret; + + if (!client || !slave_cb) { + WARN(1, "insufficient data\n"); + return -EINVAL; + } + + if (!(client->flags & I2C_CLIENT_SLAVE)) + dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n", + __func__); + + if (!(client->flags & I2C_CLIENT_TEN)) { + /* Enforce stricter address checking */ + ret = i2c_check_7bit_addr_validity_strict(client->addr); + if (ret) { + dev_err(&client->dev, "%s: invalid address\n", __func__); + return ret; + } + } + + if (!client->adapter->algo->reg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); + return -EOPNOTSUPP; + } + + client->slave_cb = slave_cb; + + i2c_lock_adapter(client->adapter); + ret = client->adapter->algo->reg_slave(client); + i2c_unlock_adapter(client->adapter); + + if (ret) { + client->slave_cb = NULL; + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_register); + +int i2c_slave_unregister(struct i2c_client *client) +{ + int ret; + + if (!client->adapter->algo->unreg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); + return -EOPNOTSUPP; + } + + i2c_lock_adapter(client->adapter); + ret = client->adapter->algo->unreg_slave(client); + i2c_unlock_adapter(client->adapter); + + if (ret == 0) + client->slave_cb = NULL; + else + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_unregister); + +/** + * i2c_detect_slave_mode - detect operation mode + * @dev: The device owning the bus + * + * This checks the device nodes for an I2C slave by checking the address + * used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS + * flag this means the device is configured to act as a I2C slave and it will + * be listening at that address. + * + * Returns true if an I2C own slave address is detected, otherwise returns + * false. + */ +bool i2c_detect_slave_mode(struct device *dev) +{ + if (IS_BUILTIN(CONFIG_OF) && dev->of_node) { + struct device_node *child; + u32 reg; + + for_each_child_of_node(dev->of_node, child) { + of_property_read_u32(child, "reg", ®); + 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 <frodol@dds.nl> + * SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and + * Jean Delvare <jdelvare@suse.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> + +#define CREATE_TRACE_POINTS +#include <trace/events/smbus.h> + + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* Incremental CRC8 over count bytes in the array pointed to by p */ +static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} + +/* Assume a 7-bit address, which is reasonable for SMBus */ +static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = i2c_8bit_addr_from_msg(msg); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/** + * i2c_smbus_read_block_data - SMBus "block read" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most 32 bytes. + * + * This executes the SMBus "block read" protocol, returning negative errno + * else the number of data bytes in the slave's response. + * + * Note that using this function requires that the client's adapter support + * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers + * support this; its emulation through I2C messaging relies on a specific + * mechanism (I2C_M_RECV_LEN) which may not be implemented. + */ +s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 *values) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BLOCK_DATA, &data); + if (status) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_block_data); + +/** + * i2c_smbus_write_block_data - SMBus "block write" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most 32 bytes + * @values: Byte array which will be written. + * + * This executes the SMBus "block write" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int num = read_write == I2C_SMBUS_READ ? 2 : 1; + int i; + u8 partial_pec = 0; + int status; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = flags, + .len = 1, + .buf = msgbuf0, + }, { + .addr = addr, + .flags = flags | I2C_M_RD, + .len = 0, + .buf = msgbuf1, + }, + }; + + msgbuf0[0] = command; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + for (i = 1; i < msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + num = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + msg[0].len = data->block[0] + 2; + for (i = 1; i < msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + } else { + msg[0].len = data->block[0] + 1; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + for (i = 1; i <= data->block[0]; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + if (i) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + if (num == 1) /* Write only */ + i2c_smbus_add_pec(&msg[0]); + else /* Write followed by read */ + partial_pec = i2c_smbus_msg_pec(0, &msg[0]); + } + /* Ask for PEC if last message is a read */ + if (msg[num-1].flags & I2C_M_RD) + msg[num-1].len++; + } + + status = i2c_transfer(adapter, msg, num); + if (status < 0) + return status; + + /* Check PEC if last message is a read */ + if (i && (msg[num-1].flags & I2C_M_RD)) { + status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); + if (status < 0) + return status; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < data->block[0]; i++) + data->block[i+1] = msgbuf1[i]; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i < msgbuf1[0] + 1; i++) + data->block[i] = msgbuf1[i]; + break; + } + return 0; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int protocol, + union i2c_smbus_data *data) +{ + unsigned long orig_jiffies; + int try; + s32 res; + + /* If enabled, the following two tracepoints are conditional on + * read_write and protocol. + */ + trace_smbus_write(adapter, addr, flags, read_write, + command, protocol, data); + trace_smbus_read(adapter, addr, flags, read_write, + command, protocol); + + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; + + if (adapter->algo->smbus_xfer) { + i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adapter->retries; try++) { + res = adapter->algo->smbus_xfer(adapter, addr, flags, + read_write, command, + protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } + i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) + goto trace; + /* + * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't + * implement native support for the SMBus operation. + */ + } + + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + +trace: + /* If enabled, the reply tracepoint is conditional on read_write. */ + trace_smbus_reply(adapter, addr, flags, read_write, + command, protocol, data); + trace_smbus_result(adapter, addr, flags, read_write, + command, protocol, res); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); + +/** + * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most + * I2C_SMBUS_BLOCK_MAX bytes. + * + * This executes the SMBus "block read" protocol if supported by the adapter. + * If block read is not supported, it emulates it using either word or byte + * read protocols depending on availability. + * + * The addresses of the I2C slave device that are accessed with this function + * must be mapped to a linear region, so that a block read will have the same + * effect as a byte read. Before using this function you must double-check + * if the I2C slave does support exchanging a block transfer with a byte + * transfer. + */ +s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + u8 i = 0; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return i2c_smbus_read_i2c_block_data(client, command, length, values); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EOPNOTSUPP; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { + while ((i + 2) <= length) { + status = i2c_smbus_read_word_data(client, command + i); + if (status < 0) + return status; + values[i] = status & 0xff; + values[i + 1] = status >> 8; + i += 2; + } + } + + while (i < length) { + status = i2c_smbus_read_byte_data(client, command + i); + if (status < 0) + return status; + values[i] = status; + i++; + } + + return i; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 17700bfddcf5..3d3d9bf02101 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -27,3 +27,36 @@ 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 +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client); +void i2c_acpi_register_devices(struct i2c_adapter *adap); +#else /* CONFIG_ACPI */ +static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } +static inline const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + return NULL; +} +#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 <linux/errno.h> #include <linux/i2c.h> @@ -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/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 1e160fc37ecc..0f5c8fc36625 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -8,7 +8,7 @@ menu "Multiplexer I2C Chip support" config I2C_ARB_GPIO_CHALLENGE tristate "GPIO-based I2C arbitration" depends on GPIOLIB || COMPILE_TEST - depends on OF + depends on OF || COMPILE_TEST help If you say yes to this option, support will be included for an I2C multimaster arbitration scheme using GPIOs and a challenge & @@ -30,6 +30,19 @@ config I2C_MUX_GPIO This driver can also be built as a module. If so, the module will be called i2c-mux-gpio. +config I2C_MUX_GPMUX + tristate "General Purpose I2C multiplexer" + select MULTIPLEXER + depends on OF || COMPILE_TEST + help + If you say yes to this option, support will be included for a + general purpose I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which in turn is controlled + by a MUX-controller from the MUX subsystem. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-gpmux. + config I2C_MUX_LTC4306 tristate "LTC LTC4306/5 I2C multiplexer" select GPIOLIB @@ -63,6 +76,7 @@ config I2C_MUX_PCA954x config I2C_MUX_PINCTRL tristate "pinctrl-based I2C multiplexer" depends on PINCTRL + depends on OF || COMPILE_TEST help If you say yes to this option, support will be included for an I2C multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing. @@ -70,7 +84,7 @@ config I2C_MUX_PINCTRL different sets of pins at run-time. This driver can also be built as a module. If so, the module will be - called pinctrl-i2cmux. + called i2c-mux-pinctrl. config I2C_MUX_REG tristate "Register-based I2C multiplexer" diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index ff7618cd5312..4a67d3199877 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o +obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index 3e6fe1760d82..33ce032cb701 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -167,8 +167,8 @@ static ssize_t available_masters_show(struct device *dev, int count = 0, i; for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++) - count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%s%c", - i, priv->chan[i].parent_np->full_name, + count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%pOF%c", + i, priv->chan[i].parent_np, i == priv->num_chan - 1 ? '\n' : ' '); return count; diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c new file mode 100644 index 000000000000..92cf5f48afe6 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -0,0 +1,173 @@ +/* + * General Purpose I2C multiplexer + * + * Copyright (C) 2017 Axentia Technologies AB + * + * Author: Peter Rosin <peda@axentia.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.h> +#include <linux/mux/consumer.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +struct mux { + struct mux_control *control; + + bool do_not_deselect; +}; + +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + int ret; + + ret = mux_control_select(mux->control, chan); + mux->do_not_deselect = ret < 0; + + return ret; +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct mux *mux = i2c_mux_priv(muxc); + + if (mux->do_not_deselect) + return 0; + + return mux_control_deselect(mux->control); +} + +static struct i2c_adapter *mux_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + +static const struct of_device_id i2c_mux_of_match[] = { + { .compatible = "i2c-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_of_match); + +static int i2c_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct i2c_mux_core *muxc; + struct mux *mux; + struct i2c_adapter *parent; + int children; + int ret; + + if (!np) + return -ENODEV; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->control = devm_mux_control_get(dev, NULL); + if (IS_ERR(mux->control)) { + if (PTR_ERR(mux->control) != -EPROBE_DEFER) + dev_err(dev, "failed to get control-mux\n"); + return PTR_ERR(mux->control); + } + + parent = mux_parent_adapter(dev); + if (IS_ERR(parent)) { + if (PTR_ERR(parent) != -EPROBE_DEFER) + dev_err(dev, "failed to get i2c-parent adapter\n"); + return PTR_ERR(parent); + } + + children = of_get_child_count(np); + + muxc = i2c_mux_alloc(parent, dev, children, 0, 0, + i2c_mux_select, i2c_mux_deselect); + if (!muxc) { + ret = -ENOMEM; + goto err_parent; + } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + muxc->mux_locked = of_property_read_bool(np, "mux-locked"); + + for_each_child_of_node(np, child) { + u32 chan; + + ret = of_property_read_u32(child, "reg", &chan); + if (ret < 0) { + dev_err(dev, "no reg property for node '%s'\n", + child->name); + goto err_children; + } + + if (chan >= mux_control_states(mux->control)) { + dev_err(dev, "invalid reg %u\n", chan); + ret = -EINVAL; + goto err_children; + } + + ret = i2c_mux_add_adapter(muxc, 0, chan, 0); + if (ret) + goto err_children; + } + + dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name); + + return 0; + +err_children: + i2c_mux_del_adapters(muxc); +err_parent: + i2c_put_adapter(parent); + + return ret; +} + +static int i2c_mux_remove(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); + + return 0; +} + +static struct platform_driver i2c_mux_driver = { + .probe = i2c_mux_probe, + .remove = i2c_mux_remove, + .driver = { + .name = "i2c-mux-gpmux", + .of_match_table = i2c_mux_of_match, + }, +}; +module_platform_driver(i2c_mux_driver); + +MODULE_DESCRIPTION("General Purpose I2C multiplexer driver"); +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c index e53f2abd1350..12ad8d65faf6 100644 --- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c +++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c @@ -38,9 +38,9 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/platform_data/x86/mlxcpld.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/i2c/mlxcpld.h> #define CPLD_MUX_MAX_NCHANS 8 diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 9e318c9516c7..6a39adaf433f 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -16,15 +16,14 @@ * warranty of any kind, whether express or implied. */ -#include <linux/module.h> -#include <linux/jiffies.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> - -#include <linux/i2c/pca954x.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/platform_data/pca954x.h> +#include <linux/slab.h> /* * The PCA9541 is a bus master selector. It supports two I2C masters connected diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index f1751c290af6..7b992db38021 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -39,13 +39,13 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> -#include <linux/i2c/pca954x.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/platform_data/pca954x.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/spinlock.h> diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 7c0c264b07bc..cc6818aabab5 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -20,17 +20,14 @@ #include <linux/i2c-mux.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> -#include <linux/i2c-mux-pinctrl.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> #include "../../pinctrl/core.h" struct i2c_mux_pinctrl { - struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; - struct pinctrl_state *state_idle; }; static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) @@ -42,85 +39,9 @@ static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); - - return pinctrl_select_state(mux->pinctrl, mux->state_idle); + return i2c_mux_pinctrl_select(muxc, muxc->num_adapters); } -#ifdef CONFIG_OF -static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, - struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - int num_names, i, ret; - struct device_node *adapter_np; - struct i2c_adapter *adapter; - - if (!np) - return 0; - - mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) - return -ENOMEM; - - num_names = of_property_count_strings(np, "pinctrl-names"); - if (num_names < 0) { - dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", - num_names); - return num_names; - } - - mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, - sizeof(*mux->pdata->pinctrl_states) * num_names, - GFP_KERNEL); - if (!mux->pdata->pinctrl_states) - return -ENOMEM; - - for (i = 0; i < num_names; i++) { - ret = of_property_read_string_index(np, "pinctrl-names", i, - &mux->pdata->pinctrl_states[mux->pdata->bus_count]); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", - ret); - return ret; - } - if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], - "idle")) { - if (i != num_names - 1) { - dev_err(&pdev->dev, - "idle state must be last\n"); - return -EINVAL; - } - mux->pdata->pinctrl_state_idle = "idle"; - } else { - mux->pdata->bus_count++; - } - } - - adapter_np = of_parse_phandle(np, "i2c-parent", 0); - if (!adapter_np) { - dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); - return -ENODEV; - } - adapter = of_find_i2c_adapter_by_node(adapter_np); - of_node_put(adapter_np); - if (!adapter) { - dev_err(&pdev->dev, "Cannot find parent bus\n"); - return -EPROBE_DEFER; - } - mux->pdata->parent_bus_num = i2c_adapter_id(adapter); - put_device(&adapter->dev); - - return 0; -} -#else -static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, - struct platform_device *pdev) -{ - return 0; -} -#endif - static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( struct pinctrl_state *state) { @@ -141,110 +62,108 @@ static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( return root; } +static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + static int i2c_mux_pinctrl_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct i2c_mux_core *muxc; struct i2c_mux_pinctrl *mux; + struct i2c_adapter *parent; struct i2c_adapter *root; - int i, ret; - - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - ret = -ENOMEM; - goto err; - } + int num_names, i, ret; + const char *name; - mux->pdata = dev_get_platdata(&pdev->dev); - if (!mux->pdata) { - ret = i2c_mux_pinctrl_parse_dt(mux, pdev); - if (ret < 0) - goto err; - } - if (!mux->pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); - ret = -ENODEV; - goto err; + num_names = of_property_count_strings(np, "pinctrl-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", + num_names); + return num_names; } - mux->states = devm_kzalloc(&pdev->dev, - sizeof(*mux->states) * mux->pdata->bus_count, - GFP_KERNEL); - if (!mux->states) { - dev_err(&pdev->dev, "Cannot allocate states\n"); - ret = -ENOMEM; - goto err; - } + parent = i2c_mux_pinctrl_parent_adapter(dev); + if (IS_ERR(parent)) + return PTR_ERR(parent); - muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0, - i2c_mux_pinctrl_select, NULL); + muxc = i2c_mux_alloc(parent, dev, num_names, + sizeof(*mux) + num_names * sizeof(*mux->states), + 0, i2c_mux_pinctrl_select, NULL); if (!muxc) { ret = -ENOMEM; - goto err; + goto err_put_parent; } - muxc->priv = mux; + mux = i2c_mux_priv(muxc); + mux->states = (struct pinctrl_state **)(mux + 1); platform_set_drvdata(pdev, muxc); - mux->pinctrl = devm_pinctrl_get(&pdev->dev); + mux->pinctrl = devm_pinctrl_get(dev); if (IS_ERR(mux->pinctrl)) { ret = PTR_ERR(mux->pinctrl); - dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); - goto err; + dev_err(dev, "Cannot get pinctrl: %d\n", ret); + goto err_put_parent; } - for (i = 0; i < mux->pdata->bus_count; i++) { - mux->states[i] = pinctrl_lookup_state(mux->pinctrl, - mux->pdata->pinctrl_states[i]); + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "pinctrl-names", i, + &name); + if (ret < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret); + goto err_put_parent; + } + + mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name); if (IS_ERR(mux->states[i])) { ret = PTR_ERR(mux->states[i]); - dev_err(&pdev->dev, - "Cannot look up pinctrl state %s: %d\n", - mux->pdata->pinctrl_states[i], ret); - goto err; - } - } - if (mux->pdata->pinctrl_state_idle) { - mux->state_idle = pinctrl_lookup_state(mux->pinctrl, - mux->pdata->pinctrl_state_idle); - if (IS_ERR(mux->state_idle)) { - ret = PTR_ERR(mux->state_idle); - dev_err(&pdev->dev, - "Cannot look up pinctrl state %s: %d\n", - mux->pdata->pinctrl_state_idle, ret); - goto err; + dev_err(dev, "Cannot look up pinctrl state %s: %d\n", + name, ret); + goto err_put_parent; } - muxc->deselect = i2c_mux_pinctrl_deselect; - } + if (strcmp(name, "idle")) + continue; - muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num); - if (!muxc->parent) { - dev_err(&pdev->dev, "Parent adapter (%d) not found\n", - mux->pdata->parent_bus_num); - ret = -EPROBE_DEFER; - goto err; + if (i != num_names - 1) { + dev_err(dev, "idle state must be last\n"); + ret = -EINVAL; + goto err_put_parent; + } + muxc->deselect = i2c_mux_pinctrl_deselect; } root = i2c_root_adapter(&muxc->parent->dev); muxc->mux_locked = true; - for (i = 0; i < mux->pdata->bus_count; i++) { + for (i = 0; i < num_names; i++) { if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { muxc->mux_locked = false; break; } } - if (muxc->mux_locked && mux->pdata->pinctrl_state_idle && - root != i2c_mux_pinctrl_root_adapter(mux->state_idle)) - muxc->mux_locked = false; - if (muxc->mux_locked) - dev_info(&pdev->dev, "mux-locked i2c mux\n"); + dev_info(dev, "mux-locked i2c mux\n"); - for (i = 0; i < mux->pdata->bus_count; i++) { - u32 bus = mux->pdata->base_bus_num ? - (mux->pdata->base_bus_num + i) : 0; - - ret = i2c_mux_add_adapter(muxc, bus, i, 0); + /* Do not add any adapter for the idle state (if it's there at all). */ + for (i = 0; i < num_names - !!muxc->deselect; i++) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); if (ret) goto err_del_adapter; } @@ -253,8 +172,9 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) err_del_adapter: i2c_mux_del_adapters(muxc); - i2c_put_adapter(muxc->parent); -err: +err_put_parent: + i2c_put_adapter(parent); + return ret; } @@ -264,16 +184,15 @@ static int i2c_mux_pinctrl_remove(struct platform_device *pdev) i2c_mux_del_adapters(muxc); i2c_put_adapter(muxc->parent); + return 0; } -#ifdef CONFIG_OF static const struct of_device_id i2c_mux_pinctrl_of_match[] = { { .compatible = "i2c-mux-pinctrl", }, {}, }; MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); -#endif static struct platform_driver i2c_mux_pinctrl_driver = { .driver = { |