Merge branch 'i2c/software-nodes' into i2c/for-5.13
authorWolfram Sang <wsa@kernel.org>
Sat, 10 Apr 2021 19:48:34 +0000 (21:48 +0200)
committerWolfram Sang <wsa@kernel.org>
Sat, 10 Apr 2021 19:48:34 +0000 (21:48 +0200)
20 files changed:
MAINTAINERS
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-brcmstb.c
drivers/i2c/busses/i2c-cp2615.c [new file with mode: 0644]
drivers/i2c/busses/i2c-designware-common.c
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-master.c
drivers/i2c/busses/i2c-designware-pcidrv.c
drivers/i2c/busses/i2c-mlxbf.c
drivers/i2c/busses/i2c-powermac.c
drivers/i2c/busses/i2c-qcom-cci.c
drivers/i2c/busses/i2c-rcar.c
drivers/i2c/busses/i2c-scmi.c
drivers/i2c/busses/i2c-stm32f7.c
drivers/i2c/busses/i2c-tegra-bpmp.c
drivers/i2c/busses/i2c-xgene-slimpro.c
drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-dev.c
include/linux/i2c.h

index c80ad73..2e7607a 100644 (file)
@@ -4585,6 +4585,11 @@ F:       drivers/counter/
 F:     include/linux/counter.h
 F:     include/linux/counter_enum.h
 
+CP2615 I2C DRIVER
+M:     Bence Csókás <bence98@sch.bme.hu>
+S:     Maintained
+F:     drivers/i2c/busses/i2c-cp2615.c
+
 CPMAC ETHERNET DRIVER
 M:     Florian Fainelli <f.fainelli@gmail.com>
 L:     netdev@vger.kernel.org
index 05ebf75..338a306 100644 (file)
@@ -1199,6 +1199,16 @@ config I2C_DLN2
         This driver can also be built as a module.  If so, the module
         will be called i2c-dln2.
 
+config I2C_CP2615
+       tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
+       depends on USB
+       help
+         If you say yes to this option, support will be included for Silicon
+         Labs CP2615's I2C interface.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-cp2615.
+
 config I2C_PARPORT
        tristate "Parallel port adapter"
        depends on PARPORT
index 615f35e..45cd535 100644 (file)
@@ -123,6 +123,7 @@ obj-$(CONFIG_I2C_RCAR)              += i2c-rcar.o
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)   += i2c-diolan-u2c.o
 obj-$(CONFIG_I2C_DLN2)         += i2c-dln2.o
+obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
 obj-$(CONFIG_I2C_PARPORT)      += i2c-parport.o
 obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF)       += i2c-robotfuzz-osif.o
 obj-$(CONFIG_I2C_TAOS_EVM)     += i2c-taos-evm.o
index ba766d2..490ee39 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 
 #define N_DATA_REGS                                    8
 
diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c
new file mode 100644 (file)
index 0000000..78cfecd
--- /dev/null
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * i2c support for Silicon Labs' CP2615 Digital Audio Bridge
+ *
+ * (c) 2021, Bence Csókás <bence98@sch.bme.hu>
+ */
+
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+
+/** CP2615 I/O Protocol implementation */
+
+#define CP2615_VID 0x10c4
+#define CP2615_PID 0xeac1
+
+#define IOP_EP_IN  0x82
+#define IOP_EP_OUT 0x02
+#define IOP_IFN 1
+#define IOP_ALTSETTING 2
+
+#define MAX_IOP_SIZE 64
+#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6)
+#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4)
+
+enum cp2615_iop_msg_type {
+       iop_GetAccessoryInfo = 0xD100,
+       iop_AccessoryInfo = 0xA100,
+       iop_GetPortConfiguration = 0xD203,
+       iop_PortConfiguration = 0xA203,
+       iop_DoI2cTransfer = 0xD400,
+       iop_I2cTransferResult = 0xA400,
+       iop_GetSerialState = 0xD501,
+       iop_SerialState = 0xA501
+};
+
+struct __packed cp2615_iop_msg {
+       __be16 preamble, length, msg;
+       u8 data[MAX_IOP_PAYLOAD_SIZE];
+};
+
+#define PART_ID_A01 0x1400
+#define PART_ID_A02 0x1500
+
+struct __packed cp2615_iop_accessory_info {
+       __be16 part_id, option_id, proto_ver;
+};
+
+struct __packed cp2615_i2c_transfer {
+       u8 tag, i2caddr, read_len, write_len;
+       u8 data[MAX_I2C_SIZE];
+};
+
+/* Possible values for struct cp2615_i2c_transfer_result.status */
+enum cp2615_i2c_status {
+       /* Writing to the internal EEPROM failed, because it is locked */
+       CP2615_CFG_LOCKED = -6,
+       /* read_len or write_len out of range */
+       CP2615_INVALID_PARAM = -4,
+       /* I2C slave did not ACK in time */
+       CP2615_TIMEOUT,
+       /* I2C bus busy */
+       CP2615_BUS_BUSY,
+       /* I2C bus error (ie. device NAK'd the request) */
+       CP2615_BUS_ERROR,
+       CP2615_SUCCESS
+};
+
+struct __packed cp2615_i2c_transfer_result {
+       u8 tag, i2caddr;
+       s8 status;
+       u8 read_len;
+       u8 data[MAX_I2C_SIZE];
+};
+
+static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg,
+                       const void *data, size_t data_len)
+{
+       if (data_len > MAX_IOP_PAYLOAD_SIZE)
+               return -EFBIG;
+
+       if (!ret)
+               return -EINVAL;
+
+       ret->preamble = 0x2A2A;
+       ret->length = htons(data_len + 6);
+       ret->msg = htons(msg);
+       if (data && data_len)
+               memcpy(&ret->data, data, data_len);
+       return 0;
+}
+
+static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data)
+{
+       return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len);
+}
+
+/* Translates status codes to Linux errno's */
+static int cp2615_check_status(enum cp2615_i2c_status status)
+{
+       switch (status) {
+       case CP2615_SUCCESS:
+                       return 0;
+       case CP2615_BUS_ERROR:
+               return -ENXIO;
+       case CP2615_BUS_BUSY:
+               return -EAGAIN;
+       case CP2615_TIMEOUT:
+               return -ETIMEDOUT;
+       case CP2615_INVALID_PARAM:
+               return -EINVAL;
+       case CP2615_CFG_LOCKED:
+               return -EPERM;
+       }
+       /* Unknown error code */
+       return -EPROTO;
+}
+
+/** Driver code */
+
+static int
+cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = cp2615_init_i2c_msg(msg, i2c_w);
+
+       if (!res)
+               res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+                                  msg, ntohs(msg->length), NULL, 0);
+       kfree(msg);
+       return res;
+}
+
+static int
+cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct cp2615_i2c_transfer_result *i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
+                              msg, sizeof(struct cp2615_iop_msg), NULL, 0);
+
+       if (res < 0) {
+               kfree(msg);
+               return res;
+       }
+
+       if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) {
+               kfree(msg);
+               return -EIO;
+       }
+
+       res = cp2615_check_status(i2c_r->status);
+       if (!res)
+               memcpy(buf, &i2c_r->data, i2c_r->read_len);
+
+       kfree(msg);
+       return res;
+}
+
+/* Checks if the IOP is functional by querying the part's ID */
+static int cp2615_check_iop(struct usb_interface *usbif)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0);
+
+       if (res)
+               goto out;
+
+       res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+                                  msg, ntohs(msg->length), NULL, 0);
+       if (res)
+               goto out;
+
+       res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
+                              msg, sizeof(struct cp2615_iop_msg), NULL, 0);
+       if (res)
+               goto out;
+
+       if (msg->msg != htons(iop_AccessoryInfo)) {
+               res = -EIO;
+               goto out;
+       }
+
+       switch (ntohs(info->part_id)) {
+       case PART_ID_A01:
+               dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n");
+               break;
+       case PART_ID_A02:
+               dev_dbg(&usbif->dev, "Found good A02 part.\n");
+               break;
+       default:
+               dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id));
+       }
+
+out:
+       kfree(msg);
+       return res;
+}
+
+static int
+cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct usb_interface *usbif = adap->algo_data;
+       int i = 0, ret = 0;
+       struct i2c_msg *msg;
+       struct cp2615_i2c_transfer i2c_w = {0};
+
+       dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num);
+
+       for (; !ret && i < num; i++) {
+               msg = &msgs[i];
+
+               i2c_w.tag = 0xdd;
+               i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg);
+               if (msg->flags & I2C_M_RD) {
+                       i2c_w.read_len = msg->len;
+                       i2c_w.write_len = 0;
+               } else {
+                       i2c_w.read_len = 0;
+                       i2c_w.write_len = msg->len;
+                       memcpy(&i2c_w.data, msg->buf, i2c_w.write_len);
+               }
+               ret = cp2615_i2c_send(usbif, &i2c_w);
+               if (ret)
+                       break;
+               ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf);
+       }
+       if (ret < 0)
+               return ret;
+       return i;
+}
+
+static u32
+cp2615_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm cp2615_i2c_algo = {
+       .master_xfer    = cp2615_i2c_master_xfer,
+       .functionality  = cp2615_i2c_func,
+};
+
+/*
+ * This chip has some limitations: one is that the USB endpoint
+ * can only receive 64 bytes/transfer, that leaves 54 bytes for
+ * the I2C transfer. On top of that, EITHER read_len OR write_len
+ * may be zero, but not both. If both are non-zero, the adapter
+ * issues a write followed by a read. And the chip does not
+ * support repeated START between the write and read phases.
+ */
+static struct i2c_adapter_quirks cp2615_i2c_quirks = {
+       .max_write_len = MAX_I2C_SIZE,
+       .max_read_len = MAX_I2C_SIZE,
+       .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
+       .max_comb_1st_msg_len = MAX_I2C_SIZE,
+       .max_comb_2nd_msg_len = MAX_I2C_SIZE
+};
+
+static void
+cp2615_i2c_remove(struct usb_interface *usbif)
+{
+       struct i2c_adapter *adap = usb_get_intfdata(usbif);
+
+       usb_set_intfdata(usbif, NULL);
+       i2c_del_adapter(adap);
+}
+
+static int
+cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id)
+{
+       int ret = 0;
+       struct i2c_adapter *adap;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+
+       ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING);
+       if (ret)
+               return ret;
+
+       ret = cp2615_check_iop(usbif);
+       if (ret)
+               return ret;
+
+       adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
+       if (!adap)
+               return -ENOMEM;
+
+       strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1);
+       adap->owner = THIS_MODULE;
+       adap->dev.parent = &usbif->dev;
+       adap->dev.of_node = usbif->dev.of_node;
+       adap->timeout = HZ;
+       adap->algo = &cp2615_i2c_algo;
+       adap->quirks = &cp2615_i2c_quirks;
+       adap->algo_data = usbif;
+
+       ret = i2c_add_adapter(adap);
+       if (ret)
+               return ret;
+
+       usb_set_intfdata(usbif, adap);
+       return 0;
+}
+
+static const struct usb_device_id id_table[] = {
+       { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver cp2615_i2c_driver = {
+       .name = "i2c-cp2615",
+       .probe = cp2615_i2c_probe,
+       .disconnect = cp2615_i2c_remove,
+       .id_table = id_table,
+};
+
+module_usb_driver(cp2615_i2c_driver);
+
+MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>");
+MODULE_DESCRIPTION("CP2615 I2C bus driver");
+MODULE_LICENSE("GPL");
index 3c19aad..fdc34d9 100644 (file)
@@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
        reg = readl(dev->base + DW_IC_COMP_TYPE);
        i2c_dw_release_lock(dev);
 
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+               map_cfg.max_register = AMD_UCSI_INTR_REG;
+
        if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
                map_cfg.reg_read = dw_reg_read_swab;
                map_cfg.reg_write = dw_reg_write_swab;
index 5392b82..6a53f75 100644 (file)
@@ -295,8 +295,16 @@ 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_MASK             GENMASK(11, 8)
 
+/*
+ * Enable UCSI interrupt by writing 0xd at register
+ * offset 0x474 specified in hardware specification.
+ */
+#define AMD_UCSI_INTR_REG      0x474
+#define AMD_UCSI_INTR_EN       0xd
+
 int i2c_dw_init_regmap(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);
index dd27b9d..8a59470 100644 (file)
 
 #include "i2c-designware-core.h"
 
+#define AMD_TIMEOUT_MIN_US     25
+#define AMD_TIMEOUT_MAX_US     250
+#define AMD_MASTERCFG_MASK     GENMASK(15, 0)
+
 static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
 {
        /* Configure Tx/Rx FIFO threshold levels */
@@ -78,7 +82,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
         * difference is the timing parameter values since the registers are
         * the same.
         */
-       if (t->bus_freq_hz == 1000000) {
+       if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) {
                /*
                 * Check are Fast Mode Plus parameters available. Calculate
                 * SCL timing parameters for Fast Mode Plus if not set.
@@ -259,6 +263,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
        regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
 }
 
+static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev)
+{
+       u32 val;
+       int ret;
+
+       ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val,
+                                      !(val & DW_IC_INTR_STOP_DET),
+                                       1100, 20000);
+       if (ret)
+               dev_err(dev->dev, "i2c timeout error %d\n", ret);
+
+       return ret;
+}
+
+static int i2c_dw_status(struct dw_i2c_dev *dev)
+{
+       int status;
+
+       status = i2c_dw_wait_bus_not_busy(dev);
+       if (status)
+               return status;
+
+       return i2c_dw_check_stopbit(dev);
+}
+
+/*
+ * Initiate and continue master read/write transaction with polling
+ * based transfer routine afterward write messages into the Tx buffer.
+ */
+static int amd_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_wrt_idx, msg_itr_lmt, buf_len, data_idx;
+       int cmd = 0, status;
+       u8 *tx_buf;
+       u32 val;
+
+       /*
+        * In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
+        * it is mandatory to set the right value in specific register
+        * (offset:0x474) as per the hardware IP specification.
+        */
+       regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN);
+
+       dev->msgs = msgs;
+       dev->msgs_num = num_msgs;
+       i2c_dw_xfer_init(dev);
+       i2c_dw_disable_int(dev);
+
+       /* Initiate messages read/write transaction */
+       for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) {
+               tx_buf = msgs[msg_wrt_idx].buf;
+               buf_len = msgs[msg_wrt_idx].len;
+
+               if (!(msgs[msg_wrt_idx].flags & I2C_M_RD))
+                       regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1);
+               /*
+                * Initiate the i2c read/write transaction of buffer length,
+                * and poll for bus busy status. For the last message transfer,
+                * update the command with stopbit enable.
+                */
+               for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
+                       if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
+                               cmd |= BIT(9);
+
+                       if (msgs[msg_wrt_idx].flags & I2C_M_RD) {
+                               /* Due to hardware bug, need to write the same command twice. */
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x100);
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd);
+                               if (cmd) {
+                                       regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1));
+                                       regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1));
+                                       /*
+                                        * Need to check the stop bit. However, it cannot be
+                                        * detected from the registers so we check it always
+                                        * when read/write the last byte.
+                                        */
+                                       status = i2c_dw_status(dev);
+                                       if (status)
+                                               return status;
+
+                                       for (data_idx = 0; data_idx < buf_len; data_idx++) {
+                                               regmap_read(dev->map, DW_IC_DATA_CMD, &val);
+                                               tx_buf[data_idx] = val;
+                                       }
+                                       status = i2c_dw_check_stopbit(dev);
+                                       if (status)
+                                               return status;
+                               }
+                       } else {
+                               regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd);
+                               usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US);
+                       }
+               }
+               status = i2c_dw_check_stopbit(dev);
+               if (status)
+                       return status;
+       }
+
+       return 0;
+}
+
 /*
  * Initiate (and continue) low level master read/write transaction.
  * This function is only called from i2c_dw_isr, and pumping i2c_msg
@@ -462,6 +568,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 
        pm_runtime_get_sync(dev->dev);
 
+       /*
+        * Initiate I2C message transfer when AMD NAVI GPU card is enabled,
+        * As it is polling based transfer mechanism, which does not support
+        * interrupt based functionalities of existing DesignWare driver.
+        */
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
+               ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
+               goto done_nolock;
+       }
+
        if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
                ret = -ESHUTDOWN;
                goto done_nolock;
@@ -738,6 +854,20 @@ 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)
+{
+       struct i2c_adapter *adap = &dev->adapter;
+       int ret;
+
+       pm_runtime_get_noresume(dev->dev);
+       ret = i2c_add_numbered_adapter(adap);
+       if (ret)
+               dev_err(dev->dev, "Failed to add adapter: %d\n", ret);
+       pm_runtime_put_noidle(dev->dev);
+
+       return ret;
+}
+
 int i2c_dw_probe_master(struct dw_i2c_dev *dev)
 {
        struct i2c_adapter *adap = &dev->adapter;
@@ -774,6 +904,9 @@ 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 (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
                irq_flags = IRQF_NO_SUSPEND;
        } else {
index 55c83a7..0f409a4 100644 (file)
@@ -26,6 +26,7 @@
 #include "i2c-designware-core.h"
 
 #define DRIVER_NAME "i2c-designware-pci"
+#define AMD_CLK_RATE_HZ        100000
 
 enum dw_pci_ctl_id_t {
        medfield,
@@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t {
        cherrytrail,
        haswell,
        elkhartlake,
+       navi_amd,
 };
 
 struct dw_scl_sda_cfg {
@@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = {
        .sda_hold = 0x9,
 };
 
+/* NAVI-AMD HCNT/LCNT/SDA hold time */
+static struct dw_scl_sda_cfg navi_amd_config = {
+       .ss_hcnt = 0x1ae,
+       .ss_lcnt = 0x23a,
+       .sda_hold = 0x9,
+};
+
 static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
 {
        return 25000;
 }
 
+static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+       return AMD_CLK_RATE_HZ;
+}
+
 static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
 {
        struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
        return -ENODEV;
 }
 
+ /*
+  * TODO find a better way how to deduplicate instantiation
+  * of USB PD slave device from nVidia GPU driver.
+  */
+static int navi_amd_register_client(struct dw_i2c_dev *dev)
+{
+       struct i2c_board_info   info;
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
+       info.addr = 0x08;
+       info.irq = dev->irq;
+
+       dev->slave = i2c_new_client_device(&dev->adapter, &info);
+       if (IS_ERR(dev->slave))
+               return PTR_ERR(dev->slave);
+
+       return 0;
+}
+
+static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
+{
+       struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
+
+       dev->flags |= MODEL_AMD_NAVI_GPU;
+       dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+       return 0;
+}
+
 static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
 {
        /*
@@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = {
                .bus_num = -1,
                .get_clk_rate_khz = ehl_get_clk_rate_khz,
        },
+       [navi_amd] = {
+               .bus_num = -1,
+               .scl_sda_cfg = &navi_amd_config,
+               .setup =  navi_amd_setup,
+               .get_clk_rate_khz = navi_amd_get_clk_rate_khz,
+       },
 };
 
 #ifdef CONFIG_PM
@@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
                return r;
        }
 
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
+               r = navi_amd_register_client(dev);
+               if (r) {
+                       dev_err(dev->dev, "register client failed with %d\n", r);
+                       return r;
+               }
+       }
+
        pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_put_autosuspend(&pdev->dev);
@@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
        { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake },
        { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake },
        { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake },
+       { PCI_VDEVICE(ATI,  0x7314), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73a4), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73e4), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73c4), navi_amd },
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
index 2fb0532..80ab831 100644 (file)
 #define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF            0x14
 #define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT           0x18
 
-enum {
-       MLXBF_I2C_TIMING_100KHZ = 100000,
-       MLXBF_I2C_TIMING_400KHZ = 400000,
-       MLXBF_I2C_TIMING_1000KHZ = 1000000,
-};
-
 /*
  * Defines SMBus operating frequency and core clock frequency.
  * According to ADB files, default values are compliant to 100KHz SMBus
@@ -1202,7 +1196,7 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
 
        ret = device_property_read_u32(dev, "clock-frequency", &config_khz);
        if (ret < 0)
-               config_khz = MLXBF_I2C_TIMING_100KHZ;
+               config_khz = I2C_MAX_STANDARD_MODE_FREQ;
 
        switch (config_khz) {
        default:
@@ -1210,15 +1204,15 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
                pr_warn("Illegal value %d: defaulting to 100 KHz\n",
                        config_khz);
                fallthrough;
-       case MLXBF_I2C_TIMING_100KHZ:
+       case I2C_MAX_STANDARD_MODE_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ;
                break;
 
-       case MLXBF_I2C_TIMING_400KHZ:
+       case I2C_MAX_FAST_MODE_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ;
                break;
 
-       case MLXBF_I2C_TIMING_1000KHZ:
+       case I2C_MAX_FAST_MODE_PLUS_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ;
                break;
        }
index 3e38e11..5241e6f 100644 (file)
@@ -76,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer(  struct i2c_adapter*     adap,
         * but I think the current API makes no sense and I don't want
         * any driver that I haven't verified for correctness to go
         * anywhere near a pmac i2c bus anyway ...
-        *
-        * I'm also not completely sure what kind of phases to do between
-        * the actual command and the data (what I am _supposed_ to do that
-        * is). For now, I assume writes are a single stream and reads have
-        * a repeat start/addr phase (but not stop in between)
         */
         case I2C_SMBUS_BLOCK_DATA:
                buf = data->block;
index 1c259b5..c63d554 100644 (file)
@@ -569,9 +569,9 @@ static int cci_probe(struct platform_device *pdev)
                cci->master[idx].mode = I2C_MODE_STANDARD;
                ret = of_property_read_u32(child, "clock-frequency", &val);
                if (!ret) {
-                       if (val == 400000)
+                       if (val == I2C_MAX_FAST_MODE_FREQ)
                                cci->master[idx].mode = I2C_MODE_FAST;
-                       else if (val == 1000000)
+                       else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
                                cci->master[idx].mode = I2C_MODE_FAST_PLUS;
                }
 
index 12f6d45..1f0c164 100644 (file)
@@ -141,6 +141,7 @@ struct rcar_i2c_priv {
        enum dma_data_direction dma_direction;
 
        struct reset_control *rstc;
+       bool atomic_xfer;
        int irq;
 
        struct i2c_client *host_notify_client;
@@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
                        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);
+
+       if (!priv->atomic_xfer)
+               rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
 }
 
 static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
@@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
        int len;
 
        /* Do various checks to see if DMA is feasible at all */
-       if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
+       if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
            !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
                return false;
 
@@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
        /* Nack */
        if (msr & MNR) {
                /* HW automatically sends STOP after received NACK */
-               rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
+               if (!priv->atomic_xfer)
+                       rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
                priv->flags |= ID_NACK;
                goto out;
        }
@@ -667,7 +671,8 @@ out:
        if (priv->flags & ID_DONE) {
                rcar_i2c_write(priv, ICMIER, 0);
                rcar_i2c_write(priv, ICMSR, 0);
-               wake_up(&priv->wait);
+               if (!priv->atomic_xfer)
+                       wake_up(&priv->wait);
        }
 
        return IRQ_HANDLED;
@@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
 
        /* Only handle interrupts that are currently enabled */
        msr = rcar_i2c_read(priv, ICMSR);
-       msr &= rcar_i2c_read(priv, ICMIER);
+       if (!priv->atomic_xfer)
+               msr &= rcar_i2c_read(priv, ICMIER);
 
        return rcar_i2c_irq(irq, priv, msr);
 }
@@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
 
        /* Only handle interrupts that are currently enabled */
        msr = rcar_i2c_read(priv, ICMSR);
-       msr &= rcar_i2c_read(priv, ICMIER);
+       if (!priv->atomic_xfer)
+               msr &= rcar_i2c_read(priv, ICMIER);
 
        /*
         * Clear START or STOP immediately, except for REPSTART after read or
@@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
        int i, ret;
        long time_left;
 
+       priv->atomic_xfer = false;
+
        pm_runtime_get_sync(dev);
 
        /* Check bus state before init otherwise bus busy info will be lost */
@@ -858,6 +867,68 @@ out:
        return ret;
 }
 
+static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap,
+                               struct i2c_msg *msgs,
+                               int num)
+{
+       struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+       struct device *dev = rcar_i2c_priv_to_dev(priv);
+       unsigned long j;
+       bool time_left;
+       int ret;
+
+       priv->atomic_xfer = true;
+
+       pm_runtime_get_sync(dev);
+
+       /* Check bus state before init otherwise bus busy info will be lost */
+       ret = rcar_i2c_bus_barrier(priv);
+       if (ret < 0)
+               goto out;
+
+       rcar_i2c_init(priv);
+
+       /* init first message */
+       priv->msg = msgs;
+       priv->msgs_left = num;
+       priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
+       rcar_i2c_prepare_msg(priv);
+
+       j = jiffies + num * adap->timeout;
+       do {
+               u32 msr = rcar_i2c_read(priv, ICMSR);
+
+               msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP;
+
+               if (msr) {
+                       if (priv->devtype < I2C_RCAR_GEN3)
+                               rcar_i2c_gen2_irq(0, priv);
+                       else
+                               rcar_i2c_gen3_irq(0, priv);
+               }
+
+               time_left = time_before_eq(jiffies, j);
+       } while (!(priv->flags & ID_DONE) && time_left);
+
+       if (!time_left) {
+               rcar_i2c_init(priv);
+               ret = -ETIMEDOUT;
+       } else if (priv->flags & ID_NACK) {
+               ret = -ENXIO;
+       } else if (priv->flags & ID_ARBLOST) {
+               ret = -EAGAIN;
+       } else {
+               ret = num - priv->msgs_left; /* The number of transfer */
+       }
+out:
+       pm_runtime_put(dev);
+
+       if (ret < 0 && ret != -ENXIO)
+               dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+       return ret;
+}
+
 static int rcar_reg_slave(struct i2c_client *slave)
 {
        struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
@@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
 
 static const struct i2c_algorithm rcar_i2c_algo = {
        .master_xfer    = rcar_i2c_master_xfer,
+       .master_xfer_atomic = rcar_i2c_master_xfer_atomic,
        .functionality  = rcar_i2c_func,
        .reg_slave      = rcar_reg_slave,
        .unreg_slave    = rcar_unreg_slave,
index 1dc3873..6746aa4 100644 (file)
@@ -18,8 +18,6 @@
 /* SMBUS HID definition as supported by Microsoft Windows */
 #define ACPI_SMBUS_MS_HID              "SMB0001"
 
-ACPI_MODULE_NAME("smbus_cmi");
-
 struct smbus_methods_t {
        char *mt_info;
        char *mt_sbr;
index c62c815..7e06e6b 100644 (file)
@@ -164,7 +164,6 @@ enum {
 #define STM32F7_I2C_DNF_DEFAULT                        0
 #define STM32F7_I2C_DNF_MAX                    15
 
-#define STM32F7_I2C_ANALOG_FILTER_ENABLE       1
 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN    50      /* ns */
 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX    260     /* ns */
 
@@ -223,8 +222,6 @@ struct stm32f7_i2c_spec {
  * @clock_src: I2C clock source frequency (Hz)
  * @rise_time: Rise time (ns)
  * @fall_time: Fall time (ns)
- * @dnf: Digital filter coefficient (0-16)
- * @analog_filter: Analog filter delay (On/Off)
  * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
  */
 struct stm32f7_i2c_setup {
@@ -232,8 +229,6 @@ struct stm32f7_i2c_setup {
        u32 clock_src;
        u32 rise_time;
        u32 fall_time;
-       u8 dnf;
-       bool analog_filter;
        u32 fmp_clr_offset;
 };
 
@@ -312,6 +307,9 @@ struct stm32f7_i2c_msg {
  * @wakeup_src: boolean to know if the device is a wakeup source
  * @smbus_mode: states that the controller is configured in SMBus mode
  * @host_notify_client: SMBus host-notify client
+ * @analog_filter: boolean to indicate enabling of the analog filter
+ * @dnf_dt: value of digital filter requested via dt
+ * @dnf: value of digital filter to apply
  */
 struct stm32f7_i2c_dev {
        struct i2c_adapter adap;
@@ -340,6 +338,9 @@ struct stm32f7_i2c_dev {
        bool wakeup_src;
        bool smbus_mode;
        struct i2c_client *host_notify_client;
+       bool analog_filter;
+       u32 dnf_dt;
+       u32 dnf;
 };
 
 /*
@@ -385,15 +386,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = {
 static const struct stm32f7_i2c_setup stm32f7_setup = {
        .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
        .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
-       .dnf = STM32F7_I2C_DNF_DEFAULT,
-       .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
 };
 
 static const struct stm32f7_i2c_setup stm32mp15_setup = {
        .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
        .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
-       .dnf = STM32F7_I2C_DNF_DEFAULT,
-       .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
        .fmp_clr_offset = 0x40,
 };
 
@@ -462,27 +459,28 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
                return -EINVAL;
        }
 
-       if (setup->dnf > STM32F7_I2C_DNF_MAX) {
+       i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk);
+       if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
                dev_err(i2c_dev->dev,
                        "DNF out of bound %d/%d\n",
-                       setup->dnf, STM32F7_I2C_DNF_MAX);
+                       i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
                return -EINVAL;
        }
 
        /*  Analog and Digital Filters */
        af_delay_min =
-               (setup->analog_filter ?
+               (i2c_dev->analog_filter ?
                 STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
        af_delay_max =
-               (setup->analog_filter ?
+               (i2c_dev->analog_filter ?
                 STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
-       dnf_delay = setup->dnf * i2cclk;
+       dnf_delay = i2c_dev->dnf * i2cclk;
 
        sdadel_min = specs->hddat_min + setup->fall_time -
-               af_delay_min - (setup->dnf + 3) * i2cclk;
+               af_delay_min - (i2c_dev->dnf + 3) * i2cclk;
 
        sdadel_max = specs->vddat_max - setup->rise_time -
-               af_delay_max - (setup->dnf + 4) * i2cclk;
+               af_delay_max - (i2c_dev->dnf + 4) * i2cclk;
 
        scldel_min = setup->rise_time + specs->sudat_min;
 
@@ -648,6 +646,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
        setup->speed_freq = t->bus_freq_hz;
        i2c_dev->setup.rise_time = t->scl_rise_ns;
        i2c_dev->setup.fall_time = t->scl_fall_ns;
+       i2c_dev->dnf_dt = t->digital_filter_width_ns;
        setup->clock_src = clk_get_rate(i2c_dev->clk);
 
        if (!setup->clock_src) {
@@ -655,6 +654,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
                return -EINVAL;
        }
 
+       if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
+               i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT;
+
        do {
                ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
                                                 &i2c_dev->timing);
@@ -676,12 +678,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
                return ret;
        }
 
+       i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
+                                                      "i2c-analog-filter");
+
        dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
                setup->speed_freq, setup->clock_src);
        dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
                setup->rise_time, setup->fall_time);
        dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
-               (setup->analog_filter ? "On" : "Off"), setup->dnf);
+               (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf);
 
        i2c_dev->bus_rate = setup->speed_freq;
 
@@ -720,8 +725,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
        timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
        writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
 
-       /* Enable I2C */
-       if (i2c_dev->setup.analog_filter)
+       /* Configure the Analog Filter */
+       if (i2c_dev->analog_filter)
                stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
                                     STM32F7_I2C_CR1_ANFOFF);
        else
@@ -732,7 +737,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
        stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
                             STM32F7_I2C_CR1_DNF_MASK);
        stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
-                            STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf));
+                            STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
 
        stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
                             STM32F7_I2C_CR1_PE);
@@ -1597,7 +1602,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
 
        /* Bus error */
        if (status & STM32F7_I2C_ISR_BERR) {
-               dev_err(dev, "<%s>: Bus error\n", __func__);
+               dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
                stm32f7_i2c_release_bus(&i2c_dev->adap);
                f7_msg->result = -EIO;
@@ -1605,13 +1611,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
 
        /* Arbitration loss */
        if (status & STM32F7_I2C_ISR_ARLO) {
-               dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
+               dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
                f7_msg->result = -EAGAIN;
        }
 
        if (status & STM32F7_I2C_ISR_PECERR) {
-               dev_err(dev, "<%s>: PEC error in reception\n", __func__);
+               dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
                f7_msg->result = -EINVAL;
        }
@@ -2027,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
        phy_addr = (dma_addr_t)res->start;
 
        irq_event = platform_get_irq(pdev, 0);
-       if (irq_event <= 0) {
-               if (irq_event != -EPROBE_DEFER)
-                       dev_err(&pdev->dev, "Failed to get IRQ event: %d\n",
-                               irq_event);
+       if (irq_event <= 0)
                return irq_event ? : -ENOENT;
-       }
 
        irq_error = platform_get_irq(pdev, 1);
        if (irq_error <= 0)
@@ -2267,8 +2271,7 @@ static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
+static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
 {
        int ret;
        struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
@@ -2289,7 +2292,7 @@ static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
        return ret;
 }
 
-static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
+static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
 {
        u32 cr1;
        int ret;
@@ -2320,7 +2323,7 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
        return ret;
 }
 
-static int stm32f7_i2c_suspend(struct device *dev)
+static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
@@ -2341,7 +2344,7 @@ static int stm32f7_i2c_suspend(struct device *dev)
        return 0;
 }
 
-static int stm32f7_i2c_resume(struct device *dev)
+static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
@@ -2361,7 +2364,6 @@ static int stm32f7_i2c_resume(struct device *dev)
 
        return 0;
 }
-#endif
 
 static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
        SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
index c0c7d01..3680d60 100644 (file)
@@ -38,49 +38,31 @@ struct tegra_bpmp_i2c {
  * firmware I2C driver to avoid any issues in future if Linux I2C flags are
  * changed.
  */
-static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
+static void tegra_bpmp_xlate_flags(u16 flags, u16 *out)
 {
-       if (flags & I2C_M_TEN) {
+       if (flags & I2C_M_TEN)
                *out |= SERIALI2C_TEN;
-               flags &= ~I2C_M_TEN;
-       }
 
-       if (flags & I2C_M_RD) {
+       if (flags & I2C_M_RD)
                *out |= SERIALI2C_RD;
-               flags &= ~I2C_M_RD;
-       }
 
-       if (flags & I2C_M_STOP) {
+       if (flags & I2C_M_STOP)
                *out |= SERIALI2C_STOP;
-               flags &= ~I2C_M_STOP;
-       }
 
-       if (flags & I2C_M_NOSTART) {
+       if (flags & I2C_M_NOSTART)
                *out |= SERIALI2C_NOSTART;
-               flags &= ~I2C_M_NOSTART;
-       }
 
-       if (flags & I2C_M_REV_DIR_ADDR) {
+       if (flags & I2C_M_REV_DIR_ADDR)
                *out |= SERIALI2C_REV_DIR_ADDR;
-               flags &= ~I2C_M_REV_DIR_ADDR;
-       }
 
-       if (flags & I2C_M_IGNORE_NAK) {
+       if (flags & I2C_M_IGNORE_NAK)
                *out |= SERIALI2C_IGNORE_NAK;
-               flags &= ~I2C_M_IGNORE_NAK;
-       }
 
-       if (flags & I2C_M_NO_RD_ACK) {
+       if (flags & I2C_M_NO_RD_ACK)
                *out |= SERIALI2C_NO_RD_ACK;
-               flags &= ~I2C_M_NO_RD_ACK;
-       }
 
-       if (flags & I2C_M_RECV_LEN) {
+       if (flags & I2C_M_RECV_LEN)
                *out |= SERIALI2C_RECV_LEN;
-               flags &= ~I2C_M_RECV_LEN;
-       }
-
-       return 0;
 }
 
 /**
@@ -97,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
  *
  * See deserialize_i2c documentation for the data format in the other direction.
  */
-static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
+static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
                                        struct mrq_i2c_request *request,
                                        struct i2c_msg *msgs,
                                        unsigned int num)
 {
        char *buf = request->xfer.data_buf;
        unsigned int i, j, pos = 0;
-       int err;
 
        for (i = 0; i < num; i++) {
                struct i2c_msg *msg = &msgs[i];
                u16 flags = 0;
 
-               err = tegra_bpmp_xlate_flags(msg->flags, &flags);
-               if (err < 0)
-                       return err;
+               tegra_bpmp_xlate_flags(msg->flags, &flags);
 
                buf[pos++] = msg->addr & 0xff;
                buf[pos++] = (msg->addr & 0xff00) >> 8;
@@ -128,8 +107,6 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
        }
 
        request->xfer.data_size = pos;
-
-       return 0;
 }
 
 /**
@@ -217,7 +194,32 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
        else
                err = tegra_bpmp_transfer(i2c->bpmp, &msg);
 
-       return err;
+       if (err < 0) {
+               dev_err(i2c->dev, "failed to transfer message: %d\n", err);
+               return err;
+       }
+
+       if (msg.rx.ret != 0) {
+               if (msg.rx.ret == -BPMP_EAGAIN) {
+                       dev_dbg(i2c->dev, "arbitration lost\n");
+                       return -EAGAIN;
+               }
+
+               if (msg.rx.ret == -BPMP_ETIMEDOUT) {
+                       dev_dbg(i2c->dev, "timeout\n");
+                       return -ETIMEDOUT;
+               }
+
+               if (msg.rx.ret == -BPMP_ENXIO) {
+                       dev_dbg(i2c->dev, "NAK\n");
+                       return -ENXIO;
+               }
+
+               dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret);
+               return -EIO;
+       }
+
+       return 0;
 }
 
 static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
@@ -238,12 +240,7 @@ static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
        memset(&request, 0, sizeof(request));
        memset(&response, 0, sizeof(response));
 
-       err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
-       if (err < 0) {
-               dev_err(i2c->dev, "failed to serialize message: %d\n", err);
-               return err;
-       }
-
+       tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
        err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
        if (err < 0) {
                dev_err(i2c->dev, "failed to transfer message: %d\n", err);
index 63cbb9c..bba08cb 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
-#include <linux/version.h>
 
 #define MAILBOX_OP_TIMEOUT             1000    /* Operation time out in ms */
 #define MAILBOX_I2C_INDEX              0
index d296b50..c81cc9a 100644 (file)
@@ -249,7 +249,7 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
 int i2c_recover_bus(struct i2c_adapter *adap)
 {
        if (!adap->bus_recovery_info)
-               return -EOPNOTSUPP;
+               return -EBUSY;
 
        dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
        return adap->bus_recovery_info->recover_bus(adap);
@@ -1016,15 +1016,9 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address
 }
 EXPORT_SYMBOL_GPL(i2c_new_dummy_device);
 
-struct i2c_dummy_devres {
-       struct i2c_client *client;
-};
-
-static void devm_i2c_release_dummy(struct device *dev, void *res)
+static void devm_i2c_release_dummy(void *client)
 {
-       struct i2c_dummy_devres *this = res;
-
-       i2c_unregister_device(this->client);
+       i2c_unregister_device(client);
 }
 
 /**
@@ -1041,20 +1035,16 @@ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev,
                                             struct i2c_adapter *adapter,
                                             u16 address)
 {
-       struct i2c_dummy_devres *dr;
        struct i2c_client *client;
-
-       dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL);
-       if (!dr)
-               return ERR_PTR(-ENOMEM);
+       int ret;
 
        client = i2c_new_dummy_device(adapter, address);
-       if (IS_ERR(client)) {
-               devres_free(dr);
-       } else {
-               dr->client = client;
-               devres_add(dev, dr);
-       }
+       if (IS_ERR(client))
+               return client;
+
+       ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client);
+       if (ret)
+               return ERR_PTR(ret);
 
        return client;
 }
index 6ceb11c..6ef38a8 100644 (file)
@@ -440,8 +440,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                   sizeof(rdwr_arg)))
                        return -EFAULT;
 
-               /* Put an arbitrary limit on the number of messages that can
-                * be sent at once */
+               if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
+                       return -EINVAL;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
                if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
                        return -EINVAL;
 
index 54b3ccc..59b892c 100644 (file)
@@ -687,6 +687,8 @@ struct i2c_adapter_quirks {
 #define I2C_AQ_NO_ZERO_LEN_READ                BIT(5)
 #define I2C_AQ_NO_ZERO_LEN_WRITE       BIT(6)
 #define I2C_AQ_NO_ZERO_LEN             (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE)
+/* adapter cannot do repeated START */
+#define I2C_AQ_NO_REP_START            BIT(7)
 
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along