@@ -588,6 +588,10 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
if (ret)
return ret;
+ /* workaround for IP hardware issue */
+ if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP)
+ param |= 0x80800;
+
tx_fifo_depth = ((param >> 16) & 0xff) + 1;
rx_fifo_depth = ((param >> 8) & 0xff) + 1;
if (!dev->tx_fifo_depth) {
@@ -303,6 +303,7 @@ struct dw_i2c_dev {
#define MODEL_MSCC_OCELOT BIT(8)
#define MODEL_BAIKAL_BT1 BIT(9)
#define MODEL_AMD_NAVI_GPU BIT(10)
+#define MODEL_WANGXUN_SP BIT(11)
#define MODEL_MASK GENMASK(11, 8)
/*
@@ -354,6 +354,75 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
return 0;
}
+static int i2c_dw_poll_tx_empty(struct dw_i2c_dev *dev)
+{
+ u32 val;
+
+ return regmap_read_poll_timeout(dev->map, DW_IC_RAW_INTR_STAT, val,
+ val & DW_IC_INTR_TX_EMPTY,
+ 100, 1000);
+}
+
+static int i2c_dw_poll_rx_full(struct dw_i2c_dev *dev)
+{
+ u32 val;
+
+ return regmap_read_poll_timeout(dev->map, DW_IC_RAW_INTR_STAT, val,
+ val & DW_IC_INTR_RX_FULL,
+ 100, 1000);
+}
+
+static int txgbe_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num_msgs)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ int msg_idx, buf_len, data_idx, ret;
+ unsigned int val;
+ u8 dev_addr;
+ u8 *buf;
+
+ dev->msgs = msgs;
+ dev->msgs_num = num_msgs;
+ i2c_dw_xfer_init(dev);
+ regmap_write(dev->map, DW_IC_INTR_MASK, 0);
+
+ dev_addr = msgs[0].buf[0];
+
+ for (msg_idx = 0; msg_idx < num_msgs; msg_idx++) {
+ buf = msgs[msg_idx].buf;
+ buf_len = msgs[msg_idx].len;
+
+ for (data_idx = 0; data_idx < buf_len; data_idx++) {
+ if (msgs[msg_idx].flags & I2C_M_RD) {
+ ret = i2c_dw_poll_tx_empty(dev);
+ if (ret)
+ return ret;
+
+ regmap_write(dev->map, DW_IC_DATA_CMD,
+ (dev_addr + data_idx) | BIT(9));
+ regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | BIT(9));
+
+ ret = i2c_dw_poll_rx_full(dev);
+ if (ret)
+ return ret;
+
+ regmap_read(dev->map, DW_IC_DATA_CMD, &val);
+ buf[data_idx] = 0xFF & val;
+ } else {
+ ret = i2c_dw_poll_tx_empty(dev);
+ if (ret)
+ return ret;
+
+ regmap_write(dev->map, DW_IC_DATA_CMD, buf[data_idx]);
+ if (data_idx == (buf_len - 1))
+ regmap_write(dev->map, DW_IC_DATA_CMD, BIT(9));
+ }
+ }
+ }
+
+ return num_msgs;
+}
+
/*
* Initiate (and continue) low level master read/write transaction.
* This function is only called from i2c_dw_isr, and pumping i2c_msg
@@ -568,6 +637,11 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
goto done_nolock;
}
+ if ((dev->flags & MODEL_MASK) == MODEL_WANGXUN_SP) {
+ ret = txgbe_i2c_dw_xfer_quirk(adap, msgs, num);
+ goto done_nolock;
+ }
+
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -848,7 +922,7 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
return 0;
}
-static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev)
+static int poll_i2c_adap_quirk(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
int ret;
@@ -862,6 +936,17 @@ static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev)
return ret;
}
+static bool i2c_is_model_poll(struct dw_i2c_dev *dev)
+{
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_AMD_NAVI_GPU:
+ case MODEL_WANGXUN_SP:
+ return true;
+ default:
+ return false;
+ }
+}
+
int i2c_dw_probe_master(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
@@ -917,8 +1002,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
- if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
- return amd_i2c_adap_quirk(dev);
+ if (i2c_is_model_poll(dev))
+ return poll_i2c_adap_quirk(dev);
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
irq_flags = IRQF_NO_SUSPEND;
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/platform_data/i2c-dw.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -179,12 +180,14 @@ static void dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *dev)
static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
{
struct platform_device *pdev = to_platform_device(dev->dev);
- int ret;
+ int ret = 0;
switch (dev->flags & MODEL_MASK) {
case MODEL_BAIKAL_BT1:
ret = bt1_i2c_request_regs(dev);
break;
+ case MODEL_WANGXUN_SP:
+ break;
default:
dev->base = devm_platform_ioremap_resource(pdev, 0);
ret = PTR_ERR_OR_ZERO(dev->base);
@@ -194,6 +197,35 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
return ret;
}
+static void dw_i2c_get_plat_data(struct dw_i2c_dev *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev->dev);
+ struct dw_i2c_platform_data *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata)
+ return;
+
+ dev->flags |= pdata->flags;
+ dev->base = pdata->base;
+
+ if (pdata->ss_hcnt && pdata->ss_lcnt) {
+ dev->ss_hcnt = pdata->ss_hcnt;
+ dev->ss_lcnt = pdata->ss_lcnt;
+ } else {
+ dev->ss_hcnt = 6;
+ dev->ss_lcnt = 8;
+ }
+
+ if (pdata->fs_hcnt && pdata->fs_lcnt) {
+ dev->fs_hcnt = pdata->fs_hcnt;
+ dev->fs_lcnt = pdata->fs_lcnt;
+ } else {
+ dev->fs_hcnt = 6;
+ dev->fs_lcnt = 8;
+ }
+}
+
static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
{
.ident = "Qtechnology QT5222",
@@ -282,6 +314,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
dev->irq = irq;
platform_set_drvdata(pdev, dev);
+ dw_i2c_get_plat_data(dev);
+
ret = dw_i2c_plat_request_regs(dev);
if (ret)
return ret;
new file mode 100644
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_I2C_DW_H
+#define _LINUX_I2C_DW_H
+
+struct dw_i2c_platform_data {
+ void __iomem *base;
+ unsigned int flags;
+ unsigned int ss_hcnt;
+ unsigned int ss_lcnt;
+ unsigned int fs_hcnt;
+ unsigned int fs_lcnt;
+};
+
+#endif /* _LINUX_I2C_DW_H */
Wangxun 10Gb ethernet chip is connected to Designware I2C, to communicate with SFP. Add platform data to pass IOMEM base address, board flag and other parameters, since resource address was mapped on ethernet driver. The exists IP limitations are dealt as workarounds: - IP does not support interrupt mode, it works on polling mode. - I2C cannot read continuously, only one byte can at a time. - Additionally set FIFO depth address the chip issue. Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com> --- drivers/i2c/busses/i2c-designware-common.c | 4 + drivers/i2c/busses/i2c-designware-core.h | 1 + drivers/i2c/busses/i2c-designware-master.c | 91 ++++++++++++++++++++- drivers/i2c/busses/i2c-designware-platdrv.c | 36 +++++++- include/linux/platform_data/i2c-dw.h | 15 ++++ 5 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 include/linux/platform_data/i2c-dw.h