summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig11
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c76
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h2
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c191
-rw-r--r--drivers/i2c/busses/i2c-designware-slave.c47
-rw-r--r--drivers/i2c/busses/i2c-gpio.c57
-rw-r--r--drivers/i2c/busses/i2c-i801.c4
-rw-r--r--drivers/i2c/busses/i2c-tegra.c90
-rw-r--r--drivers/i2c/i2c-core-smbus.c28
-rw-r--r--drivers/i2c/i2c-mux.c4
-rw-r--r--drivers/i2c/muxes/i2c-mux-mlxcpld.c28
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca9541.c69
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c27
13 files changed, 366 insertions, 268 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2ec87b1..e7a4d288c8b7 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.
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 48914dfc8ce8..bcc1bcbf0350 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -31,6 +31,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 +95,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 +184,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..29b92f05bb2b 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -295,8 +295,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..fc7c255c80af 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -45,90 +45,78 @@ 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;
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 */
-
+ /* Set standard and fast speed dividers for high/low periods */
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
- /* Set SCL timing parameters for standard-mode */
- if (dev->ss_hcnt && dev->ss_lcnt) {
- hcnt = dev->ss_hcnt;
- lcnt = dev->ss_lcnt;
- } else {
- hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
+ /* 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 (dev->clk_freq == 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 +124,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);
@@ -681,6 +702,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;
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index 8ce2cd368477..8af4c978938e 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -51,53 +51,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 +264,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-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 20b3dd756fc7..c008d209f0b8 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -101,17 +101,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%ll
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;
-
- if (addr > 0x7f)
- return -EINVAL;
-
- /* ADDR (7 bit) + RD (1 bit) + SDA hi (1 bit) */
- pattern = (addr << 2) | 3;
+ int i;
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
@@ -119,8 +113,8 @@ static int fops_incomplete_transfer_set(void *data, u64 addr)
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);
@@ -130,10 +124,43 @@ static int fops_incomplete_transfer_set(void *data, u64 addr)
}
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-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 5fccd1f1bca8..edde464dfb72 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)
@@ -803,6 +846,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 = {
@@ -815,6 +859,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 = {
@@ -827,6 +872,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 = {
@@ -839,6 +885,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 = {
@@ -851,10 +898,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/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index 51970bae3c4a..9cd66cabb84f 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -463,7 +463,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
msg[num-1].len++;
}
- status = i2c_transfer(adapter, msg, num);
+ status = __i2c_transfer(adapter, msg, num);
if (status < 0)
goto cleanup;
if (status != num) {
@@ -524,9 +524,24 @@ cleanup:
* This executes an SMBus protocol operation, and returns a negative
* errno code else zero on success.
*/
-s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
- char read_write, u8 command, int protocol,
- union i2c_smbus_data *data)
+s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int protocol, union i2c_smbus_data *data)
+{
+ s32 res;
+
+ i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
+ res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
+ command, protocol, data);
+ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
+
+ return res;
+}
+EXPORT_SYMBOL(i2c_smbus_xfer);
+
+s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int protocol, union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
int try;
@@ -543,8 +558,6 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
if (adapter->algo->smbus_xfer) {
- i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
-
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
@@ -557,7 +570,6 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
orig_jiffies + adapter->timeout))
break;
}
- i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
goto trace;
@@ -579,7 +591,7 @@ trace:
return res;
}
-EXPORT_SYMBOL(i2c_smbus_xfer);
+EXPORT_SYMBOL(__i2c_smbus_xfer);
/**
* i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 300ab4b672e4..221365eb7c05 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -87,8 +87,8 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
ret = muxc->select(muxc, priv->chan_id);
if (ret >= 0)
- ret = parent->algo->smbus_xfer(parent, addr, flags,
- read_write, command, size, data);
+ ret = __i2c_smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
if (muxc->deselect)
muxc->deselect(muxc, priv->chan_id);
diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
index 12ad8d65faf6..f2bf3e57ed67 100644
--- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c
+++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c
@@ -94,31 +94,11 @@ static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
- int ret = -ENODEV;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- u8 msgbuf[] = {pdata->sel_reg_addr, val};
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = msgbuf;
- ret = __i2c_transfer(adap, &msg, 1);
-
- if (ret >= 0 && ret != 1)
- ret = -EREMOTEIO;
- } else if (adap->algo->smbus_xfer) {
- union i2c_smbus_data data;
-
- data.byte = val;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags, I2C_SMBUS_WRITE,
- pdata->sel_reg_addr,
- I2C_SMBUS_BYTE_DATA, &data);
- }
+ union i2c_smbus_data data = { .byte = val };
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, pdata->sel_reg_addr,
+ I2C_SMBUS_BYTE_DATA, &data);
}
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
index bc7c8cee5a8c..9e75d6b9140b 100644
--- a/drivers/i2c/muxes/i2c-mux-pca9541.c
+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
@@ -99,31 +99,11 @@ MODULE_DEVICE_TABLE(of, pca9541_of_match);
static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
{
struct i2c_adapter *adap = client->adapter;
- int ret;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- char buf[2];
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- buf[0] = command;
- buf[1] = val;
- msg.buf = buf;
- ret = __i2c_transfer(adap, &msg, 1);
- } else {
- union i2c_smbus_data data;
-
- data.byte = val;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_WRITE,
- command,
- I2C_SMBUS_BYTE_DATA, &data);
- }
+ union i2c_smbus_data data = { .byte = val };
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BYTE_DATA, &data);
}
/*
@@ -133,41 +113,14 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
static int pca9541_reg_read(struct i2c_client *client, u8 command)
{
struct i2c_adapter *adap = client->adapter;
+ union i2c_smbus_data data;
int ret;
- u8 val;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg[2] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = &command
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = &val
- }
- };
- ret = __i2c_transfer(adap, msg, 2);
- if (ret == 2)
- ret = val;
- else if (ret >= 0)
- ret = -EIO;
- } else {
- union i2c_smbus_data data;
-
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_READ,
- command,
- I2C_SMBUS_BYTE_DATA, &data);
- if (!ret)
- ret = data.byte;
- }
- return ret;
+
+ ret = __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+
+ return ret ?: data.byte;
}
/*
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index fbc748027087..0e9a0e66e92f 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -220,30 +220,11 @@ MODULE_DEVICE_TABLE(of, pca954x_of_match);
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
- int ret = -ENODEV;
-
- if (adap->algo->master_xfer) {
- struct i2c_msg msg;
- char buf[1];
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 1;
- buf[0] = val;
- msg.buf = buf;
- ret = __i2c_transfer(adap, &msg, 1);
-
- if (ret >= 0 && ret != 1)
- ret = -EREMOTEIO;
- } else {
- union i2c_smbus_data data;
- ret = adap->algo->smbus_xfer(adap, client->addr,
- client->flags,
- I2C_SMBUS_WRITE,
- val, I2C_SMBUS_BYTE, &data);
- }
+ union i2c_smbus_data dummy;
- return ret;
+ return __i2c_smbus_xfer(adap, client->addr, client->flags,
+ I2C_SMBUS_WRITE, val,
+ I2C_SMBUS_BYTE, &dummy);
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)