diff mbox series

[v2,1/2] i2c: i2c-mlx: I2C SMBus driver for Mellanox BlueField SoC

Message ID 78365cf0ad3321eae4afdf97dc32a555fc4cb34a.1543941145.git.kblaiech@mellanox.com
State Superseded
Headers show
Series i2c: added driver for Mellanox BlueField SoC | expand

Commit Message

Khalil Blaiech Dec. 4, 2018, 5:40 p.m. UTC
Added BlueField I2C driver to offer master and slave support for
Mellanox BlueField Socs.  The driver implements an SMBus adapter
and interfaces to multiple busses that can be probed using both
ACPI and Device Tree infrastructures.

Reviewed-by: David Woods <dwoods@mellanox.com>
Signed-off-by: Khalil Blaiech <kblaiech@mellanox.com>
---
 drivers/i2c/busses/Kconfig   |   13 +
 drivers/i2c/busses/Makefile  |    1 +
 drivers/i2c/busses/i2c-mlx.c | 2341 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2355 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-mlx.c
diff mbox series

Patch

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f2c6819..b2e6a72 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -692,6 +692,19 @@  config I2C_LPC2K
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-lpc2k.
 
+config I2C_MELLANOX
+        tristate "Mellanox BlueField I2C controller"
+        depends on ARCH_MLNX_BLUEFIELD || COMPILE_TEST
+        help
+          Enabling this option will add specific I2C SMBus support for Mellanox
+          BlueField system.
+
+          This driver can also be built as a module.  If so, the module will be
+          called i2c-mlx.
+
+          This driver implements an I2C SMBus host controller and enables both
+          master and slave functions.
+
 config I2C_MESON
 	tristate "Amlogic Meson I2C controller"
 	depends on ARCH_MESON || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5f0cb69..5fb96cf 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@  obj-$(CONFIG_I2C_IOP3XX)	+= i2c-iop3xx.o
 obj-$(CONFIG_I2C_JZ4780)	+= i2c-jz4780.o
 obj-$(CONFIG_I2C_KEMPLD)	+= i2c-kempld.o
 obj-$(CONFIG_I2C_LPC2K)		+= i2c-lpc2k.o
+obj-$(CONFIG_I2C_MELLANOX)	+= i2c-mlx.o
 obj-$(CONFIG_I2C_MESON)		+= i2c-meson.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
 obj-$(CONFIG_I2C_MT65XX)	+= i2c-mt65xx.o
diff --git a/drivers/i2c/busses/i2c-mlx.c b/drivers/i2c/busses/i2c-mlx.c
new file mode 100644
index 0000000..87adc4e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mlx.c
@@ -0,0 +1,2341 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *  Mellanox i2c bus driver
+ *
+ *  Copyright (C) 2018 Mellanox Technologies, Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License v2.0 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/mutex.h>
+
+#define MLX_I2C_DRIVER_NAME        "i2c-mlx"
+#define MLX_I2C_DRIVER_VERSION     "1.0"
+#define MLX_I2C_DRIVER_DESCRIPTION "Mellanox I2C bus driver"
+
+#define MLX_I2C_DEVICE_PROFILE          "mlnx-bf18"
+#define MLX_I2C_DEVICE_PROFILE_SIZE_MAX 10
+
+#define I2C_SMBUS_INVALID       0xff
+#ifndef I2C_FUNC_SMBUS_HOST_NOTIFY
+#define I2C_FUNC_SMBUS_HOST_NOTIFY      0x10000000
+#endif
+
+#define I2C_SMBUS_MAX           3
+
+/* Defines what functionality is present */
+#define MLX_I2C_FUNC_SMBUS_BLOCK \
+	(I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
+
+#define MLX_I2C_FUNC_SMBUS_DEFAULT \
+	(I2C_FUNC_SMBUS_BYTE      | I2C_FUNC_SMBUS_BYTE_DATA | \
+	 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK | \
+	 I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_HOST_NOTIFY)
+
+#define MLX_I2C_FUNC_ALL \
+	(MLX_I2C_FUNC_SMBUS_DEFAULT | MLX_I2C_FUNC_SMBUS_BLOCK | \
+	 I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SLAVE)
+
+/*
+ * Note that the following SMBus, CAUSE, GPIO and PLL register addresses
+ * refer to their respective offsets relative to the corresponding
+ * memory-mapped region whose address is specified in either the DT or
+ * the ACPI tables.
+ */
+
+/*
+ * TYU - Configuration for PLL:
+ */
+
+/*
+ * SMBus Master core clock frequency. Timing configurations are
+ * strongly dependent on the core clock frequency of the SMBus
+ * Master. Default value is set to 400MHz.
+ */
+#define BLUEFIELD_TYU_PLL_OUT_FREQ  (400 * 1000 * 1000)
+/* Reference clock for Bluefield - 156 MHz */
+#define BLUEFIELD_TYU_PLL_IN_FREQ   (156 * 1000 * 1000)
+
+/* YU PLL configuration bits are exposed to SW in TYU boot record block */
+#define TYU_CORE_PLL_CFG_LSB        0x4
+
+#define GET_PLL_CORE_F(val)         (((val) >>  3) & 0x1fff) /* 13 bits */
+#define GET_PLL_CORE_OD(val)        (((val) >> 16) & 0x000f) /*  4 bits */
+#define GET_PLL_CORE_R(val)         (((val) >> 20) & 0x003f) /*  6 bits */
+
+/*
+ * TYU - Configuration for cause:
+ */
+
+/* OR cause register */
+#define TYU_CAUSE_OR_EVTEN2_BITS    0x0c
+#define TYU_CAUSE_OR_EVTEN1_BITS    0x10
+#define TYU_CAUSE_OR_EVTEN0_BITS    0x14
+#define TYU_CAUSE_OR_CLEAR_BITS     0x18
+
+/* Arbiter Cause Register */
+#define TYU_CAUSE_ARBITER_BITS      0x1c
+
+/*
+ * TYU Cause Status flags. Note that those bits might be considered
+ * as interrupt enabled bits.
+ */
+#define CAUSE_TRANSACTION_ENDED     0x001 /* Transaction ended with STOP */
+#define CAUSE_M_ARBITRATION_LOST    0x002 /* Master arbitration lost */
+#define CAUSE_UNEXPECTED_START      0x004 /* Unexpected start detected */
+#define CAUSE_UNEXPECTED_STOP       0x008 /* Unexpected stop detected */
+#define CAUSE_WAIT_FOR_FW_DATA      0x010 /* Wait for transfer continuation */
+#define CAUSE_PUT_STOP_FAILED       0x020 /* Failed to generate STOP */
+#define CAUSE_PUT_START_FAILED      0x040 /* Failed to generate START */
+#define CAUSE_CLK_TOGGLE_DONE       0x080 /* Clock toggle completed */
+#define CAUSE_M_FW_TIMEOUT          0x100 /* Transfer timeout occurred */
+#define CAUSE_M_GW_BUSY_FALL        0x200 /* Master busy bit reset */
+
+#define CAUSE_MASTER_ARBITER_BITS_MASK     0x000003ff /* 10 bits */
+
+/*
+ * TYU Slave cause status flags. Note that those bits might be considered
+ * as interrupt enabled bits.
+ */
+
+/* Write transaction received successfully */
+#define CAUSE_WRITE_SUCCESS         0x000001
+/* Write transaction terminated due to unexpected token */
+#define CAUSE_WRITE_UNEXPECTED_TOK  0x000002
+/* External master is trying to write more than 128 Bytes */
+#define CAUSE_WRITE_TOO_LONG        0x000004
+/* Read transaction ended successfully with NACK */
+#define CAUSE_READ_SUCCESS_NACK     0x000008
+/* Read transaction ended unexpected with NACK */
+#define CAUSE_READ_UNEXPECTED_NACK  0x000010
+/* Transaction failed due to arbitration lost */
+#define CAUSE_S_ARBITRATION_LOST    0x000080
+/* Read transaction terminated due to unexpected start */
+#define CAUSE_READ_UNEXPECTED_START 0x000100
+/* Read transaction terminated due to unexpected stop */
+#define CAUSE_READ_UNEXPECTED_STOP  0x000200
+/* Read transaction aborted due to stretch timeout */
+#define CAUSE_READ_TIMEOUT          0x000400
+/* Waiting for ACK/NACK */
+#define CAUSE_WAIT_FOR_ACK_NACK     0x001000
+/* Read transaction received, waiting for response */
+#define CAUSE_READ_WAIT_FW_RESPONSE 0x002000
+/* Write transaction aborted due to stretch timeout */
+#define CAUSE_WRITE_TIMEOUT         0x004000
+/* Incorrect slave address at the beginning of read phase */
+#define CAUSE_BAD_SLAVE_ADDRESS     0x008000
+/* SCL is idle while SDA is driven by slave */
+#define CAUSE_SCL_IDLE_SLAVE_SDA    0x010000
+/* Timeout while waiting for response */
+#define CAUSE_S_FW_TIMEOUT          0x020000
+/* Slave busy bit reset */
+#define CAUSE_S_GW_BUSY_FALL        0x040000
+/* Master acked last written byte, need to supply more bytes */
+#define CAUSE_MASTER_EXPECTING_DATA 0x080000
+/* Master nacked byte but didn't generate stop */
+#define CAUSE_NO_STOP_AFTER_NACK    0x100000
+
+#define CAUSE_SLAVE_ARBITER_BITS_MASK     0x001fffff /* 21 bits */
+
+/* Cause Coalesce registers */
+#define TYU_CAUSE_COALESCE_0        0x00
+#define TYU_CAUSE_COALESCE_1        0x04
+#define TYU_CAUSE_COALESCE_2        0x08
+
+/*
+ * TYU - Configuration for GPIO:
+ */
+/* Functional enable register */
+#define TYU_GPIO_0_FUNC_EN_0    0x28
+/* Force OE enable register */
+#define TYU_GPIO_0_FORCE_OE_EN  0x30
+/*
+ * Note that Smbus GWs are on GPIOs 30:25. Two pins are used to control
+ * SDA/SCL lines:
+ *
+ *  SMBUS GW0 -> bits[26:25]
+ *  SMBUS GW1 -> bits[28:27]
+ *  SMBUS GW2 -> bits[30:29]
+ */
+#define TYU_GPIO_SMBUS_GW_PINS(num) (25 + ((num) << 1))
+
+#define TYU_GPIO_SMBUS_GW_0_MASK    0xf9ffffff
+#define TYU_GPIO_SMBUS_GW_1_MASK    0xe7ffffff
+#define TYU_GPIO_SMBUS_GW_2_MASK    0x9fffffff
+
+#define TYU_GPIO_SMBUS_GW_RESET_PINS(val, mask) ((val) & (mask))
+
+#define TYU_GPIO_SMBUS_GW_ASSERT_PINS(num, val) \
+	((val) | (0x3 << TYU_GPIO_SMBUS_GW_PINS((num))))
+
+/*
+ * SMBus Timing Parameters:
+ */
+#define SMBUS_TIMER_SCL_LOW_SCL_HIGH    0x00
+#define SMBUS_TIMER_FALL_RISE_SPIKE     0x04
+#define SMBUS_TIMER_THOLD               0x08
+#define SMBUS_TIMER_TSETUP_START_STOP   0x0c
+#define SMBUS_TIMER_TSETUP_DATA         0x10
+#define SMBUS_THIGH_MAX_TBUF            0x14
+#define SMBUS_SCL_LOW_TIMEOUT           0x18
+
+/*
+ * Defines SMBus operating frequency and core clock frequency.
+ * According to ADB files, default values are compliant to 100KHz SMBus
+ * @ 400MHz core clock. The driver should be able to calculate core
+ * frequency based on PLL parameters.
+ */
+#define MLX_I2C_CORE_FREQ          BLUEFIELD_TYU_PLL_OUT_FREQ
+
+#define MLX_I2C_TIMING_CONFIG_HZ  100000
+
+/* SMBus SCL clock high period setup */
+enum {
+	SMBUS_SCL_HIGH_100KHZ  = 4810,
+	SMBUS_SCL_HIGH_400KHZ  = 1011,
+	SMBUS_SCL_HIGH_1000KHZ = 600
+};
+
+/*
+ * SMBus Master GW Registers:
+ */
+
+/* SMBus Master GW */
+#define SMBUS_MASTER_GW     0x200
+/* Number of bytes received and sent */
+#define SMBUS_RS_BYTES      0x300
+/* Packet error check (PEC) value */
+#define SMBUS_MASTER_PEC    0x304
+/* Status bits (ACK/NACK/FW Timeout) */
+#define SMBUS_MASTER_STATUS 0x308
+/* Shift left GW data bytes */
+#define SMBUS_READ_SHIFT    0x30c
+/* SMbus Master Finite State Machine */
+#define SMBUS_MASTER_FSM    0x310
+/* Toggle Clock */
+#define SMBUS_MASTER_CLK    0x314
+/* SDA and SCL configuration */
+#define SMBUS_MASTER_CFG    0x318
+/*
+ * When enabled, the master will issue a stop condition in case of
+ * timeout while waiting for FW response.
+ */
+#define SMBUS_EN_FW_TIMEOUT 0x31c
+
+/* SMBus Master GW control bits offset in SMBUS_MASTER_GW[31:3] */
+#define MASTER_LOCK_BIT_OFF         31	/* Lock bit */
+#define MASTER_BUSY_BIT_OFF         30	/* Busy bit */
+#define MASTER_START_BIT_OFF        29	/* Control start */
+#define MASTER_CTL_WRITE_BIT_OFF    28	/* Control write phase */
+#define MASTER_WRITE_BIT_OFF        21	/* Control write bytes */
+#define MASTER_SEND_PEC_BIT_OFF     20	/* Send PEC byte when set to 1 */
+#define MASTER_CTL_READ_BIT_OFF     19	/* Control read phase */
+#define MASTER_PARSE_EXP_BIT_OFF    11	/* Control parse expected bytes */
+#define MASTER_SLV_ADDR_BIT_OFF     12	/* Slave address */
+#define MASTER_READ_BIT_OFF         4	/* Control read bytes */
+#define MASTER_STOP_BIT_OFF         3	/* Control stop */
+
+/* SMBus Master GW Data descriptor */
+#define MASTER_DATA_DESC_ADDR   0x280	/* Address */
+#define MASTER_DATA_DESC_SIZE   0x80	/* Data descriptor size in bytes */
+#define MASTER_CTL_DATA_MAX_SIZE    4	/* Control data size in bytes */
+#define MASTER_DATA_W_OFF \
+	(MASTER_DATA_DESC_ADDR + MASTER_CTL_DATA_MAX_SIZE)
+
+/* Maximum bytes to read/write per SMBus transaction */
+#define MASTER_DATA_R_LENGTH  MASTER_DATA_DESC_SIZE
+#define MASTER_DATA_W_LENGTH (MASTER_DATA_DESC_SIZE - 1)
+
+/* SMBus Master GW Status flags */
+#define SMBUS_STATUS_BYTE_CNT_DONE  0x1 /* All bytes were transmitted */
+#define SMBUS_STATUS_NACK_RCV       0x2 /* NACK received */
+#define SMBUS_STATUS_READ_ERR       0x4 /* Slave's byte count > 128 bytes */
+#define SMBUS_STATUS_FW_TIMEOUT     0x8 /* Timeout occurred */
+
+#define SMBUS_MASTER_STATUS_MASK        0x0000000f /* 4 bits */
+
+#define SMBUS_MASTER_FSM_STOP_MASK      0x80000000
+#define SMBUS_MASTER_FSM_PS_STATE_MASK  0x00008000
+
+/*
+ * SMBus Slave Parameters:
+ */
+
+/* SMBus slave GW */
+#define SMBUS_SLAVE_GW              0x400
+/* Number of bytes received and sent from/to master */
+#define SMBUS_SLAVE_RS_MASTER_BYTES 0x500
+/* Packet error check (PEC) value */
+#define SMBUS_SLAVE_PEC             0x504
+/* Shift left GW data bytes */
+#define SMBUS_SLAVE_READ_SHIFT      0x508
+/* SMbus Slave Finite State Machine (FSM) */
+#define SMBUS_SLAVE_FSM             0x510
+/* SMBus CR Master configuration register */
+#define SMBUS_SLAVE_CRMASTER_CFG    0x524
+/*
+ * When enabled, FSM will return to idle in case of stretch timeout
+ * while waiting for FW response.
+ */
+#define SMBUS_SLAVE_EN_FW_TIMEOUT   0x528
+/*
+ * Should be set when all raised causes handled, and cleared by HW on
+ * every new cause.
+ */
+#define SMBUS_SLAVE_READY           0x52c
+/* SMBus Device Default Address as defined in SMBus spec */
+#define SMBUS_SLAVE_ARP_ADDR        0x530
+/* If set, then the Slave is in middle of ARP transaction */
+#define SMBUS_SLAVE_ARP_STATUS      0x534
+/* Slave cause register */
+#define SMBUS_SLAVE_CAUSE           0x53c
+/* SMBus CR Master FSM */
+#define SMBUS_SLAVE_CRMASTER_FSM    0x540
+/* Slave SDA and SCL output */
+#define SMBUS_SLAVE_CLK_OUTPUT      0x544
+
+/* SMBus Slave GW control bits offset in SMBUS_SLAVE_GW[31:19] */
+#define SLAVE_LOCK_BIT_OFF         31	/* Lock bit                    */
+#define SLAVE_BUSY_BIT_OFF         30	/* Busy bit                    */
+#define SLAVE_WRITE_BIT_OFF        29	/* Control write enable        */
+#define SLAVE_WRITE_BYTES_BIT_OFF  22	/* Number of bytes to write    */
+#define SLAVE_SEND_PEC_BIT_OFF     21	/* Send PEC byte when set to 1 */
+#define SLAVE_NACK_BIT_OFF         20	/* Nack bit                    */
+#define SLAVE_CONT_WRITE_BIT_OFF   19	/* Continue write transaction  */
+
+/* SMBus Slave GW Data descriptor */
+#define SLAVE_DATA_DESC_ADDR   0x480	/* Address */
+#define SLAVE_DATA_DESC_SIZE   0x80	/* Data descriptor size in bytes */
+#define SLAVE_DATA_DESC_SKIP   1 /* Bytes to skip within data descriptor */
+
+/* SMbus Slave configuration registers */
+#define SMBUS_SLAVE_ADDR_CFG        0x514
+#define SMBUS_SLAVE_ADDR_CNT        16
+#define SMBUS_SLAVE_ADDR_EN_BIT     7
+#define SMBUS_SLAVE_ADDR_MASK       0x7f
+
+/*
+ * Timeout is given in microsends. Note also that timeout handling is not
+ * exact.
+ */
+#define SMBUS_TIMEOUT   (300 * 1000) /* 300ms */
+
+/* Encapsulates timing parameters */
+struct mlx_i2c_timings {
+	u16 scl_high;		/* Clock high period    */
+	u16 scl_low;		/* Clock low period     */
+	u8  sda_rise;		/* Data Rise Time       */
+	u8  sda_fall;		/* Data Fall Time       */
+	u8  scl_rise;		/* Clock Rise Time      */
+	u8  scl_fall;		/* Clock Fall Time      */
+	u16 hold_start;		/* Hold time after (REPEATED) START */
+	u16 hold_data;		/* Data hold time       */
+	u16 setup_start;	/* REPEATED START Condition setup time  */
+	u16 setup_stop;		/* STOP Condition setup time            */
+	u16 setup_data;		/* Data setup time      */
+	u16 pad;		/* Padding              */
+	u16 buf;		/* Bus free time between STOP and START */
+	u16 thigh_max;		/* Thigh max            */
+	u32 timeout;		/* Detect clock low timeout */
+};
+
+enum {
+	I2C_F_READ               = 0x01,
+	I2C_F_WRITE              = 0x02,
+	I2C_F_NORESTART          = 0x08,
+	I2C_F_SMBUS_OPERATION    = 0x10,
+	I2C_F_SMBUS_BLOCK        = 0x20,
+	I2C_F_SMBUS_PEC          = 0x40,
+	I2C_F_SMBUS_PROCESS_CALL = 0x80
+};
+
+struct mlx_smbus_operation {
+	u32  flags;
+	u32  length;		/* buffer length in bytes */
+	u8  *buffer;
+};
+
+#define I2C_SMBUS_OPERATION_CNT        3
+
+struct mlx_smbus_request {
+	u8 slave;
+	u8 operation_cnt;
+	struct mlx_smbus_operation operation[I2C_SMBUS_OPERATION_CNT];
+};
+
+struct mlx_i2c_priv {
+	void __iomem *smbus_io;
+	void __iomem *mst_cause_io;
+	void __iomem *slv_cause_io;
+	struct resource *smbus_res;
+	struct resource *mst_cause_res;
+	struct resource *slv_cause_res;
+	struct i2c_adapter adap;
+	u64 frequency; /* Core frequency in Hz */
+	int bus; /* physical bus identifier */
+	struct i2c_client *slave;
+};
+
+enum {
+	MLX_SMBUS_RES,
+	MLX_MST_CAUSE_RES,
+	MLX_SLV_CAUSE_RES,
+	MLX_COALESCE_RES,
+	MLX_GPIO_RES,
+	MLX_COREPLL_RES
+};
+
+/* TYU core frequency  */
+static u64 tyu_core_frequency;
+
+/* TYU cause coalesce group */
+struct mlx_i2c_coalesce {
+	void __iomem *io;
+	struct resource *res;
+	bool valid;
+};
+
+static struct mlx_i2c_coalesce tyu_coalesce;
+
+static DEFINE_MUTEX(tyu_coalesce_lock);
+
+static u8 i2c_bus_count;
+
+static DEFINE_MUTEX(i2c_bus_lock);
+
+/* Polling frequency in microseconds */
+#define POLL_FREQ_IN_USEC        200
+
+static void smbus_write(void __iomem *io, int reg, u32 val)
+{
+	writel(val, io + reg);
+}
+
+static u32 smbus_read(void __iomem *io, int reg)
+{
+	return readl(io + reg);
+}
+
+/*
+ * This function is used to read data from Master GW Data Descriptor.
+ * Data bytes in the Master GW Data Descriptor are shifted left so the
+ * data starts at the MSB of the descriptor registers as set by the
+ * underlying hardware. TYU_READ_DATA enables byte swapping while
+ * reading data bytes, and MUST be called by the SMBus read routines
+ * to copy data from the 32 * 32-bit HW Data registers a.k.a Master GW
+ * Data Descriptor.
+ */
+static u32 smbus_read_data(void __iomem *io, int reg)
+{
+	return be32_to_cpu(smbus_read(io, reg));
+}
+
+/*
+ * This function is used to write data to the Master GW Data Descriptor.
+ * Data copied to the Master GW Data Descriptor MUST be shifted left so
+ * the data starts at the MSB of the descriptor registers as required by
+ * the underlying hardware. TYU_WRITE_DATA enables byte swapping when
+ * writing data bytes, and MUST be called by the SMBus write routines to
+ * copy data to the 32 * 32-bit HW Data registers a.k.a Master GW Data
+ * Descriptor.
+ */
+static void smbus_write_data(void __iomem *io, int reg, u32 val)
+{
+	smbus_write(io, reg, cpu_to_be32(val));
+}
+
+/*
+ * I2C SMBus operations
+ */
+
+/*
+ * Function to poll a set of bits at a specific address; it checks whether
+ * the bits are equal to zero when eq_zero is set to 'true', and not equal
+ * to zero when eq_zero is set to 'false'.
+ * Note that the timeout is given in microseconds.
+ */
+static u32 mlx_smbus_poll(void __iomem *io, u32 addr, u32 mask,
+			  bool eq_zero, u32  timeout)
+{
+	u32 bits;
+
+	timeout = (timeout / POLL_FREQ_IN_USEC) + 1;
+
+	do {
+		bits = smbus_read(io, addr) & mask;
+		if (eq_zero ? bits == 0 : bits != 0)
+			return eq_zero ? 1 : bits;
+		udelay(POLL_FREQ_IN_USEC);
+	} while (timeout-- != 0);
+
+	return 0;
+}
+
+/*
+ * SW must make sure that the SMBus Master GW is idle before starting
+ * a transaction. Accordingly, this function polls the Master FSM stop
+ * bit; it returns false when the bit is asserted, true if not.
+ */
+static bool mlx_smbus_master_wait_for_idle(struct mlx_i2c_priv *priv)
+{
+	u32 addr    = SMBUS_MASTER_FSM;
+	u32 mask    = SMBUS_MASTER_FSM_STOP_MASK;
+	u32 timeout = SMBUS_TIMEOUT;
+
+	if (mlx_smbus_poll(priv->smbus_io, addr, mask, true, timeout))
+		return true;
+
+	return false;
+}
+
+/*
+ * Poll SMBus master status and return transaction status,
+ * i.e. whether succeeded or failed. I2C and SMBus fault codes
+ * are returned as negative numbers from most calls, with zero
+ * or some positive number indicating a non-fault return.
+ */
+static int mlx_i2c_smbus_check_status(struct mlx_i2c_priv *priv)
+{
+	u32 cause_status_bits;
+	u32 master_status_bits;
+
+	/*
+	 * GW busy bit is raised by the driver and cleared by the HW
+	 * when the transaction is completed. The busy bit is a good
+	 * indicator of transaction status. So poll the busy bit, and
+	 * then read the cause and master status bits to determine if
+	 * errors occurred during the transaction.
+	 */
+	mlx_smbus_poll(priv->smbus_io, SMBUS_MASTER_GW,
+		       1 << MASTER_BUSY_BIT_OFF, true,
+		       SMBUS_TIMEOUT);
+
+	/* Read cause status bits */
+	cause_status_bits =
+		smbus_read(priv->mst_cause_io, TYU_CAUSE_ARBITER_BITS) &
+		CAUSE_MASTER_ARBITER_BITS_MASK;
+
+	/*
+	 * Parse both Cause and Master GW bits, then return transaction status.
+	 */
+
+	master_status_bits  = smbus_read(priv->smbus_io, SMBUS_MASTER_STATUS);
+	master_status_bits &= SMBUS_MASTER_STATUS_MASK;
+
+	/*
+	 * When transaction ended with STOP, all bytes were transmitted,
+	 * and no NACK received, then the transaction ended successfully.
+	 * On the other hand, when the GW is configured with the stop bit
+	 * de-asserted then the SMBus expects the following GW configuration
+	 * for transfer continuation.
+	 */
+	if ((cause_status_bits & CAUSE_WAIT_FOR_FW_DATA) ||
+	    ((cause_status_bits & CAUSE_TRANSACTION_ENDED) &&
+	     (master_status_bits & SMBUS_STATUS_BYTE_CNT_DONE) &&
+	     !(master_status_bits & SMBUS_STATUS_NACK_RCV)))
+		return 0;
+
+	/*
+	 * In case of timeout on GW busy, the ISR will clear busy bit but
+	 * transaction ended bits cause will not be set so the transaction
+	 * fails. Then, we must check Master GW status bits.
+	 */
+	if ((master_status_bits & (SMBUS_STATUS_NACK_RCV |
+				   SMBUS_STATUS_READ_ERR |
+				   SMBUS_STATUS_FW_TIMEOUT)) &&
+	    (cause_status_bits & (CAUSE_TRANSACTION_ENDED |
+				  CAUSE_M_GW_BUSY_FALL)))
+		return -EIO;
+
+	if (cause_status_bits & (CAUSE_M_ARBITRATION_LOST |
+				 CAUSE_UNEXPECTED_START |
+				 CAUSE_UNEXPECTED_STOP  |
+				 CAUSE_PUT_STOP_FAILED  |
+				 CAUSE_PUT_START_FAILED |
+				 CAUSE_CLK_TOGGLE_DONE  |
+				 CAUSE_M_FW_TIMEOUT))
+		return -EAGAIN;
+
+	return  -ETIMEDOUT;
+}
+
+static void mlx_smbus_write_data(struct mlx_i2c_priv *priv,
+				  const u8 *data, u8 length, u32 addr)
+{
+	u32 data32;
+	u8  offset;
+
+	/* Copy data bytes from 4-byte aligned source buffer */
+	for (offset = 0; offset < round_up(length, 4); offset += 4) {
+		data32 = *((u32 *) (data + offset));
+		smbus_write_data(priv->smbus_io, addr + offset, data32);
+	}
+}
+
+static void mlx_smbus_read_data(struct mlx_i2c_priv *priv,
+				 u8 *data, u8 length, u32 addr)
+{
+	u32 data32;
+	u8  byte, offset;
+
+	for (offset = 0; offset < (length & ~0x3); offset += 4) {
+		data32 = smbus_read_data(priv->smbus_io, addr + offset);
+		*((u32 *) (data + offset)) = data32;
+	}
+
+	if (!(length & 0x3))
+		return;
+
+	data32 = smbus_read_data(priv->smbus_io, addr + offset);
+
+	for (byte = 0; byte < (length & 0x3); byte++) {
+		data[offset + byte] = data32 & 0xff;
+		data32 >>= 8;
+	}
+}
+
+static int mlx_smbus_enable(struct mlx_i2c_priv *priv, u8 slave,
+			     u8 len, u8 block_en, u8 pec_en, bool read)
+{
+	u32 command;
+
+	/* Set Master GW control word */
+	command  = 0;
+	command |= 0x1   << MASTER_LOCK_BIT_OFF;
+	command |= 0x1   << MASTER_BUSY_BIT_OFF;
+	command |= slave << MASTER_SLV_ADDR_BIT_OFF;
+	command |= 0x1   << MASTER_START_BIT_OFF;
+	command |= 0x1   << MASTER_STOP_BIT_OFF;
+	if (read) {
+		command |= len << MASTER_READ_BIT_OFF;
+		command |= 1   << MASTER_CTL_READ_BIT_OFF;
+	} else {
+		command |= len << MASTER_WRITE_BIT_OFF;
+		command |= 1   << MASTER_CTL_WRITE_BIT_OFF;
+	}
+	command |= block_en << MASTER_PARSE_EXP_BIT_OFF;
+	command |= pec_en   << MASTER_SEND_PEC_BIT_OFF;
+
+	/* Clear status bits  */
+	smbus_write(priv->smbus_io, SMBUS_MASTER_STATUS, 0x0);
+	/* Set the cause data */
+	smbus_write(priv->smbus_io, TYU_CAUSE_OR_CLEAR_BITS, ~0x0);
+	/* Zero PEC byte */
+	smbus_write(priv->smbus_io, SMBUS_MASTER_PEC, 0x0);
+	/* Zero byte count    */
+	smbus_write(priv->smbus_io, SMBUS_RS_BYTES, 0x0);
+
+	/* GW activation */
+	smbus_write(priv->smbus_io, SMBUS_MASTER_GW, command);
+
+	/*
+	 * Poll master status and check status bits. An ACK is sent when
+	 * completing writing data to the bus (Master 'byte_count_done' bit
+	 * is set to 1).
+	 */
+	return mlx_i2c_smbus_check_status(priv);
+}
+
+static int mlx_smbus_start_transaction(struct mlx_i2c_priv *priv,
+					struct mlx_smbus_request *request)
+{
+	struct mlx_smbus_operation *operation;
+	u8  data_desc[MASTER_DATA_DESC_SIZE] = { 0 };
+	u8  op_idx, data_idx, data_len, write_len, read_len;
+	u8  read_en, write_en, block_en, pec_en;
+	u8  slave, flags, addr;
+	u8 *read_buf;
+	int ret = 0;
+
+	if (request->operation_cnt > I2C_SMBUS_OPERATION_CNT)
+		return -EINVAL;
+
+	read_buf  = NULL;
+	data_idx  = 0;
+	read_en   = write_en = 0;
+	write_len = read_len = 0;
+	block_en  = 0;
+	pec_en    = 0;
+	slave     = request->slave & 0x7f;
+	addr      = slave << 1;
+
+	/* First of all, check whether the HW is idle */
+	if (WARN_ON(!mlx_smbus_master_wait_for_idle(priv)))
+		return -EBUSY;
+
+	/* Set first byte */
+	data_desc[data_idx++] = addr;
+
+	for (op_idx = 0; op_idx < request->operation_cnt; op_idx++) {
+		operation = &request->operation[op_idx];
+		flags     = operation->flags;
+
+		/*
+		 * Note that read and write operations might be handled by a
+		 * single command. If the I2C_F_SMBUS_OPERATION is set then
+		 * write command byte and set the optional SMBus specific bits
+		 * such as block_en and pec_en. These bits MUST be submitted by
+		 * the first operation only.
+		 */
+		if ((op_idx == 0) && (flags & I2C_F_SMBUS_OPERATION)) {
+			block_en = flags & I2C_F_SMBUS_BLOCK;
+			pec_en   = flags & I2C_F_SMBUS_PEC;
+		}
+
+		if (flags & I2C_F_WRITE) {
+			write_en   = 1;
+			write_len += operation->length;
+			memcpy(data_desc + data_idx,
+			       operation->buffer, operation->length);
+			data_idx  += operation->length;
+		}
+		/*
+		 * We assume that read operations are performed only once per
+		 * SMBus transaction. *TBD* protect this statement so it won't
+		 * be executed twice? or return an error if we try to read more
+		 * than once?
+		 */
+		if (flags & I2C_F_READ) {
+			read_en  = 1;
+			/* Subtract 1 as required by HW */
+			read_len = operation->length - 1;
+			read_buf = operation->buffer;
+		}
+	}
+
+	/* Set Master GW data descriptor */
+	data_len = write_len + 1;	/* add one byte of the slave address */
+	/*
+	 * Note that data_len cannot be 0. Indeed, the slave address byte
+	 * must be written to the data registers.
+	 */
+	mlx_smbus_write_data(priv, (const u8 *)data_desc, data_len,
+			      MASTER_DATA_DESC_ADDR);
+
+	if (write_en) {
+		ret = mlx_smbus_enable(priv, slave, write_len, block_en,
+				       pec_en, 0);
+		if (ret != 0)
+			return ret;
+	}
+
+	if (read_en) {
+		/* Write slave address to Master GW data descriptor */
+		mlx_smbus_write_data(priv, (const u8 *) &addr, 1,
+				      MASTER_DATA_DESC_ADDR);
+		ret = mlx_smbus_enable(priv, slave, read_len, block_en,
+					pec_en, 1);
+		if (ret == 0) {
+			/* Get Master GW data descriptor */
+			mlx_smbus_read_data(priv, data_desc, read_len + 1,
+					     MASTER_DATA_DESC_ADDR);
+
+			/* Get data from Master GW data descriptor */
+			memcpy(read_buf, data_desc, read_len + 1);
+		}
+
+		/*
+		 * After a read operation the SMBus FSM ps (present state)
+		 * needs to be 'manually' reset. This should be removed in
+		 * next tag integration.
+		 */
+		smbus_write(priv->smbus_io, SMBUS_MASTER_FSM,
+			    SMBUS_MASTER_FSM_PS_STATE_MASK);
+	}
+
+	return ret;
+}
+
+/*
+ * I2C SMBus protocols
+ */
+
+static void mlx_smbus_quick_command(struct mlx_smbus_request *request,
+				     u8 read)
+{
+	/*
+	 * QuickWrite:         OperationCount=1,
+	 *                     LengthInBytes=0,   Flags=I2C_F_WRITE
+	 *
+	 * QuickRead:          OperationCount=1,
+	 *                     LengthInBytes=0,   Flags=I2C_F_WRITE
+	 *                                            | I2C_F_READ
+	 */
+	request->operation_cnt = 1;
+
+	request->operation[0].length = 0;
+	request->operation[0].flags  = I2C_F_WRITE;
+	request->operation[0].flags |= (read) ? I2C_F_READ : 0;
+}
+
+static void mlx_smbus_byte_func(struct mlx_smbus_request *request,
+				 u8 *data, bool read, bool pec_check)
+{
+	/*
+	 * ReceiveByte:        OperationCount=1,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_READ
+	 * ReceiveByte+PEC:    OperationCount=1,
+	 *                     LengthInBytes=2,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_READ
+	 *                                            | I2C_F_SMBUS_PEC
+	 *
+	 *
+	 * SendByte:           OperationCount=1,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 * SendByte+PEC:       OperationCount=1,
+	 *                     LengthInBytes=2,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 */
+
+	request->operation_cnt = 1;
+
+	request->operation[0].length  = 1;
+	request->operation[0].length += (pec_check);
+
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION;
+	request->operation[0].flags |= (read) ? I2C_F_READ : I2C_F_WRITE;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+
+	request->operation[0].buffer = data;
+}
+
+static void mlx_smbus_data_byte_func(struct mlx_smbus_request *request,
+				      u8 *command,
+				      u8 *data, bool read, bool pec_check)
+{
+	/*
+	 * ReadDataByte:       OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=1,   Flags=I2C_F_READ
+	 * ReadDataByte+PEC:   OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=2,   Flags=I2C_F_READ
+	 *
+	 *
+	 * WriteDataByte:      OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=1,   Flags=I2C_F_WRITE
+	 * WriteDataByte+PEC:  OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=2,   Flags=I2C_F_WRITE
+	 */
+
+	request->operation_cnt = 2;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	request->operation[1].length  = 1;
+	request->operation[1].length += (pec_check);
+	request->operation[1].flags   = (read) ? I2C_F_READ : I2C_F_WRITE;
+	request->operation[1].buffer  = data;
+}
+
+static void mlx_smbus_data_word_func(struct mlx_smbus_request *request,
+				      u8 *command,
+				      u8 *data, bool read, bool pec_check)
+{
+	/*
+	 * ReadDataWord:       OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=2,   Flags=I2C_F_READ
+	 * ReadDataWord+PEC:   OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=3,   Flags=I2C_F_READ
+	 *
+	 *
+	 * WriteDataWord:      OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=2,   Flags=I2C_F_WRITE
+	 * WriteDataWord+PEC:  OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=3,   Flags=I2C_F_WRITE
+	 */
+
+	request->operation_cnt = 2;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	request->operation[1].length  = 2;
+	request->operation[1].length += (pec_check);
+	request->operation[1].flags   = (read) ? I2C_F_READ : I2C_F_WRITE;
+	request->operation[1].buffer  = data;
+}
+
+static void mlx_smbus_i2c_block_func(struct mlx_smbus_request *request,
+				      u8 *command,
+				      u8 *data,
+				      u8 *data_len, bool read, bool pec_check)
+{
+	/*
+	 * ReadBlock:          OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=N,   Flags=I2C_F_READ
+	 * ReadBlock+PEC:      OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=N+1, Flags=I2C_F_READ
+	 *
+	 *
+	 * WriteBlock:         OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=N,   Flags=I2C_F_WRITE
+	 * WriteBlock+PEC:     OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=N+1, Flags=I2C_F_WRITE
+	 */
+
+	request->operation_cnt = 2;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	/*
+	 * As specified in the standard, the max number of bytes to read/write
+	 * per block operation is 32 bytes. In Golan code, the controller can
+	 * read up to 128 bytes and write up to 127 bytes.
+	 */
+	request->operation[1].length =
+	    (((*data_len) + (pec_check)) > I2C_SMBUS_BLOCK_MAX) ?
+	    I2C_SMBUS_BLOCK_MAX : ((*data_len) + (pec_check));
+	request->operation[1].flags  = (read) ? I2C_F_READ : I2C_F_WRITE;
+	/*
+	 * Skip the first data byte, which corresponds to the number of bytes
+	 * to read/write.
+	 */
+	request->operation[1].buffer = data + 1;
+
+	*data_len = request->operation[1].length;
+
+	/* Set the number of byte to read. This will be used by userspace. */
+	if (read)
+		data[0] = *data_len;
+}
+
+static void mlx_smbus_block_func(struct mlx_smbus_request *request,
+				  u8 *command,
+				  u8 *data,
+				  u8 *data_len, bool read, bool pec_check)
+{
+	/*
+	 * ReadBlock:          OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                     LengthInBytes=N,   Flags=I2C_F_READ
+	 * ReadBlock+PEC:      OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=N+1, Flags=I2C_F_READ
+	 *
+	 *
+	 * WriteBlock:         OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                     LengthInBytes=N,   Flags=I2C_F_WRITE
+	 * WriteBlock+PEC:     OperationCount=2,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=N+1, Flags=I2C_F_WRITE
+	 */
+
+	request->operation_cnt = 2;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= I2C_F_SMBUS_BLOCK;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	request->operation[1].length =
+	    (((*data_len) + (pec_check)) > I2C_SMBUS_BLOCK_MAX) ?
+	    I2C_SMBUS_BLOCK_MAX : ((*data_len) + (pec_check));
+	request->operation[1].flags  = (read) ? I2C_F_READ : I2C_F_WRITE;
+	request->operation[1].buffer = data + 1;
+
+	*data_len = request->operation[1].length;
+
+	/* Set the number of bytes to read. This will be used by userspace. */
+	if (read)
+		data[0] = *data_len;
+}
+
+static void
+mlx_smbus_process_call_func(struct mlx_smbus_request *request,
+			     u8 *command, u8 *data, bool pec_check)
+{
+	/*
+	 * ProcessCall:        OperationCount=3,
+	 *                     LengthInBytes=2,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                     LengthInBytes=2,   Flags=I2C_F_WRITE
+	 *                     LengthInBytes=2,   Flags=I2C_F_READ
+	 * ProcessCall+PEC:    OperationCount=3,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=2, Flags=I2C_F_WRITE
+	 *                     LengthInBytes=3, Flags=I2C_F_READ
+	 */
+
+	request->operation_cnt = 3;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= I2C_F_SMBUS_BLOCK;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	request->operation[1].length = 2;
+	request->operation[1].flags  = I2C_F_WRITE;
+	request->operation[1].buffer = data;
+
+	request->operation[2].length = 3;
+	request->operation[2].flags  = I2C_F_READ;
+	request->operation[2].buffer = data;
+}
+
+static void
+mlx_smbus_blk_process_call_func(struct mlx_smbus_request *request,
+				 u8 *command,
+				 u8 *data, u8 *data_len, bool pec_check)
+{
+	/*
+	 * BlkProcessCall:     OperationCount=3,
+	 *                     LengthInBytes=2,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                     LengthInBytes=N,   Flags=I2C_F_WRITE
+	 *                     LengthInBytes=N,   Flags=I2C_F_READ
+	 * BlkProcessCall+PEC: OperationCount=3,
+	 *                     LengthInBytes=1,   Flags=I2C_F_SMBUS_OPERATION
+	 *                                            | I2C_F_WRITE
+	 *                                            | I2C_F_SMBUS_BLOCK
+	 *                                            | I2C_F_SMBUS_PEC
+	 *                     LengthInBytes=N,   Flags=I2C_F_WRITE
+	 *                     LengthInBytes=N+1, Flags=I2C_F_READ
+	 */
+
+	u32 length;
+
+	request->operation_cnt = 3;
+
+	request->operation[0].length = 1;
+	request->operation[0].flags  = I2C_F_SMBUS_OPERATION | I2C_F_WRITE;
+	request->operation[0].flags |= I2C_F_SMBUS_BLOCK;
+	request->operation[0].flags |= (pec_check) ? I2C_F_SMBUS_PEC : 0;
+	request->operation[0].buffer = command;
+
+	length = (((*data_len) + (pec_check)) > I2C_SMBUS_BLOCK_MAX) ?
+	    I2C_SMBUS_BLOCK_MAX : ((*data_len) + (pec_check));
+
+	request->operation[1].length = length - (pec_check);
+	request->operation[1].flags  = I2C_F_WRITE;
+	request->operation[1].buffer = data;
+
+	request->operation[2].length = length;
+	request->operation[2].flags  = I2C_F_READ;
+	request->operation[2].buffer = data;
+
+	*data_len = length;	/* including PEC byte */
+}
+
+static void mlx_smbus_host_notify_func(struct mlx_smbus_request *request,
+					u8 *command,
+					u8 *data, bool read, bool pec_check)
+{
+	return mlx_smbus_data_word_func(request, command, data, read,
+					 pec_check);
+}
+
+/*
+ * Initialization functions
+ */
+
+static u32 mlx_i2c_get_ticks(struct mlx_i2c_priv *priv, u64 nanoseconds,
+			      bool minimum)
+{
+	u64 frequency;
+	u32 ticks;
+
+	/*
+	 * Compute ticks as follow:
+	 *
+	 *           Ticks
+	 * Time = --------- x 10^9    =>    Ticks = Time x Frequency x 10^-9
+	 *         Frequency
+	 *
+	 */
+
+	frequency = priv->frequency;
+
+	ticks     = (nanoseconds * frequency) / 1000000000;
+	/*
+	 * The number of ticks is rounded down and if minimum is equal to 1
+	 * then add one tick
+	 */
+	if (minimum)
+		ticks += 1;
+
+	return ticks;
+}
+
+static u32 mlx_i2c_set_timer(struct mlx_i2c_priv *priv,
+			      u64  nsec,
+			      bool opt,
+			      u32  mask,
+			      u8   offset)
+{
+	return ((mlx_i2c_get_ticks(priv, nsec, opt) & mask) << offset);
+}
+
+static void mlx_i2c_set_timings(struct mlx_i2c_priv *priv,
+				 struct mlx_i2c_timings *timings)
+{
+	u32 timer;
+
+	timer  = mlx_i2c_set_timer(priv, timings->scl_high,
+				    false, 0xffff,  0);
+	timer |= mlx_i2c_set_timer(priv, timings->scl_low,
+				    false, 0xffff, 16);
+	smbus_write(priv->smbus_io, SMBUS_TIMER_SCL_LOW_SCL_HIGH, timer);
+
+	timer  = mlx_i2c_set_timer(priv, timings->sda_rise, false, 0xff,  0);
+	timer |= mlx_i2c_set_timer(priv, timings->sda_fall, false, 0xff,  8);
+	timer |= mlx_i2c_set_timer(priv, timings->scl_rise, false, 0xff, 16);
+	timer |= mlx_i2c_set_timer(priv, timings->scl_fall, false, 0xff, 24);
+	smbus_write(priv->smbus_io, SMBUS_TIMER_FALL_RISE_SPIKE, timer);
+
+	timer  = mlx_i2c_set_timer(priv, timings->hold_start,
+				    true, 0xffff,  0);
+	timer |= mlx_i2c_set_timer(priv, timings->hold_data,
+				    true, 0xffff, 16);
+	smbus_write(priv->smbus_io, SMBUS_TIMER_THOLD, timer);
+
+	timer  = mlx_i2c_set_timer(priv, timings->setup_start,
+				    true, 0xffff,  0);
+	timer |= mlx_i2c_set_timer(priv, timings->setup_stop,
+				    true, 0xffff, 16);
+	smbus_write(priv->smbus_io, SMBUS_TIMER_TSETUP_START_STOP, timer);
+
+	timer = mlx_i2c_set_timer(priv, timings->setup_data, true, 0xffff, 0);
+	smbus_write(priv->smbus_io, SMBUS_TIMER_TSETUP_DATA, timer);
+
+	timer  = mlx_i2c_set_timer(priv, timings->buf,
+				    false, 0xffff,  0);
+	timer |= mlx_i2c_set_timer(priv, timings->thigh_max,
+				    false, 0xffff, 16);
+	smbus_write(priv->smbus_io, SMBUS_THIGH_MAX_TBUF, timer);
+
+	timer = timings->timeout;
+	smbus_write(priv->smbus_io, SMBUS_SCL_LOW_TIMEOUT, timer);
+}
+
+static void mlx_i2c_init_timings(struct device       *dev,
+				 struct mlx_i2c_priv *priv)
+{
+	struct mlx_i2c_timings timings;
+	u32 config_khz;
+	int ret;
+
+	/*
+	 * Smbus Timing initialization
+	 */
+
+	ret = device_property_read_u32(dev, "clock-frequency", &config_khz);
+	if (ret < 0)
+		config_khz = MLX_I2C_TIMING_CONFIG_HZ;
+
+	switch (config_khz) {
+	default:
+		/* Default settings is 100 KHz */
+		pr_warn("Illegal value %d: defaulting to 100 KHz\n",
+			config_khz);
+
+		/* FALLTHROUGH */
+
+	case 100000:
+		timings.scl_high    = SMBUS_SCL_HIGH_100KHZ;
+		timings.scl_low     = 5000;
+		timings.hold_start  = 4000;
+		timings.setup_start = 4800;
+		timings.setup_stop  = 4000;
+		timings.setup_data  = 250;
+		break;
+
+	case 400000:
+		timings.scl_high    = SMBUS_SCL_HIGH_400KHZ;
+		timings.scl_low     = 1300;
+		timings.hold_start  = 600;
+		timings.setup_start = 700;
+		timings.setup_stop  = 600;
+		timings.setup_data  = 100;
+		break;
+
+	case 1000000:
+		timings.scl_high    = SMBUS_SCL_HIGH_1000KHZ;
+		timings.scl_low     = 1300;
+		timings.hold_start  = 600;
+		timings.setup_start = 600;
+		timings.setup_stop  = 600;
+		timings.setup_data  = 100;
+		break;
+	}
+
+	timings.sda_rise  = timings.sda_fall = 50;
+	timings.scl_rise  = timings.scl_fall = 50;
+	timings.hold_data = 300;
+	timings.buf       = 20000;
+	timings.thigh_max = 5000;
+	/*
+	 * Note that the SCL_LOW_TIMEOUT value is not related to the bus
+	 * frequency, it is impacted by the time it takes the driver to
+	 * complete data transmission before transaction abort.
+	 */
+	timings.timeout   = 106500;
+
+	return mlx_i2c_set_timings(priv, &timings);
+}
+
+static int mlx_i2c_init_master(struct platform_device *pdev, int bus)
+{
+	struct resource *res;
+	struct device   *dev = &pdev->dev;
+	void __iomem    *gpio_io;
+	u32 config_reg, mask = 0;
+
+	static DEFINE_MUTEX(gpio_lock);
+
+	/*
+	 * The GPIO region in TYU space is shared among I2C busses.
+	 * This function MUST be serialized to avoid racing when
+	 * claiming the memory region and setting up the GPIO.
+	 */
+
+	mutex_lock(&gpio_lock);
+
+	/*
+	 * Retrieve GPIO region in the TYU.
+	 */
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, MLX_GPIO_RES);
+	if (!res) {
+		dev_err(dev, "invalid GPIO resource\n");
+		mutex_unlock(&gpio_lock);
+		return -EINVAL;
+	}
+
+	gpio_io = devm_ioremap_resource(dev, res);
+	if (IS_ERR(gpio_io)) {
+		mutex_unlock(&gpio_lock);
+		return PTR_ERR(gpio_io);
+	}
+
+	/*
+	 * Smbus master initialization
+	 */
+
+	/*
+	 * TYU - Configuration for GPIO pins. Those pins must be asserted in
+	 * TYU_GPIO_0_FUNC_EN_0, i.e. GPIO 0 is controlled by HW, and must
+	 * be reset in TYU_GPIO_0_FORCE_OE_EN, i.e. GPIO_OE will be driven
+	 * instead of HW_OE.
+	 * For now, we do not reset the GPIO state when the driver is removed.
+	 * First, it is not necessary to disable the bus since we are using
+	 * the same busses. Then, some busses might be shared among Linux and
+	 * firmware such as ATF; disabling the bus would compromise the system
+	 * functionality.
+	 */
+
+	/*
+	 * Note that Smbus GWs are on GPIOs 30:25. Two pins are used to control
+	 * SDA/SCL lines:
+	 *
+	 *  SMBUS GW0 -> bits[26:25]
+	 *  SMBUS GW1 -> bits[28:27]
+	 *  SMBUS GW2 -> bits[30:29]
+	 */
+
+	config_reg = smbus_read(gpio_io, TYU_GPIO_0_FUNC_EN_0);
+	config_reg = TYU_GPIO_SMBUS_GW_ASSERT_PINS(bus, config_reg);
+	smbus_write(gpio_io, TYU_GPIO_0_FUNC_EN_0, config_reg);
+
+	/* Work out what mask we should be using to reset GPIO pins */
+	switch (bus) {
+	case 0:
+		mask = TYU_GPIO_SMBUS_GW_0_MASK;
+		break;
+	case 1:
+		mask = TYU_GPIO_SMBUS_GW_1_MASK;
+		break;
+	case 2:
+		mask = TYU_GPIO_SMBUS_GW_2_MASK;
+		break;
+	default:
+		mutex_unlock(&gpio_lock);
+		return -EINVAL;
+	}
+
+	config_reg = smbus_read(gpio_io, TYU_GPIO_0_FORCE_OE_EN);
+	config_reg = TYU_GPIO_SMBUS_GW_RESET_PINS(config_reg, mask);
+	smbus_write(gpio_io, TYU_GPIO_0_FORCE_OE_EN, config_reg);
+
+	/*
+	 * Release the GPIO resource
+	 */
+	devm_release_mem_region(dev, res->start, resource_size(res));
+
+	mutex_unlock(&gpio_lock);
+
+	return 0;
+}
+
+/*
+ * Compute PLL output frequency as follow:
+ *
+ *                                       CORE_F + 1
+ * PLL_OUT_FREQ = PLL_IN_FREQ * ----------------------------
+ *                              (CORE_R + 1) * (CORE_OD + 1)
+ *
+ * Where PLL_OUT_FREQ and PLL_IN_FREQ refer to CoreFrequency and
+ * PadFrequency, respectively.
+ *
+ * For a CoreFrequency of 400MHz, expected PLL parameters are:
+ *
+ *  CORE_F     = 511
+ *  CORE_R     = 24
+ *  CORE_OD    = 7
+ *  CORE_BWADJ = 255
+ *
+ */
+static int mlx_i2c_freq_calculate(struct platform_device *pdev, u64 *freq)
+{
+	struct resource *res;
+	struct device   *dev = &pdev->dev;
+	void __iomem    *corepll_io;
+	u64 core_frequency;
+	u64 pad_frequency;
+	u32 core_pll_val;
+	u16 core_f;
+	u8  core_od, core_r;
+
+	static DEFINE_MUTEX(corepll_lock);
+
+	/*
+	 * First, check whether the TYU core Clock frequency is set.
+	 * The TYU core frequency is the same for all I2C busses; when
+	 * the first device gets probed the frequency is determined and
+	 * stored into a globally visible variable. So, first of all,
+	 * check whether the frequency is already set. Here, we assume
+	 * that the frequency is expected to be greater than 0.
+	 */
+	if (tyu_core_frequency != 0) {
+		*freq = tyu_core_frequency;
+		return 0;
+	}
+
+	/*
+	 * The COREPLL region in TYU space is shared among I2C busses.
+	 * This function MUST be serialized to avoid racing when
+	 * claiming the memory region.
+	 */
+
+	mutex_lock(&corepll_lock);
+
+	/*
+	 * Retrieve COREPLL region in the TYU.
+	 */
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, MLX_COREPLL_RES);
+	if (!res) {
+		dev_err(dev, "invalid COREPLL resource\n");
+		mutex_unlock(&corepll_lock);
+		return -EINVAL;
+	}
+
+	corepll_io = devm_ioremap_resource(dev, res);
+	if (IS_ERR(corepll_io)) {
+		mutex_unlock(&corepll_lock);
+		return PTR_ERR(corepll_io);
+	}
+
+	/*
+	 * PLL configuration bits are exposed to SW in TYU. This function
+	 * is responsible for reading these bits and deriving the TYU
+	 * frequency.
+	 */
+
+	core_pll_val  = smbus_read(corepll_io, TYU_CORE_PLL_CFG_LSB);
+	pad_frequency = BLUEFIELD_TYU_PLL_IN_FREQ;
+
+	/* Get Core PLL configuration bits */
+	core_f  = GET_PLL_CORE_F(core_pll_val);
+	core_od = GET_PLL_CORE_OD(core_pll_val);
+	core_r  = GET_PLL_CORE_R(core_pll_val);
+
+	/*
+	 * Compute PLL output frequency as follow:
+	 *
+	 *                                       CORE_F + 1
+	 * PLL_OUT_FREQ = PLL_IN_FREQ * ----------------------------
+	 *                              (CORE_R + 1) * (CORE_OD + 1)
+	 *
+	 * Where PLL_OUT_FREQ and PLL_IN_FREQ refer to CoreFrequency
+	 * and PadFrequency, respectively.
+	 */
+
+	core_frequency  = pad_frequency * (core_f + 1);
+	core_frequency /= ((core_r + 1) * (core_od + 1));
+
+	*freq = tyu_core_frequency = core_frequency;
+
+	/*
+	 * Release the COREPLL resource. This might be done immediately
+	 * after the smbus_read() call, but let's keep it clear and
+	 * do it last.
+	 */
+	devm_release_mem_region(dev, res->start, resource_size(res));
+
+	mutex_unlock(&corepll_lock);
+
+	return 0;
+}
+
+static int mlx_slave_enable(struct mlx_i2c_priv *priv, u8 addr)
+{
+	u8   reg, reg_cnt, byte, addr_tmp, reg_avail, byte_avail;
+	bool exist, avail, disabled;
+	u32  slave_reg, slave_reg_tmp, slave_reg_avail;
+
+	exist    = false;
+	avail    = false;
+	disabled = false;
+
+	if (!priv)
+		return -EPERM;
+
+	reg_cnt = SMBUS_SLAVE_ADDR_CNT >> 2;
+
+	/*
+	 * Read the slave registers. There are 4 * 32-bit slave registers.
+	 * Each slave register can hold up to 4 * 8-bit slave configuration
+	 * (7-bit address, 1 status bit (1 if enabled, 0 if not)).
+	 */
+	for (reg = 0; reg < reg_cnt; reg++) {
+		slave_reg = smbus_read(priv->smbus_io,
+				       SMBUS_SLAVE_ADDR_CFG + (reg * 0x4));
+		/*
+		 * Each register holds 4 slave addresses. So, we have to keep
+		 * the byte order consistent with the value read in order to
+		 * update the register correctly, if needed.
+		 */
+		slave_reg_tmp = slave_reg;
+		for (byte = 0; byte < 4; byte++) {
+			addr_tmp = slave_reg_tmp & 0xff;
+
+			/*
+			 * Mark the first available slave address slot, i.e. its
+			 * enabled bit should be unset. This slot might be used
+			 * later on to register our slave.
+			 */
+			if (!avail &&
+			      !(addr_tmp & (1 << SMBUS_SLAVE_ADDR_EN_BIT))) {
+				avail           = true;
+				reg_avail       = reg;
+				byte_avail      = byte;
+				slave_reg_avail = slave_reg;
+			}
+
+			/*
+			 * Parse slave address bytes and check whether the
+			 * slave address already exists and it's enabled,
+			 * i.e. most significant bit is set.
+			 */
+			if ((addr_tmp & SMBUS_SLAVE_ADDR_MASK) == addr) {
+				if (addr_tmp & (1 << SMBUS_SLAVE_ADDR_EN_BIT))
+					return 0;
+				disabled = true;
+				break;
+			}
+
+			/* Parse next byte */
+			slave_reg_tmp >>= 8;
+		}
+
+		/* Exit the loop if the slave address is found */
+		if (disabled)
+			break;
+	}
+
+	if (!avail && !disabled)
+		return -EINVAL;         /* No room for a new slave address */
+
+	if (avail) {
+		reg  = reg_avail;
+		byte = byte_avail;
+		/* Set the slave address */
+		slave_reg_avail |= (addr << (byte * 8));
+		slave_reg        = slave_reg_avail;
+	}
+
+	/* Enable the slave address and update the register */
+	slave_reg |= ((1 << SMBUS_SLAVE_ADDR_EN_BIT) << (byte * 8));
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_ADDR_CFG + (reg * 0x4),
+		    slave_reg);
+
+	return 0;
+}
+
+static int mlx_slave_disable(struct mlx_i2c_priv *priv)
+{
+	struct i2c_client *client = priv->slave;
+	u8   addr, addr_tmp, reg, reg_cnt, slave_byte;
+	bool exist;
+	u32  slave_reg, slave_reg_tmp;
+
+	exist = false;
+
+	addr    = client->addr;
+	reg_cnt = SMBUS_SLAVE_ADDR_CNT >> 2;
+
+	/*
+	 * Read the slave registers. There are 4 * 32-bit slave registers.
+	 * Each slave register can hold up to 4 * 8-bit slave configuration
+	 * (7-bit address, 1 status bit (1 if enabled, 0 if not)).
+	 */
+	for (reg = 0; reg < reg_cnt; reg++) {
+		slave_reg = smbus_read(priv->smbus_io,
+				       SMBUS_SLAVE_ADDR_CFG + (reg * 0x4));
+
+		/* Check whether the address slots are empty */
+		if (slave_reg == 0)
+			continue;
+
+		/*
+		 * Each register holds 4 slave addresses. So, we have to keep
+		 * the byte order consistent with the value read in order to
+		 * update the register correctly, if needed.
+		 */
+		slave_reg_tmp = slave_reg;
+		slave_byte    = 0;
+		while (slave_reg_tmp != 0) {
+			addr_tmp = slave_reg_tmp & SMBUS_SLAVE_ADDR_MASK;
+			/*
+			 * Parse slave address bytes and check whether the
+			 * slave address already exists.
+			 */
+			if (addr_tmp == addr) {
+				exist = true;
+				break;
+			}
+
+			/* Parse next byte */
+			slave_reg_tmp >>= 8;
+			slave_byte     += 1;
+		}
+
+		/* Exit the loop if the slave address is found */
+		if (exist)
+			break;
+	}
+
+	if (!exist)
+		return 0;	/* slave is not registered, nothing to do */
+
+	/* Cleanup the slave address slot. */
+	slave_reg &= ~(0xFF << (slave_byte * 8));
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_ADDR_CFG + (reg * 0x4),
+		    slave_reg);
+
+	return 0;
+}
+
+static int mlx_i2c_init_slave(struct mlx_i2c_priv *priv)
+{
+	u32 int_reg;
+
+	/*
+	 * Smbus slave initialization
+	 */
+
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_FSM, 0); /* reset FSM */
+
+	/*
+	 * Enable slave cause interrupt bits. Drive CAUSE_READ_WAIT_FW_RESPONSE
+	 * and CAUSE_WRITE_SUCCESS, these are enabled when an external masters
+	 * issue a Read and Write, respectively. But, clear all interrupts
+	 * first.
+	 */
+	smbus_write(priv->slv_cause_io, TYU_CAUSE_OR_CLEAR_BITS, ~0);
+	int_reg = CAUSE_READ_WAIT_FW_RESPONSE | CAUSE_WRITE_SUCCESS;
+	smbus_write(priv->slv_cause_io, TYU_CAUSE_OR_EVTEN0_BITS, int_reg);
+
+	/* Finally, set the 'ready' bit to start handling transactions */
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_READY, 0x1);
+
+	return 0;
+}
+
+static int mlx_i2c_init_coalesce(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct device   *dev = &pdev->dev;
+	void __iomem    *io;
+
+	/*
+	 * The Cause Coalesce group in TYU space is shared among
+	 * I2C busses. This function MUST be serialized to avoid
+	 * racing when claiming the memory region.
+	 */
+
+	mutex_lock(&tyu_coalesce_lock);
+
+	if (tyu_coalesce.valid) {
+		mutex_unlock(&tyu_coalesce_lock);
+		return 0;
+	}
+
+	/*
+	 * Retrieve Cause Coalesce region in the TYU.
+	 */
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, MLX_COALESCE_RES);
+	if (!res) {
+		dev_err(dev, "invalid Cause Coalesce resource\n");
+		mutex_unlock(&tyu_coalesce_lock);
+		return -EINVAL;
+	}
+
+	io = devm_ioremap_resource(dev, res);
+	if (IS_ERR(io)) {
+		mutex_unlock(&tyu_coalesce_lock);
+		return PTR_ERR(io);
+	}
+
+	tyu_coalesce.res   = res;
+	tyu_coalesce.io    = io;
+	tyu_coalesce.valid = true;
+
+	mutex_unlock(&tyu_coalesce_lock);
+
+	return 0;
+}
+
+static int mlx_i2c_release_coalesce(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct device   *dev = &pdev->dev;
+
+	mutex_lock(&tyu_coalesce_lock);
+
+	if (tyu_coalesce.valid) {
+		res = tyu_coalesce.res;
+		/* Release the Cause Coalesce resource */
+		devm_release_mem_region(dev, res->start, resource_size(res));
+		tyu_coalesce.valid = false;
+	}
+
+	mutex_unlock(&tyu_coalesce_lock);
+
+	return 0;
+}
+
+static bool mlnx_i2c_has_coalesce(struct mlx_i2c_priv *priv, bool *read,
+				  bool *write)
+{
+	u32 coalesce0_reg, cause_reg;
+	u8  is_set, slave, slave_shift;
+
+	*read  = *write = false;
+	is_set = 0;
+
+	slave = priv->bus;
+	/*
+	 * Slave interrupt bits start after master interrupt bits. Set slave
+	 * bit shift, then.
+	 */
+	slave_shift = I2C_SMBUS_MAX;
+
+	mutex_lock(&tyu_coalesce_lock);
+
+	if (tyu_coalesce.valid) {
+		coalesce0_reg =
+			smbus_read(tyu_coalesce.io, TYU_CAUSE_COALESCE_0);
+		is_set        = coalesce0_reg & (1 << (slave + slave_shift));
+	}
+
+	mutex_unlock(&tyu_coalesce_lock);
+
+	if (!is_set)
+		return false;
+
+	/* Check the source of the interrupt, i.e. whether a Read or Write */
+	cause_reg = smbus_read(priv->slv_cause_io, TYU_CAUSE_ARBITER_BITS);
+	if (cause_reg & CAUSE_READ_WAIT_FW_RESPONSE)
+		*read = true;
+	else if (cause_reg & CAUSE_WRITE_SUCCESS)
+		*write = true;
+
+	/* Clear cause bits */
+	smbus_write(priv->slv_cause_io, TYU_CAUSE_OR_CLEAR_BITS, ~0x0);
+
+	return true;
+}
+
+static bool mlx_smbus_slave_wait_for_idle(struct mlx_i2c_priv *priv,
+					  u32 timeout)
+{
+	u32 addr = TYU_CAUSE_ARBITER_BITS;
+	u32 mask = CAUSE_S_GW_BUSY_FALL;
+
+	if (mlx_smbus_poll(priv->slv_cause_io, addr, mask, false, timeout))
+		return true;
+
+	return false;
+}
+
+/* Send byte to 'external' smbus master */
+static int mlx_smbus_irq_send(struct mlx_i2c_priv *priv, u8 recv_bytes)
+{
+	struct i2c_client *slave = priv->slave;
+	u8   data_desc[SLAVE_DATA_DESC_SIZE] = { 0 };
+	u32  control32, data32;
+	u8   write_size, pec_en, addr, byte, value, byte_cnt;
+	int  ret;
+
+	if (!slave)
+		return -EINVAL;
+
+	addr = byte = 0;
+
+	/*
+	 * Read bytes received from the external master. These bytes should
+	 * be located in the first data descriptor register of the slave GW.
+	 * These bytes are the slave address byte and the internal register
+	 * address, if supplied.
+	 */
+	if (recv_bytes > 0) {
+		data32 = smbus_read_data(priv->smbus_io, SLAVE_DATA_DESC_ADDR);
+
+		/* Parse the received bytes */
+		switch (recv_bytes) {
+		case 2:
+			byte = (data32 >> 8) & 0xff;
+			/* fall-through */
+		case 1:
+			addr = (data32 & 0xff) >> 1;
+		}
+
+		/* Check whether it's our slave address. */
+		if (slave->addr != addr)
+			return -EINVAL;
+	}
+
+	/*
+	 * I2C read transactions may start by a WRITE followed by a READ.
+	 * Indeed, most slave devices would expect the internal address
+	 * following the slave address byte. So, write that byte first,
+	 * and then, send the requested data bytes to the master.
+	 */
+	if (recv_bytes > 1) {
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+		value = byte;
+		ret   = i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED,
+					&value);
+		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+		if (ret < 0)
+			return ret;
+	}
+
+	/*
+	 * Now, send data to the master; currently, the driver supports
+	 * READ_BYTE, READ_WORD and BLOCK READ protocols. Note that the
+	 * hardware can send up to 128 bytes per transfer. That is the
+	 * size of its data registers.
+	 */
+	i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+	for (byte_cnt = 0; byte_cnt < SLAVE_DATA_DESC_SIZE; byte_cnt++) {
+		data_desc[byte_cnt] = value;
+		i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
+	}
+
+	/* Send a stop condition to the backend. */
+	i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+	/*
+	 * Handle the actual transfer.
+	 */
+
+	/* Set the number of bytes to write to master */
+	write_size = (byte_cnt - 1) & 0x7f;
+
+	/* Write data to Slave GW data descriptor */
+	mlx_smbus_write_data(priv, data_desc, byte_cnt, SLAVE_DATA_DESC_ADDR);
+
+	pec_en = 0; /* Disable PEC since it is not supported */
+
+	/* Prepare control word */
+	control32  = 0;
+	control32 |= 0          << SLAVE_LOCK_BIT_OFF;
+	control32 |= 1          << SLAVE_BUSY_BIT_OFF;
+	control32 |= 1          << SLAVE_WRITE_BIT_OFF;
+	control32 |= write_size << SLAVE_WRITE_BYTES_BIT_OFF;
+	control32 |= pec_en     << SLAVE_SEND_PEC_BIT_OFF;
+
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_GW, control32);
+
+	/*
+	 * Wait until the transfer is completed; the driver will wait
+	 * until the GW is idle, a cause will rise on fall of GW busy.
+	 */
+	mlx_smbus_slave_wait_for_idle(priv, SMBUS_TIMEOUT);
+
+	/* Release the Slave GW */
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_RS_MASTER_BYTES, 0x0);
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_PEC,             0x0);
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_READY,           0x1);
+
+	return 0;
+}
+
+/* Receive bytes from 'external' smbus master */
+static int mlx_smbus_irq_recv(struct mlx_i2c_priv *priv, u8 recv_bytes)
+{
+	struct i2c_client *slave = priv->slave;
+	u8   data_desc[SLAVE_DATA_DESC_SIZE] = { 0 };
+	u8   value, byte, addr;
+	int  ret = 0;
+
+	if (!slave)
+		return -EINVAL;
+
+	/* Read data from Slave GW data descriptor */
+	mlx_smbus_read_data(priv, data_desc, recv_bytes, SLAVE_DATA_DESC_ADDR);
+
+	/* Check whether its our slave address */
+	addr = data_desc[0] >> 1;
+	if (slave->addr != addr)
+		return -EINVAL;
+
+	/*
+	 * Notify the slave backend; another I2C master wants to write data
+	 * to us. This event is sent once the slave address and the write bit
+	 * is detected.
+	 */
+	i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+	/* Send the received data to the slave backend */
+	for (byte = 1; byte < recv_bytes; byte++) {
+		value = data_desc[byte];
+		ret   = i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED,
+					&value);
+		if (ret < 0)
+			break;
+	}
+
+	/* Send a stop condition to the backend */
+	i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+
+	/* Release the Slave GW */
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_RS_MASTER_BYTES, 0x0);
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_PEC,             0x0);
+	smbus_write(priv->smbus_io, SMBUS_SLAVE_READY,           0x1);
+
+	return ret;
+}
+
+static irqreturn_t mlx_smbus_irq(int irq, void *ptr)
+{
+	struct mlx_i2c_priv *priv = ptr;
+	bool read, write, irq_is_set;
+	u32  rw_bytes_reg;
+	u8   recv_bytes;
+
+	/*
+	 * Read TYU interrupt register and determine the source of the
+	 * interrupt. Based on the source of the interrupt one of the
+	 * following actions are performed:
+	 *  - Receive data and send response to master.
+	 *  - Send data and release slave GW.
+	 *
+	 * Handle read/write transaction only. CRmaster and Iarp requests
+	 * are ignored for now.
+	 */
+	irq_is_set = mlnx_i2c_has_coalesce(priv, &read, &write);
+	if (!irq_is_set || (!read && !write)) {
+		/* Nothing to do here, interrupt was not from this device */
+		return IRQ_NONE;
+	}
+
+	/*
+	 * The SMBUS_SLAVE_RS_MASTER_BYTES includes the number of bytes
+	 * from/to master. These are defined by 8-bits each. If the lower
+	 * 8 bits are set, then the master expect to read N bytes from the
+	 * slave, if the higher 8 bits are sent then the slave expect N
+	 * bytes from the master.
+	 */
+	rw_bytes_reg = smbus_read(priv->smbus_io, SMBUS_SLAVE_RS_MASTER_BYTES);
+	recv_bytes   = (rw_bytes_reg >> 8) & 0xff;
+
+	/*
+	 * For now, the slave supports 128 bytes transfer. Discard remaining
+	 * data bytes if the master wrote more than SLAVE_DATA_DESC_SIZE, i.e,
+	 * the actual size of the slave data descriptor.
+	 *
+	 * Note that we will never expect to transfer more than 128 bytes; as
+	 * specified in the SMBus standard, block transactions cannot exceed
+	 * 32 bytes.
+	 */
+	recv_bytes = (recv_bytes > SLAVE_DATA_DESC_SIZE) ?
+		SLAVE_DATA_DESC_SIZE : recv_bytes;
+
+	if (read)
+		mlx_smbus_irq_send(priv, recv_bytes);
+
+	if (write)
+		mlx_smbus_irq_recv(priv, recv_bytes);
+
+	return IRQ_HANDLED;
+}
+
+/* Return negative errno on error */
+static s32 mlx_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size,
+			       union i2c_smbus_data *data)
+{
+	struct mlx_smbus_request  request = { 0 };
+	struct mlx_i2c_priv      *priv;
+	bool read, pec;
+	u8   byte_cnt;
+
+	request.slave = addr;
+
+	read = (read_write == I2C_SMBUS_READ);
+	pec  = flags & I2C_FUNC_SMBUS_PEC;
+
+	switch (size) {
+
+	case I2C_SMBUS_QUICK:
+		mlx_smbus_quick_command(&request, read);
+		dev_dbg(&adap->dev, "smbus quick, slave 0x%02x\n", addr);
+		break;
+
+	case I2C_SMBUS_BYTE:
+		mlx_smbus_byte_func(&request, (read) ? &data->byte : &command,
+				     read, pec);
+		dev_dbg(&adap->dev, "smbus %s byte, slave 0x%02x.\n",
+			(read) ? "read" : "write", addr);
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		mlx_smbus_data_byte_func(&request, &command, &data->byte,
+					  read, pec);
+		dev_dbg(&adap->dev,
+			"smbus %s byte data at 0x%02x, slave 0x%02x.\n",
+			(read) ? "read" : "write", command, addr);
+		break;
+
+	case I2C_SMBUS_WORD_DATA:
+		mlx_smbus_data_word_func(&request, &command,
+					  (u8 *) &data->word, read, pec);
+		dev_dbg(&adap->dev,
+			"smbus %s word data at 0x%02x, slave 0x%02x.\n",
+			(read) ? "read" : "write", command, addr);
+		break;
+
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		byte_cnt = data->block[0];
+		mlx_smbus_i2c_block_func(&request, &command, data->block,
+					  &byte_cnt, read, pec);
+		dev_dbg(&adap->dev,
+		 "i2c %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
+			(read) ? "read" : "write", byte_cnt, command, addr);
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		byte_cnt = (read) ? I2C_SMBUS_BLOCK_MAX : data->block[0];
+		mlx_smbus_block_func(&request, &command, data->block,
+				      &byte_cnt, read, pec);
+		dev_dbg(&adap->dev,
+		 "smbus %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
+			(read) ? "read" : "write", byte_cnt, command, addr);
+		break;
+
+	case I2C_FUNC_SMBUS_PROC_CALL:
+		mlx_smbus_process_call_func(&request, &command,
+					     (u8 *) &data->word, pec);
+		dev_dbg(&adap->dev,
+			"process call, wr/rd at 0x%02x, slave 0x%02x.\n",
+			command, addr);
+		break;
+
+	case I2C_FUNC_SMBUS_BLOCK_PROC_CALL:
+		byte_cnt = data->block[0];
+		mlx_smbus_blk_process_call_func(&request, &command,
+						 data->block, &byte_cnt, pec);
+		dev_dbg(&adap->dev,
+			"block process call, wr/rd %d bytes, slave 0x%02x.\n",
+			byte_cnt, addr);
+		break;
+
+	case I2C_FUNC_SMBUS_HOST_NOTIFY:
+		mlx_smbus_host_notify_func(&request, &command,
+					    (u8 *) &data->word, read, pec);
+		dev_dbg(&adap->dev,
+			"smbus host notify, %s at 0x%02x, slave 0x%02x.\n",
+			(read) ? "read" : "write", command, addr);
+		break;
+
+	default:
+		dev_dbg(&adap->dev, "Unsupported I2C/SMBus command %d\n",
+			size);
+		return -EOPNOTSUPP;
+	}
+
+	priv = i2c_get_adapdata(adap);
+
+	return mlx_smbus_start_transaction(priv, &request);
+}
+
+static int mlx_i2c_reg_slave(struct i2c_client *slave)
+{
+	struct mlx_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+	int ret;
+
+	if (priv->slave)
+		return -EBUSY;
+
+	/*
+	 * Do not support ten bit chip address and do not use Packet Error
+	 * Checking (PEC).
+	 */
+	if (slave->flags & (I2C_CLIENT_TEN | I2C_CLIENT_PEC))
+		return -EAFNOSUPPORT;
+
+	ret = mlx_slave_enable(priv, slave->addr);
+	if (ret < 0)
+		return ret;
+
+	priv->slave = slave;
+
+	return 0;
+}
+
+static int mlx_i2c_unreg_slave(struct i2c_client *slave)
+{
+	struct mlx_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
+	int ret;
+
+	WARN_ON(!priv->slave);
+
+	/* Unregister slave, i.e. disable the slave address in hardware. */
+	ret = mlx_slave_disable(priv);
+	if (ret < 0)
+		return ret;
+
+	priv->slave = NULL;
+
+	return 0;
+}
+
+static u32 mlx_i2c_functionality(struct i2c_adapter *adap)
+{
+	return MLX_I2C_FUNC_ALL;
+}
+
+static const struct i2c_algorithm mlx_i2c_algo = {
+	.smbus_xfer    = mlx_i2c_smbus_xfer,
+	.functionality = mlx_i2c_functionality,
+	.reg_slave     = mlx_i2c_reg_slave,
+	.unreg_slave   = mlx_i2c_unreg_slave,
+};
+
+static const struct of_device_id mlx_i2c_dt_ids[] = {
+	{.compatible = "mellanox,i2c-mlxbf"},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mlx_i2c_dt_ids);
+
+static const struct acpi_device_id mlx_i2c_acpi_ids[] = {
+	{"MLNXBF03", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(acpi, mlx_i2c_acpi_ids);
+
+static int mlx_i2c_acpi_probe(struct device *dev, struct mlx_i2c_priv *priv)
+{
+	unsigned long bus_id = 0;
+	const char *uid;
+	int ret;
+
+	if (acpi_disabled)
+		return -ENOENT;
+
+	uid = acpi_device_uid(ACPI_COMPANION(dev));
+	if (!uid || !(*uid)) {
+		dev_err(dev, "cannot retrieve _UID\n");
+		return -ENODEV;
+	}
+
+	ret = kstrtoul(uid, 0, &bus_id);
+	if (ret == 0) {
+		/* TBD Check the bus_id range */
+		priv->bus = bus_id;
+	}
+
+	return ret;
+}
+
+static int mlx_i2c_of_probe(struct device *dev, struct mlx_i2c_priv *priv)
+{
+	int bus_id = -1;
+
+	if (dev->of_node) {
+		bus_id = of_alias_get_id(dev->of_node, "i2c");
+		if (bus_id >= 0)
+			priv->bus = bus_id;
+	}
+
+	if (WARN(bus_id < 0, "couldn't get bus id"))
+		return bus_id;
+
+	return 0;
+}
+
+static int mlx_i2c_probe(struct platform_device *pdev)
+{
+	struct mlx_i2c_priv *priv;
+	struct i2c_adapter  *adap;
+	struct device       *dev = &pdev->dev;
+	int irq, ret;
+
+	priv = devm_kzalloc(dev, sizeof(struct mlx_i2c_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	ret = mlx_i2c_acpi_probe(dev, priv);
+	if (ret < 0)
+		ret = mlx_i2c_of_probe(dev, priv);
+
+	if (ret < 0)
+		return ret;
+
+	/* Smbus region in the TYU. */
+	priv->smbus_res = platform_get_resource(pdev, IORESOURCE_MEM,
+						  MLX_SMBUS_RES);
+	if (!priv->smbus_res) {
+		dev_err(dev, "Cannot fetch smbus resource info");
+		return -EIO;
+	}
+
+	priv->smbus_io  = devm_ioremap_resource(dev, priv->smbus_res);
+	if (IS_ERR(priv->smbus_io))
+		return PTR_ERR(priv->smbus_io);
+
+	/* Smbus master cause region in the TYU. */
+	priv->mst_cause_res = platform_get_resource(pdev, IORESOURCE_MEM,
+						      MLX_MST_CAUSE_RES);
+	if (!priv->mst_cause_res) {
+		dev_err(dev, "Cannot fetch cause master resource info");
+		return -EIO;
+	}
+
+	priv->mst_cause_io  = devm_ioremap_resource(dev, priv->mst_cause_res);
+	if (IS_ERR(priv->mst_cause_io))
+		return PTR_ERR(priv->mst_cause_io);
+
+	/* Smbus slave cause region in the TYU. */
+	priv->slv_cause_res = platform_get_resource(pdev, IORESOURCE_MEM,
+						      MLX_SLV_CAUSE_RES);
+	if (!priv->slv_cause_res) {
+		dev_err(dev, "Cannot fetch cause slave resource info");
+		return -EIO;
+	}
+
+	priv->slv_cause_io  = devm_ioremap_resource(dev, priv->slv_cause_res);
+	if (IS_ERR(priv->slv_cause_io))
+		return PTR_ERR(priv->slv_cause_io);
+
+	adap              = &priv->adap;
+	adap->owner       = THIS_MODULE;
+	adap->class       = I2C_CLASS_HWMON;
+	adap->algo        = &mlx_i2c_algo;
+	adap->dev.parent  = dev;
+	adap->dev.of_node = dev->of_node;
+	/*
+	 * Set the device minor number based on the physical bus. The device
+	 * character file will be added to the 'dev' interface (and sysfs).
+	 * Right now, we assume that the driver probes bus 1 and 2, only. Thus,
+	 * bus 1 and bus 2 would be referred to as /dev/i2c-1 and /dev/i2c-2,
+	 * respectively. This is hardcoded because we do want to know which
+	 * device software should be talking to.
+	 */
+	adap->nr          = priv->bus;
+
+	snprintf(adap->name, sizeof(adap->name), "i2c%d", adap->nr);
+	i2c_set_adapdata(adap, priv);
+
+	/* Read core frequency */
+	ret = mlx_i2c_freq_calculate(pdev, &priv->frequency);
+	if (ret < 0) {
+		dev_err(dev, "cannot get core clock frequency\n");
+		/* Set to default value */
+		priv->frequency = MLX_I2C_CORE_FREQ;
+	}
+
+	/*
+	 * Initialize master.
+	 * Note that a physical bus might be shared among Linux and firmware
+	 * (e.g., ATF). Thus, the bus should be initialized and ready and
+	 * bus initialization would be unnecessary. This requires additional
+	 * knowledge about physical busses. But, since an extra initialization
+	 * does not really hurt, then keep the code as is.
+	 */
+	ret = mlx_i2c_init_master(pdev, priv->bus);
+	if (ret < 0) {
+		dev_err(dev, "failed to initialize smbus master %d",
+			priv->bus);
+		return ret;
+	}
+
+	/* Configure timing */
+	mlx_i2c_init_timings(dev, priv);
+
+	/* Initialize slave gw */
+	mlx_i2c_init_slave(priv);
+
+	/* Initialize the TYU cause coalesce resource */
+	ret = mlx_i2c_init_coalesce(pdev);
+	if (ret < 0) {
+		dev_err(dev, "failed to initialize TYU cause coalesce\n");
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, mlx_smbus_irq,
+			       IRQF_SHARED | IRQF_PROBE_SHARED,
+			       dev_name(dev), priv);
+	if (ret < 0) {
+		dev_err(dev, "cannot get irq %d\n", irq);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = i2c_add_numbered_adapter(adap);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&i2c_bus_lock);
+	i2c_bus_count++;
+	mutex_unlock(&i2c_bus_lock);
+	dev_info(dev, "probed\n");
+
+	return 0;
+}
+
+static int mlx_i2c_remove(struct platform_device *pdev)
+{
+	struct mlx_i2c_priv *priv = platform_get_drvdata(pdev);
+	struct device       *dev = &pdev->dev;
+	struct resource     *res;
+
+	/* Release the smbus region */
+	res = priv->smbus_res;
+	devm_release_mem_region(dev, res->start, resource_size(res));
+
+	/* Release the cause master region */
+	res = priv->mst_cause_res;
+	devm_release_mem_region(dev, res->start, resource_size(res));
+
+	/* Release the cause slave region */
+	res = priv->slv_cause_res;
+	devm_release_mem_region(dev, res->start, resource_size(res));
+
+	/*
+	 * Release coalesce. This should be done when releasing the
+	 * I2C controller.
+	 */
+	mutex_lock(&i2c_bus_lock);
+	if (--i2c_bus_count == 0)
+		mlx_i2c_release_coalesce(pdev);
+	mutex_unlock(&i2c_bus_lock);
+
+
+	i2c_del_adapter(&priv->adap);
+
+	return 0;
+}
+
+static struct platform_driver mlx_i2c_driver = {
+	.probe  = mlx_i2c_probe,
+	.remove = mlx_i2c_remove,
+	.driver = {
+		   .name = "i2c-mlx",
+		   .of_match_table   = mlx_i2c_dt_ids,
+		   .acpi_match_table = ACPI_PTR(mlx_i2c_acpi_ids),
+		   },
+};
+
+module_platform_driver(mlx_i2c_driver);
+
+MODULE_DESCRIPTION(MLX_I2C_DRIVER_DESCRIPTION);
+MODULE_VERSION(MLX_I2C_DRIVER_VERSION);
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL");