diff options
Diffstat (limited to 'drivers/i2c/busses')
30 files changed, 2467 insertions, 428 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4f8df2ec87b1..451d4ae50e66 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -116,24 +116,21 @@ config I2C_I801 DH89xxCC (PCH) Panther Point (PCH) Lynx Point (PCH) - Lynx Point-LP (PCH) Avoton (SOC) Wellsburg (PCH) Coleto Creek (PCH) Wildcat Point (PCH) - Wildcat Point-LP (PCH) BayTrail (SOC) Braswell (SOC) - Sunrise Point-H (PCH) - Sunrise Point-LP (PCH) - Kaby Lake-H (PCH) + Sunrise Point (PCH) + Kaby Lake (PCH) DNV (SOC) Broxton (SOC) Lewisburg (PCH) Gemini Lake (SOC) - Cannon Lake-H (PCH) - Cannon Lake-LP (PCH) + Cannon Lake (PCH) Cedar Fork (PCH) + Ice Lake (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -762,6 +759,13 @@ config I2C_OMAP Like OMAP1510/1610/1710/5912 and OMAP242x. For details see http://www.ti.com/omap. +config I2C_OWL + tristate "Actions Semiconductor Owl I2C Controller" + depends on ARCH_ACTIONS || COMPILE_TEST + help + Say Y here if you want to use the I2C bus controller on + the Actions Semiconductor Owl SoC's. + config I2C_PASEMI tristate "PA Semi SMBus interface" depends on PPC_PASEMI && PCI @@ -828,6 +832,19 @@ config I2C_PXA_SLAVE is necessary for systems where the PXA may be a target on the I2C bus. +config I2C_QCOM_GENI + tristate "Qualcomm Technologies Inc.'s GENI based I2C controller" + depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_GENI_SE + help + This driver supports GENI serial engine based I2C controller in + master mode on the Qualcomm Technologies Inc.'s SoCs. If you say + yes to this option, support will be included for the built-in I2C + interface on the Qualcomm Technologies Inc.'s SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-qcom-geni. + config I2C_QUP tristate "Qualcomm QUP based I2C controller" depends on ARCH_QCOM @@ -1330,4 +1347,15 @@ config I2C_ZX2967 This driver can also be built as a module. If so, the module will be called i2c-zx2967. +config I2C_FSI + tristate "FSI I2C driver" + depends on FSI + help + Driver for FSI bus attached I2C masters. These are I2C masters that + are connected to the system over an FSI bus, instead of the more + common PCI or MMIO interface. + + This driver can also be built as a module. If so, the module will be + called as i2c-fsi. + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5a869144a0c5..18b26af82b1c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o +obj-$(CONFIG_I2C_OWL) += i2c-owl.o obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o @@ -83,6 +84,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o +obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_RIIC) += i2c-riic.o obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o @@ -137,5 +139,6 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o +obj-$(CONFIG_I2C_FSI) += i2c-fsi.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 95a80a8f81b5..134567f3019f 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -384,6 +384,7 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, if (status) return status; len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + /* fall through */ case I2C_SMBUS_I2C_BLOCK_DATA: for (i = 0; i < len; i++) { status = amd_ec_read(smbus, AMD_SMB_DATA + i, diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 60e4d0e939a3..a4f956c6d567 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -111,22 +111,22 @@ #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) enum aspeed_i2c_master_state { + ASPEED_I2C_MASTER_INACTIVE, 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_STOP, 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 { @@ -234,7 +234,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus) bool irq_handled = true; u8 value; - spin_lock(&bus->lock); if (!slave) { irq_handled = false; goto out; @@ -325,7 +324,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus) writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG); out: - spin_unlock(&bus->lock); return irq_handled; } #endif /* CONFIG_I2C_SLAVE */ @@ -389,7 +387,6 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) 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); @@ -407,7 +404,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) */ ret = aspeed_i2c_is_irq_error(irq_status); if (ret < 0) { - dev_dbg(bus->dev, "received error interrupt: 0x%08x", + dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", irq_status); bus->cmd_err = ret; bus->master_state = ASPEED_I2C_MASTER_INACTIVE; @@ -416,7 +413,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) /* We are in an invalid state; reset bus to a known state. */ if (!bus->msgs) { - dev_err(bus->dev, "bus in unknown state"); + dev_err(bus->dev, "bus in unknown state\n"); bus->cmd_err = -EIO; if (bus->master_state != ASPEED_I2C_MASTER_STOP) aspeed_i2c_do_stop(bus); @@ -431,7 +428,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) */ 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); + pr_devel("no slave present at %02x\n", msg->addr); status_ack |= ASPEED_I2CD_INTR_TX_NAK; bus->cmd_err = -ENXIO; aspeed_i2c_do_stop(bus); @@ -451,11 +448,11 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) 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"); + dev_dbg(bus->dev, "slave NACKed TX\n"); 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"); + dev_err(bus->dev, "slave failed to ACK TX\n"); goto error_and_stop; } status_ack |= ASPEED_I2CD_INTR_TX_ACK; @@ -478,7 +475,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) /* fallthrough intended */ case ASPEED_I2C_MASTER_RX: if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) { - dev_err(bus->dev, "master failed to RX"); + dev_err(bus->dev, "master failed to RX\n"); goto error_and_stop; } status_ack |= ASPEED_I2CD_INTR_RX_DONE; @@ -509,7 +506,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *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"); + dev_err(bus->dev, "master failed to STOP\n"); bus->cmd_err = -EIO; /* Do not STOP as we have already tried. */ } else { @@ -520,7 +517,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) goto out_complete; case ASPEED_I2C_MASTER_INACTIVE: dev_err(bus->dev, - "master received interrupt 0x%08x, but is inactive", + "master received interrupt 0x%08x, but is inactive\n", irq_status); bus->cmd_err = -EIO; /* Do not STOP as we should be inactive. */ @@ -547,22 +544,29 @@ out_no_complete: 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; + bool ret; + + spin_lock(&bus->lock); #if IS_ENABLED(CONFIG_I2C_SLAVE) if (aspeed_i2c_slave_irq(bus)) { dev_dbg(bus->dev, "irq handled by slave.\n"); - return IRQ_HANDLED; + ret = true; + goto out; } #endif /* CONFIG_I2C_SLAVE */ - return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE; + ret = aspeed_i2c_master_irq(bus); + +out: + spin_unlock(&bus->lock); + return ret ? IRQ_HANDLED : IRQ_NONE; } static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, @@ -851,7 +855,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(bus->rst)) { dev_err(&pdev->dev, - "missing or invalid reset controller device tree entry"); + "missing or invalid reset controller device tree entry\n"); return PTR_ERR(bus->rst); } reset_control_deassert(bus->rst); @@ -868,7 +872,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) if (!match) bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val; else - bus->get_clk_reg_val = match->data; + bus->get_clk_reg_val = (u32 (*)(u32))match->data; /* Initialize the I2C adapter */ spin_lock_init(&bus->lock); diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 78792b4d6437..826d32049996 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -689,9 +689,9 @@ static int brcmstb_i2c_suspend(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_adapter(&i2c_dev->adapter); + i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = true; - i2c_unlock_adapter(&i2c_dev->adapter); + i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); return 0; } @@ -700,10 +700,10 @@ static int brcmstb_i2c_resume(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_adapter(&i2c_dev->adapter); + i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); brcmstb_i2c_set_bsc_reg_defaults(i2c_dev); i2c_dev->is_suspended = false; - i2c_unlock_adapter(&i2c_dev->adapter); + i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); return 0; } diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 7379043711df..11caafa0e050 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -718,14 +718,14 @@ static int i2c_davinci_cpufreq_transition(struct notifier_block *nb, dev = container_of(nb, struct davinci_i2c_dev, freq_transition); - i2c_lock_adapter(&dev->adapter); + i2c_lock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER); if (val == CPUFREQ_PRECHANGE) { davinci_i2c_reset_ctrl(dev, 0); } else if (val == CPUFREQ_POSTCHANGE) { i2c_davinci_calc_clk_dividers(dev); davinci_i2c_reset_ctrl(dev, 1); } - i2c_unlock_adapter(&dev->adapter); + i2c_unlock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER); return 0; } diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index dbda8c9c8a1c..a2a275cfc1f6 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel BayTrail PMIC I2C bus semaphore implementaion * Copyright (c) 2014, Intel Corporation. - * - * 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. */ #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 48914dfc8ce8..69ec4a791f23 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Synopsys DesignWare I2C adapter driver. * @@ -6,20 +7,6 @@ * 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/clk.h> #include <linux/delay.h> @@ -31,6 +18,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/swab.h> #include "i2c-designware-core.h" @@ -94,6 +82,40 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset) } } +/** + * i2c_dw_set_reg_access() - Set register access flags + * @dev: device private data + * + * Autodetects needed register access mode and sets access flags accordingly. + * This must be called before doing any other register access. + */ +int i2c_dw_set_reg_access(struct dw_i2c_dev *dev) +{ + u32 reg; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + reg = dw_readl(dev, DW_IC_COMP_TYPE); + i2c_dw_release_lock(dev); + + if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) { + /* Configure register endianess 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); + return -ENODEV; + } + + return 0; +} + u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) { /* @@ -149,6 +171,47 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset; } +int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev) +{ + u32 reg; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + /* 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; + + dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n", + dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK, + dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT); + } else if (dev->sda_hold_time) { + dev_warn(dev->dev, + "Hardware too old to adjust SDA hold time.\n"); + dev->sda_hold_time = 0; + } + + i2c_dw_release_lock(dev); + + return 0; +} + void __i2c_dw_disable(struct dw_i2c_dev *dev) { int timeout = 100; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index d690e648bc01..e367b1af4ab2 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Synopsys DesignWare I2C adapter driver. * @@ -6,20 +7,6 @@ * 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/i2c.h> @@ -212,7 +199,8 @@ * @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 - * @clk_freq: bus clock frequency + * @timings: bus clock frequency, SDA hold and other timings + * @sda_hold_time: SDA hold value * @ss_hcnt: standard speed HCNT value * @ss_lcnt: standard speed LCNT value * @fs_hcnt: fast speed HCNT value @@ -264,10 +252,8 @@ struct dw_i2c_dev { unsigned int tx_fifo_depth; unsigned int rx_fifo_depth; int rx_outstanding; - u32 clk_freq; + struct i2c_timings timings; u32 sda_hold_time; - u32 sda_falling_time; - u32 scl_falling_time; u16 ss_hcnt; u16 ss_lcnt; u16 fs_hcnt; @@ -295,8 +281,10 @@ struct dw_i2c_dev { u32 dw_readl(struct dw_i2c_dev *dev, int offset); void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset); +int i2c_dw_set_reg_access(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); +int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev); unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev); int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare); int i2c_dw_acquire_lock(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 27436a937492..e18442b9973a 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Synopsys DesignWare I2C adapter driver (master only). * @@ -6,20 +7,6 @@ * 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/err.h> @@ -45,90 +32,79 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) dw_writel(dev, dev->master_cfg, DW_IC_CON); } -/** - * 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. - */ -static int i2c_dw_init_master(struct dw_i2c_dev *dev) +static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) { - u32 hcnt, lcnt; - u32 reg, comp_param1; + u32 ic_clk = i2c_dw_clk_rate(dev); + const char *mode_str, *fp_str = ""; + u32 comp_param1; u32 sda_falling_time, scl_falling_time; + struct i2c_timings *t = &dev->timings; 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 endianess 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); + i2c_dw_release_lock(dev); - /* Disable the adapter */ - __i2c_dw_disable(dev); - - /* Set standard and fast speed deviders for high/low periods */ - - sda_falling_time = dev->sda_falling_time ?: 300; /* ns */ - scl_falling_time = dev->scl_falling_time ?: 300; /* ns */ + /* Set standard and fast speed dividers for high/low periods */ + sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ + scl_falling_time = t->scl_fall_ns ?: 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), + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + dev->ss_hcnt = + i2c_dw_scl_hcnt(ic_clk, 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), + dev->ss_lcnt = + i2c_dw_scl_lcnt(ic_clk, 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), + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", + dev->ss_hcnt, dev->ss_lcnt); + + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers are + * the same. + */ + if (t->bus_freq_hz == 1000000) { + /* + * Check are fast mode plus parameters available and use + * fast mode if not. + */ + if (dev->fp_hcnt && dev->fp_lcnt) { + dev->fs_hcnt = dev->fp_hcnt; + dev->fs_lcnt = dev->fp_lcnt; + fp_str = " Plus"; + } + } + /* + * Calculate SCL timing parameters for fast mode if not set. They are + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + dev->fs_hcnt = + i2c_dw_scl_hcnt(ic_clk, 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), + dev->fs_lcnt = + i2c_dw_scl_lcnt(ic_clk, 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); + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", + fp_str, dev->fs_hcnt, dev->fs_lcnt); + /* Check is high speed possible and fall back to fast mode if not */ if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) == DW_IC_CON_SPEED_HIGH) { if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK) @@ -136,37 +112,70 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev) dev_err(dev->dev, "High Speed not supported!\n"); dev->master_cfg &= ~DW_IC_CON_SPEED_MASK; dev->master_cfg |= DW_IC_CON_SPEED_FAST; + dev->hs_hcnt = 0; + dev->hs_lcnt = 0; } 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); + dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", + dev->hs_hcnt, dev->hs_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 if (dev->sda_hold_time) { - dev_warn(dev->dev, - "Hardware too old to adjust SDA hold time.\n"); + ret = i2c_dw_set_sda_hold(dev); + if (ret) + goto out; + + switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) { + case DW_IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case DW_IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; } + dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + +out: + return ret; +} + +/** + * 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. + */ +static int i2c_dw_init_master(struct dw_i2c_dev *dev) +{ + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + + /* Disable the adapter */ + __i2c_dw_disable(dev); + + /* Write standard speed timing parameters */ + dw_writel(dev, dev->ss_hcnt, DW_IC_SS_SCL_HCNT); + dw_writel(dev, dev->ss_lcnt, DW_IC_SS_SCL_LCNT); + + /* Write fast mode/fast mode plus timing parameters */ + dw_writel(dev, dev->fs_hcnt, DW_IC_FS_SCL_HCNT); + dw_writel(dev, dev->fs_lcnt, DW_IC_FS_SCL_LCNT); + + /* Write high speed timing parameters if supported */ + if (dev->hs_hcnt && dev->hs_lcnt) { + dw_writel(dev, dev->hs_hcnt, DW_IC_HS_SCL_HCNT); + dw_writel(dev, dev->hs_lcnt, DW_IC_HS_SCL_LCNT); + } + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD); i2c_dw_configure_fifo_master(dev); i2c_dw_release_lock(dev); @@ -253,13 +262,6 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) break; } - if (msgs[dev->msg_write_idx].len == 0) { - dev_err(dev->dev, - "%s: invalid message length\n", __func__); - dev->msg_err = -EINVAL; - break; - } - if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ buf = msgs[dev->msg_write_idx].buf; @@ -502,6 +504,10 @@ static const struct i2c_algorithm i2c_dw_algo = { .functionality = i2c_dw_func, }; +static const struct i2c_adapter_quirks i2c_dw_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) { u32 stat; @@ -681,6 +687,14 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) dev->disable = i2c_dw_disable; dev->disable_int = i2c_dw_disable_int; + ret = i2c_dw_set_reg_access(dev); + if (ret) + return ret; + + ret = i2c_dw_set_timings_master(dev); + if (ret) + return ret; + ret = dev->init(dev); if (ret) return ret; @@ -689,6 +703,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) "Synopsys DesignWare I2C adapter"); adap->retries = 3; adap->algo = &i2c_dw_algo; + adap->quirks = &i2c_dw_quirks; adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 86e1bd0b82e9..d50f80487214 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Synopsys DesignWare I2C adapter driver (master only). * @@ -7,22 +8,7 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. * Copyright (C) 2011, 2015, 2016 Intel Corporation. - * - * ---------------------------------------------------------------------------- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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/acpi.h> #include <linux/delay.h> #include <linux/err.h> @@ -105,6 +91,7 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) case 0x0817: c->bus_cfg &= ~DW_IC_CON_SPEED_MASK; c->bus_cfg |= DW_IC_CON_SPEED_STD; + /* fall through */ case 0x0818: case 0x0819: c->bus_num = pdev->device - 0x817 + 3; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 5660daf6c92e..1a8d2da5b000 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Synopsys DesignWare I2C adapter driver. * @@ -6,20 +7,6 @@ * 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/acpi.h> #include <linux/clk-provider.h> @@ -96,6 +83,7 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[], static int dw_i2c_acpi_configure(struct platform_device *pdev) { struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + struct i2c_timings *t = &dev->timings; u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; acpi_handle handle = ACPI_HANDLE(&pdev->dev); const struct acpi_device_id *id; @@ -115,7 +103,7 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev) dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht); dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht); - switch (dev->clk_freq) { + switch (t->bus_freq_hz) { case 100000: dev->sda_hold_time = ss_ht; break; @@ -175,6 +163,8 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev) static void i2c_dw_configure_master(struct dw_i2c_dev *dev) { + struct i2c_timings *t = &dev->timings; + dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | @@ -182,7 +172,7 @@ static void i2c_dw_configure_master(struct dw_i2c_dev *dev) dev->mode = DW_IC_MASTER; - switch (dev->clk_freq) { + switch (t->bus_freq_hz) { case 100000: dev->master_cfg |= DW_IC_CON_SPEED_STD; break; @@ -240,7 +230,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev); struct i2c_adapter *adap; struct dw_i2c_dev *dev; - u32 acpi_speed, ht = 0; + struct i2c_timings *t; + u32 acpi_speed; struct resource *mem; int i, irq, ret; static const int supported_speeds[] = { @@ -272,18 +263,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) reset_control_deassert(dev->rst); } - if (pdata) { - dev->clk_freq = pdata->i2c_scl_freq; - } else { - device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns", - &ht); - device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns", - &dev->sda_falling_time); - device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns", - &dev->scl_falling_time); - device_property_read_u32(&pdev->dev, "clock-frequency", - &dev->clk_freq); - } + t = &dev->timings; + if (pdata) + t->bus_freq_hz = pdata->i2c_scl_freq; + else + i2c_parse_fw_timings(&pdev->dev, t, false); acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); /* @@ -300,12 +284,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) * Find bus speed from the "clock-frequency" device property, ACPI * or by using fast mode if neither is set. */ - if (acpi_speed && dev->clk_freq) - dev->clk_freq = min(dev->clk_freq, acpi_speed); - else if (acpi_speed || dev->clk_freq) - dev->clk_freq = max(dev->clk_freq, acpi_speed); + if (acpi_speed && t->bus_freq_hz) + t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); + else if (acpi_speed || t->bus_freq_hz) + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); else - dev->clk_freq = 400000; + t->bus_freq_hz = 400000; if (has_acpi_companion(&pdev->dev)) dw_i2c_acpi_configure(pdev); @@ -314,11 +298,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) * Only standard mode at 100kHz, fast mode at 400kHz, * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. */ - if (dev->clk_freq != 100000 && dev->clk_freq != 400000 - && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) { + if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 && + t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) { dev_err(&pdev->dev, "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n", - dev->clk_freq); + t->bus_freq_hz); ret = -EINVAL; goto exit_reset; } @@ -334,12 +318,14 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) dev->clk = devm_clk_get(&pdev->dev, NULL); if (!i2c_dw_prepare_clk(dev, true)) { + u64 clk_khz; + dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); - if (!dev->sda_hold_time && ht) - dev->sda_hold_time = div_u64( - (u64)dev->get_clk_rate_khz(dev) * ht + 500000, - 1000000); + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = + div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000); } dw_i2c_set_fifo_size(dev, pdev->id); diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index 8ce2cd368477..e7f9305b2dd9 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * 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> @@ -51,53 +38,18 @@ static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev) */ static int i2c_dw_init_slave(struct dw_i2c_dev *dev) { - u32 reg, comp_param1; 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_disable(dev); - /* 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; + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) 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); @@ -299,6 +251,14 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev) dev->disable = i2c_dw_disable; dev->disable_int = i2c_dw_disable_int; + ret = i2c_dw_set_reg_access(dev); + if (ret) + return ret; + + ret = i2c_dw_set_sda_hold(dev); + if (ret) + return ret; + ret = dev->init(dev); if (ret) return ret; diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index de82ad8ff534..c1ce2299a76e 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -176,7 +176,10 @@ #define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100)) -#define HSI2C_EXYNOS7 BIT(0) +enum i2c_type_exynos { + I2C_TYPE_EXYNOS5, + I2C_TYPE_EXYNOS7, +}; struct exynos5_i2c { struct i2c_adapter adap; @@ -212,27 +215,30 @@ struct exynos5_i2c { /** * struct exynos_hsi2c_variant - platform specific HSI2C driver data * @fifo_depth: the fifo depth supported by the HSI2C module + * @hw: the hardware variant of Exynos I2C controller * * Specifies platform specific configuration of HSI2C module. * Note: A structure for driver specific platform data is used for future * expansion of its usage. */ struct exynos_hsi2c_variant { - unsigned int fifo_depth; - unsigned int hw; + unsigned int fifo_depth; + enum i2c_type_exynos hw; }; static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = { .fifo_depth = 64, + .hw = I2C_TYPE_EXYNOS5, }; static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = { .fifo_depth = 16, + .hw = I2C_TYPE_EXYNOS5, }; static const struct exynos_hsi2c_variant exynos7_hsi2c_data = { .fifo_depth = 16, - .hw = HSI2C_EXYNOS7, + .hw = I2C_TYPE_EXYNOS7, }; static const struct of_device_id exynos5_i2c_match[] = { @@ -300,7 +306,7 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings) */ t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7; temp = clkin / op_clk - 8 - t_ftl_cycle; - if (i2c->variant->hw != HSI2C_EXYNOS7) + if (i2c->variant->hw != I2C_TYPE_EXYNOS7) temp -= t_ftl_cycle; div = temp / 512; clk_cycle = temp / (div + 1) - 2; @@ -424,7 +430,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) writel(int_status, i2c->regs + HSI2C_INT_STATUS); /* handle interrupt related to the transfer status */ - if (i2c->variant->hw == HSI2C_EXYNOS7) { + if (i2c->variant->hw == I2C_TYPE_EXYNOS7) { if (int_status & HSI2C_INT_TRANS_DONE) { i2c->trans_done = 1; i2c->state = 0; @@ -571,7 +577,7 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c) { unsigned long timeout; - if (i2c->variant->hw != HSI2C_EXYNOS7) + if (i2c->variant->hw != I2C_TYPE_EXYNOS7) return; /* @@ -612,7 +618,7 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop) unsigned long flags; unsigned short trig_lvl; - if (i2c->variant->hw == HSI2C_EXYNOS7) + if (i2c->variant->hw == I2C_TYPE_EXYNOS7) int_en |= HSI2C_INT_I2C_TRANS; else int_en |= HSI2C_INT_I2C; diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c new file mode 100644 index 000000000000..1e2be2219a60 --- /dev/null +++ b/drivers/i2c/busses/i2c-fsi.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * FSI-attached I2C master algorithm + * + * Copyright 2018 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/fsi.h> +#include <linux/i2c.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/slab.h> + +#define FSI_ENGID_I2C 0x7 + +#define I2C_DEFAULT_CLK_DIV 6 + +/* i2c registers */ +#define I2C_FSI_FIFO 0x00 +#define I2C_FSI_CMD 0x04 +#define I2C_FSI_MODE 0x08 +#define I2C_FSI_WATER_MARK 0x0C +#define I2C_FSI_INT_MASK 0x10 +#define I2C_FSI_INT_COND 0x14 +#define I2C_FSI_OR_INT_MASK 0x14 +#define I2C_FSI_INTS 0x18 +#define I2C_FSI_AND_INT_MASK 0x18 +#define I2C_FSI_STAT 0x1C +#define I2C_FSI_RESET_I2C 0x1C +#define I2C_FSI_ESTAT 0x20 +#define I2C_FSI_RESET_ERR 0x20 +#define I2C_FSI_RESID_LEN 0x24 +#define I2C_FSI_SET_SCL 0x24 +#define I2C_FSI_PORT_BUSY 0x28 +#define I2C_FSI_RESET_SCL 0x2C +#define I2C_FSI_SET_SDA 0x30 +#define I2C_FSI_RESET_SDA 0x34 + +/* cmd register */ +#define I2C_CMD_WITH_START BIT(31) +#define I2C_CMD_WITH_ADDR BIT(30) +#define I2C_CMD_RD_CONT BIT(29) +#define I2C_CMD_WITH_STOP BIT(28) +#define I2C_CMD_FORCELAUNCH BIT(27) +#define I2C_CMD_ADDR GENMASK(23, 17) +#define I2C_CMD_READ BIT(16) +#define I2C_CMD_LEN GENMASK(15, 0) + +/* mode register */ +#define I2C_MODE_CLKDIV GENMASK(31, 16) +#define I2C_MODE_PORT GENMASK(15, 10) +#define I2C_MODE_ENHANCED BIT(3) +#define I2C_MODE_DIAG BIT(2) +#define I2C_MODE_PACE_ALLOW BIT(1) +#define I2C_MODE_WRAP BIT(0) + +/* watermark register */ +#define I2C_WATERMARK_HI GENMASK(15, 12) +#define I2C_WATERMARK_LO GENMASK(7, 4) + +#define I2C_FIFO_HI_LVL 4 +#define I2C_FIFO_LO_LVL 4 + +/* interrupt register */ +#define I2C_INT_INV_CMD BIT(15) +#define I2C_INT_PARITY BIT(14) +#define I2C_INT_BE_OVERRUN BIT(13) +#define I2C_INT_BE_ACCESS BIT(12) +#define I2C_INT_LOST_ARB BIT(11) +#define I2C_INT_NACK BIT(10) +#define I2C_INT_DAT_REQ BIT(9) +#define I2C_INT_CMD_COMP BIT(8) +#define I2C_INT_STOP_ERR BIT(7) +#define I2C_INT_BUSY BIT(6) +#define I2C_INT_IDLE BIT(5) + +/* status register */ +#define I2C_STAT_INV_CMD BIT(31) +#define I2C_STAT_PARITY BIT(30) +#define I2C_STAT_BE_OVERRUN BIT(29) +#define I2C_STAT_BE_ACCESS BIT(28) +#define I2C_STAT_LOST_ARB BIT(27) +#define I2C_STAT_NACK BIT(26) +#define I2C_STAT_DAT_REQ BIT(25) +#define I2C_STAT_CMD_COMP BIT(24) +#define I2C_STAT_STOP_ERR BIT(23) +#define I2C_STAT_MAX_PORT GENMASK(19, 16) +#define I2C_STAT_ANY_INT BIT(15) +#define I2C_STAT_SCL_IN BIT(11) +#define I2C_STAT_SDA_IN BIT(10) +#define I2C_STAT_PORT_BUSY BIT(9) +#define I2C_STAT_SELF_BUSY BIT(8) +#define I2C_STAT_FIFO_COUNT GENMASK(7, 0) + +#define I2C_STAT_ERR (I2C_STAT_INV_CMD | \ + I2C_STAT_PARITY | \ + I2C_STAT_BE_OVERRUN | \ + I2C_STAT_BE_ACCESS | \ + I2C_STAT_LOST_ARB | \ + I2C_STAT_NACK | \ + I2C_STAT_STOP_ERR) +#define I2C_STAT_ANY_RESP (I2C_STAT_ERR | \ + I2C_STAT_DAT_REQ | \ + I2C_STAT_CMD_COMP) + +/* extended status register */ +#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24) +#define I2C_ESTAT_SCL_IN_SY BIT(15) +#define I2C_ESTAT_SDA_IN_SY BIT(14) +#define I2C_ESTAT_S_SCL BIT(13) +#define I2C_ESTAT_S_SDA BIT(12) +#define I2C_ESTAT_M_SCL BIT(11) +#define I2C_ESTAT_M_SDA BIT(10) +#define I2C_ESTAT_HI_WATER BIT(9) +#define I2C_ESTAT_LO_WATER BIT(8) +#define I2C_ESTAT_PORT_BUSY BIT(7) +#define I2C_ESTAT_SELF_BUSY BIT(6) +#define I2C_ESTAT_VERSION GENMASK(4, 0) + +/* port busy register */ +#define I2C_PORT_BUSY_RESET BIT(31) + +/* wait for command complete or data request */ +#define I2C_CMD_SLEEP_MAX_US 500 +#define I2C_CMD_SLEEP_MIN_US 50 + +/* wait after reset; choose time from legacy driver */ +#define I2C_RESET_SLEEP_MAX_US 2000 +#define I2C_RESET_SLEEP_MIN_US 1000 + +/* choose timeout length from legacy driver; it's well tested */ +#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100) + +struct fsi_i2c_master { + struct fsi_device *fsi; + u8 fifo_size; + struct list_head ports; + struct mutex lock; +}; + +struct fsi_i2c_port { + struct list_head list; + struct i2c_adapter adapter; + struct fsi_i2c_master *master; + u16 port; + u16 xfrd; +}; + +static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg, + u32 *data) +{ + int rc; + __be32 data_be; + + rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be)); + if (rc) + return rc; + + *data = be32_to_cpu(data_be); + + return 0; +} + +static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg, + u32 *data) +{ + __be32 data_be = cpu_to_be32p(data); + + return fsi_device_write(fsi, reg, &data_be, sizeof(data_be)); +} + +static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c) +{ + int rc; + u32 mode = I2C_MODE_ENHANCED, extended_status, watermark; + u32 interrupt = 0; + + /* since we use polling, disable interrupts */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt); + if (rc) + return rc; + + mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV); + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status); + if (rc) + return rc; + + i2c->fifo_size = FIELD_GET(I2C_ESTAT_FIFO_SZ, extended_status); + watermark = FIELD_PREP(I2C_WATERMARK_HI, + i2c->fifo_size - I2C_FIFO_HI_LVL); + watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL); + + return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark); +} + +static int fsi_i2c_set_port(struct fsi_i2c_port *port) +{ + int rc; + struct fsi_device *fsi = port->master->fsi; + u32 mode, dummy = 0; + + rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + if (FIELD_GET(I2C_MODE_PORT, mode) == port->port) + return 0; + + mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port); + rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + /* reset engine when port is changed */ + return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy); +} + +static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg, + bool stop) +{ + struct fsi_i2c_master *i2c = port->master; + u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR; + + port->xfrd = 0; + + if (msg->flags & I2C_M_RD) + cmd |= I2C_CMD_READ; + + if (stop || msg->flags & I2C_M_STOP) + cmd |= I2C_CMD_WITH_STOP; + + cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr); + cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len); + + return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd); +} + +static int fsi_i2c_get_op_bytes(int op_bytes) +{ + /* fsi is limited to max 4 byte aligned ops */ + if (op_bytes > 4) + return 4; + else if (op_bytes == 3) + return 2; + return op_bytes; +} + +static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg, + u8 fifo_count) +{ + int write; + int rc; + struct fsi_i2c_master *i2c = port->master; + int bytes_to_write = i2c->fifo_size - fifo_count; + int bytes_remaining = msg->len - port->xfrd; + + bytes_to_write = min(bytes_to_write, bytes_remaining); + + while (bytes_to_write) { + write = fsi_i2c_get_op_bytes(bytes_to_write); + + rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO, + &msg->buf[port->xfrd], write); + if (rc) + return rc; + + port->xfrd += write; + bytes_to_write -= write; + } + + return 0; +} + +static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg, + u8 fifo_count) +{ + int read; + int rc; + struct fsi_i2c_master *i2c = port->master; + int bytes_to_read; + int xfr_remaining = msg->len - port->xfrd; + u32 dummy; + + bytes_to_read = min_t(int, fifo_count, xfr_remaining); + + while (bytes_to_read) { + read = fsi_i2c_get_op_bytes(bytes_to_read); + + if (xfr_remaining) { + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, + &msg->buf[port->xfrd], read); + if (rc) + return rc; + + port->xfrd += read; + xfr_remaining -= read; + } else { + /* no more buffer but data in fifo, need to clear it */ + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy, + read); + if (rc) + return rc; + } + + bytes_to_read -= read; + } + + return 0; +} + +static int fsi_i2c_get_scl(struct i2c_adapter *adap) +{ + u32 stat = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + + return !!(stat & I2C_STAT_SCL_IN); +} + +static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + u32 dummy = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + if (val) + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy); + else + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy); +} + +static int fsi_i2c_get_sda(struct i2c_adapter *adap) +{ + u32 stat = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + + return !!(stat & I2C_STAT_SDA_IN); +} + +static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val) +{ + u32 dummy = 0; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + if (val) + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy); + else + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy); +} + +static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + int rc; + u32 mode; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return; + + mode |= I2C_MODE_DIAG; + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); +} + +static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + int rc; + u32 mode; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *i2c = port->master; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return; + + mode &= ~I2C_MODE_DIAG; + fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); +} + +static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c, + struct fsi_i2c_port *port) +{ + int rc; + u32 stat, dummy = 0; + + /* force bus reset, ignore errors */ + i2c_recover_bus(&port->adapter); + + /* reset errors */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy); + if (rc) + return rc; + + /* wait for command complete */ + usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US); + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat); + if (rc) + return rc; + + if (stat & I2C_STAT_CMD_COMP) + return 0; + + /* failed to get command complete; reset engine again */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy); + if (rc) + return rc; + + /* re-init engine again */ + return fsi_i2c_dev_init(i2c); +} + +static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port) +{ + int rc; + u32 mode, dummy = 0; + + /* reset engine */ + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy); + if (rc) + return rc; + + /* re-init engine */ + rc = fsi_i2c_dev_init(i2c); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + + /* set port; default after reset is 0 */ + if (port) { + mode &= ~I2C_MODE_PORT; + mode |= FIELD_PREP(I2C_MODE_PORT, port); + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode); + if (rc) + return rc; + } + + /* reset busy register; hw workaround */ + dummy = I2C_PORT_BUSY_RESET; + rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy); + if (rc) + return rc; + + return 0; +} + +static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status) +{ + int rc; + unsigned long start; + u32 cmd = I2C_CMD_WITH_STOP; + u32 stat; + struct fsi_i2c_master *i2c = port->master; + struct fsi_device *fsi = i2c->fsi; + + rc = fsi_i2c_reset_engine(i2c, port->port); + if (rc) + return rc; + + rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat); + if (rc) + return rc; + + /* if sda is low, peform full bus reset */ + if (!(stat & I2C_STAT_SDA_IN)) { + rc = fsi_i2c_reset_bus(i2c, port); + if (rc) + return rc; + } + + /* skip final stop command for these errors */ + if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR)) + return 0; + + /* write stop command */ + rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd); + if (rc) + return rc; + + /* wait until we see command complete in the master */ + start = jiffies; + + do { + rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status); + if (rc) + return rc; + + if (status & I2C_STAT_CMD_COMP) + return 0; + + usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US); + } while (time_after(start + I2C_ABORT_TIMEOUT, jiffies)); + + return -ETIMEDOUT; +} + +static int fsi_i2c_handle_status(struct fsi_i2c_port *port, + struct i2c_msg *msg, u32 status) +{ + int rc; + u8 fifo_count; + + if (status & I2C_STAT_ERR) { + rc = fsi_i2c_abort(port, status); + if (rc) + return rc; + + if (status & I2C_STAT_INV_CMD) + return -EINVAL; + + if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN | + I2C_STAT_BE_ACCESS)) + return -EPROTO; + + if (status & I2C_STAT_NACK) + return -ENXIO; + + if (status & I2C_STAT_LOST_ARB) + return -EAGAIN; + + if (status & I2C_STAT_STOP_ERR) + return -EBADMSG; + + return -EIO; + } + + if (status & I2C_STAT_DAT_REQ) { + fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status); + + if (msg->flags & I2C_M_RD) + return fsi_i2c_read_fifo(port, msg, fifo_count); + + return fsi_i2c_write_fifo(port, msg, fifo_count); + } + + if (status & I2C_STAT_CMD_COMP) { + if (port->xfrd < msg->len) + return -ENODATA; + + return msg->len; + } + + return 0; +} + +static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg, + unsigned long timeout) +{ + u32 status = 0; + int rc; + unsigned long start = jiffies; + + do { + rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, + &status); + if (rc) + return rc; + + if (status & I2C_STAT_ANY_RESP) { + rc = fsi_i2c_handle_status(port, msg, status); + if (rc < 0) + return rc; + + /* cmd complete and all data xfrd */ + if (rc == msg->len) + return 0; + + /* need to xfr more data, but maybe don't need wait */ + continue; + } + + usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US); + } while (time_after(start + timeout, jiffies)); + + return -ETIMEDOUT; +} + +static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int i, rc; + unsigned long start_time; + struct fsi_i2c_port *port = adap->algo_data; + struct fsi_i2c_master *master = port->master; + struct i2c_msg *msg; + + mutex_lock(&master->lock); + + rc = fsi_i2c_set_port(port); + if (rc) + goto unlock; + + for (i = 0; i < num; i++) { + msg = msgs + i; + start_time = jiffies; + + rc = fsi_i2c_start(port, msg, i == num - 1); + if (rc) + goto unlock; + + rc = fsi_i2c_wait(port, msg, + adap->timeout - (jiffies - start_time)); + if (rc) + goto unlock; + } + +unlock: + mutex_unlock(&master->lock); + return rc ? : num; +} + +static u32 fsi_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | + I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = fsi_i2c_get_scl, + .set_scl = fsi_i2c_set_scl, + .get_sda = fsi_i2c_get_sda, + .set_sda = fsi_i2c_set_sda, + .prepare_recovery = fsi_i2c_prepare_recovery, + .unprepare_recovery = fsi_i2c_unprepare_recovery, +}; + +static const struct i2c_algorithm fsi_i2c_algorithm = { + .master_xfer = fsi_i2c_xfer, + .functionality = fsi_i2c_functionality, +}; + +static int fsi_i2c_probe(struct device *dev) +{ + struct fsi_i2c_master *i2c; + struct fsi_i2c_port *port; + struct device_node *np; + int rc; + u32 port_no; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + mutex_init(&i2c->lock); + i2c->fsi = to_fsi_dev(dev); + INIT_LIST_HEAD(&i2c->ports); + + rc = fsi_i2c_dev_init(i2c); + if (rc) + return rc; + + /* Add adapter for each i2c port of the master. */ + for_each_available_child_of_node(dev->of_node, np) { + rc = of_property_read_u32(np, "reg", &port_no); + if (rc || port_no > USHRT_MAX) + continue; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + break; + + port->master = i2c; + port->port = port_no; + + port->adapter.owner = THIS_MODULE; + port->adapter.dev.of_node = np; + port->adapter.dev.parent = dev; + port->adapter.algo = &fsi_i2c_algorithm; + port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info; + port->adapter.algo_data = port; + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "i2c_bus-%u", port_no); + + rc = i2c_add_adapter(&port->adapter); + if (rc < 0) { + dev_err(dev, "Failed to register adapter: %d\n", rc); + kfree(port); + continue; + } + + list_add(&port->list, &i2c->ports); + } + + dev_set_drvdata(dev, i2c); + + return 0; +} + +static int fsi_i2c_remove(struct device *dev) +{ + struct fsi_i2c_master *i2c = dev_get_drvdata(dev); + struct fsi_i2c_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &i2c->ports, list) { + list_del(&port->list); + i2c_del_adapter(&port->adapter); + kfree(port); + } + + return 0; +} + +static const struct fsi_device_id fsi_i2c_ids[] = { + { FSI_ENGID_I2C, FSI_VERSION_ANY }, + { } +}; + +static struct fsi_driver fsi_i2c_driver = { + .id_table = fsi_i2c_ids, + .drv = { + .name = "i2c-fsi", + .bus = &fsi_bus_type, + .probe = fsi_i2c_probe, + .remove = fsi_i2c_remove, + }, +}; + +module_fsi_driver(fsi_i2c_driver); + +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>"); +MODULE_DESCRIPTION("FSI attached I2C master"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 66f85bbf3591..c008d209f0b8 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -78,49 +78,43 @@ static struct dentry *i2c_gpio_debug_dir; #define getscl(bd) ((bd)->getscl((bd)->data)) #define WIRE_ATTRIBUTE(wire) \ -static int fops_##wire##_get(void *data, u64 *val) \ -{ \ - struct i2c_gpio_private_data *priv = data; \ - \ - i2c_lock_adapter(&priv->adap); \ - *val = get##wire(&priv->bit_data); \ - i2c_unlock_adapter(&priv->adap); \ - return 0; \ -} \ -static int fops_##wire##_set(void *data, u64 val) \ -{ \ - struct i2c_gpio_private_data *priv = data; \ - \ - i2c_lock_adapter(&priv->adap); \ - set##wire(&priv->bit_data, val); \ - i2c_unlock_adapter(&priv->adap); \ - return 0; \ -} \ +static int fops_##wire##_get(void *data, u64 *val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + *val = get##wire(&priv->bit_data); \ + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + return 0; \ +} \ +static int fops_##wire##_set(void *data, u64 val) \ +{ \ + struct i2c_gpio_private_data *priv = data; \ + \ + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + set##wire(&priv->bit_data, val); \ + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \ + return 0; \ +} \ DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n") WIRE_ATTRIBUTE(scl); WIRE_ATTRIBUTE(sda); -static int fops_incomplete_transfer_set(void *data, u64 addr) +static void i2c_gpio_incomplete_transfer(struct i2c_gpio_private_data *priv, + u32 pattern, u8 pattern_size) { - struct i2c_gpio_private_data *priv = data; struct i2c_algo_bit_data *bit_data = &priv->bit_data; - int i, pattern; + int i; - if (addr > 0x7f) - return -EINVAL; - - /* ADDR (7 bit) + RD (1 bit) + SDA hi (1 bit) */ - pattern = (addr << 2) | 3; - - i2c_lock_adapter(&priv->adap); + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); /* START condition */ setsda(bit_data, 0); udelay(bit_data->udelay); - /* Send ADDR+RD, request ACK, don't send STOP */ - for (i = 8; i >= 0; i--) { + /* Send pattern, request ACK, don't send STOP */ + for (i = pattern_size - 1; i >= 0; i--) { setscl(bit_data, 0); udelay(bit_data->udelay / 2); setsda(bit_data, (pattern >> i) & 1); @@ -129,11 +123,44 @@ static int fops_incomplete_transfer_set(void *data, u64 addr) udelay(bit_data->udelay); } - i2c_unlock_adapter(&priv->adap); + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); +} + +static int fops_incomplete_addr_phase_set(void *data, u64 addr) +{ + struct i2c_gpio_private_data *priv = data; + u32 pattern; + + if (addr > 0x7f) + return -EINVAL; + + /* ADDR (7 bit) + RD (1 bit) + Client ACK, keep SDA hi (1 bit) */ + pattern = (addr << 2) | 3; + + i2c_gpio_incomplete_transfer(priv, pattern, 9); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_addr_phase, NULL, fops_incomplete_addr_phase_set, "%llu\n"); + +static int fops_incomplete_write_byte_set(void *data, u64 addr) +{ + struct i2c_gpio_private_data *priv = data; + u32 pattern; + + if (addr > 0x7f) + return -EINVAL; + + /* ADDR (7 bit) + WR (1 bit) + Client ACK (1 bit) */ + pattern = (addr << 2) | 1; + /* 0x00 (8 bit) + Client ACK, keep SDA hi (1 bit) */ + pattern = (pattern << 9) | 1; + + i2c_gpio_incomplete_transfer(priv, pattern, 18); return 0; } -DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_transfer, NULL, fops_incomplete_transfer_set, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n"); static void i2c_gpio_fault_injector_init(struct platform_device *pdev) { @@ -156,8 +183,10 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); - debugfs_create_file_unsafe("incomplete_transfer", 0200, priv->debug_dir, - priv, &fops_incomplete_transfer); + debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, + priv, &fops_incomplete_addr_phase); + debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, + priv, &fops_incomplete_write_byte); } static void i2c_gpio_fault_injector_exit(struct platform_device *pdev) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index aa726607645e..941c223f6491 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -70,6 +70,7 @@ * 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 + * Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -220,6 +221,7 @@ #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0 #define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4 +#define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -1034,6 +1036,7 @@ static const struct pci_device_id i801_ids[] = { { 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) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) }, { 0, } }; @@ -1518,6 +1521,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_CDF_SMBUS: case PCI_DEVICE_ID_INTEL_DNV_SMBUS: case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS: + case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS: priv->features |= FEATURE_I2C_BLOCK_READ; priv->features |= FEATURE_IRQ; priv->features |= FEATURE_SMBUS_PEC; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 498c5e891649..c406700789e1 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -421,10 +421,14 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) return -EAGAIN; } - if (for_busy && (temp & I2SR_IBB)) + if (for_busy && (temp & I2SR_IBB)) { + i2c_imx->stopped = 0; break; - if (!for_busy && !(temp & I2SR_IBB)) + } + if (!for_busy && !(temp & I2SR_IBB)) { + i2c_imx->stopped = 1; break; + } if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C bus is busy\n", __func__); @@ -538,7 +542,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) result = i2c_imx_bus_busy(i2c_imx, 1); if (result) return result; - i2c_imx->stopped = 0; temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; temp &= ~I2CR_DMAEN; @@ -567,10 +570,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) udelay(i2c_imx->disable_delay); } - if (!i2c_imx->stopped) { + if (!i2c_imx->stopped) i2c_imx_bus_busy(i2c_imx, 0); - i2c_imx->stopped = 1; - } /* Disable I2C controller */ temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, @@ -668,9 +669,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, struct imx_i2c_dma *dma = i2c_imx->dma; struct device *dev = &i2c_imx->adapter.dev; - temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); - temp |= I2CR_DMAEN; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); dma->chan_using = dma->chan_rx; dma->dma_transfer_dir = DMA_DEV_TO_MEM; @@ -727,7 +725,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); i2c_imx_bus_busy(i2c_imx, 0); - i2c_imx->stopped = 1; } else { /* * For i2c master receiver repeat restart operation like: @@ -783,6 +780,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo int i, result; unsigned int temp; int block_data = msgs->flags & I2C_M_RECV_LEN; + int use_dma = i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data; dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n", @@ -809,12 +807,14 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo */ if ((msgs->len - 1) || block_data) temp &= ~I2CR_TXAK; + if (use_dma) + temp |= I2CR_DMAEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */ dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); - if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) + if (use_dma) return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); /* read data */ @@ -850,7 +850,6 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); i2c_imx_bus_busy(i2c_imx, 0); - i2c_imx->stopped = 1; } else { /* * For i2c master receiver repeat restart operation like: diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 642c58946d8d..7d79317a1046 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -567,9 +567,6 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", msg->addr, msg->len, msg->flags, stop); - if (msg->len == 0) - return -EINVAL; - /* * The MX28 I2C IP block can only do PIO READ for transfer of to up * 4 bytes of length. The write transfer is not limited as it can use @@ -683,6 +680,10 @@ static const struct i2c_algorithm mxs_i2c_algo = { .functionality = mxs_i2c_func, }; +static const struct i2c_adapter_quirks mxs_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed) { /* The I2C block clock runs at 24MHz */ @@ -854,6 +855,7 @@ static int mxs_i2c_probe(struct platform_device *pdev) strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); adap->owner = THIS_MODULE; adap->algo = &mxs_i2c_algo; + adap->quirks = &mxs_i2c_quirks; adap->dev.parent = dev; adap->nr = pdev->id; adap->dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c new file mode 100644 index 000000000000..96b4572e6d9c --- /dev/null +++ b/drivers/i2c/busses/i2c-owl.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Actions Semiconductor Owl SoC's I2C driver + * + * Copyright (c) 2014 Actions Semi Inc. + * Author: David Liu <liuwei@actions-semi.com> + * + * Copyright (c) 2018 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> + +/* I2C registers */ +#define OWL_I2C_REG_CTL 0x0000 +#define OWL_I2C_REG_CLKDIV 0x0004 +#define OWL_I2C_REG_STAT 0x0008 +#define OWL_I2C_REG_ADDR 0x000C +#define OWL_I2C_REG_TXDAT 0x0010 +#define OWL_I2C_REG_RXDAT 0x0014 +#define OWL_I2C_REG_CMD 0x0018 +#define OWL_I2C_REG_FIFOCTL 0x001C +#define OWL_I2C_REG_FIFOSTAT 0x0020 +#define OWL_I2C_REG_DATCNT 0x0024 +#define OWL_I2C_REG_RCNT 0x0028 + +/* I2Cx_CTL Bit Mask */ +#define OWL_I2C_CTL_RB BIT(1) +#define OWL_I2C_CTL_GBCC(x) (((x) & 0x3) << 2) +#define OWL_I2C_CTL_GBCC_NONE OWL_I2C_CTL_GBCC(0) +#define OWL_I2C_CTL_GBCC_START OWL_I2C_CTL_GBCC(1) +#define OWL_I2C_CTL_GBCC_STOP OWL_I2C_CTL_GBCC(2) +#define OWL_I2C_CTL_GBCC_RSTART OWL_I2C_CTL_GBCC(3) +#define OWL_I2C_CTL_IRQE BIT(5) +#define OWL_I2C_CTL_EN BIT(7) +#define OWL_I2C_CTL_AE BIT(8) +#define OWL_I2C_CTL_SHSM BIT(10) + +#define OWL_I2C_DIV_FACTOR(x) ((x) & 0xff) + +/* I2Cx_STAT Bit Mask */ +#define OWL_I2C_STAT_RACK BIT(0) +#define OWL_I2C_STAT_BEB BIT(1) +#define OWL_I2C_STAT_IRQP BIT(2) +#define OWL_I2C_STAT_LAB BIT(3) +#define OWL_I2C_STAT_STPD BIT(4) +#define OWL_I2C_STAT_STAD BIT(5) +#define OWL_I2C_STAT_BBB BIT(6) +#define OWL_I2C_STAT_TCB BIT(7) +#define OWL_I2C_STAT_LBST BIT(8) +#define OWL_I2C_STAT_SAMB BIT(9) +#define OWL_I2C_STAT_SRGC BIT(10) + +/* I2Cx_CMD Bit Mask */ +#define OWL_I2C_CMD_SBE BIT(0) +#define OWL_I2C_CMD_RBE BIT(4) +#define OWL_I2C_CMD_DE BIT(8) +#define OWL_I2C_CMD_NS BIT(9) +#define OWL_I2C_CMD_SE BIT(10) +#define OWL_I2C_CMD_MSS BIT(11) +#define OWL_I2C_CMD_WRS BIT(12) +#define OWL_I2C_CMD_SECL BIT(15) + +#define OWL_I2C_CMD_AS(x) (((x) & 0x7) << 1) +#define OWL_I2C_CMD_SAS(x) (((x) & 0x7) << 5) + +/* I2Cx_FIFOCTL Bit Mask */ +#define OWL_I2C_FIFOCTL_NIB BIT(0) +#define OWL_I2C_FIFOCTL_RFR BIT(1) +#define OWL_I2C_FIFOCTL_TFR BIT(2) + +/* I2Cc_FIFOSTAT Bit Mask */ +#define OWL_I2C_FIFOSTAT_RNB BIT(1) +#define OWL_I2C_FIFOSTAT_RFE BIT(2) +#define OWL_I2C_FIFOSTAT_TFF BIT(5) +#define OWL_I2C_FIFOSTAT_TFD GENMASK(23, 16) +#define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8) + +/* I2C bus timeout */ +#define OWL_I2C_TIMEOUT msecs_to_jiffies(4 * 1000) + +#define OWL_I2C_MAX_RETRIES 50 + +#define OWL_I2C_DEF_SPEED_HZ 100000 +#define OWL_I2C_MAX_SPEED_HZ 400000 + +struct owl_i2c_dev { + struct i2c_adapter adap; + struct i2c_msg *msg; + struct completion msg_complete; + struct clk *clk; + spinlock_t lock; + void __iomem *base; + unsigned long clk_rate; + u32 bus_freq; + u32 msg_ptr; + int err; +}; + +static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state) +{ + unsigned int regval; + + regval = readl(reg); + + if (state) + regval |= val; + else + regval &= ~val; + + writel(regval, reg); +} + +static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev) +{ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, false); + mdelay(1); + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, true); + + /* Clear status registers */ + writel(0, i2c_dev->base + OWL_I2C_REG_STAT); +} + +static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev) +{ + unsigned int val, timeout = 0; + + /* Reset FIFO */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR, + true); + + /* Wait 50ms for FIFO reset complete */ + do { + val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL); + if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR))) + break; + usleep_range(500, 1000); + } while (timeout++ < OWL_I2C_MAX_RETRIES); + + if (timeout > OWL_I2C_MAX_RETRIES) { + dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev) +{ + unsigned int val; + + val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16); + + /* Set clock divider factor */ + writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV); +} + +static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) +{ + struct owl_i2c_dev *i2c_dev = _dev; + struct i2c_msg *msg = i2c_dev->msg; + unsigned long flags; + unsigned int stat, fifostat; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + i2c_dev->err = 0; + + /* Handle NACK from slave */ + fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT); + if (fifostat & OWL_I2C_FIFOSTAT_RNB) { + i2c_dev->err = -ENXIO; + goto stop; + } + + /* Handle bus error */ + stat = readl(i2c_dev->base + OWL_I2C_REG_STAT); + if (stat & OWL_I2C_STAT_BEB) { + i2c_dev->err = -EIO; + goto stop; + } + + /* Handle FIFO read */ + if (msg->flags & I2C_M_RD) { + while ((readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_RFE) && i2c_dev->msg_ptr < msg->len) { + msg->buf[i2c_dev->msg_ptr++] = readl(i2c_dev->base + + OWL_I2C_REG_RXDAT); + } + } else { + /* Handle the remaining bytes which were not sent */ + while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) { + writel(msg->buf[i2c_dev->msg_ptr++], + i2c_dev->base + OWL_I2C_REG_TXDAT); + } + } + +stop: + /* Clear pending interrupts */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT, + OWL_I2C_STAT_IRQP, true); + + complete_all(&i2c_dev->msg_complete); + spin_unlock_irqrestore(&i2c_dev->lock, flags); + + return IRQ_HANDLED; +} + +static u32 owl_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int owl_i2c_check_bus_busy(struct i2c_adapter *adap) +{ + struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + unsigned long timeout; + + /* Check for Bus busy */ + timeout = jiffies + OWL_I2C_TIMEOUT; + while (readl(i2c_dev->base + OWL_I2C_REG_STAT) & OWL_I2C_STAT_BBB) { + if (time_after(jiffies, timeout)) { + dev_err(&adap->dev, "Bus busy timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + struct i2c_msg *msg; + unsigned long time_left, flags; + unsigned int i2c_cmd, val; + unsigned int addr; + int ret, idx; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + /* Reset I2C controller */ + owl_i2c_reset(i2c_dev); + + /* Set bus frequency */ + owl_i2c_set_freq(i2c_dev); + + /* + * Spinlock should be released before calling reset FIFO and + * bus busy check since those functions may sleep + */ + spin_unlock_irqrestore(&i2c_dev->lock, flags); + + /* Reset FIFO */ + ret = owl_i2c_reset_fifo(i2c_dev); + if (ret) + goto unlocked_err_exit; + + /* Check for bus busy */ + ret = owl_i2c_check_bus_busy(adap); + if (ret) + goto unlocked_err_exit; + + spin_lock_irqsave(&i2c_dev->lock, flags); + + /* Check for Arbitration lost */ + val = readl(i2c_dev->base + OWL_I2C_REG_STAT); + if (val & OWL_I2C_STAT_LAB) { + val &= ~OWL_I2C_STAT_LAB; + writel(val, i2c_dev->base + OWL_I2C_REG_STAT); + ret = -EAGAIN; + goto err_exit; + } + + reinit_completion(&i2c_dev->msg_complete); + + /* Enable I2C controller interrupt */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_IRQE, true); + + /* + * Select: FIFO enable, Master mode, Stop enable, Data count enable, + * Send start bit + */ + i2c_cmd = OWL_I2C_CMD_SECL | OWL_I2C_CMD_MSS | OWL_I2C_CMD_SE | + OWL_I2C_CMD_NS | OWL_I2C_CMD_DE | OWL_I2C_CMD_SBE; + + /* Handle repeated start condition */ + if (num > 1) { + /* Set internal address length and enable repeated start */ + i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) | + OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE; + + /* Write slave address */ + addr = i2c_8bit_addr_from_msg(&msgs[0]); + writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT); + + /* Write internal register address */ + for (idx = 0; idx < msgs[0].len; idx++) + writel(msgs[0].buf[idx], + i2c_dev->base + OWL_I2C_REG_TXDAT); + + msg = &msgs[1]; + } else { + /* Set address length */ + i2c_cmd |= OWL_I2C_CMD_AS(1); + msg = &msgs[0]; + } + + i2c_dev->msg = msg; + i2c_dev->msg_ptr = 0; + + /* Set data count for the message */ + writel(msg->len, i2c_dev->base + OWL_I2C_REG_DATCNT); + + addr = i2c_8bit_addr_from_msg(msg); + writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT); + + if (!(msg->flags & I2C_M_RD)) { + /* Write data to FIFO */ + for (idx = 0; idx < msg->len; idx++) { + /* Check for FIFO full */ + if (readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) & + OWL_I2C_FIFOSTAT_TFF) + break; + + writel(msg->buf[idx], + i2c_dev->base + OWL_I2C_REG_TXDAT); + } + + i2c_dev->msg_ptr = idx; + } + + /* Ignore the NACK if needed */ + if (msg->flags & I2C_M_IGNORE_NAK) + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_NIB, true); + else + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL, + OWL_I2C_FIFOCTL_NIB, false); + + /* Start the transfer */ + writel(i2c_cmd, i2c_dev->base + OWL_I2C_REG_CMD); + + spin_unlock_irqrestore(&i2c_dev->lock, flags); + + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + adap->timeout); + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (time_left == 0) { + dev_err(&adap->dev, "Transaction timed out\n"); + /* Send stop condition and release the bus */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB, + true); + ret = -ETIMEDOUT; + goto err_exit; + } + + ret = i2c_dev->err < 0 ? i2c_dev->err : num; + +err_exit: + spin_unlock_irqrestore(&i2c_dev->lock, flags); + +unlocked_err_exit: + /* Disable I2C controller */ + owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL, + OWL_I2C_CTL_EN, false); + + return ret; +} + +static const struct i2c_algorithm owl_i2c_algorithm = { + .master_xfer = owl_i2c_master_xfer, + .functionality = owl_i2c_func, +}; + +static const struct i2c_adapter_quirks owl_i2c_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST, + .max_read_len = 240, + .max_write_len = 240, + .max_comb_1st_msg_len = 6, + .max_comb_2nd_msg_len = 240, +}; + +static int owl_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct owl_i2c_dev *i2c_dev; + struct resource *res; + int ret, irq; + + i2c_dev = devm_kzalloc(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(dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get IRQ number\n"); + return irq; + } + + if (of_property_read_u32(dev->of_node, "clock-frequency", + &i2c_dev->bus_freq)) + i2c_dev->bus_freq = OWL_I2C_DEF_SPEED_HZ; + + /* We support only frequencies of 100k and 400k for now */ + if (i2c_dev->bus_freq != OWL_I2C_DEF_SPEED_HZ && + i2c_dev->bus_freq != OWL_I2C_MAX_SPEED_HZ) { + dev_err(dev, "invalid clock-frequency %d\n", i2c_dev->bus_freq); + return -EINVAL; + } + + i2c_dev->clk = devm_clk_get(dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + i2c_dev->clk_rate = clk_get_rate(i2c_dev->clk); + if (!i2c_dev->clk_rate) { + dev_err(dev, "input clock rate should not be zero\n"); + ret = -EINVAL; + goto disable_clk; + } + + init_completion(&i2c_dev->msg_complete); + spin_lock_init(&i2c_dev->lock); + i2c_dev->adap.owner = THIS_MODULE; + i2c_dev->adap.algo = &owl_i2c_algorithm; + i2c_dev->adap.timeout = OWL_I2C_TIMEOUT; + i2c_dev->adap.quirks = &owl_i2c_quirks; + i2c_dev->adap.dev.parent = dev; + i2c_dev->adap.dev.of_node = dev->of_node; + snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name), + "%s", "OWL I2C adapter"); + i2c_set_adapdata(&i2c_dev->adap, i2c_dev); + + platform_set_drvdata(pdev, i2c_dev); + + ret = devm_request_irq(dev, irq, owl_i2c_interrupt, 0, pdev->name, + i2c_dev); + if (ret) { + dev_err(dev, "failed to request irq %d\n", irq); + goto disable_clk; + } + + return i2c_add_adapter(&i2c_dev->adap); + +disable_clk: + clk_disable_unprepare(i2c_dev->clk); + + return ret; +} + +static const struct of_device_id owl_i2c_of_match[] = { + { .compatible = "actions,s900-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, owl_i2c_of_match); + +static struct platform_driver owl_i2c_driver = { + .probe = owl_i2c_probe, + .driver = { + .name = "owl-i2c", + .of_match_table = of_match_ptr(owl_i2c_of_match), + }, +}; +module_platform_driver(owl_i2c_driver); + +MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>"); +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("Actions Semiconductor Owl SoC's I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c index 55fd5c6f3cca..50803e5d995b 100644 --- a/drivers/i2c/busses/i2c-pasemi.c +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -365,7 +365,6 @@ static int pasemi_smb_probe(struct pci_dev *dev, smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; - smbus->adapter.nr = PCI_FUNC(dev->devfn); /* set up the sysfs linkage to our parent device */ smbus->adapter.dev.parent = &dev->dev; @@ -373,7 +372,7 @@ static int pasemi_smb_probe(struct pci_dev *dev, reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | (CLK_100K_DIV & CTL_CLK_M))); - error = i2c_add_numbered_adapter(&smbus->adapter); + error = i2c_add_adapter(&smbus->adapter); if (error) goto out_release_region; diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index dae8ac618a52..0829cb696d9d 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -444,16 +444,6 @@ static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd( { enum pmcmsptwi_xfer_result retval; - if ((cmd->type == MSP_TWI_CMD_WRITE && cmd->write_len == 0) || - (cmd->type == MSP_TWI_CMD_READ && cmd->read_len == 0) || - (cmd->type == MSP_TWI_CMD_WRITE_READ && - (cmd->read_len == 0 || cmd->write_len == 0))) { - dev_err(&pmcmsptwi_adapter.dev, - "%s: Cannot transfer less than 1 byte\n", - __func__); - return -EINVAL; - } - mutex_lock(&data->lock); dev_dbg(&pmcmsptwi_adapter.dev, "Setting address to 0x%04x\n", cmd->addr); @@ -532,11 +522,6 @@ static int pmcmsptwi_master_xfer(struct i2c_adapter *adap, cmd.write_data = msg->buf; } - if (msg->len == 0) { - dev_err(&adap->dev, "Zero-byte messages unsupported\n"); - return -EINVAL; - } - cmd.addr = msg->addr; if (msg->flags & I2C_M_TEN) { @@ -578,7 +563,7 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter) } static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { - .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN, .max_write_len = MSP_MAX_BYTES_PER_RW, .max_read_len = MSP_MAX_BYTES_PER_RW, .max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW, diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c new file mode 100644 index 000000000000..36732eb688a4 --- /dev/null +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/qcom-geni-se.h> +#include <linux/spinlock.h> + +#define SE_I2C_TX_TRANS_LEN 0x26c +#define SE_I2C_RX_TRANS_LEN 0x270 +#define SE_I2C_SCL_COUNTERS 0x278 + +#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\ + M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN) +#define SE_I2C_ABORT BIT(1) + +/* M_CMD OP codes for I2C */ +#define I2C_WRITE 0x1 +#define I2C_READ 0x2 +#define I2C_WRITE_READ 0x3 +#define I2C_ADDR_ONLY 0x4 +#define I2C_BUS_CLEAR 0x6 +#define I2C_STOP_ON_BUS 0x7 +/* M_CMD params for I2C */ +#define PRE_CMD_DELAY BIT(0) +#define TIMESTAMP_BEFORE BIT(1) +#define STOP_STRETCH BIT(2) +#define TIMESTAMP_AFTER BIT(3) +#define POST_COMMAND_DELAY BIT(4) +#define IGNORE_ADD_NACK BIT(6) +#define READ_FINISHED_WITH_ACK BIT(7) +#define BYPASS_ADDR_PHASE BIT(8) +#define SLV_ADDR_MSK GENMASK(15, 9) +#define SLV_ADDR_SHFT 9 +/* I2C SCL COUNTER fields */ +#define HIGH_COUNTER_MSK GENMASK(29, 20) +#define HIGH_COUNTER_SHFT 20 +#define LOW_COUNTER_MSK GENMASK(19, 10) +#define LOW_COUNTER_SHFT 10 +#define CYCLE_COUNTER_MSK GENMASK(9, 0) + +enum geni_i2c_err_code { + GP_IRQ0, + NACK, + GP_IRQ2, + BUS_PROTO, + ARB_LOST, + GP_IRQ5, + GENI_OVERRUN, + GENI_ILLEGAL_CMD, + GENI_ABORT_DONE, + GENI_TIMEOUT, +}; + +#define DM_I2C_CB_ERR ((BIT(NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \ + << 5) + +#define I2C_AUTO_SUSPEND_DELAY 250 +#define KHZ(freq) (1000 * freq) +#define PACKING_BYTES_PW 4 + +#define ABORT_TIMEOUT HZ +#define XFER_TIMEOUT HZ +#define RST_TIMEOUT HZ + +struct geni_i2c_dev { + struct geni_se se; + u32 tx_wm; + int irq; + int err; + struct i2c_adapter adap; + struct completion done; + struct i2c_msg *cur; + int cur_wr; + int cur_rd; + spinlock_t lock; + u32 clk_freq_out; + const struct geni_i2c_clk_fld *clk_fld; + int suspended; +}; + +struct geni_i2c_err_log { + int err; + const char *msg; +}; + +static const struct geni_i2c_err_log gi2c_log[] = { + [GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"}, + [NACK] = {-ENXIO, "NACK: slv unresponsive, check its power/reset-ln"}, + [GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"}, + [BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unepxected start/stop"}, + [ARB_LOST] = {-EAGAIN, "Bus arbitration lost, clock line undriveable"}, + [GP_IRQ5] = {-EIO, "Unknown I2C err GP IRQ5"}, + [GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"}, + [GENI_ILLEGAL_CMD] = {-EIO, "Illegal cmd, check GENI cmd-state machine"}, + [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"}, + [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"}, +}; + +struct geni_i2c_clk_fld { + u32 clk_freq_out; + u8 clk_div; + u8 t_high_cnt; + u8 t_low_cnt; + u8 t_cycle_cnt; +}; + +/* + * Hardware uses the underlying formula to calculate time periods of + * SCL clock cycle. Firmware uses some additional cycles excluded from the + * below formula and it is confirmed that the time periods are within + * specification limits. + * + * time of high period of SCL: t_high = (t_high_cnt * clk_div) / source_clock + * time of low period of SCL: t_low = (t_low_cnt * clk_div) / source_clock + * time of full period of SCL: t_cycle = (t_cycle_cnt * clk_div) / source_clock + * clk_freq_out = t / t_cycle + * source_clock = 19.2 MHz + */ +static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = { + {KHZ(100), 7, 10, 11, 26}, + {KHZ(400), 2, 5, 12, 24}, + {KHZ(1000), 1, 3, 9, 18}, +}; + +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c) +{ + int i; + const struct geni_i2c_clk_fld *itr = geni_i2c_clk_map; + + for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) { + if (itr->clk_freq_out == gi2c->clk_freq_out) { + gi2c->clk_fld = itr; + return 0; + } + } + return -EINVAL; +} + +static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c) +{ + const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; + u32 val; + + writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL); + + val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN; + writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG); + + val = itr->t_high_cnt << HIGH_COUNTER_SHFT; + val |= itr->t_low_cnt << LOW_COUNTER_SHFT; + val |= itr->t_cycle_cnt; + writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS); +} + +static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c) +{ + u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0); + u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS); + u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS); + u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS); + u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN); + u32 rx_st, tx_st; + + if (dma) { + rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT); + tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT); + } else { + rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS); + tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS); + } + dev_dbg(gi2c->se.dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n", + dma, tx_st, rx_st, m_stat); + dev_dbg(gi2c->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n", + m_cmd, geni_s, geni_ios); +} + +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err) +{ + if (!gi2c->err) + gi2c->err = gi2c_log[err].err; + if (gi2c->cur) + dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n", + gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags); + + if (err != NACK && err != GENI_ABORT_DONE) { + dev_err(gi2c->se.dev, "%s\n", gi2c_log[err].msg); + geni_i2c_err_misc(gi2c); + } +} + +static irqreturn_t geni_i2c_irq(int irq, void *dev) +{ + struct geni_i2c_dev *gi2c = dev; + int j; + u32 m_stat; + u32 rx_st; + u32 dm_tx_st; + u32 dm_rx_st; + u32 dma; + struct i2c_msg *cur; + unsigned long flags; + + spin_lock_irqsave(&gi2c->lock, flags); + m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS); + rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS); + dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT); + dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT); + dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN); + cur = gi2c->cur; + + if (!cur || + m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) || + dm_rx_st & (DM_I2C_CB_ERR)) { + if (m_stat & M_GP_IRQ_1_EN) + geni_i2c_err(gi2c, NACK); + if (m_stat & M_GP_IRQ_3_EN) + geni_i2c_err(gi2c, BUS_PROTO); + if (m_stat & M_GP_IRQ_4_EN) + geni_i2c_err(gi2c, ARB_LOST); + if (m_stat & M_CMD_OVERRUN_EN) + geni_i2c_err(gi2c, GENI_OVERRUN); + if (m_stat & M_ILLEGAL_CMD_EN) + geni_i2c_err(gi2c, GENI_ILLEGAL_CMD); + if (m_stat & M_CMD_ABORT_EN) + geni_i2c_err(gi2c, GENI_ABORT_DONE); + if (m_stat & M_GP_IRQ_0_EN) + geni_i2c_err(gi2c, GP_IRQ0); + + /* Disable the TX Watermark interrupt to stop TX */ + if (!dma) + writel_relaxed(0, gi2c->se.base + + SE_GENI_TX_WATERMARK_REG); + goto irqret; + } + + if (dma) { + dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n", + dm_tx_st, dm_rx_st); + goto irqret; + } + + if (cur->flags & I2C_M_RD && + m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) { + u32 rxcnt = rx_st & RX_FIFO_WC_MSK; + + for (j = 0; j < rxcnt; j++) { + u32 val; + int p = 0; + + val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn); + while (gi2c->cur_rd < cur->len && p < sizeof(val)) { + cur->buf[gi2c->cur_rd++] = val & 0xff; + val >>= 8; + p++; + } + if (gi2c->cur_rd == cur->len) + break; + } + } else if (!(cur->flags & I2C_M_RD) && + m_stat & M_TX_FIFO_WATERMARK_EN) { + for (j = 0; j < gi2c->tx_wm; j++) { + u32 temp; + u32 val = 0; + int p = 0; + + while (gi2c->cur_wr < cur->len && p < sizeof(val)) { + temp = cur->buf[gi2c->cur_wr++]; + val |= temp << (p * 8); + p++; + } + writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn); + /* TX Complete, Disable the TX Watermark interrupt */ + if (gi2c->cur_wr == cur->len) { + writel_relaxed(0, gi2c->se.base + + SE_GENI_TX_WATERMARK_REG); + break; + } + } + } +irqret: + if (m_stat) + writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR); + + if (dma) { + if (dm_tx_st) + writel_relaxed(dm_tx_st, gi2c->se.base + + SE_DMA_TX_IRQ_CLR); + if (dm_rx_st) + writel_relaxed(dm_rx_st, gi2c->se.base + + SE_DMA_RX_IRQ_CLR); + } + /* if this is err with done-bit not set, handle that through timeout. */ + if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN) + complete(&gi2c->done); + else if (dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE) + complete(&gi2c->done); + else if (dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE) + complete(&gi2c->done); + + spin_unlock_irqrestore(&gi2c->lock, flags); + return IRQ_HANDLED; +} + +static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c) +{ + u32 val; + unsigned long time_left = ABORT_TIMEOUT; + unsigned long flags; + + spin_lock_irqsave(&gi2c->lock, flags); + geni_i2c_err(gi2c, GENI_TIMEOUT); + gi2c->cur = NULL; + geni_se_abort_m_cmd(&gi2c->se); + spin_unlock_irqrestore(&gi2c->lock, flags); + do { + time_left = wait_for_completion_timeout(&gi2c->done, time_left); + val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS); + } while (!(val & M_CMD_ABORT_EN) && time_left); + + if (!(val & M_CMD_ABORT_EN)) + dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n"); +} + +static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c) +{ + u32 val; + unsigned long time_left = RST_TIMEOUT; + + writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST); + do { + time_left = wait_for_completion_timeout(&gi2c->done, time_left); + val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT); + } while (!(val & RX_RESET_DONE) && time_left); + + if (!(val & RX_RESET_DONE)) + dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n"); +} + +static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c) +{ + u32 val; + unsigned long time_left = RST_TIMEOUT; + + writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST); + do { + time_left = wait_for_completion_timeout(&gi2c->done, time_left); + val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT); + } while (!(val & TX_RESET_DONE) && time_left); + + if (!(val & TX_RESET_DONE)) + dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n"); +} + +static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, + u32 m_param) +{ + dma_addr_t rx_dma; + enum geni_se_xfer_mode mode; + unsigned long time_left = XFER_TIMEOUT; + + gi2c->cur = msg; + mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO; + geni_se_select_mode(&gi2c->se, mode); + writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN); + geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param); + if (mode == GENI_SE_DMA) { + int ret; + + ret = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len, + &rx_dma); + if (ret) { + mode = GENI_SE_FIFO; + geni_se_select_mode(&gi2c->se, mode); + } + } + + time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); + if (!time_left) + geni_i2c_abort_xfer(gi2c); + + gi2c->cur_rd = 0; + if (mode == GENI_SE_DMA) { + if (gi2c->err) + geni_i2c_rx_fsm_rst(gi2c); + geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len); + } + return gi2c->err; +} + +static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, + u32 m_param) +{ + dma_addr_t tx_dma; + enum geni_se_xfer_mode mode; + unsigned long time_left; + + gi2c->cur = msg; + mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO; + geni_se_select_mode(&gi2c->se, mode); + writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN); + geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param); + if (mode == GENI_SE_DMA) { + int ret; + + ret = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len, + &tx_dma); + if (ret) { + mode = GENI_SE_FIFO; + geni_se_select_mode(&gi2c->se, mode); + } + } + + if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */ + writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG); + + time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); + if (!time_left) + geni_i2c_abort_xfer(gi2c); + + gi2c->cur_wr = 0; + if (mode == GENI_SE_DMA) { + if (gi2c->err) + geni_i2c_tx_fsm_rst(gi2c); + geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len); + } + return gi2c->err; +} + +static int geni_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], + int num) +{ + struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap); + int i, ret; + + gi2c->err = 0; + reinit_completion(&gi2c->done); + ret = pm_runtime_get_sync(gi2c->se.dev); + if (ret < 0) { + dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret); + pm_runtime_put_noidle(gi2c->se.dev); + /* Set device in suspended since resume failed */ + pm_runtime_set_suspended(gi2c->se.dev); + return ret; + } + + qcom_geni_i2c_conf(gi2c); + for (i = 0; i < num; i++) { + u32 m_param = i < (num - 1) ? STOP_STRETCH : 0; + + m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK); + + if (msgs[i].flags & I2C_M_RD) + ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param); + else + ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param); + + if (ret) + break; + } + if (ret == 0) + ret = num; + + pm_runtime_mark_last_busy(gi2c->se.dev); + pm_runtime_put_autosuspend(gi2c->se.dev); + gi2c->cur = NULL; + gi2c->err = 0; + return ret; +} + +static u32 geni_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm geni_i2c_algo = { + .master_xfer = geni_i2c_xfer, + .functionality = geni_i2c_func, +}; + +static int geni_i2c_probe(struct platform_device *pdev) +{ + struct geni_i2c_dev *gi2c; + struct resource *res; + u32 proto, tx_depth; + int ret; + + gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL); + if (!gi2c) + return -ENOMEM; + + gi2c->se.dev = &pdev->dev; + gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gi2c->se.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gi2c->se.base)) + return PTR_ERR(gi2c->se.base); + + gi2c->se.clk = devm_clk_get(&pdev->dev, "se"); + if (IS_ERR(gi2c->se.clk)) { + ret = PTR_ERR(gi2c->se.clk); + dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret); + return ret; + } + + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &gi2c->clk_freq_out); + if (ret) { + dev_info(&pdev->dev, + "Bus frequency not specified, default to 100kHz.\n"); + gi2c->clk_freq_out = KHZ(100); + } + + gi2c->irq = platform_get_irq(pdev, 0); + if (gi2c->irq < 0) { + dev_err(&pdev->dev, "IRQ error for i2c-geni\n"); + return gi2c->irq; + } + + ret = geni_i2c_clk_map_idx(gi2c); + if (ret) { + dev_err(&pdev->dev, "Invalid clk frequency %d Hz: %d\n", + gi2c->clk_freq_out, ret); + return ret; + } + + gi2c->adap.algo = &geni_i2c_algo; + init_completion(&gi2c->done); + spin_lock_init(&gi2c->lock); + platform_set_drvdata(pdev, gi2c); + ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq, + IRQF_TRIGGER_HIGH, "i2c_geni", gi2c); + if (ret) { + dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n", + gi2c->irq, ret); + return ret; + } + /* Disable the interrupt so that the system can enter low-power mode */ + disable_irq(gi2c->irq); + i2c_set_adapdata(&gi2c->adap, gi2c); + gi2c->adap.dev.parent = &pdev->dev; + gi2c->adap.dev.of_node = pdev->dev.of_node; + strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name)); + + ret = geni_se_resources_on(&gi2c->se); + if (ret) { + dev_err(&pdev->dev, "Error turning on resources %d\n", ret); + return ret; + } + proto = geni_se_read_proto(&gi2c->se); + tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se); + if (proto != GENI_SE_I2C) { + dev_err(&pdev->dev, "Invalid proto %d\n", proto); + geni_se_resources_off(&gi2c->se); + return -ENXIO; + } + gi2c->tx_wm = tx_depth - 1; + geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth); + geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW, + true, true, true); + ret = geni_se_resources_off(&gi2c->se); + if (ret) { + dev_err(&pdev->dev, "Error turning off resources %d\n", ret); + return ret; + } + + dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); + + ret = i2c_add_adapter(&gi2c->adap); + if (ret) { + dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret); + return ret; + } + + gi2c->suspended = 1; + pm_runtime_set_suspended(gi2c->se.dev); + pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY); + pm_runtime_use_autosuspend(gi2c->se.dev); + pm_runtime_enable(gi2c->se.dev); + + return 0; +} + +static int geni_i2c_remove(struct platform_device *pdev) +{ + struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); + + pm_runtime_disable(gi2c->se.dev); + i2c_del_adapter(&gi2c->adap); + return 0; +} + +static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) +{ + int ret; + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + disable_irq(gi2c->irq); + ret = geni_se_resources_off(&gi2c->se); + if (ret) { + enable_irq(gi2c->irq); + return ret; + + } else { + gi2c->suspended = 1; + } + + return 0; +} + +static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) +{ + int ret; + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + ret = geni_se_resources_on(&gi2c->se); + if (ret) + return ret; + + enable_irq(gi2c->irq); + gi2c->suspended = 0; + return 0; +} + +static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + if (!gi2c->suspended) { + geni_i2c_runtime_suspend(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + return 0; +} + +static const struct dev_pm_ops geni_i2c_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL) + SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume, + NULL) +}; + +static const struct of_device_id geni_i2c_dt_match[] = { + { .compatible = "qcom,geni-i2c" }, + {} +}; +MODULE_DEVICE_TABLE(of, geni_i2c_dt_match); + +static struct platform_driver geni_i2c_driver = { + .probe = geni_i2c_probe, + .remove = geni_i2c_remove, + .driver = { + .name = "geni_i2c", + .pm = &geni_i2c_pm_ops, + .of_match_table = geni_i2c_dt_match, + }, +}; + +module_platform_driver(geni_i2c_driver); + +MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 3c1c817f6968..43ad933df0f0 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -19,6 +19,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/dmaengine.h> @@ -112,9 +113,10 @@ #define ID_ARBLOST (1 << 3) #define ID_NACK (1 << 4) /* persistent flags */ -#define ID_P_NO_RXDMA (1 << 30) /* HW forbids RXDMA sometimes */ -#define ID_P_PM_BLOCKED (1 << 31) -#define ID_P_MASK (ID_P_PM_BLOCKED | ID_P_NO_RXDMA) +#define ID_P_REP_AFTER_RD BIT(29) +#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */ +#define ID_P_PM_BLOCKED BIT(31) +#define ID_P_MASK GENMASK(31, 29) enum rcar_i2c_type { I2C_RCAR_GEN1, @@ -183,8 +185,6 @@ static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val) rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr); }; -/* No get_sda, because the HW only reports its bus free logic, not SDA itself */ - static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val) { struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); @@ -197,10 +197,19 @@ static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val) rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr); }; +static int rcar_i2c_get_bus_free(struct i2c_adapter *adap) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + return !(rcar_i2c_read(priv, ICMCR) & FSDA); + +}; + static struct i2c_bus_recovery_info rcar_i2c_bri = { .get_scl = rcar_i2c_get_scl, .set_scl = rcar_i2c_set_scl, .set_sda = rcar_i2c_set_sda, + .get_bus_free = rcar_i2c_get_bus_free, .recover_bus = i2c_generic_scl_recovery, }; static void rcar_i2c_init(struct rcar_i2c_priv *priv) @@ -215,7 +224,7 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) { - int i, ret; + int i; for (i = 0; i < LOOP_TIMEOUT; i++) { /* make sure that bus is not busy */ @@ -226,13 +235,7 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) /* Waiting did not help, try to recover */ priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL; - ret = i2c_recover_bus(&priv->adap); - - /* No failure when recovering, so check bus busy bit again */ - if (ret == 0) - ret = (rcar_i2c_read(priv, ICMCR) & FSDA) ? -EBUSY : 0; - - return ret; + return i2c_recover_bus(&priv->adap); } static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t) @@ -343,7 +346,10 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMSR, 0); rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); } else { - rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); + if (priv->flags & ID_P_REP_AFTER_RD) + priv->flags &= ~ID_P_REP_AFTER_RD; + else + rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); rcar_i2c_write(priv, ICMSR, 0); } rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); @@ -548,15 +554,15 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) priv->pos++; } - /* - * If next received data is the _LAST_, go to STOP phase. Might be - * overwritten by REP START when setting up a new msg. Not elegant - * but the only stable sequence for REP START I have found so far. - * If you want to change this code, make sure sending one transfer with - * four messages (WR-RD-WR-RD) works! - */ - if (priv->pos + 1 >= msg->len) - rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP); + /* If next received data is the _LAST_, go to new phase. */ + if (priv->pos + 1 == msg->len) { + if (priv->flags & ID_LAST_MSG) { + rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP); + } else { + rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); + priv->flags |= ID_P_REP_AFTER_RD; + } + } if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG)) rcar_i2c_next_msg(priv); @@ -624,9 +630,11 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) struct rcar_i2c_priv *priv = ptr; u32 msr, val; - /* Clear START or STOP as soon as we can */ - val = rcar_i2c_read(priv, ICMCR); - rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA); + /* Clear START or STOP immediately, except for REPSTART after read */ + if (likely(!(priv->flags & ID_P_REP_AFTER_RD))) { + val = rcar_i2c_read(priv, ICMCR); + rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA); + } msr = rcar_i2c_read(priv, ICMSR); @@ -795,14 +803,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, if (ret < 0) goto out; - for (i = 0; i < num; i++) { - /* This HW can't send STOP after address phase */ - if (msgs[i].len == 0) { - ret = -EOPNOTSUPP; - goto out; - } + for (i = 0; i < num; i++) rcar_i2c_request_dma(priv, msgs + i); - } /* init first message */ priv->msg = msgs; @@ -889,6 +891,10 @@ static const struct i2c_algorithm rcar_i2c_algo = { .unreg_slave = rcar_unreg_slave, }; +static const struct i2c_adapter_quirks rcar_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static const struct of_device_id rcar_i2c_dt_ids[] = { { .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 }, { .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 }, @@ -942,6 +948,7 @@ static int rcar_i2c_probe(struct platform_device *pdev) adap->dev.parent = dev; adap->dev.of_node = dev->of_node; adap->bus_recovery_info = &rcar_i2c_bri; + adap->quirks = &rcar_i2c_quirks; i2c_set_adapdata(adap, priv); strlcpy(adap->name, pdev->name, sizeof(adap->name)); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 9fe2b6951895..2f2e28d60ef5 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -919,9 +919,9 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) || (val == CPUFREQ_PRECHANGE && delta_f > 0)) { - i2c_lock_adapter(&i2c->adap); + i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER); ret = s3c24xx_i2c_clockrate(i2c, &got); - i2c_unlock_adapter(&i2c->adap); + i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER); if (ret < 0) dev_err(i2c->dev, "cannot find frequency (%d)\n", ret); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 5fda4188a9e5..9c7f6f8ceb22 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -613,11 +613,6 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, bool do_init) { - if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) { - dev_err(pd->dev, "Unsupported zero length i2c read\n"); - return -EOPNOTSUPP; - } - if (do_init) { /* Initialize channel registers */ iic_wr(pd, ICCR, ICCR_SCP); @@ -758,6 +753,10 @@ static const struct i2c_algorithm sh_mobile_i2c_algorithm = { .master_xfer = sh_mobile_i2c_xfer, }; +static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN_READ, +}; + /* * r8a7740 chip has lasting errata on I2C I/O pad reset. * this is work-around for it. @@ -925,6 +924,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) adap->owner = THIS_MODULE; adap->algo = &sh_mobile_i2c_algorithm; + adap->quirks = &sh_mobile_i2c_quirks; adap->dev.parent = &dev->dev; adap->retries = 5; adap->nr = dev->id; diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 4053259bccb8..a94e724f51dc 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -590,9 +590,9 @@ static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); - i2c_lock_adapter(&i2c_dev->adap); + i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = true; - i2c_unlock_adapter(&i2c_dev->adap); + i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); return pm_runtime_force_suspend(pdev); } @@ -601,9 +601,9 @@ static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); - i2c_lock_adapter(&i2c_dev->adap); + i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = false; - i2c_unlock_adapter(&i2c_dev->adap); + i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); return pm_runtime_force_resume(pdev); } diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index fce52bdab2b7..5503fa171df0 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -673,12 +673,6 @@ static int stu300_xfer_msg(struct i2c_adapter *adap, msg->addr, msg->len, msg->flags, stop); } - /* Zero-length messages are not supported by this hardware */ - if (msg->len == 0) { - ret = -EINVAL; - goto exit_disable; - } - /* * For some reason, sending the address sometimes fails when running * on the 13 MHz clock. No interrupt arrives. This is a work around, @@ -863,6 +857,10 @@ static const struct i2c_algorithm stu300_algo = { .functionality = stu300_func, }; +static const struct i2c_adapter_quirks stu300_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static int stu300_probe(struct platform_device *pdev) { struct stu300_dev *dev; @@ -920,6 +918,8 @@ static int stu300_probe(struct platform_device *pdev) adap->algo = &stu300_algo; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; + adap->quirks = &stu300_quirks; + i2c_set_adapdata(adap, dev); /* i2c device drivers may be active on return from add_adapter() */ diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 797def5319f1..60c8561fbe65 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -115,6 +115,18 @@ #define I2C_CONFIG_LOAD_TIMEOUT 1000000 +#define I2C_MST_FIFO_CONTROL 0x0b4 +#define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0) +#define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1) +#define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4) +#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16) + +#define I2C_MST_FIFO_STATUS 0x0b8 +#define I2C_MST_FIFO_STATUS_RX_MASK 0xff +#define I2C_MST_FIFO_STATUS_RX_SHIFT 0 +#define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 +#define I2C_MST_FIFO_STATUS_TX_SHIFT 16 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -154,6 +166,7 @@ struct tegra_i2c_hw_feature { u16 clk_divisor_fast_plus_mode; bool has_multi_master_mode; bool has_slcg_override_reg; + bool has_mst_fifo; }; /** @@ -266,13 +279,24 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) { unsigned long timeout = jiffies + HZ; - u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL); + unsigned int offset; + u32 mask, val; + + if (i2c_dev->hw->has_mst_fifo) { + mask = I2C_MST_FIFO_CONTROL_TX_FLUSH | + I2C_MST_FIFO_CONTROL_RX_FLUSH; + offset = I2C_MST_FIFO_CONTROL; + } else { + mask = I2C_FIFO_CONTROL_TX_FLUSH | + I2C_FIFO_CONTROL_RX_FLUSH; + offset = I2C_FIFO_CONTROL; + } - val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH; - i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + val = i2c_readl(i2c_dev, offset); + val |= mask; + i2c_writel(i2c_dev, val, offset); - while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) & - (I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) { + while (i2c_readl(i2c_dev, offset) & mask) { if (time_after(jiffies, timeout)) { dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n"); return -ETIMEDOUT; @@ -290,9 +314,15 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) size_t buf_remaining = i2c_dev->msg_buf_remaining; int words_to_transfer; - val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); - rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >> - I2C_FIFO_STATUS_RX_SHIFT; + if (i2c_dev->hw->has_mst_fifo) { + val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS); + rx_fifo_avail = (val & I2C_MST_FIFO_STATUS_RX_MASK) >> + I2C_MST_FIFO_STATUS_RX_SHIFT; + } else { + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >> + I2C_FIFO_STATUS_RX_SHIFT; + } /* Rounds down to not include partial word at the end of buf */ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; @@ -321,6 +351,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0); i2c_dev->msg_buf_remaining = buf_remaining; i2c_dev->msg_buf = buf; + return 0; } @@ -332,9 +363,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) size_t buf_remaining = i2c_dev->msg_buf_remaining; int words_to_transfer; - val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); - tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> - I2C_FIFO_STATUS_TX_SHIFT; + if (i2c_dev->hw->has_mst_fifo) { + val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS); + tx_fifo_avail = (val & I2C_MST_FIFO_STATUS_TX_MASK) >> + I2C_MST_FIFO_STATUS_TX_SHIFT; + } else { + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> + I2C_FIFO_STATUS_TX_SHIFT; + } /* Rounds down to not include partial word at the end of buf */ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; @@ -516,9 +553,15 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); } - val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | - 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; - i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + if (i2c_dev->hw->has_mst_fifo) { + val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | + I2C_MST_FIFO_CONTROL_RX_TRIG(1); + i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL); + } else { + val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | + 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; + i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + } err = tegra_i2c_flush_fifos(i2c_dev); if (err) @@ -802,6 +845,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_config_load_reg = false, .has_multi_master_mode = false, .has_slcg_override_reg = false, + .has_mst_fifo = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -814,6 +858,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_config_load_reg = false, .has_multi_master_mode = false, .has_slcg_override_reg = false, + .has_mst_fifo = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -826,6 +871,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_config_load_reg = false, .has_multi_master_mode = false, .has_slcg_override_reg = false, + .has_mst_fifo = false, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -838,6 +884,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_config_load_reg = true, .has_multi_master_mode = false, .has_slcg_override_reg = true, + .has_mst_fifo = false, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -850,10 +897,25 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_config_load_reg = true, .has_multi_master_mode = true, .has_slcg_override_reg = true, + .has_mst_fifo = false, +}; + +static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .has_single_clk_source = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_fast_plus_mode = 0x10, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, + .has_mst_fifo = true, }; /* Match table for of_platform binding */ static const struct of_device_id tegra_i2c_of_match[] = { + { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c index 484bfa15d58e..34cd4b308540 100644 --- a/drivers/i2c/busses/i2c-xlr.c +++ b/drivers/i2c/busses/i2c-xlr.c @@ -173,9 +173,6 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len, u8 offset; u32 xfer; - if (!len) - return -EOPNOTSUPP; - offset = buf[0]; xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset); xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); @@ -241,9 +238,6 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr) unsigned long timeout, stoptime, checktime; int nbytes, timedout; - if (!len) - return -EOPNOTSUPP; - xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra); xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1); @@ -340,6 +334,10 @@ static const struct i2c_algorithm xlr_i2c_algo = { .functionality = xlr_func, }; +static const struct i2c_adapter_quirks xlr_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static const struct xlr_i2c_config xlr_i2c_config_default = { .status_busy = XLR_I2C_BUS_BUSY, .cfg_extra = 0, @@ -427,6 +425,7 @@ static int xlr_i2c_probe(struct platform_device *pdev) priv->adap.owner = THIS_MODULE; priv->adap.algo_data = priv; priv->adap.algo = &xlr_i2c_algo; + priv->adap.quirks = &xlr_i2c_quirks; priv->adap.nr = pdev->id; priv->adap.class = I2C_CLASS_HWMON; snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c"); |