[PATCH linux dev-4.13 v1 2/2] i2c: npcm: add NPCM7xx I2C bus driver

Tomer Maimon tmaimon77 at gmail.com
Thu Jan 4 23:31:34 AEDT 2018


Add Nuvoton BMC NPCM7xx I2C bus driver.

Signed-off-by: Tomer Maimon <tmaimon77 at gmail.com>
---
 drivers/i2c/busses/Kconfig       |   10 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/i2c-npcm7xx.c | 3512 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 3523 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 65fa29591d21..5971789814df 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -728,6 +728,16 @@ config I2C_NOMADIK
 	  I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
 	  as well as the STA2X11 PCIe I/O HUB.
 
+config I2C_NPCM7XX
+	tristate "Nuvoton I2C Controller"
+	depends on ARCH_NPCM7XX
+	help
+	  If you say yes to this option, support will be included for the
+	  Nuvoton I2C controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-npcm7xx.
+
 config I2C_OCORES
 	tristate "OpenCores I2C Controller"
 	help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 1b2fc815a4d8..80acad9903ff 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_I2C_MT65XX)	+= i2c-mt65xx.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_MXS)		+= i2c-mxs.o
 obj-$(CONFIG_I2C_NOMADIK)	+= i2c-nomadik.o
+obj-$(CONFIG_I2C_NPCM7XX)	+= i2c-npcm7xx.o
 obj-$(CONFIG_I2C_OCORES)	+= i2c-ocores.o
 obj-$(CONFIG_I2C_OMAP)		+= i2c-omap.o
 obj-$(CONFIG_I2C_PASEMI)	+= i2c-pasemi.o
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
new file mode 100644
index 000000000000..6ed33a1e7a03
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,3512 @@
+/*
+ * Copyright (c) 2014-2017 Nuvoton Technology corporation.
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk/nuvoton.h>
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+static struct regmap *gcr_regmap;
+
+#define  I2CSEGCTL_OFFSET 0xE4
+#define  I2CSEGCTL_VAL	  0x0333F000
+
+#define ENABLE      1
+#define DISABLE     0
+
+#define _1Hz_           1UL
+#define _1KHz_          (1000 * _1Hz_)
+#define _1MHz_          (1000 * _1KHz_)
+#define _1GHz_          (1000 * _1MHz_)
+
+#ifndef ASSERT
+#ifdef DEBUG
+#define ASSERT(cond)  {if (!(cond)) for (;;) ; }           /* infinite loop                                  */
+#else
+#define ASSERT(cond)
+#endif
+#endif
+
+#define ROUND_UP(val, n)     (((val)+(n)-1) & ~((n)-1))
+#define DIV_CEILING(a, b)       (((a) + ((b)-1)) / (b))
+
+#define I2C_VERSION "0.0.1"
+
+//#define CONFIG_NPCM750_I2C_DEBUG
+#ifdef CONFIG_NPCM750_I2C_DEBUG
+#define dev_err(a, f, x...) pr_err("NPCM750-I2C: %s() dev_err:" f, __func__, \
+					## x)
+#define I2C_DEBUG(f, x...) pr_info("NPCM750-I2C: %s():%d " f, __func__, \
+					__LINE__, ## x)
+#else
+#define I2C_DEBUG(f, x...)
+#endif
+#define HAL_PRINT(f, x...)          printk(f, ## x)
+
+
+typedef struct bit_field {
+	u8 offset;
+	u8 size;
+} bit_field_t;
+
+#ifdef REG_READ
+#undef REG_READ
+#endif
+static inline u8 REG_READ(unsigned char __iomem *mem)
+{
+	return ioread8(mem);
+}
+
+#ifdef REG_WRITE
+#undef REG_WRITE
+#endif
+static inline void REG_WRITE(unsigned char __iomem *mem, u8 val)
+{
+	iowrite8(val, mem);
+}
+
+#ifdef SET_REG_FIELD
+#undef SET_REG_FIELD
+#endif
+static inline void SET_REG_FIELD(unsigned char __iomem *mem,
+				 bit_field_t bit_field, u8 val)
+{
+	u8 tmp = ioread8(mem);
+	tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); // mask the field size
+	tmp |= val << bit_field.offset;  // or with the requested value
+	iowrite8(tmp, mem);
+}
+
+#ifdef SET_VAR_FIELD
+#undef SET_VAR_FIELD
+#endif
+// bit_field should be of bit_field_t type
+#define SET_VAR_FIELD(var, bit_field, value) { \
+    typeof(var) tmp = var;                 \
+    tmp &= ~(((1 << bit_field.size) - 1) << bit_field.offset); /* mask the field size */ \
+    tmp |= value << bit_field.offset;  /* or with the requested value */               \
+    var = tmp;                                                                         \
+}
+
+#ifdef READ_REG_FIELD
+#undef READ_REG_FIELD
+#endif
+static inline u8 READ_REG_FIELD(unsigned char __iomem *mem,
+				bit_field_t bit_field)
+{
+	u8 tmp = ioread8(mem);
+	tmp = tmp >> bit_field.offset;     // shift right the offset
+	tmp &= (1 << bit_field.size) - 1;  // mask the size
+	return tmp;
+}
+
+#ifdef READ_VAR_FIELD
+#undef READ_VAR_FIELD
+#endif
+// bit_field should be of bit_field_t type
+#define READ_VAR_FIELD(var, bit_field) ({ \
+    typeof(var) tmp = var;           \
+    tmp = tmp >> bit_field.offset;     /* shift right the offset */ \
+    tmp &= (1 << bit_field.size) - 1;  /* mask the size */          \
+    tmp;                                                     \
+})
+
+#ifdef MASK_FIELD
+#undef MASK_FIELD
+#endif
+#define MASK_FIELD(bit_field) \
+    (((1 << bit_field.size) - 1) << bit_field.offset) /* mask the field size */
+
+#ifdef BUILD_FIELD_VAL
+#undef BUILD_FIELD_VAL
+#endif
+#define BUILD_FIELD_VAL(bit_field, value)  \
+    ((((1 << bit_field.size) - 1) & (value)) << bit_field.offset)
+
+
+#ifdef SET_REG_MASK
+#undef SET_REG_MASK
+#endif
+static inline void SET_REG_MASK(unsigned char __iomem *mem, u8 val)
+{
+	iowrite8(ioread8(mem) | val, mem);
+}
+
+#ifndef CONFIG_I2C_SLAVE
+#define SMB_MASTER_ONLY
+#endif
+
+//#define SMB_CAPABILITY_WAKEUP_SUPPORT
+#define SMB_CAPABILITY_FAST_MODE_SUPPORT
+#define SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT
+#define SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+#define SMB_CAPABILITY_TIMEOUT_SUPPORT
+
+// Using SW PEC instead of HW PEC:
+//#define SMB_CAPABILITY_HW_PEC_SUPPORT
+//#define SMB_STALL_TIMEOUT_SUPPORT
+#define SMB_RECOVERY_SUPPORT
+
+// override issue #614:  TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation
+//#define SMB_SW_BYPASS_HW_ISSUE_SMB_STOP
+
+// if end device reads more data than avalilable, ask issuer or request for more data.
+#define SMB_WRAP_AROUND_BUFFER
+
+#define SMB_BYTES_QUICK_PROT                        0xFFFF
+#define SMB_BYTES_BLOCK_PROT                        0xFFFE
+#define SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER    0xFFFD
+
+#define ARP_ADDRESS_VAL            0x61
+
+typedef enum {
+	SMB_SLAVE = 1,
+	SMB_MASTER
+} SMB_MODE_T;
+
+/*
+ * External SMB Interface driver states values, which indicate to the
+ * upper-level layer the status of the
+ * operation it initiated or wake up events from one of the buses
+ */
+typedef enum {
+	SMB_NO_STATUS_IND,
+	SMB_SLAVE_RCV_IND,
+	SMB_SLAVE_XMIT_IND,
+#ifdef SMB_WRAP_AROUND_BUFFER
+	SMB_SLAVE_XMIT_MISSING_DATA_IND,
+#endif
+	SMB_SLAVE_RESTART_IND,
+	SMB_SLAVE_DONE_IND,
+	SMB_MASTER_DONE_IND,
+	SMB_NO_DATA_IND,
+	SMB_NACK_IND,
+	SMB_BUS_ERR_IND,
+	SMB_WAKE_UP_IND,
+	SMB_MASTER_PEC_ERR_IND,
+	SMB_MASTER_BLOCK_BYTES_ERR_IND,
+	SMB_SLAVE_PEC_ERR_IND
+} SMB_STATE_IND_T;
+
+typedef enum {
+	SMB_SLAVE_ADDR1,
+	SMB_SLAVE_ADDR2,
+	SMB_SLAVE_ADDR3,
+	SMB_SLAVE_ADDR4,
+	SMB_SLAVE_ADDR5,
+	SMB_SLAVE_ADDR6,
+	SMB_SLAVE_ADDR7,
+	SMB_SLAVE_ADDR8,
+	SMB_SLAVE_ADDR9,
+	SMB_SLAVE_ADDR10,
+	SMB_GC_ADDR,
+	SMB_ARP_ADDR
+} SMB_ADDR_T;
+
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+typedef enum {
+	SMB_LEVEL_LOW  = 0,
+	SMB_LEVEL_HIGH = 1
+} SMB_LEVEL_T;
+#endif // SMB_CAPABILITY_FORCE_SCL_SDA
+
+
+// Common registers
+#define SMBSDA(bus)               (bus->base + 0x000)
+#define SMBST(bus)                (bus->base + 0x002)
+#define SMBCST(bus)               (bus->base + 0x004)
+#define SMBCTL1(bus)              (bus->base + 0x006)
+#define SMBADDR1(bus)             (bus->base + 0x008)
+#define SMBCTL2(bus)              (bus->base + 0x00A)
+#define SMBADDR2(bus)             (bus->base + 0x00C)
+#define SMBCTL3(bus)              (bus->base + 0x00E)
+#define SMBCST2(bus)              (bus->base + 0x018)  // Control Status 2
+#define SMBCST3(bus)              (bus->base + 0x019)  // Control Status 3 Register
+#define SMB_VER(bus)              (bus->base + 0x01F)  // SMB Version Register
+
+// BANK 0 registers
+#define SMBADDR3(bus)             (bus->base + 0x010)
+#define SMBADDR7(bus)             (bus->base + 0x011)
+#define SMBADDR4(bus)             (bus->base + 0x012)
+#define SMBADDR8(bus)             (bus->base + 0x013)
+#define SMBADDR5(bus)             (bus->base + 0x014)
+#define SMBADDR9(bus)             (bus->base + 0x015)
+#define SMBADDR6(bus)             (bus->base + 0x016)
+#define SMBADDR10(bus)            (bus->base + 0x017)
+
+#define SMBADDR(bus, i)           (bus->base + 0x008 + (u32)(((int)i*4) + (((int)i < 2) ? 0 : ((int)i-2)*(-2)) + (((int)i < 6) ? 0 : (-7))))
+
+#define SMBCTL4(bus)              (bus->base + 0x01A)
+#define SMBCTL5(bus)              (bus->base + 0x01B)
+#define SMBSCLLT(bus)             (bus->base + 0x01C)  // SMB SCL Low Time (Fast-Mode)
+#define SMBFIF_CTL(bus)           (bus->base + 0x01D)  // FIFO Control
+#define SMBSCLHT(bus)             (bus->base + 0x01E)  // SMB SCL High Time (Fast-Mode)
+
+// BANK 1 registers
+#define SMBFIF_CTS(bus)           (bus->base + 0x010)  // FIFO Control and Status
+#define SMBTXF_CTL(bus)           (bus->base + 0x012)  // Tx-FIFO Control
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+#define SMBT_OUT(bus)             (bus->base + 0x014)  // Bus Time-Out
+#endif
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+#define SMBPEC(bus)               (bus->base + 0x016)  // PEC Data
+#endif
+#define SMBTXF_STS(bus)           (bus->base + 0x01A)  // Tx-FIFO Status
+#define SMBRXF_STS(bus)           (bus->base + 0x01C)  // Rx-FIFO Status
+#define SMBRXF_CTL(bus)           (bus->base + 0x01E)  // Rx-FIFO Control
+
+#ifdef SMB_CAPABILITY_WAKEUP_SUPPORT
+#define SMB_SBD                 ((GLUE_BASE_ADDR + 0x002), GLUE_ACCESS, 8)
+#define SMB_EEN                 ((GLUE_BASE_ADDR + 0x003), GLUE_ACCESS, 8)
+#endif
+
+
+/* SMBST register fields */
+static const bit_field_t SMBST_XMIT              = { 0,  1 };
+static const bit_field_t SMBST_MASTER            = { 1, 1 };
+static const bit_field_t SMBST_NMATCH            = { 2, 1 };
+static const bit_field_t SMBST_STASTR            = { 3, 1 };
+static const bit_field_t SMBST_NEGACK            = { 4, 1 };
+static const bit_field_t SMBST_BER               = { 5, 1 };
+static const bit_field_t SMBST_SDAST             = { 6, 1 };
+static const bit_field_t SMBST_SLVSTP            = { 7, 1 };
+
+/* SMBCST register fields */
+static const bit_field_t SMBCST_BUSY             = { 0, 1 };
+static const bit_field_t SMBCST_BB               = { 1, 1 };
+static const bit_field_t SMBCST_MATCH            = { 2, 1 };
+static const bit_field_t SMBCST_GCMATCH          = { 3, 1 };
+static const bit_field_t SMBCST_TSDA             = { 4, 1 };
+static const bit_field_t SMBCST_TGSCL            = { 5, 1 };
+static const bit_field_t SMBCST_MATCHAF          = { 6, 1 };
+static const bit_field_t SMBCST_ARPMATCH         = { 7, 1 };
+
+/* SMBCTL1 register fields */
+static const bit_field_t SMBCTL1_START           = { 0, 1 };
+static const bit_field_t SMBCTL1_STOP            = { 1, 1 };
+static const bit_field_t SMBCTL1_INTEN           = { 2, 1 };
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+static const bit_field_t SMBCTL1_EOBINTE         = { 3, 1 };
+#endif
+static const bit_field_t SMBCTL1_ACK             = { 4, 1 };
+static const bit_field_t SMBCTL1_GCMEN           = { 5, 1 };
+static const bit_field_t SMBCTL1_NMINTE          = { 6, 1 };
+static const bit_field_t SMBCTL1_STASTRE         = { 7, 1 };
+
+/* SMBADDRx register fields */
+static const bit_field_t SMBADDRx_ADDR           = { 0, 7 };
+static const bit_field_t SMBADDRx_SAEN           = { 7, 1 };
+
+/* SMBCTL2 register fields                                                                                 */
+static const bit_field_t SMBCTL2_ENABLE          = { 0, 1 };
+static const bit_field_t SMBCTL2_SCLFRQ6_0       = { 1, 7 };
+
+/* SMBCTL3 register fields */
+static const bit_field_t SMBCTL3_SCLFRQ8_7       = { 0, 2 };
+static const bit_field_t SMBCTL3_ARPMEN          = { 2, 1 };
+static const bit_field_t SMBCTL3_IDL_START       = { 3, 1 };
+static const bit_field_t SMBCTL3_400K_MODE       = { 4, 1 };
+static const bit_field_t SMBCTL3_BNK_SEL         = { 5, 1 };
+static const bit_field_t SMBCTL3_SDA_LVL         = { 6, 1 };
+static const bit_field_t SMBCTL3_SCL_LVL         = { 7, 1 };
+
+/* SMBCST2 register fields */
+static const bit_field_t SMBCST2_MATCHA1F        = { 0, 1 };
+static const bit_field_t SMBCST2_MATCHA2F        = { 1, 1 };
+static const bit_field_t SMBCST2_MATCHA3F        = { 2, 1 };
+static const bit_field_t SMBCST2_MATCHA4F        = { 3, 1 };
+static const bit_field_t SMBCST2_MATCHA5F        = { 4, 1 };
+static const bit_field_t SMBCST2_MATCHA6F        = { 5, 1 };
+static const bit_field_t SMBCST2_MATCHA7F        = { 5, 1 };
+static const bit_field_t SMBCST2_INTSTS          = { 7, 1 };
+
+/* SMBCST3 register fields */
+static const bit_field_t SMBCST3_MATCHA8F        = { 0, 1 };
+static const bit_field_t SMBCST3_MATCHA9F        = { 1, 1 };
+static const bit_field_t SMBCST3_MATCHA10F       = { 2, 1 };
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+static const bit_field_t SMBCST3_EO_BUSY         = { 7, 1 };
+#endif
+
+/* SMBCTL4 register fields */
+static const bit_field_t SMBCTL4_HLDT            = { 0, 6 };
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+static const bit_field_t SMBCTL4_LVL_WE          = { 7, 1 };
+#endif
+
+/* SMBCTL5 register fields */
+static const bit_field_t SMBCTL5_DBNCT           = { 0, 4 };
+
+/* SMBFIF_CTS register fields */
+static const bit_field_t SMBFIF_CTS_RXF_TXE      = { 1, 1 };
+static const bit_field_t SMBFIF_CTS_RFTE_IE      = { 3, 1 };
+static const bit_field_t SMBFIF_CTS_CLR_FIFO     = { 6, 1 };
+static const bit_field_t SMBFIF_CTS_SLVRSTR      = { 7, 1 };
+
+/* SMBTXF_CTL register fields */
+#ifdef SMB_CAPABILITY_32B_FIFO
+static const bit_field_t SMBTXF_CTL_TX_THR       = { 0, 6 };
+#else
+static const bit_field_t SMBTXF_CTL_TX_THR       = { 0, 5 };
+#endif
+static const bit_field_t SMBTXF_CTL_THR_TXIE     = { 6, 1 };
+
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+
+/* SMBT_OUT register fields */
+static const bit_field_t SMBT_OUT_TO_CKDIV       = { 0, 6 };
+static const bit_field_t SMBT_OUT_T_OUTIE        = { 6, 1 };
+static const bit_field_t SMBT_OUT_T_OUTST        = { 7, 1 };
+#endif
+
+/* SMBTXF_STS register fields  */
+#ifdef SMB_CAPABILITY_32B_FIFO
+static const bit_field_t SMBTXF_STS_TX_BYTES     = { 0, 6 };
+#else
+static const bit_field_t SMBTXF_STS_TX_BYTES     = { 0, 5 };
+#endif
+static const bit_field_t SMBTXF_STS_TX_THST      = { 6, 1 };
+
+/* SMBRXF_STS register fields */
+#ifdef SMB_CAPABILITY_32B_FIFO
+static const bit_field_t SMBRXF_STS_RX_BYTES     = { 0, 6 };
+#else
+static const bit_field_t SMBRXF_STS_RX_BYTES     = { 0, 5 };
+#endif
+static const bit_field_t SMBRXF_STS_RX_THST      = { 6, 1 };
+
+/* SMBFIF_CTL register fields */
+static const bit_field_t SMBFIF_CTL_FIFO_EN      = { 4, 1 };
+
+/* SMBRXF_CTL register fields */
+#ifdef SMB_CAPABILITY_32B_FIFO
+static const bit_field_t SMBRXF_CTL_RX_THR       = { 0, 6 };
+static const bit_field_t SMBRXF_CTL_THR_RXIE     = { 6, 1 };
+static const bit_field_t SMBRXF_CTL_LAST_PEC     = { 7, 1 };
+#else
+static const bit_field_t SMBRXF_CTL_RX_THR       = { 0, 5 };
+static const bit_field_t SMBRXF_CTL_LAST_PEC     = { 5, 1 };
+static const bit_field_t SMBRXF_CTL_THR_RXIE     = { 6, 1 };
+#endif
+
+/* SMB_VER register fields */
+static const bit_field_t SMB_VER_VERSION         = { 0, 7 };
+static const bit_field_t SMB_VER_FIFO_EN         = { 7, 1 };
+
+#ifdef SMB_CAPABILITY_WAKEUP_SUPPORT
+
+/* SMB_SBD register fields */
+#define SMB_SBD_SMBnSBD(n)      ((n),  1)
+
+
+/* SMB_EEN register fields */
+#define SMB_EEN_SMBnEEN(n)      ((n),  1)
+#endif // SMB_CAPABILITY_WAKEUP_SUPPORT
+
+
+/* Module Dependencies */
+#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+#include __MODULE_IF_HEADER_FROM_DRV(miwu)
+#endif
+
+
+#if defined (CLK_MODULE_TYPE)
+#include __MODULE_IF_HEADER_FROM_DRV(clk)
+#endif
+
+
+
+
+
+/* TYPES & DEFINITIONS */
+#ifdef SMB_SLAVE_ONLY
+//The Stall Timeout feature is only relevant in Master mode.
+#undef SMB_STALL_TIMEOUT_SUPPORT
+#endif
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+
+/* stall/stuck timeout                                                                                     */
+#define DEFAULT_STALL_COUNT         25
+#endif
+
+/* Data abort timeout                                                                                      */
+#define ABORT_TIMEOUT       1000
+
+/* SMBus spec. values in KHz                                                                               */
+#define SMBUS_FREQ_MIN      10
+
+#if defined SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT
+#define SMBUS_FREQ_MAX      1000
+#elif defined SMB_CAPABILITY_FAST_MODE_SUPPORT
+#define SMBUS_FREQ_MAX      400
+#else
+#define SMBUS_FREQ_MAX      100
+#endif
+
+#define SMBUS_FREQ_100KHz   100
+#define SMBUS_FREQ_400KHz   400
+#define SMBUS_FREQ_1MHz     1000
+
+
+
+/* SMBus FIFO SIZE (when FIFO hardware exist)                                                              */
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define SMBUS_FIFO_SIZE     32
+#else
+#define SMBUS_FIFO_SIZE     16
+#endif
+
+
+/* SCLFRQ min/max field values                                                                             */
+#define SCLFRQ_MIN          10
+#define SCLFRQ_MAX          511
+
+/* SCLFRQ field position                                                                                   */
+static const bit_field_t SCLFRQ_0_TO_6       = { 0, 7 };
+static const bit_field_t SCLFRQ_7_TO_8       = { 7, 2 };
+
+/* SMB Maximum Retry Trials (on Bus Arbitration Loss)                                                      */
+#define SMB_RETRY_MAX_COUNT     3
+
+/* SMBus Operation type values                                                                             */
+typedef enum {
+	SMB_NO_OPER     = 0,
+	SMB_WRITE_OPER  = 1,
+	SMB_READ_OPER   = 2
+} SMB_OPERATION_T;
+
+
+
+/* SMBus Bank (FIFO mode)                                                                                  */
+
+typedef enum {
+	SMB_BANK_0  = 0,
+	SMB_BANK_1  = 1
+} SMB_BANK_T;
+
+/* Internal SMBus Interface driver states values, which reflect events which occurred on the bus           */
+typedef enum {
+	SMB_DISABLE,
+	SMB_IDLE,
+	SMB_MASTER_START,
+	SMB_SLAVE_MATCH,
+	SMB_OPER_STARTED,
+	SMB_REPEATED_START,
+	SMB_STOP_PENDING
+} SMB_OPERATION_STATE_T;
+
+
+#define SMB_NUM_OF_ADDR                  10 // TBD move to device tree
+#define SMB_FIFO(bus)                    true   /* All modules support FIFO */
+void  npcm750_clk_GetTimeStamp(u32 time_quad[2]);
+
+
+/* Status of one SMBus module                                                                              */
+typedef struct nuvoton_i2c_bus {
+	struct i2c_adapter              adap;
+	struct device			*dev;
+	unsigned char __iomem	        *base;
+	/* Synchronizes I/O mem access to base. */
+	spinlock_t			lock;
+	spinlock_t			bank_lock;
+	struct completion		cmd_complete;
+	int				irq;
+	int				cmd_err;
+	struct i2c_msg                  *msgs;
+	int                             msgs_num;
+	int                             module__num;
+	u32                             apb_clk;
+#ifdef CONFIG_I2C_SLAVE
+	struct i2c_client		*slave;
+#endif /* CONFIG_I2C_SLAVE */
+
+	/* Current state of SMBus */
+	volatile SMB_OPERATION_STATE_T  operation_state;
+
+	/* Type of the last SMBus operation */
+	SMB_OPERATION_T        operation;
+
+	/* Mode of operation on SMBus */
+	SMB_MODE_T             master_or_slave;
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+	/* The indication to the hi level after Master Stop */
+	SMB_STATE_IND_T        stop_indication;
+#endif
+	/* SMBus slave device's Slave Address in 8-bit format - for master transactions */
+	u8                  dest_addr;
+
+	/* Buffer where read data should be placed */
+	u8 *read_data_buf;
+
+	/* Number of bytes to be read */
+	u16                 read_size;
+
+	/* Number of bytes already read */
+	u16                 read_index;
+
+	/* Buffer with data to be written */
+	u8 *write_data_buf;
+
+	/* Number of bytes to write */
+	u16                 write_size;
+
+	/* Number of bytes already written */
+	u16                 write_index;
+
+	/* use fifo hardware or not */
+	bool                fifo_use;
+
+	/* fifo threshold size */
+	u8                  threshold_fifo;
+
+	/* PEC bit mask per slave address.
+	   1: use PEC for this address,
+	   0: do not use PEC for this address */
+	u16                PEC_mask;
+
+	/* Use PEC CRC  */
+	bool                PEC_use;
+
+	/* PEC CRC data */
+	u8                  crc_data;
+
+	/* Use read block */
+	bool                read_block_use;
+
+	/* Number of retries remaining */
+	u8                  retry_count;
+
+#if !defined SMB_MASTER_ONLY
+	u8 SMB_CurSlaveAddr;
+#endif
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	u8                  stall_counter;
+	u8                  stall_threshold;
+#endif
+
+
+// override issue #614:  TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation. If needed : define it at hal_cfg.h
+#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP
+	/* The indication to the hi level after Master Stop */
+	u32                clk_period_us;
+	u32                interrupt_time_stamp[2];
+#endif
+} nuvoton_i2c_bus_t;
+
+
+
+/* GLOBAL VARIABLES */
+/* Callback function provided by next-higher level driver or application,
+   implementing operation handling  */
+/* state-machine                                                                                           */
+
+//static SMB_CALLBACK_T SMB_callback;
+
+
+
+/*                                           INTERFACE FUNCTIONS                                           */
+
+
+
+static bool SMB_InitModule(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, u16 bus_freq);
+
+#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+static void SMB_WakeupEnable(nuvoton_i2c_bus_t *bus, bool enable);
+#endif  /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */
+
+#if !defined SMB_SLAVE_ONLY
+static bool SMB_StartMasterTransaction(nuvoton_i2c_bus_t *bus, u8 slave_addr, u16 nwrite, u16 nread,
+				       u8 *write_data, u8 *read_data, bool use_PEC);
+static void SMB_MasterAbort(nuvoton_i2c_bus_t *bus);
+
+#ifdef TBD
+static void SMB_Recovery(nuvoton_i2c_bus_t *bus);
+#endif //TBD
+
+#endif  /* !SMB_SLAVE_ONLY */
+
+#if !defined SMB_MASTER_ONLY
+static DEFS_STATUS SMB_SlaveGlobalCallEnable(nuvoton_i2c_bus_t *bus, bool enable);
+static DEFS_STATUS SMB_SlaveARPEnable(nuvoton_i2c_bus_t *bus, bool enable);
+static bool SMB_StartSlaveReceive(nuvoton_i2c_bus_t *bus, u16 nread, u8 *read_data);
+static bool SMB_StartSlaveTransmit(nuvoton_i2c_bus_t *bus, u16 nwrite, u8 *write_data);
+static DEFS_STATUS SMB_GetCurrentSlaveAddress(nuvoton_i2c_bus_t *bus, u8 *currSlaveAddr);
+static DEFS_STATUS SMB_RemSlaveAddress(nuvoton_i2c_bus_t *bus, u8 slaveAddrToRemove);
+static DEFS_STATUS SMB_AddSlaveAddress(nuvoton_i2c_bus_t *bus, u8 slaveAddrToAssign, bool use_PEC);
+static bool SMB_IsSlaveAddressExist(nuvoton_i2c_bus_t *bus, u8 addr);
+static void SMB_Disable(nuvoton_i2c_bus_t *bus);
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+static void SMB_EnableTimeout(nuvoton_i2c_bus_t *bus, bool enable);
+#endif
+#if defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+static void SMB_SetStallAfterStartIdle(nuvoton_i2c_bus_t *bus, bool enable);
+#endif
+#endif  /* !SMB_MASTER_ONLY */
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+static void SMB_ConfigStallThreshold(nuvoton_i2c_bus_t *bus, u8 threshold);
+static void SMB_StallHandler(nuvoton_i2c_bus_t *bus);
+#endif
+
+#ifdef TBD
+static void SMB_Init(SMB_CALLBACK_T operation_done);
+static bool SMB_ModuleIsBusy(nuvoton_i2c_bus_t *bus);
+static bool SMB_BusIsBusy(nuvoton_i2c_bus_t *bus);
+static void SMB_ReEnableModule(nuvoton_i2c_bus_t *bus);
+static bool SMB_InterruptIsPending(void);
+#endif
+
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+static void SMB_WriteSCL(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level);
+static void SMB_WriteSDA(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level);
+#endif // SMB_CAPABILITY_FORCE_SCL_SDA
+
+#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT
+static void SMB_PrintRegs(nuvoton_i2c_bus_t *bus);
+static void SMB_PrintModuleRegs(nuvoton_i2c_bus_t *bus);
+static void SMB_PrintVersion(void);
+#endif
+
+
+typedef void (*SMB_CALLBACK_T)(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info);
+
+#ifdef SMB_SAMPLE
+void SMB_callback(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info)
+{
+	switch (op_status) {
+	case SMB_SLAVE_RCV_IND:
+		// Slave got an address match with direction bit clear so it should receive data
+		//     the interrupt must call SMB_StartSlaveReceive()
+		// info: the enum SMB_ADDR_T address match
+		extern u16 read_size;
+		extern u8 *read_data_buf;
+		SMB_StartSlaveReceive(bus, read_size, read_data_buf);
+		break;
+	case SMB_SLAVE_XMIT_IND:
+		// Slave got an address match with direction bit set so it should transmit data
+		//     the interrupt must call SMB_StartSlaveTransmit()
+		// info: the enum SMB_ADDR_T address match
+		extern u16 write_size;
+		extern u8 *write_data_buf;
+		SMB_StartSlaveTransmit(bus, write_size, write_data_buf);
+		break;
+	case SMB_SLAVE_DONE_IND:
+		// Slave done transmitting or receiving
+		// info:
+		//     on receive: number of actual bytes received
+		//     on transmit: number of actual bytes transmitted,
+		//                  when PEC is used 'info' should be (nwrite+1) which means that 'nwrite' bytes
+		//                     were sent + the PEC byte
+		//                     'nwrite' is the second parameter SMB_StartSlaveTransmit()
+		break;
+	case SMB_MASTER_DONE_IND:
+		// Master transaction finished and all transmit bytes were sent
+		// info: number of bytes actually received after the Master receive operation
+		//       (if Master didn't issue receive it should be 0)
+		break;
+	case SMB_NO_DATA_IND:
+		// Notify that not all data was received on Master or Slave
+		// info:
+		//     on receive: number of actual bytes received
+		//                 when PEC is used even if 'info' is the expected number of bytes,
+		//                     it means that PEC error occured.
+		break;
+	case SMB_NACK_IND:
+		// MASTER transmit got a NAK before transmitting all bytes
+		// info: number of transmitted bytes
+		break;
+	case SMB_BUS_ERR_IND:
+		// Bus error occured
+		// info: has no meaning
+		break;
+	case SMB_WAKE_UP_IND:
+		// SMBus wake up occured
+		// info: has no meaning
+		break;
+	default:
+		break;
+	}
+}
+#endif    /* SMB_SAMPLE */
+
+
+
+
+
+/*                                  LOCAL FUNCTIONS FORWARD DECLARATIONS                                   */
+
+
+static inline void SMB_WriteByte(nuvoton_i2c_bus_t *bus, u8 data);
+static inline bool SMB_ReadByte(nuvoton_i2c_bus_t *bus, u8 *data);
+static inline void SMB_SelectBank(nuvoton_i2c_bus_t *bus, SMB_BANK_T bank);
+static inline u16 SMB_GetIndex(nuvoton_i2c_bus_t *bus);
+
+#if !defined SMB_SLAVE_ONLY
+static inline void SMB_Start(nuvoton_i2c_bus_t *bus);
+static inline void SMB_Stop(nuvoton_i2c_bus_t *bus);
+static inline void SMB_AbortData(nuvoton_i2c_bus_t *bus);
+static inline void SMB_StallAfterStart(nuvoton_i2c_bus_t *bus, bool stall);
+static inline void SMB_Nack(nuvoton_i2c_bus_t *bus);
+#endif  /* !SMB_SLAVE_ONLY */
+
+static          void SMB_Reset(nuvoton_i2c_bus_t *bus);
+static          void SMB_InterruptEnable(nuvoton_i2c_bus_t *bus, bool enable);
+#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+static          void SMB_WakeupHandler(MIWU_SRC_T source);
+#endif
+
+static          bool SMB_InitClock(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode,
+				   u16 bus_freq);
+static          void SMB_InterruptHandler(nuvoton_i2c_bus_t *bus);
+#if !defined SMB_MASTER_ONLY
+static          u8 SMB_GetSlaveAddress_l(nuvoton_i2c_bus_t *bus,
+					 SMB_ADDR_T addrEnum);
+#endif //!defined SMB_MASTER_ONLY
+static          void SMB_WriteToFifo(nuvoton_i2c_bus_t *bus,
+				     u16 max_bytes_to_send);
+
+static          void SMB_CalcPEC(nuvoton_i2c_bus_t *bus, u8 data);
+#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT
+static          void SMBF_PrintModuleRegs(nuvoton_i2c_bus_t *bus);
+#endif
+static          void SMB_callback(nuvoton_i2c_bus_t *bus,
+				  SMB_STATE_IND_T op_status, u16 info);
+
+
+/* SMB Recovery of the SMBus interface driver */
+#if !defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT
+static          void    SMB_SlaveAbort(nuvoton_i2c_bus_t *bus);  /* SMB slave abort data        */
+#endif
+
+
+
+/* INTERFACE FUNCTIONS */
+
+static inline void SMB_WriteByte(nuvoton_i2c_bus_t *bus, u8 data)
+{
+	REG_WRITE(SMBSDA(bus), data);
+	SMB_CalcPEC(bus, data);
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+}
+
+static inline bool SMB_ReadByte(nuvoton_i2c_bus_t *bus, u8 *data)
+{
+	/* Read data                                                                                           */
+	*data = REG_READ(SMBSDA(bus));
+	SMB_CalcPEC(bus, *data);
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+
+	return true;
+}
+
+static inline void SMB_SelectBank(nuvoton_i2c_bus_t *bus, SMB_BANK_T bank)
+{
+	if (bus->fifo_use == true)
+		SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_BNK_SEL, bank);
+}
+
+static inline u16 SMB_GetIndex(nuvoton_i2c_bus_t *bus)
+{
+	u16 index = 0;
+
+	if (bus->operation == SMB_READ_OPER)
+		index = bus->read_index;
+	else
+		if (bus->operation == SMB_WRITE_OPER)
+			index = bus->write_index;
+
+	return index;
+}
+
+#if !defined SMB_SLAVE_ONLY
+
+static inline void SMB_Start(nuvoton_i2c_bus_t *bus)
+{
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_START, true);
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+}
+static inline void SMB_Stop(nuvoton_i2c_bus_t *bus)
+{
+#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP
+	// override issue #614:  TITLE :CP_FW: SMBus may fail to supply stop condition in Master Write operation. If needed : define it at hal_cfg.h
+	npcm750_clk_Delay_Since((bus->clk_period_us >> 1), bus->interrupt_time_stamp);
+
+#endif //   SMB_SW_BYPASS_HW_ISSUE_SMB_STOP
+
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_STOP, true);
+
+	if (bus->fifo_use) {
+		u8 smbfif_cts;
+		SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1);
+		smbfif_cts = REG_READ(SMBFIF_CTS(bus));
+		SET_VAR_FIELD(smbfif_cts, SMBFIF_CTS_SLVRSTR, 1);
+		SET_VAR_FIELD(smbfif_cts, SMBFIF_CTS_RXF_TXE, 1);
+		REG_WRITE(SMBFIF_CTS(bus), smbfif_cts);
+		SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) |
+			     MASK_FIELD(SMBFIF_CTS_RXF_TXE));
+
+		REG_WRITE(SMBTXF_CTL(bus), 0);
+	}
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+
+}
+
+static inline void SMB_AbortData(nuvoton_i2c_bus_t *bus)
+{
+	unsigned int timeout = ABORT_TIMEOUT;
+
+	/* Generate a STOP condition                                                                           */
+	SMB_Stop(bus);
+
+	/* Clear NEGACK, STASTR and BER bits                                                                   */
+	REG_WRITE(SMBST(bus), (MASK_FIELD(SMBST_STASTR) |
+			       MASK_FIELD(SMBST_NEGACK) |
+			       MASK_FIELD(SMBST_BER)));
+
+	/* Wait till STOP condition is generated                                                               */
+	while (--timeout)
+		if (!READ_REG_FIELD(SMBCTL1(bus), SMBCTL1_STOP))
+			break;
+
+	/* Clear BB (BUS BUSY) bit                                                                             */
+	REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_BB));
+}
+
+static inline void SMB_StallAfterStart(nuvoton_i2c_bus_t *bus, bool stall)
+{
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_STASTRE, stall);
+}
+
+static inline void SMB_Nack(nuvoton_i2c_bus_t *bus)
+{
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_ACK, true);
+}
+#endif  /* !SMB_SLAVE_ONLY */
+
+static void SMB_Disable(nuvoton_i2c_bus_t *bus)
+{
+	int i;
+
+	/* Slave Addresses Removal                                                                             */
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++)
+		REG_WRITE(SMBADDR(bus, i), 0);
+
+	/* Disable module.                                                                                     */
+	SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE);
+
+
+	/* Set module disable                                                                                  */
+	bus->operation_state = SMB_DISABLE;
+}
+
+static bool SMB_Enable(nuvoton_i2c_bus_t *bus)
+{
+	SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, ENABLE);
+	return true;
+}
+
+static bool SMB_InitModule(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode,
+			   u16 bus_freq)
+{
+	unsigned long bank_flags;
+	int     i;
+
+	/* Check whether module already enabled or frequency is out of bounds                                  */
+	if (((bus->operation_state != SMB_DISABLE) &&
+	     (bus->operation_state != SMB_IDLE)) ||
+	    (bus_freq < SMBUS_FREQ_MIN) || (bus_freq > SMBUS_FREQ_MAX))
+		return false;
+
+	/* Mux SMB module pins                                                                                 */
+	//lint -e{792} suppress PC-Lint warning on 'void cast of void expression'
+#ifdef TBD
+	SMB_MUX(bus);
+#endif
+	/* Configure FIFO mode                                                                                 */
+	//lint -e{774, 506} suppress PC-Lint warning on 'bool within 'left side of && within if' always evaluates to true'
+	if (SMB_FIFO(bus) && READ_REG_FIELD(SMB_VER(bus), SMB_VER_FIFO_EN)) {
+		bus->fifo_use = true;
+		bus->threshold_fifo = SMBUS_FIFO_SIZE;
+		SET_REG_FIELD(SMBFIF_CTL(bus), SMBFIF_CTL_FIFO_EN, 1);
+	} else
+		bus->fifo_use = false;
+
+	/* Configure SMB module clock frequency                                                                */
+	if (!SMB_InitClock(bus, mode, bus_freq)) {
+		I2C_DEBUG("SMB_InitClock failed\n");
+		return false;
+	}
+
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0); // select bank 0 for SMB addresses
+
+	/* Configure slave addresses (by default they are disabled)                                            */
+	for (i = 0; i < SMB_NUM_OF_ADDR; i++)
+		REG_WRITE(SMBADDR(bus, i), 0);
+
+	SMB_SelectBank(bus, SMB_BANK_1); // by default most access is in bank 1
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	/* Enable module - before configuring CTL1 !                                                           */
+	if (!SMB_Enable(bus))
+		return false;
+	else
+		bus->operation_state = SMB_IDLE;
+
+	/* Enable SMB interrupt and New Address Match interrupt source                                         */
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE);
+	SMB_InterruptEnable(bus, true);
+
+	return true;
+}
+
+#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+static void SMB_WakeupEnable(nuvoton_i2c_bus_t *bus, bool enable)
+{
+	const MIWU_SRC_T SMbusToMiwu[] = SMB_WAKEUP_SRC;
+	MIWU_SRC_T miwu_src = SMbusToMiwu[bus];
+
+	if (enable) {
+		/* Configure MIWU module to generate an interrupt to the ICU following SMBus wake-up conditions    */
+		MIWU_Config(miwu_src, MIWU_RISING_EDGE, SMB_WakeupHandler);
+
+		/* Configure SMBus Wake-up (in System Glue Function)                                               */
+		REG_WRITE(SMB_SBD, MASK_BIT(bus));   /* Clear Start condition detection                     */
+		SET_REG_BIT(SMB_EEN, bus);           /* Enable Event assertion                              */
+		SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, 1); /* Enable start detect in IDLE           */
+	} else {    /* Disable */
+		/*  Disable SMB Wake-Up indication via MIWU                                                        */
+		MIWU_EnableChannel(miwu_src, false);
+
+		/* Disable SMBus Wake-up (in System Glue Function)                                                 */
+		CLEAR_REG_BIT(SMB_EEN, bus);        /* Disable Event assertion                              */
+
+		SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, 0); /* Disable start detect in IDLE          */
+	}
+}
+#endif  /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */
+
+
+#if !defined SMB_MASTER_ONLY
+static DEFS_STATUS SMB_SlaveEnable_l(nuvoton_i2c_bus_t *bus,
+				     SMB_ADDR_T addr_type, u8 addr, bool enable)
+{
+	unsigned long bank_flags;
+	u8 SmbAddrX_Addr = BUILD_FIELD_VAL(SMBADDRx_ADDR, addr) |
+		BUILD_FIELD_VAL(SMBADDRx_SAEN, enable);
+
+	if (addr_type == SMB_GC_ADDR) {
+		SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_GCMEN, enable);
+		return DEFS_STATUS_OK;
+	}
+	if (addr_type == SMB_ARP_ADDR) {
+		SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_ARPMEN, enable);
+		return DEFS_STATUS_OK;
+	}
+	if (addr_type >= SMB_NUM_OF_ADDR)
+		return DEFS_STATUS_FAIL;
+
+	/* Disable interrupts and select bank 0 for address 3 to ...                                           */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	/* Set and enable the address                                                                          */
+	REG_WRITE(SMBADDR(bus, addr_type), SmbAddrX_Addr);
+
+	/* return to bank 1 and enable interrupts (if needed)                                                  */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	return DEFS_STATUS_OK;
+}
+
+static u8 SMB_GetSlaveAddress_l(nuvoton_i2c_bus_t *bus, SMB_ADDR_T addrEnum)
+{
+	unsigned long bank_flags;
+	u8 slaveAddress;
+
+	/* disable interrupts and select bank 0 for address 3 to ...                                           */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	slaveAddress = REG_READ(SMBADDR(bus, addrEnum));
+
+	/* return to bank 1 and enable interrupts (if needed)                                                  */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	return  slaveAddress;
+}
+
+static bool SMB_IsSlaveAddressExist(nuvoton_i2c_bus_t *bus, u8 addr)
+{
+	int i;
+
+	addr |= 0x80; //Set the enable bit
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++)
+		if (addr == SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)i))
+			return true;
+
+	return false;
+}
+
+static DEFS_STATUS SMB_AddSlaveAddress(nuvoton_i2c_bus_t *bus,
+				       u8 slaveAddrToAssign, bool use_PEC)
+{
+	int i;
+	DEFS_STATUS ret = DEFS_STATUS_FAIL;
+
+	slaveAddrToAssign |= 0x80; //set the enable bit
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) {
+		u8 currentSlaveAddr = SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)i);
+		if (currentSlaveAddr == slaveAddrToAssign) {
+			ret = DEFS_STATUS_OK;
+			break;
+		} else if ((currentSlaveAddr & 0x7F) == 0) {
+			ret = SMB_SlaveEnable_l(bus, (SMB_ADDR_T)i, slaveAddrToAssign, true);
+			break;
+		}
+	}
+
+	if (ret == DEFS_STATUS_OK) {
+		if (use_PEC)
+			SET_VAR_BIT(bus->PEC_mask, i);
+		else
+			CLEAR_VAR_BIT(bus->PEC_mask, i);
+	}
+	return ret;
+}
+
+static DEFS_STATUS SMB_RemSlaveAddress(nuvoton_i2c_bus_t *bus,
+				       u8 slaveAddrToRemove)
+{
+	int i;
+	unsigned long bank_flags;
+
+	slaveAddrToRemove |= 0x80; //Set the enable bit
+
+	/* disable interrupts and select bank 0 for address 3 to ...                                           */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) {
+		if (REG_READ(SMBADDR(bus, i)) == slaveAddrToRemove)
+			REG_WRITE(SMBADDR(bus, i), 0);
+	}
+
+	/* return to bank 1 and enable interrupts (if needed)                                                  */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	return DEFS_STATUS_OK;
+}
+
+static DEFS_STATUS SMB_SlaveGlobalCallEnable(nuvoton_i2c_bus_t *bus,
+					     bool enable)
+{
+	return SMB_SlaveEnable_l(bus, SMB_GC_ADDR, 0, enable);
+}
+
+static DEFS_STATUS SMB_SlaveARPEnable(nuvoton_i2c_bus_t *bus, bool enable)
+{
+	return SMB_SlaveEnable_l(bus, SMB_ARP_ADDR, 0, enable);
+}
+
+#endif  /* !SMB_MASTER_ONLY */
+
+
+#if !defined SMB_SLAVE_ONLY
+
+static bool SMB_StartMasterTransaction(nuvoton_i2c_bus_t *bus, u8 slave_addr,
+				       u16 nwrite, u16 nread, u8 *write_data,
+				       u8 *read_data, bool use_PEC)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_NPCM750_I2C_DEBUG
+	I2C_DEBUG("bus=%d slave_addr=%x nwrite=%d nread=%d write_data=%p "
+		  "read_data=%p use_PEC=%d\n", bus, slave_addr, nwrite, nread,
+		  write_data, read_data, use_PEC);
+
+	if (nwrite && nwrite != SMB_BYTES_QUICK_PROT) {
+		int i;
+		char str[32 * 3 + 4];
+		char *s = str;
+
+		for (i = 0; (i < nwrite && i < 32); i++)
+			s += sprintf(s, "%02x ", write_data[i]);
+
+		printk("write_data = %s\n", str);
+	}
+#endif
+
+
+	/* Allow only if bus is not busy                                                                       */
+	if ((bus->operation_state != SMB_IDLE)
+#if defined SMBUS_SIZE_CHECK
+	    ||
+	    ((nwrite >= _32KB_) && (nwrite != SMB_BYTES_QUICK_PROT)) ||
+	    ((nread >= _32KB_) && (nread != SMB_BYTES_BLOCK_PROT) &&
+	     (nread != SMB_BYTES_QUICK_PROT))
+#endif
+	    )
+		return false;
+
+	spin_lock_irqsave(&bus->lock, flags);
+
+
+	/* Update driver state                                                                                 */
+	bus->master_or_slave = SMB_MASTER;
+	bus->operation_state = SMB_MASTER_START;
+	if (nwrite > 0)
+		bus->operation = SMB_WRITE_OPER;
+	else
+		bus->operation = SMB_READ_OPER;
+
+	bus->dest_addr      = (u8)(slave_addr << 1);  /* Translate 7-bit to 8-bit format  */
+	bus->write_data_buf = write_data;
+	bus->write_size     = nwrite;
+	bus->write_index    = 0;
+	bus->read_data_buf  = read_data;
+	bus->read_size      = nread;
+	bus->read_index     = 0;
+	bus->PEC_use        = use_PEC;
+	bus->read_block_use = false;
+	bus->retry_count    = SMB_RETRY_MAX_COUNT;
+
+	/* Check if transaction uses Block read protocol                                                       */
+	if ((bus->read_size == SMB_BYTES_BLOCK_PROT) ||
+	    (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER)) {
+		bus->read_block_use = true;
+
+		/* Change nread in order to configure recieve threshold to 1                                       */
+		nread = 1;
+	}
+
+	/* clear BER just in case it is set due to a previous transaction                                      */
+	REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER));
+
+	/* Initiate SMBus master transaction                                                                   */
+	/* Generate a Start condition on the SMBus                                                             */
+	if (bus->fifo_use == true) {
+		unsigned long flags;
+		/* select bank 1 for FIFO registers                                                                */
+		spin_lock_irqsave(&bus->bank_lock, flags);
+		SMB_SelectBank(bus, SMB_BANK_1);
+		spin_unlock_irqrestore(&bus->bank_lock, flags);
+
+		/* clear FIFO and relevant status bits.                                                            */
+		SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) |
+			     MASK_FIELD(SMBFIF_CTS_CLR_FIFO) |
+			     MASK_FIELD(SMBFIF_CTS_RXF_TXE));
+
+		if (nwrite == 0) {
+			/* This is a read only operation. Configure the FIFO                                           */
+			/* threshold according to the needed number of bytes to read.                                  */
+			if (nread > SMBUS_FIFO_SIZE)
+				SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE);
+			else {
+				SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)(nread));
+
+				if ((bus->read_size != SMB_BYTES_BLOCK_PROT) &&
+				    (bus->read_size != SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER))
+					SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1);
+			}
+		}
+	}
+
+	SMB_Start(bus);
+
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return true;
+}
+#endif  /* !SMB_SLAVE_ONLY */
+
+
+#if !defined SMB_MASTER_ONLY
+static bool SMB_StartSlaveReceive(nuvoton_i2c_bus_t *bus, u16 nread,
+				  u8 *read_data)
+{
+
+	/* Allow only if bus is not busy                                                                       */
+	if ((bus->operation_state != SMB_SLAVE_MATCH)
+#if defined SMBUS_SIZE_CHECK
+	    ||
+	    ((nread >= _32KB_) && (nread != SMB_BYTES_BLOCK_PROT) && (nread != SMB_BYTES_QUICK_PROT))
+#endif
+	    )
+		return false;
+
+	/* Update driver state                                                                                 */
+	bus->operation_state = SMB_OPER_STARTED;
+	bus->operation       = SMB_READ_OPER;
+	bus->read_data_buf   = read_data;
+	bus->read_size       = nread;
+	bus->read_index      = 0;
+	bus->write_size      = 0;
+	bus->write_index     = 0;
+
+	if (bus->fifo_use == true) {
+		if (nread > 0) {
+			u8 smbrxf_ctl;
+
+			if (nread <= SMBUS_FIFO_SIZE) {
+				smbrxf_ctl = BUILD_FIELD_VAL(SMBRXF_CTL_THR_RXIE, 0);
+				smbrxf_ctl |= BUILD_FIELD_VAL(SMBRXF_CTL_RX_THR, nread);
+			} else {
+				/* if threshold_fifo != SMBUS_FIFO_SIZE set SMBRXF_CTL.THR_RXIE to 1 otherwise to 0        */
+				smbrxf_ctl = BUILD_FIELD_VAL(SMBRXF_CTL_RX_THR, bus->threshold_fifo) |
+				    BUILD_FIELD_VAL(SMBRXF_CTL_THR_RXIE, (bool)(bus->threshold_fifo != SMBUS_FIFO_SIZE));
+			}
+			REG_WRITE(SMBRXF_CTL(bus), smbrxf_ctl);
+		}
+
+		/* triggers new data reception                                                                     */
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH));
+	}
+
+	return true;
+}
+
+static bool SMB_StartSlaveTransmit(nuvoton_i2c_bus_t *bus, u16 nwrite,
+				   u8 *write_data)
+{
+
+	/* Allow only if bus is not busy                                                                       */
+	if ((bus->operation_state != SMB_SLAVE_MATCH) || (nwrite == 0))
+		return false;
+
+
+	/* Update driver state                                                                                 */
+	if (bus->PEC_use)
+		nwrite++;
+
+	bus->operation_state = SMB_OPER_STARTED;
+	bus->operation       = SMB_WRITE_OPER;
+	bus->write_data_buf  = write_data;
+	bus->write_size      = nwrite;
+	bus->write_index     = 0;
+
+	if (bus->fifo_use == true) {
+		/* triggers new data reception                                                                     */
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH));
+
+		if (nwrite > 0) {
+			u8 smbtxf_ctl;
+
+			if (nwrite <= SMBUS_FIFO_SIZE)
+				smbtxf_ctl = BUILD_FIELD_VAL
+				(SMBTXF_CTL_THR_TXIE, 0) |
+				BUILD_FIELD_VAL(SMBTXF_CTL_TX_THR, 0);
+			else
+				/* if threshold_fifo != SMBUS_FIFO_SIZE set SMBTXF_CTL.THR_TXIE to 1 otherwise to 0        */
+				smbtxf_ctl = BUILD_FIELD_VAL
+				(SMBTXF_CTL_THR_TXIE,
+				 (bool)(bus->threshold_fifo != SMBUS_FIFO_SIZE))
+				| BUILD_FIELD_VAL(SMBTXF_CTL_TX_THR,
+						  SMBUS_FIFO_SIZE -
+						  bus->threshold_fifo);
+
+			REG_WRITE(SMBTXF_CTL(bus), smbtxf_ctl);
+
+			/* Fill the FIFO with data                                                                     */
+			SMB_WriteToFifo(bus, MIN(SMBUS_FIFO_SIZE, nwrite));
+		}
+	}
+
+	return true;
+}
+#endif  /* !SMB_MASTER_ONLY */
+
+#ifdef TBD
+static bool SMB_ModuleIsBusy(nuvoton_i2c_bus_t *bus)
+{
+	return (READ_REG_FIELD(SMBCST(bus), SMBCST_BUSY) ||
+		READ_REG_FIELD(SMBST(bus), SMBST_SLVSTP));
+}
+
+static bool SMB_BusIsBusy(nuvoton_i2c_bus_t *bus)
+{
+	return READ_REG_FIELD(SMBCST(bus), SMBCST_BB);
+}
+#endif //TBD
+
+#if !defined SMB_MASTER_ONLY
+static DEFS_STATUS SMB_GetCurrentSlaveAddress(nuvoton_i2c_bus_t *bus,
+					      u8 *currSlaveAddr)
+{
+	if (currSlaveAddr != NULL) {
+		*currSlaveAddr = bus->SMB_CurSlaveAddr;
+		return DEFS_STATUS_OK;
+	}
+
+	return DEFS_STATUS_INVALID_PARAMETER;
+}
+#endif
+
+#if defined (MIWU_MODULE_TYPE) && defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+
+static void SMB_WakeupHandler(MIWU_SRC_T source)
+{
+	nuvoton_i2c_bus_t *bus;    /* Module whose bus generated the wake-up */
+
+	/* SMB module is enabled already (otherwise a wakeup signal isn't generated) -                         */
+	/* so no need to enable the SMB. SMB HW responds with a negative acknowledge to the Start Condition -  */
+	/* so either the Master Device will re-issue a Start Condition, or the upper layer will initiate a     */
+	/* transaction as a master                                                                             */
+
+	/* Check wake-up source                                                                                */
+	for (bus = 0; bus < SMB_NUM_OF_MODULES; bus++) {
+		if (READ_REG_BIT(SMB_SBD, bus) && READ_REG_BIT(SMB_EEN, bus)) {
+			/* Clear start-bit-detected status                                                             */
+			REG_WRITE(SMB_SBD, MASK_BIT(bus));
+
+			/* Restore SMBnCTL1 register values, because the register is being reseted when the core       */
+			/* switches to Idle or Deep-Idle mode.                                                         */
+			SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE);
+			SMB_InterruptEnable(bus, true);
+
+			/* Notify upper layer of wake-up                                                               */
+			SMB_callback(bus, SMB_WAKE_UP_IND, 0);
+		}
+	}
+
+}
+#endif  /* (MIWU_MODULE_TYPE) && (SMB_CAPABILITY_WAKEUP_SUPPORT) */
+
+static void SMB_ReadFromFifo(nuvoton_i2c_bus_t *bus, u8 bytes_in_fifo)
+{
+	while (bytes_in_fifo--) {
+		/* Keep read data                                                                                  */
+		u8 data = REG_READ(SMBSDA(bus));
+
+		SMB_CalcPEC(bus, data);
+		if (bus->read_index < bus->read_size) {
+			bus->read_data_buf[bus->read_index++] = data;
+			if ((bus->read_index == 1) && bus->read_size == SMB_BYTES_BLOCK_PROT)
+				/* First byte indicates length in block protocol                                           */
+				bus->read_size = data;
+		}
+	}
+}
+
+static void SMB_MasterFifoRead(nuvoton_i2c_bus_t *bus)
+{
+	u16 rcount;
+	u8 fifo_bytes;
+	SMB_STATE_IND_T ind = SMB_MASTER_DONE_IND;
+
+	rcount = bus->read_size - bus->read_index;
+
+
+	/* In order not to change the RX_TRH during transaction (we found that this might                      */
+	/* be problematic if it takes too much time to read the FIFO) we read the data in the                  */
+	/* following way. If the number of bytes to read == FIFO Size + C (where C < FIFO Size)                */
+	/* then first read C bytes and in the next interrupt we read rest of the data.                         */
+	if ((rcount < (2 * SMBUS_FIFO_SIZE)) && (rcount > SMBUS_FIFO_SIZE))
+		fifo_bytes = (u8)(rcount - SMBUS_FIFO_SIZE);
+	else
+		fifo_bytes = READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES);
+
+	if (rcount - fifo_bytes == 0) {
+		/* last byte is about to be read - end of transaction.                                             */
+		/* Stop should be set before reading last byte.                                                    */
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+		/* Enable "End of Busy" interrupt.                                                                 */
+		SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1);
+#endif
+		SMB_Stop(bus);
+
+		SMB_ReadFromFifo(bus, fifo_bytes);
+
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+		if ((bus->PEC_use == true) && (REG_READ(SMBPEC(bus)) != 0))
+#else
+			if ((bus->PEC_use == true) && (bus->crc_data != 0))
+#endif
+				ind = SMB_MASTER_PEC_ERR_IND;
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+		bus->operation_state = SMB_STOP_PENDING;
+		bus->stop_indication = ind;
+#else
+		/* Reset state for new transaction                                                                 */
+		bus->operation_state = SMB_IDLE;
+
+		/* Notify upper layer of transaction completion                                                    */
+		SMB_callback(bus, ind, bus->read_index);
+#endif
+	} else {
+		SMB_ReadFromFifo(bus, fifo_bytes);
+		rcount = bus->read_size - bus->read_index;
+
+		if (rcount > 0) {
+			SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1);
+			SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_RXF_TXE, 1);
+
+			if (rcount > SMBUS_FIFO_SIZE)
+				SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE);
+			else {
+				SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)(rcount));
+				SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1);
+			}
+		}
+	}
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+}
+
+static void SMB_WriteToFifo(nuvoton_i2c_bus_t *bus, u16 max_bytes_to_send)
+{
+
+	/* Fill the FIFO , while the FIFO is not full and there are more bytes to write                        */
+	while ((max_bytes_to_send--) && (SMBUS_FIFO_SIZE -
+					 READ_REG_FIELD(SMBTXF_STS(bus),
+							SMBTXF_STS_TX_BYTES))) {
+		/* write the data                                                                                  */
+		if (bus->write_index < bus->write_size) {
+			if ((bus->PEC_use == true) &&
+			    ((bus->write_index + 1) == bus->write_size) &&
+			    ((bus->read_size == 0) ||
+			     (bus->master_or_slave == SMB_SLAVE))) {
+				/* Master send PEC in write protocol, Slave send PEC in read protocol.                     */
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+				REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus)));
+#else
+				REG_WRITE(SMBSDA(bus), bus->crc_data);
+#endif
+				bus->write_index++;
+			} else
+				SMB_WriteByte(bus, bus->write_data_buf[bus->write_index++]);
+		} else {
+
+/* define this at hal_cfg or chip file, if one wishes to use this feature. Otherwise driver will xmit 0xFF */
+#ifdef SMB_WRAP_AROUND_BUFFER
+			/* We're out of bytes. Ask the higher level for more bytes. Let it know that driver used all its' bytes */
+
+			/* clear the status bits                                                                       */
+			SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+
+			/* Reset state for the remaining bytes transaction                                             */
+			bus->operation_state = SMB_SLAVE_MATCH;
+
+			/* Notify upper layer of transaction completion                                                */
+			SMB_callback(bus, SMB_SLAVE_XMIT_MISSING_DATA_IND,
+				     bus->write_index);
+
+			REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_SDAST));
+#else
+			SMB_WriteByte(bus, 0xFF);
+#endif
+		}
+	}
+}
+
+static bool SMB_InitClock(nuvoton_i2c_bus_t *bus, SMB_MODE_T mode, u16 bus_freq)
+{
+#if defined (SMB_CAPABILITY_FAST_MODE_SUPPORT) || defined (SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT)
+	u16  k1          = 0;
+	u16  k2          = 0;
+	u8   dbnct       = 0;
+#endif
+	u16  sclfrq      = 0;
+	u8   hldt        = 7;
+	bool fastMode    = false;
+	unsigned long bank_flags;
+	u32  source_clock_freq;
+
+	source_clock_freq = bus->apb_clk;
+
+
+	/* Frequency is less or equal to 100 KHz                                                               */
+	if (bus_freq <= SMBUS_FREQ_100KHz) {
+		/* Set frequency:                                                                                  */
+		/* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) = FREQ(CLK) / ( FREQ(SCL)*4 )                  */
+		sclfrq = (u16)((source_clock_freq / ((u32)bus_freq * _1KHz_ * 4)));   // bus_freq is KHz
+
+		/* Check whether requested frequency can be achieved in current CLK                                */
+		if ((sclfrq < SCLFRQ_MIN) || (sclfrq > SCLFRQ_MAX))
+			return false;
+
+		if (source_clock_freq >= 40000000)
+			hldt = 17;
+		else if (source_clock_freq >= 12500000)
+			hldt = 15;
+		else
+			hldt = 7;
+	}
+
+#ifdef SMB_CAPABILITY_FAST_MODE_SUPPORT
+
+	/* Frequency equal to 400 KHz                                                                          */
+
+	else if (bus_freq == SMBUS_FREQ_400KHz) {
+		sclfrq       = 0;
+		fastMode     = true;
+
+		if ((mode == SMB_MASTER && source_clock_freq < 7500000) ||
+		    (mode == SMB_SLAVE  && source_clock_freq < 10000000))
+			/* 400KHz cannot be supported for master core clock < 7.5 MHz or slave core clock < 10 MHz     */
+			return false;
+
+		/* Master or Slave with frequency > 25 MHz                                                         */
+		if (mode == SMB_MASTER || source_clock_freq > 25000000) {
+			/* Set HLDT:                                                                                   */
+			/* SDA hold time:  (HLDT-7) * T(CLK) >= 300                                                    */
+			/* HLDT = 300/T(CLK) + 7 = 300 * FREQ(CLK) + 7                                                 */
+			hldt = (u8)DIV_CEILING((300 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 7;
+
+			if (mode == SMB_MASTER) {
+				/* Set k1:                                                                                 */
+				/* Clock low time: k1 * T(CLK) - T(SMBFO) >= 1300                                          */
+				/* T(SMBRO) = T(SMBFO) = 300                                                               */
+				/* k1 = (1300 + T(SMBFO)) / T(CLK) = 1600 * FREQ(CLK)                                      */
+				k1 = ROUND_UP(((u16)DIV_CEILING((1600 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_))), 2);
+
+				/* Set k2:                                                                                 */
+				/* START setup: (k2 - 1) * T(CLK) - T(SMBFO) >= 600                                        */
+				/* T(SMBRO) = T(SMBFO) = 300                                                               */
+				/* k2 = (600 + T(SMBFO)) / T(CLK) + 1 = 900 * FREQ(CLK) + 1                                */
+				k2 = ROUND_UP(((u16)DIV_CEILING((900 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 1), 2);
+
+				/* Check whether requested frequency can be achieved in current CLK                        */
+				if ((k1 < SCLFRQ_MIN) || (k1 > SCLFRQ_MAX) || (k2 < SCLFRQ_MIN) || (k2 > SCLFRQ_MAX))
+					return false;
+			}
+		}
+
+		/* Slave with frequency 10-25 MHz                                                                  */
+		else {
+			hldt  = 7;
+			dbnct = 2;
+		}
+	}
+#endif //SMB_CAPABILITY_FAST_MODE_SUPPORT
+
+#ifdef SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT
+
+	/* Frequency equal to 1 MHz                                                                            */
+	else if (bus_freq == SMBUS_FREQ_1MHz) {
+		sclfrq       = 0;
+		fastMode     = true;
+
+		if ((mode == SMB_MASTER && source_clock_freq < 15000000) ||
+		    (mode == SMB_SLAVE  && source_clock_freq < 24000000))
+
+			/* 1MHz cannot be supported for master core clock < 15 MHz or slave core clock < 24 MHz        */
+			return false;
+
+		/* Master or Slave with frequency > 40 MHz                                                         */
+		if (mode == SMB_MASTER || source_clock_freq > 40000000) {
+
+			/* Set HLDT:                                                                                   */
+			/* SDA hold time:  (HLDT-7) * T(CLK) >= 120                                                    */
+			/* HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7                                                 */
+			hldt = (u8)DIV_CEILING((120 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 7;
+
+			if (mode == SMB_MASTER) {
+
+				/* Set k1:                                                                                 */
+				/* Clock low time: k1 * T(CLK) - T(SMBFO) >= 500                                           */
+				/* T(SMBRO) = T(SMBFO) = 120                                                               */
+				/* k1 = (500 + T(SMBFO)) / T(CLK) = 620 * FREQ(CLK)                                        */
+				k1 = ROUND_UP(((u16)DIV_CEILING((620 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_))), 2);
+
+
+				/* Set k2:                                                                                 */
+				/* START setup: (k2 - 1) * T(CLK) - T(SMBFO) >= 260                                        */
+				/* T(SMBRO) = T(SMBFO) = 120                                                               */
+				/* k2 = (260 + T(SMBFO)) / T(CLK) + 1 = 380 * FREQ(CLK) + 1                                */
+				k2 = ROUND_UP(((u16)DIV_CEILING((380 * (source_clock_freq / _1KHz_)), ((u32)_1MHz_)) + 1), 2);
+
+
+				/* Check whether requested frequency can be achieved in current CLK                        */
+				if ((k1 < SCLFRQ_MIN) || (k1 > SCLFRQ_MAX) || (k2 < SCLFRQ_MIN) || (k2 > SCLFRQ_MAX)) {
+					return false;
+				}
+			}
+		}
+
+		/* Slave with frequency 24-40 MHz                                                                  */
+		else {
+			hldt  = 7;
+			dbnct = 2;
+		}
+	}
+#endif //SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT
+
+
+	/* Frequency larger than 1 MHz                                                                         */
+	else
+		return false;
+
+
+
+	/* After clock parameters calculation update the register                                              */
+	SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_SCLFRQ6_0,
+		      READ_VAR_FIELD(sclfrq, SCLFRQ_0_TO_6));
+	SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SCLFRQ8_7,
+		      READ_VAR_FIELD(sclfrq, SCLFRQ_7_TO_8));
+	SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_400K_MODE, fastMode);
+
+
+	/* Select Bank 0 to access SMBCTL4/SMBCTL5                                                             */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+#if defined (SMB_CAPABILITY_FAST_MODE_SUPPORT) || defined (SMB_CAPABILITY_FAST_MODE_PLUS_SUPPORT)
+	if (bus_freq >= SMBUS_FREQ_400KHz) {
+
+		/* k1 and k2 are relevant for master mode only                                                     */
+		if (mode == SMB_MASTER) {
+
+			/* Set SCL Low/High Time:                                                                      */
+			/* k1 = 2 * SCLLT7-0 -> Low Time  = k1 / 2                                                     */
+			/* k2 = 2 * SCLLT7-0 -> High Time = k2 / 2                                                     */
+			REG_WRITE(SMBSCLLT(bus), (u8)k1 / 2);
+			REG_WRITE(SMBSCLHT(bus), (u8)k2 / 2);
+		}
+
+		/* DBNCT is relevant for slave mode only                                                           */
+		else
+			SET_REG_FIELD(SMBCTL5(bus), SMBCTL5_DBNCT, dbnct);
+	}
+#endif
+
+	SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_HLDT, hldt);
+
+
+	/* Return to Bank 1                                                                                    */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	return true;
+}
+
+
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+
+static void SMB_EnableTimeout(nuvoton_i2c_bus_t *bus, bool enable)
+{
+	u8 toCkDiv;
+	u8 smbEnabled;
+	u8 smbctl1 = 0;
+
+	if (enable) {
+
+		/* TO_CKDIV may be changed only when the SMB is disabled                                           */
+		smbEnabled = READ_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE);
+
+		/* If SMB is enabled - disable the SMB module                                                      */
+		if (smbEnabled) {
+
+			/* Save smbctl1 relevant bits. It is being cleared when the module is disabled                 */
+			smbctl1 = REG_READ(SMBCTL1(bus)) & (MASK_FIELD(SMBCTL1_GCMEN) | MASK_FIELD(SMBCTL1_INTEN) | MASK_FIELD(SMBCTL1_NMINTE));
+
+			/* Disable the SMB module                                                                      */
+			SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE);
+		}
+
+		/* Clear EO_BUSY pending bit                                                                       */
+		SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST, 1);
+
+		/* Configure the division of the SMB Module Basic clock (BCLK) to generate the 1 KHz clock of the  */
+		/* timeout detector.                                                                               */
+		/* The timeout detector has an “n+1” divider, controlled by TO_CKDIV and a fixed divider by 1000.  */
+		/* Together they generate the 1 ms clock cycle                                                     */
+		toCkDiv = (u8)(((bus->apb_clk / _1KHz_) / 1000) - 1);
+
+		/* Set the bus timeout clock divisor                                                               */
+		SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_TO_CKDIV, toCkDiv);
+
+		/* If SMB was enabled - re-enable the SMB module                                                   */
+		if (smbEnabled) {
+
+			/* Enable the SMB module                                                                       */
+			(void)SMB_Enable(bus);
+
+			/* Restore smbctl1 status                                                                      */
+			REG_WRITE(SMBCTL1(bus),  smbctl1);
+		}
+	}
+
+
+	/* Enable/Disable the bus timeout interrupt                                                            */
+	SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTIE, enable);
+}
+#endif
+
+static void SMB_InterruptHandler(nuvoton_i2c_bus_t *bus)
+{
+
+	/* A negative acknowledge has occurred                                                                 */
+	if (READ_REG_FIELD(SMBST(bus), SMBST_NEGACK)) {
+		if (bus->fifo_use) {
+
+			/* if there are still untransmitted bytes in TX FIFO reduce them from write_index              */
+			bus->write_index -= READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_BYTES);
+
+			/* clear the FIFO                                                                              */
+			REG_WRITE(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_CLR_FIFO));
+		}
+
+		/* In slave write operation, NACK is OK, otherwise it is a problem                                 */
+		if (!((bus->master_or_slave == SMB_SLAVE) &&
+		      (bus->write_index != 0) &&
+		      (bus->write_index == bus->write_size)))
+		/* Either not slave, or number of bytes sent to master less than required                          */
+		/* In either case notify upper layer. If we are slave - the upper layer                            */
+		/* should still wait for a Slave Stop.                                                             */
+		{
+#if !defined SMB_SLAVE_ONLY
+			if ((bus->master_or_slave == SMB_MASTER) &&
+			    READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) {
+				/* Only current master is allowed to issue Stop Condition                                  */
+				SMB_MasterAbort(bus);
+			}
+#endif  /* !SMB_SLAVE_ONLY */
+
+			//REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NEGACK));
+			bus->operation_state = SMB_IDLE;
+			SMB_callback(bus, SMB_NACK_IND, bus->write_index);
+		}
+
+		/* else:                                                                                           */
+		/* Slave has to wait for SMB_STOP to decide this is the end of the transaction.                    */
+		/* Therefore transaction is not yet considered as done                                             */
+		/*                                                                                                 */
+		/* In Master mode, NEGACK should be cleared only after generating STOP.                            */
+		/* In such case, the bus is released from stall only after the software clears NEGACK              */
+		/* bit. Then a Stop condition is sent.                                                             */
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NEGACK));
+
+		return;
+	}
+
+
+	/* A Bus Error has been identified                                                                     */
+	if (READ_REG_FIELD(SMBST(bus), SMBST_BER)) {
+
+		/* Check whether bus arbitration or Start or Stop during data transfer                             */
+#if !defined SMB_SLAVE_ONLY
+
+		/* Bus arbitration problem should not result in recovery                                           */
+		if ((bus->master_or_slave == SMB_MASTER)) {
+			if (READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) {
+
+				/* Only current master is allowed to issue Stop Condition                                  */
+				SMB_MasterAbort(bus);
+			} else {
+
+				/* Bus arbitration loss                                                                    */
+				if (--bus->retry_count > 0) {
+					/* Perform a retry (generate a Start condition as soon as the SMBus is free)           */
+					REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER));
+					SMB_Start(bus);
+					return;
+				}
+			}
+		}
+#if !defined SMB_MASTER_ONLY
+else
+#endif
+#endif  /* !SMB_SLAVE_ONLY */
+#if !defined SMB_MASTER_ONLY
+			if (bus->master_or_slave == SMB_SLAVE)
+
+				/* Reset the module                                                                            */
+				SMB_Reset(bus);
+#endif
+
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_BER));
+		bus->operation_state = SMB_IDLE;
+		SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus));
+		return;
+	}
+
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+
+	/* A Bus Timeout has been identified                                                                   */
+	if ((READ_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTIE) == 1) &&  /* bus timeout interrupt is on   */
+	    (READ_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST))) {         /* and bus timeout status is set */
+#if !defined SMB_SLAVE_ONLY
+		if (bus->master_or_slave == SMB_MASTER) {
+
+			/* Only current master is allowed to issue Stop Condition                                      */
+			SMB_MasterAbort(bus);
+		}
+#if !defined SMB_MASTER_ONLY
+else
+#endif
+#endif  /* !SMB_SLAVE_ONLY */
+#if !defined SMB_MASTER_ONLY
+			if (bus->master_or_slave == SMB_SLAVE) {
+
+				/* Reset the module                                                                            */
+
+				SMB_Reset(bus);
+			}
+#endif
+
+		SET_REG_FIELD(SMBT_OUT(bus), SMBT_OUT_T_OUTST, 1); /* Clear EO_BUSY pending bit             */
+		bus->operation_state = SMB_IDLE;
+		SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus));
+		return;
+	}
+#endif
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+
+	/* A Master End of Busy (meaning Stop Condition happened)                                              */
+
+	if ((READ_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE) == 1) &&  /* End of Busy interrupt is on     */
+	    (READ_REG_FIELD(SMBCST3(bus), SMBCST3_EO_BUSY))) {        /* and End of Busy is set          */
+		SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 0); /* Disable "End of Busy" interrupt         */
+		SET_REG_FIELD(SMBCST3(bus), SMBCST3_EO_BUSY, 1); /* Clear EO_BUSY pending bit               */
+
+		bus->operation_state = SMB_IDLE;
+
+		if ((bus->write_size == SMB_BYTES_QUICK_PROT) ||
+		    (bus->read_size == SMB_BYTES_QUICK_PROT) ||
+		    (bus->read_size == 0)) {
+			SMB_callback(bus, bus->stop_indication, 0);
+		} else {
+			SMB_callback(bus, bus->stop_indication,
+				     bus->read_index);
+		}
+		return;
+	}
+#endif
+
+#if !defined SMB_MASTER_ONLY
+
+	/* A Slave Stop Condition has been identified                                                          */
+
+	if (READ_REG_FIELD(SMBST(bus), SMBST_SLVSTP)) {
+		SMB_STATE_IND_T ind;
+		ASSERT(bus->master_or_slave == SMB_SLAVE);
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_SLVSTP));
+
+
+		/* Check whether bus arbitration or Start or Stop during data transfer                             */
+		bus->operation_state = SMB_IDLE;
+		if (bus->fifo_use) {
+			if (bus->operation == SMB_READ_OPER) {
+				SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES));
+
+				/* Be prepared for new transactions                                                        */
+				//bus->operation_state = SMB_IDLE;
+
+				/* if PEC is not used or PEC is used and PEC is correct                                    */
+				if (bus->PEC_use == false ||
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+				    (REG_READ(SMBPEC(bus)) == 0)
+#else
+				    (bus->crc_data == 0)
+#endif
+				   ){
+					ind = SMB_SLAVE_DONE_IND;
+				}
+
+				/* PEC value is not correct                                                                */
+				else {
+					ind = SMB_SLAVE_PEC_ERR_IND;
+				}
+				SMB_callback(bus,
+							    /* Notify upper layer that illegal data received */
+							    ind,
+							    bus->read_index);
+			}
+			if (bus->operation == SMB_WRITE_OPER) {
+				//bus->operation_state = SMB_IDLE;
+				SMB_callback(bus, SMB_SLAVE_DONE_IND,
+					     bus->write_index);
+			}
+
+			SET_REG_MASK(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR) |
+				     MASK_FIELD(SMBFIF_CTS_CLR_FIFO) | MASK_FIELD(SMBFIF_CTS_RXF_TXE));
+		}
+
+		/* FIFO is not used                                                                                */
+		else {
+			if (bus->operation == SMB_READ_OPER) {
+
+				/* if PEC is not used or PEC is used and PEC is correct                                    */
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+				if (bus->PEC_use == false ||
+				    (REG_READ(SMBPEC(bus)) == 0))
+#else
+					if (bus->PEC_use == false ||
+					    (bus->crc_data == 0))
+#endif
+						/* Notify upper layer of missing data or all data received */
+						ind = SMB_SLAVE_DONE_IND;
+					/* PEC value is not correct                                                                */
+				else
+					ind = SMB_SLAVE_PEC_ERR_IND;
+
+				SMB_callback(bus, ind, bus->read_index);
+			} else
+				//bus->operation_state = SMB_IDLE;
+				SMB_callback(bus, SMB_SLAVE_DONE_IND,
+					     bus->write_index);
+		}
+
+		return;
+	}
+
+	/* A Slave restart Condition has been identified                                                       */
+	if (bus->fifo_use && READ_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_SLVRSTR)) {
+		ASSERT(bus->master_or_slave == SMB_SLAVE);
+
+		if (bus->operation == SMB_READ_OPER) {
+			SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_BYTES));
+		}
+		REG_WRITE(SMBFIF_CTS(bus), MASK_FIELD(SMBFIF_CTS_SLVRSTR));
+	}
+
+	/* A Slave Address Match has been identified                                                           */
+	if (READ_REG_FIELD(SMBST(bus), SMBST_NMATCH)) {
+		bool slave_tx;
+		SMB_STATE_IND_T ind = SMB_NO_STATUS_IND;
+		u8 info = 0;
+
+		if (bus->fifo_use == false)
+			REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_NMATCH));
+
+		if (READ_REG_FIELD(SMBST(bus), SMBST_XMIT))
+			slave_tx = true;
+		else
+			slave_tx = false;
+
+		if (bus->operation_state == SMB_IDLE) {
+			/* Indicate Slave Mode                                                                         */
+			if (slave_tx)
+				ind = SMB_SLAVE_XMIT_IND;
+			else
+				ind = SMB_SLAVE_RCV_IND;
+
+			/* Check which type of address match                                                           */
+			if (READ_REG_FIELD(SMBCST(bus), SMBCST_MATCH)) {
+				u16 address_match = ((REG_READ(SMBCST3(bus)) & 0x7) << 7) |
+				    (REG_READ(SMBCST2(bus)) & 0x7F);
+				info = 0;
+				ASSERT(address_match);
+				while (address_match) {
+					if (address_match & 1)
+						break;
+					info++;
+					address_match = address_match >> 1;
+				}
+
+				bus->SMB_CurSlaveAddr = READ_VAR_FIELD(SMB_GetSlaveAddress_l(bus, (SMB_ADDR_T)info), SMBADDRx_ADDR);
+				if (READ_VAR_BIT(bus->PEC_mask, info) == 1) {
+					bus->PEC_use = true;
+					bus->crc_data = 0;
+					if (slave_tx)
+						SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1 | 1);
+					else
+						SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1);
+				} else
+					bus->PEC_use = false;
+			} else {
+				if (READ_REG_FIELD(SMBCST(bus), SMBCST_GCMATCH)) {
+					info = (u8)SMB_GC_ADDR;
+					bus->SMB_CurSlaveAddr = 0;
+				} else {
+					if (READ_REG_FIELD(SMBCST(bus), SMBCST_ARPMATCH)) {
+						info = (u8)SMB_ARP_ADDR;
+						bus->SMB_CurSlaveAddr = 0x61;
+					}
+				}
+			}
+		} else {
+			/*  Slave match can happen in two options:                                                     */
+			/*  1. Start, SA, read    ( slave read without further ado).                                   */
+			/*  2. Start, SA, read , data , restart, SA, read,  ... ( salve read in fragmented mode)       */
+			/*  3. Start, SA, write, data, restart, SA, read, .. ( regular write-read mode)                */
+			if (((bus->operation_state == SMB_OPER_STARTED) &&
+			     (bus->operation == SMB_READ_OPER) &&
+			     (bus->master_or_slave == SMB_SLAVE) &&
+			     slave_tx) ||
+			    ((bus->master_or_slave == SMB_SLAVE) &&
+			     !slave_tx))
+			/* slave transmit after slave receive w/o Slave Stop implies repeated start */
+			{
+				ind = SMB_SLAVE_RESTART_IND;
+				info = (u8)(bus->read_index);
+				SMB_CalcPEC(bus, (bus->SMB_CurSlaveAddr & 0x7F) << 1 | 1);
+			}
+		}
+
+		/* Address match automatically implies slave mode                                                  */
+		ASSERT(!READ_REG_FIELD(SMBST(bus), SMBST_MASTER));
+		bus->master_or_slave = SMB_SLAVE;
+		bus->operation_state = SMB_SLAVE_MATCH;
+
+		/* Notify upper layer                                                                              */
+		/* Upper layer must at this stage call the driver routine for slave tx or rx,                      */
+		/* to eliminate a condition of slave being notified but not yet starting                           */
+		/* transaction - and thus an endless interrupt from SDAST for the slave RCV or TX !                */
+		SMB_callback(bus, ind, info);
+
+#ifdef SMB_RECOVERY_SUPPORT
+
+		/* By now, SMB operation state should have been changed from MATCH to SMB_OPER_STARTED.            */
+		/* If state hasn't been changed already, this may suggest that the SMB slave is not ready to       */
+		/* transmit or receive data.                                                                       */
+		/*                                                                                                 */
+		/* In addition, when using FIFO, NMATCH bit is cleared only when moving to SMB_OPER_STARTED state. */
+		/* If NMATCH is not cleared, we would get an endless SMB interrupt.                                */
+		/* Therefore, Abort the slave, such that SMB HW and state machine return to a default, functional  */
+		/* state.                                                                                          */
+		if (bus->operation_state == SMB_SLAVE_MATCH) {
+			SMB_SlaveAbort(bus);
+			SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus));
+			return;
+		}
+
+		/* Slave abort data                                                                                */
+		/* if the SMBus's status is not match current status register of XMIT                              */
+		/* the Slave device will enter dead-lock and stall bus forever                                     */
+		/* Add this check rule to avoid this condition                                                     */
+		if ((bus->operation == SMB_READ_OPER  && ind == SMB_SLAVE_XMIT_IND) ||
+		    (bus->operation == SMB_WRITE_OPER && ind == SMB_SLAVE_RCV_IND)) {
+			SMB_SlaveAbort(bus);
+			SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus));
+			return;
+		}
+#endif
+
+		/* If none of the above - BER should occur                                                         */
+	}
+#endif  /* !SMB_MASTER_ONLY */
+
+#if !defined SMB_SLAVE_ONLY
+
+	/* Address sent and requested stall occurred (Master mode)                                             */
+	if (READ_REG_FIELD(SMBST(bus), SMBST_STASTR)) {
+		ASSERT(READ_REG_FIELD(SMBST(bus), SMBST_MASTER));
+		ASSERT(bus->master_or_slave == SMB_MASTER);
+
+		/* Check for Quick Command SMBus protocol */
+		if ((bus->write_size == SMB_BYTES_QUICK_PROT) ||
+		    (bus->read_size  == SMB_BYTES_QUICK_PROT)) {
+
+			/* No need to write any data bytes - reached here only in Quick Command                        */
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+
+			/* Enable "End of Busy" interrupt before issuing a STOP condition.                             */
+			SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1);
+#endif
+			SMB_Stop(bus);
+
+
+			/* Update status                                                                               */
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+			bus->operation_state = SMB_STOP_PENDING;
+			bus->stop_indication = SMB_MASTER_DONE_IND;
+#else
+			bus->operation_state = SMB_IDLE;
+
+
+			/* Notify upper layer                                                                          */
+			SMB_callback(bus, SMB_MASTER_DONE_IND, 0);
+#endif
+		} else if (bus->read_size == 1)
+
+			/* Receiving one byte only - set NACK after ensuring slave ACKed the address byte              */
+			SMB_Nack(bus);
+
+
+		/* Reset stall-after-address-byte                                                                  */
+		SMB_StallAfterStart(bus, false);
+
+
+		/* Clear stall only after setting STOP                                                             */
+		REG_WRITE(SMBST(bus), MASK_FIELD(SMBST_STASTR));
+		return;
+	}
+#endif  /* !SMB_SLAVE_ONLY */
+
+
+	/* SDA status is set - transmit or receive, master or slave                                            */
+	if (READ_REG_FIELD(SMBST(bus), SMBST_SDAST) ||
+	    (bus->fifo_use &&
+	     (READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST) || READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST)))) {
+		/* Status Bit is cleared by writing to or reading from SDA (depending on current direction)        */
+#if !defined SMB_SLAVE_ONLY
+
+		/* Handle successful bus mastership                                                                */
+		if (bus->master_or_slave == SMB_MASTER) {
+			if (bus->operation_state == SMB_IDLE) {
+
+				/* Perform SMB recovery in Master mode, where state is IDLE, which is an illegal state     */
+				SMB_MasterAbort(bus);
+				SMB_callback(bus, SMB_BUS_ERR_IND, 0);
+				return;
+			} else if (bus->operation_state == SMB_MASTER_START) {
+				if (READ_REG_FIELD(SMBST(bus), SMBST_MASTER)) {
+					u8 addr_byte = bus->dest_addr;
+
+					bus->crc_data = 0;
+					/* Check for Quick Command SMBus protocol */
+					if ((bus->write_size == SMB_BYTES_QUICK_PROT) ||
+					    (bus->read_size  == SMB_BYTES_QUICK_PROT))
+						/* Need to stall after successful completion of sending address byte */
+						SMB_StallAfterStart(bus, true);
+					/* Prepare address byte */
+					if (bus->write_size == 0) {
+						if (bus->read_size == 1)
+							/* Receiving one byte only - stall after successful completion of sending      */
+							/* address byte. If we NACK here, and slave doesn't ACK the address, we might  */
+							/* unintentionally NACK the next multi-byte read                               */
+							SMB_StallAfterStart(bus, true);
+
+						/* Set direction to Read */
+						addr_byte |= (u8)0x1;
+						bus->operation = SMB_READ_OPER;
+					} else
+						bus->operation = SMB_WRITE_OPER;
+					/* Write the address to the bus */
+					SMB_WriteByte(bus, addr_byte);
+					bus->operation_state = SMB_OPER_STARTED;
+				}
+			} else
+
+				/* SDA status is set - transmit or receive: Handle master mode                                 */
+				if (bus->operation_state == SMB_OPER_STARTED) {
+					if (bus->operation == SMB_WRITE_OPER) {
+						u16 wcount;
+
+						if ((bus->fifo_use == true))
+							SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+
+
+						/* Master write operation - perform write of required number of bytes                  */
+						if (bus->write_index == bus->write_size) {
+							if ((bus->fifo_use == true) && (READ_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_BYTES) > 0))
+								/* No more bytes to send (to add to the FIFO), however the FIFO is not empty   */
+								/* yet. It is still in the middle of transmitting. Currency there is nothing   */
+								/* to do except for waiting to the end of the transmission.                    */
+								/* We will get an interrupt when the FIFO will get empty.                      */
+								return;
+
+							if (bus->read_size == 0) {
+								/* all bytes have been written, in a pure write operation */
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+								/* Enable "End of Busy" interrupt. */
+								SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1);
+#endif
+								// Issue a STOP condition on the bus
+								SMB_Stop(bus);
+								// Clear SDA Status bit (by writing dummy byte)
+								SMB_WriteByte(bus, 0xFF);
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+								bus->operation_state = SMB_STOP_PENDING;
+								bus->stop_indication = SMB_MASTER_DONE_IND;
+#else
+								// Reset state for new transaction
+								bus->operation_state = SMB_IDLE;
+								// Notify upper layer of transaction completion
+								SMB_callback(bus, SMB_MASTER_DONE_IND, 0);
+#endif
+							} else {
+								/* last write-byte written on previous interrupt - need to restart & send slave address */
+								if ((bus->PEC_use == true) &&
+								    (bus->read_size < SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER))   // PEC is used but the protocol is not block read protocol
+																     // then we add extra bytes for PEC support
+									bus->read_size += 1;
+
+								if (bus->fifo_use == true) {
+									if (((bus->read_size == 1) ||
+									     bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER ||
+									     bus->read_size == SMB_BYTES_BLOCK_PROT)) {   // SMBus Block read transaction.
+
+										REG_WRITE(SMBTXF_CTL(bus), 0);
+										REG_WRITE(SMBRXF_CTL(bus), 1);
+									} else {
+
+										if (bus->read_size > SMBUS_FIFO_SIZE)
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE);
+										else {
+											// clear the status bits
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)bus->read_size);
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1);
+										}
+									}
+								}
+
+
+								/* Generate (Repeated) Start upon next write to SDA */
+								SMB_Start(bus);
+
+								if (bus->read_size == 1)
+
+									/* Receiving one byte only - stall after successful completion of sending  */
+									/* address byte. If we NACK here, and slave doesn't ACK the address, we    */
+									/* might unintentionally NACK the next multi-byte read                     */
+
+									SMB_StallAfterStart(bus, true);
+
+								/* send the slave address in read direction                                    */
+								SMB_WriteByte(bus, bus->dest_addr | 0x1);
+
+								/* Next interrupt will occur on read                                           */
+								bus->operation = SMB_READ_OPER;
+
+							}
+						} else {
+							if ((bus->PEC_use == true) && (bus->write_index == 0)
+							    && (bus->read_size == 0))// extra bytes for PEC support
+								bus->write_size += 1;
+
+							/* write next byte not last byte and not slave address                             */
+							if ((bus->fifo_use == false) || (bus->write_size == 1)) {
+								if ((bus->PEC_use == true) && (bus->read_size == 0) &&
+								    (bus->write_index + 1 == bus->write_size)) { // Master write protocol to send PEC byte.
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+									REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus)));
+#else
+									REG_WRITE(SMBSDA(bus), bus->crc_data);
+#endif
+									bus->write_index++;
+								} else
+									SMB_WriteByte(bus, bus->write_data_buf[bus->write_index++]);
+							}
+							// FIFO is used
+							else {
+								wcount = bus->write_size - bus->write_index;
+								if (wcount > SMBUS_FIFO_SIZE)
+									/* data to send is more then FIFO size.                                    */
+									/* Configure the FIFO interrupt to be mid of FIFO.                         */
+									REG_WRITE(SMBTXF_CTL(bus), BUILD_FIELD_VAL(SMBTXF_CTL_THR_TXIE, 1) | (SMBUS_FIFO_SIZE / 2));
+								else if ((wcount > SMBUS_FIFO_SIZE / 2) && (bus->write_index != 0))
+									/* write_index != 0 means that this is not the first write.                */
+									/* since interrupt is in the mid of FIFO, only half of the fifo is empty.  */
+									/* Continue to configure the FIFO interrupt to be mid of FIFO.             */
+									REG_WRITE(SMBTXF_CTL(bus), BUILD_FIELD_VAL(SMBTXF_CTL_THR_TXIE, 1) | (SMBUS_FIFO_SIZE / 2));
+								else {
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+									if ((bus->PEC_use) && (wcount > 1))
+										wcount--; //put the PEC byte last after the FIFO becomes empty.
+#endif
+									/* This is the first write (write_index = 0) and data to send is less or   */
+									/* equal to FIFO size.                                                     */
+									/* Or this is the last write and data to send is less or equal half FIFO   */
+									/* size.                                                                   */
+									/* In both cases disable the FIFO threshold interrupt.                     */
+									/* The next interrupt will happen after the FIFO will get empty.           */
+									REG_WRITE(SMBTXF_CTL(bus), (u8)0);
+								}
+
+								SMB_WriteToFifo(bus, wcount);
+								SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1); //clear status bit
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+								bus->stall_counter = 0;
+#endif
+							}
+						}
+					} else if (bus->operation == SMB_READ_OPER) {
+						u16 block_zero_bytes;
+						/* Master read operation (pure read or following a write operation).                   */
+
+						/* Initialize number of bytes to include only the first byte (presents a case where    */
+						/* number of bytes to read is zero); add PEC if applicable                             */
+						block_zero_bytes = 1;
+						if (bus->PEC_use == true)
+							block_zero_bytes++;
+
+						/* Perform master read, distinguishing between last byte and the rest of the           */
+						/* bytes. The last byte should be read when the clock is stopped                       */
+						if ((bus->read_index < (bus->read_size - 1)) ||
+						    bus->fifo_use == true) {
+							u8 data;
+
+							/* byte to be read is not the last one                                             */
+							/* Check if byte-before-last is about to be read                                   */
+							if ((bus->read_index == (bus->read_size - 2)) &&
+							    bus->fifo_use == false)
+
+								/* Set nack before reading byte-before-last, so that nack will be generated    */
+								/* after receive of last byte                                                  */
+								SMB_Nack(bus);
+
+							//if (!SMB_ReadByte(bus, &data))
+							if (!READ_REG_FIELD(SMBST(bus), SMBST_SDAST)) {
+								/* No data available - reset state for new transaction                         */
+								bus->operation_state = SMB_IDLE;
+
+								/* Notify upper layer of transaction completion                                */
+								SMB_callback(bus, SMB_NO_DATA_IND, bus->read_index);
+							} else if (bus->read_index == 0) {
+								if (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER ||
+								    bus->read_size == SMB_BYTES_BLOCK_PROT) {
+									(void)SMB_ReadByte(bus, &data);
+
+									/* First byte indicates length in block protocol                           */
+									if (bus->read_size == SMB_BYTES_EXCLUDE_BLOCK_SIZE_FROM_BUFFER)
+										bus->read_size = data;
+									else {
+										bus->read_data_buf[bus->read_index++] = data;
+										bus->read_size = data + 1;
+									}
+
+									if (bus->PEC_use == true) {
+										bus->read_size += 1;
+										data += 1;
+									}
+
+									if (bus->fifo_use == true) {
+										SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1);
+										SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+										//SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_CLR_FIFO, 1);
+										SET_REG_FIELD(SMBFIF_CTS(bus), SMBFIF_CTS_RXF_TXE, 1);
+										if (data > SMBUS_FIFO_SIZE)
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, SMBUS_FIFO_SIZE);
+										else {
+											if (data == 0)
+												data = 1;
+
+											/* clear the status bits                                           */
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR, (u8)data);
+											SET_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_LAST_PEC, 1);
+										}
+									}
+								} else {
+									if (bus->fifo_use == false) {
+										(void)SMB_ReadByte(bus, &data);
+										bus->read_data_buf[bus->read_index++] = data;
+									} else {
+										SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+										SMB_MasterFifoRead(bus);
+									}
+								}
+
+							} else {
+								if (bus->fifo_use == true) {   // FIFO in used.
+									if ((bus->read_size == block_zero_bytes) && (bus->read_block_use == true)) {
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+										/* Enable "End of Busy" interrupt                                      */
+										SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1);
+#endif
+										SMB_Stop(bus);
+
+										SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR));
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+										bus->operation_state = SMB_STOP_PENDING;
+										bus->stop_indication = SMB_MASTER_BLOCK_BYTES_ERR_IND;
+#else
+										/* Reset state for new transaction                                     */
+										bus->operation_state = SMB_IDLE;
+
+										/* Notify upper layer of transaction completion                        */
+										SMB_callback(bus, SMB_MASTER_BLOCK_BYTES_ERR_IND, bus->read_index);
+#endif
+									} else
+										SMB_MasterFifoRead(bus);
+								} else {
+									(void)SMB_ReadByte(bus, &data);
+									bus->read_data_buf[bus->read_index++] = data;
+								}
+							}
+						} else {
+							/* last byte is about to be read - end of transaction.                             */
+							/* Stop should be set before reading last byte.                                    */
+							u8 data;
+							SMB_STATE_IND_T ind = SMB_MASTER_DONE_IND;
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+							/* Enable "End of Busy" interrupt.                                                 */
+							SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_EOBINTE, 1);
+#endif
+							SMB_Stop(bus);
+
+							(void)SMB_ReadByte(bus, &data);
+
+							if ((bus->read_size == block_zero_bytes) && (bus->read_block_use == true))
+								ind = SMB_MASTER_BLOCK_BYTES_ERR_IND;
+							else {
+								bus->read_data_buf[bus->read_index++] = data;
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+								if ((bus->PEC_use == true) && (REG_READ(SMBPEC(bus)) != 0))
+#else
+									if ((bus->PEC_use == true) && (bus->crc_data != 0))
+#endif
+										ind = SMB_MASTER_PEC_ERR_IND;
+
+							}
+
+#ifdef SMB_CAPABILITY_END_OF_BUSY_SUPPORT
+							bus->operation_state = SMB_STOP_PENDING;
+							bus->stop_indication = ind;
+#else
+							/* Reset state for new transaction                                                 */
+							bus->operation_state = SMB_IDLE;
+
+							/* Notify upper layer of transaction completion                                    */
+							SMB_callback(bus, ind, bus->read_index);
+#endif
+						} /* last read byte */
+					} /* read operation */
+					// Edward 2014/6/17 masked.
+				} /* Master mode: if (bus->operation_state == SMB_MASTER_START) */
+			// End of Edward 2014/6/17 masked.
+		} // End of master operation: SDA status is set - transmit or receive.
+#if !defined SMB_MASTER_ONLY
+else
+#endif
+#endif  /* !SMB_SLAVE_ONLY */
+
+#if !defined SMB_MASTER_ONLY
+			/* SDA status is set - transmit or receive: Handle slave mode                                      */
+			if (bus->master_or_slave == SMB_SLAVE) {
+				/* Perform slave read. No need to distinguish between last byte and the rest of the bytes.     */
+				if ((bus->operation == SMB_READ_OPER)) {
+					if (bus->fifo_use == false) {
+						u8 data;
+
+						(void)SMB_ReadByte(bus, &data);
+						if (bus->read_index < bus->read_size) {
+							/* Keep read data                                                                  */
+							bus->read_data_buf[bus->read_index++] = data;
+							if ((bus->read_index == 1) && bus->read_size == SMB_BYTES_BLOCK_PROT)
+								/* First byte indicates length in block protocol                               */
+								bus->read_size = data;
+						}
+					}
+					// FIFO is used
+					else {
+						if (READ_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST)) {
+							SMB_ReadFromFifo(bus, READ_REG_FIELD(SMBRXF_CTL(bus), SMBRXF_CTL_RX_THR));
+
+							/* clear the status bits                                                           */
+							SET_REG_FIELD(SMBRXF_STS(bus), SMBRXF_STS_RX_THST, 1);
+						}
+					}
+				}
+				/* Perform slave write.                                                                        */
+				else {
+					/* More bytes to write                                                                     */
+					if ((bus->operation == SMB_WRITE_OPER) &&
+					    (bus->write_index < bus->write_size)) {
+						if (bus->fifo_use == false) {
+							if ((bus->write_index + 1 == bus->write_size) &&
+							    bus->PEC_use == true) {   // Send PEC byte
+#if defined SMB_CAPABILITY_HW_PEC_SUPPORT
+								REG_WRITE(SMBSDA(bus), REG_READ(SMBPEC(bus)));
+#else
+								REG_WRITE(SMBSDA(bus), bus->crc_data);
+#endif
+							} else if (bus->write_index < bus->write_size)
+								SMB_WriteByte(bus, bus->write_data_buf[bus->write_index]);
+							bus->write_index++;
+						}
+						// FIFO is used
+						else {
+							u16 wcount;
+							wcount =  (bus->write_size - bus->write_index);
+							if (wcount >= SMBUS_FIFO_SIZE)
+								wcount = SMBUS_FIFO_SIZE;
+
+							REG_WRITE(SMBTXF_CTL(bus), (u8)wcount);
+							SMB_WriteToFifo(bus, wcount);
+
+							/* clear the status bits                                                           */
+							SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+						}
+					}
+
+					/* If all bytes were written, ignore further master read requests.                         */
+					else {
+#if !defined(SMB_WRAP_AROUND_BUFFER)
+						ASSERT(false);
+#endif
+						if (bus->fifo_use == false) {
+							/* Clear SDA Status bit                                                            */
+							if (bus->write_index != 0)
+								/* Was writing                                                                 */
+								SMB_WriteByte(bus, 0xFF);
+							else {
+								u8 data;
+								/* Was reading                                                                 */
+								(void)SMB_ReadByte(bus, &data);
+							}
+						}
+						/* write\read redundant bytes with FIFO (if there are any bytes to write)              */
+						else {
+							/* Set threshold size                                                              */
+							REG_WRITE(SMBTXF_CTL(bus), (u8)SMBUS_FIFO_SIZE);
+
+							SMB_WriteToFifo(bus, SMBUS_FIFO_SIZE);
+
+							/* Clear the status bits                                                           */
+							SET_REG_FIELD(SMBTXF_STS(bus), SMBTXF_STS_TX_THST, 1);
+						}
+						/* Notify upper layer of transaction completion                                        */
+						SMB_callback(bus, SMB_NO_DATA_IND, bus->read_index);
+					} // All bytes sent/received
+				}
+			} // slave mode
+#endif  /* !SMB_MASTER_ONLY */
+	} //SDAST
+}
+
+static void SMB_Reset(nuvoton_i2c_bus_t *bus)
+{
+	/* Save smbctl1 relevant bits. It is being cleared when the module is disabled */
+	u8 smbctl1 = REG_READ(SMBCTL1(bus)) & (MASK_FIELD(SMBCTL1_GCMEN) |
+					       MASK_FIELD(SMBCTL1_INTEN) |
+					       MASK_FIELD(SMBCTL1_NMINTE));
+
+	/* Disable the SMB module */
+	SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE);
+
+	/* Enable the SMB module */
+	(void)SMB_Enable(bus);
+
+	/* Restore smbctl1 status */
+	REG_WRITE(SMBCTL1(bus),  smbctl1);
+
+	/* Reset driver status */
+	bus->operation_state = SMB_IDLE;
+}
+
+
+#if !defined SMB_SLAVE_ONLY
+static void SMB_MasterAbort(nuvoton_i2c_bus_t *bus)
+{
+	SMB_AbortData(bus);
+	SMB_Reset(bus);
+}
+
+#ifdef TBD
+static void SMB_Recovery(nuvoton_i2c_bus_t *bus)
+{
+
+	/* Disable interrupt                                                                                   */
+	SMB_InterruptEnable(bus, false);
+
+	/* Check If the SDA line is active (low) */
+	if (READ_REG_FIELD(SMBCST(bus), SMBCST_TSDA) == 0) {
+		u8   iter = 9;   // Allow one byte to be sent by the Slave
+		u16  timeout;
+		bool done = false;
+
+		/* Repeat the following sequence until SDA becomes inactive (high) */
+		do {
+			/* Issue a single SCL cycle */
+			REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_TGSCL));
+			timeout = ABORT_TIMEOUT;
+			while (READ_REG_FIELD(SMBCST(bus), SMBCST_TGSCL) && (--timeout != 0))
+				;
+			/* If SDA line is inactive (high), stop */
+			if (READ_REG_FIELD(SMBCST(bus), SMBCST_TSDA) == 1)
+				done = true;
+		} while ((done == false) && (--iter != 0));
+
+		/* If SDA line is released (high) */
+		if (done) {
+			/* Clear BB (BUS BUSY) bit */
+			REG_WRITE(SMBCST(bus), MASK_FIELD(SMBCST_BB));
+
+			/* Generate a START condition, to synchronize Master and Slave */
+			SMB_Start(bus);
+
+			/* Wait until START condition is sent, or timeout */
+			timeout = ABORT_TIMEOUT;
+			while (!READ_REG_FIELD(SMBST(bus), SMBST_MASTER) && (--timeout != 0))
+				;
+
+			/* If START condition was sent */
+			if (timeout > 0) {
+				/* Send an address byte */
+				SMB_WriteByte(bus, bus->dest_addr);
+
+				/* Generate a STOP condition */
+				SMB_Stop(bus);
+			}
+		}
+	}
+
+	/* Enable interrupt  */
+	SMB_InterruptEnable(bus, true);
+}
+#endif // TBD
+#endif  /* !SMB_SLAVE_ONLY */
+
+static void SMB_InterruptEnable(nuvoton_i2c_bus_t *bus, bool enable)
+{
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_INTEN, enable);
+}
+
+#if !defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT
+static void SMB_SlaveAbort(nuvoton_i2c_bus_t *bus)
+{
+	volatile u8 temp;
+
+	/* Disable interrupt. */
+	SMB_InterruptEnable(bus, false);
+
+	/* Dummy read to clear interface. */
+	temp = REG_READ(SMBSDA(bus));
+
+	/* Clear NMATCH and BER bits by writing 1s to them. */
+	SET_REG_FIELD(SMBST(bus), SMBST_BER, true);
+	SET_REG_FIELD(SMBST(bus), SMBST_NMATCH, true);
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter = 0;
+#endif
+
+	/* Reset driver status */
+	bus->operation_state = SMB_IDLE;
+
+	/* Disable SMB Module */
+	SET_REG_FIELD(SMBCTL2(bus), SMBCTL2_ENABLE, DISABLE);
+
+	/* Delay 100 us */
+	udelay(10); // TBD must be out of interrupt
+
+	/* Enable SMB Module */
+	(void)SMB_Enable(bus);
+
+	/* Enable interrupt. */
+	SMB_InterruptEnable(bus, true);
+
+	//lint -e{550} suppress PC-Lint warning on Symbol 'temp' not accessed
+}
+#endif //!defined SMB_MASTER_ONLY && defined SMB_RECOVERY_SUPPORT
+
+#if defined (SMB_CAPABILITY_WAKEUP_SUPPORT)
+static void SMB_SetStallAfterStartIdle(nuvoton_i2c_bus_t *bus, bool enable)
+{
+	SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_IDL_START, enable);
+}
+#endif //SMB_CAPABILITY_WAKEUP_SUPPORT
+
+#if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+static const u8 crc8_table[256] = {
+	0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
+	0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+	0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
+	0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+	0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
+	0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+	0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
+	0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+	0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
+	0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+	0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
+	0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+	0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
+	0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+	0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
+	0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+	0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
+	0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+	0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
+	0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+	0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
+	0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+	0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
+	0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+	0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
+	0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+	0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
+	0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+	0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
+	0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+	0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
+	0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+static u8 SMB_CalculateCRC8(u8 crc_data, u8 data)
+{
+	u8 tmp = crc_data ^ data;
+
+	crc_data = crc8_table[tmp];
+
+	return crc_data;
+}
+#endif // #if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+
+static void SMB_CalcPEC(nuvoton_i2c_bus_t *bus, u8 data)
+{
+#if !defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+	if (bus->PEC_use)
+		bus->crc_data = SMB_CalculateCRC8(bus->crc_data, data);
+#endif
+}
+
+
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+static void SMB_ConfigStallThreshold(nuvoton_i2c_bus_t *bus, u8 threshold)
+{
+	bus->stall_threshold = threshold;
+}
+
+static void SMB_StallHandler(nuvoton_i2c_bus_t *bus)
+{
+	if ((bus->operation_state == SMB_IDLE) ||
+	    (bus->operation_state == SMB_DISABLE) ||
+	    (bus->master_or_slave == SMB_SLAVE))
+		; // ignore this bus
+	else {
+		/* increase timeout counter                                                                    */
+		bus->stall_counter++;
+
+		/* time expired, execute recovery */
+		if ((bus->stall_counter) >= bus->stall_threshold) {
+			SMB_MasterAbort(bus);
+			SMB_callback(bus, SMB_BUS_ERR_IND, SMB_GetIndex(bus));
+			return;
+		}
+	}
+}
+#endif
+
+#ifdef TBD
+static void SMB_ReEnableModule(nuvoton_i2c_bus_t *bus)
+{
+	/* Enable SMB interrupt and New Address Match interrupt source */
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_NMINTE, ENABLE);
+	SET_REG_FIELD(SMBCTL1(bus), SMBCTL1_INTEN,  ENABLE);
+}
+
+static bool SMB_InterruptIsPending(void)
+{
+	nuvoton_i2c_bus_t *bus;
+	bool         InterruptIsPending = false;
+
+	for (bus = 0; bus < SMB_NUM_OF_MODULES; bus++)
+		InterruptIsPending |= INTERRUPT_PENDING(SMB_INTERRUPT_PROVIDER,
+	SMB_INTERRUPT(bus));
+
+	return InterruptIsPending;
+}
+#endif
+
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+static void SMB_WriteSCL(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level)
+{
+	unsigned long bank_flags;
+
+	/* Select Bank 0 to access SMBCTL4 */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	/* Set SCL_LVL, SDA_LVL bits as Read/Write (R/W) */
+	SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 1);
+
+	/* Set level */
+	SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SCL_LVL, level);
+
+	/* Set SCL_LVL, SDA_LVL bits as Read Only (RO) */
+	SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 0);
+
+	/* Return to Bank 1 */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+}
+
+static void SMB_WriteSDA(nuvoton_i2c_bus_t *bus, SMB_LEVEL_T level)
+{
+	unsigned long bank_flags;
+
+	/* Select Bank 0 to access SMBCTL4 */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	/* Set SCL_LVL, SDA_LVL bits as Read/Write (R/W) */
+	SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 1);
+
+	/* Set level */
+	SET_REG_FIELD(SMBCTL3(bus), SMBCTL3_SDA_LVL, level);
+
+	/* Set SCL_LVL, SDA_LVL bits as Read Only (RO) */
+	SET_REG_FIELD(SMBCTL4(bus), SMBCTL4_LVL_WE, 0);
+
+	/* Return to Bank 1 */
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+}
+#endif // SMB_CAPABILITY_FORCE_SCL_SDA
+
+#ifdef CONFIG_NPCM750_I2C_DEBUG_PRINT
+static void SMB_PrintRegs(nuvoton_i2c_bus_t *bus)
+{
+	unsigned int i;
+
+	HAL_PRINT("/*--------------*/\n");
+	HAL_PRINT("/*     SMB      */\n");
+	HAL_PRINT("/*--------------*/\n\n");
+
+	SMB_PrintModuleRegs(bus);
+
+	//lint -e{774, 506} suppress PC-Lint warning on ''if' always evaluates to true'
+	if (SMB_FIFO(bus)) {
+		HAL_PRINT("/*--------------*/\n");
+		HAL_PRINT("/*     SMBF     */\n");
+		HAL_PRINT("/*--------------*/\n\n");
+
+		SMBF_PrintModuleRegs(bus);
+	}
+}
+
+static void SMB_PrintModuleRegs(nuvoton_i2c_bus_t *bus)
+{
+	HAL_PRINT("SMB%d:\n", bus->module__num);
+	HAL_PRINT("------\n");
+	HAL_PRINT("SMB%dSDA             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSDA(bus)));
+	HAL_PRINT("SMB%dST              = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBST(bus)));
+	HAL_PRINT("SMB%dCST             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST(bus)));
+	HAL_PRINT("SMB%dCTL1            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL1(bus)));
+	HAL_PRINT("SMB%dADDR1           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR1(bus)));
+	HAL_PRINT("SMB%dCTL2            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL2(bus)));
+	HAL_PRINT("SMB%dADDR2           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR2(bus)));
+	HAL_PRINT("SMB%dCTL3            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL3(bus)));
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+	HAL_PRINT("SMB%dT_OUT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBT_OUT(bus)));
+#endif
+	HAL_PRINT("SMB%dADDR3           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR3(bus)));
+	HAL_PRINT("SMB%dADDR7           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR7(bus)));
+	HAL_PRINT("SMB%dADDR4           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR4(bus)));
+	HAL_PRINT("SMB%dADDR8           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR8(bus)));
+	HAL_PRINT("SMB%dADDR5           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR5(bus)));
+	HAL_PRINT("SMB%dADDR9           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR9(bus)));
+	HAL_PRINT("SMB%dADDR6           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR6(bus)));
+	HAL_PRINT("SMB%dADDR10          = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR10(bus)));
+	HAL_PRINT("SMB%dCST2            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST2(bus)));
+	HAL_PRINT("SMB%dCST3            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST3(bus)));
+	HAL_PRINT("SMB%dCTL4            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL4(bus)));
+	HAL_PRINT("SMB%dCTL5            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL5(bus)));
+	HAL_PRINT("SMB%dSCLLT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSCLLT(bus)));
+	HAL_PRINT("SMB%dSCLHT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSCLHT(bus)));
+	HAL_PRINT("SMB%dVER             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMB_VER(bus)));
+
+	HAL_PRINT("\n");
+}
+
+static void SMBF_PrintModuleRegs(nuvoton_i2c_bus_t *bus)
+{
+
+	/* Common Registers                                                                                    */
+	HAL_PRINT("SMB%1X:\n", bus->module__num);
+	HAL_PRINT("------\n");
+	HAL_PRINT("SMB%1XSDA             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSDA(bus)));
+	HAL_PRINT("SMB%1XST              = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBST(bus)));
+	HAL_PRINT("SMB%1XCST             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST(bus)));
+	HAL_PRINT("SMB%1XCTL1            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL1(bus)));
+	HAL_PRINT("SMB%1XADDR1           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR1(bus)));
+	HAL_PRINT("SMB%1XCTL2            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL2(bus)));
+	HAL_PRINT("SMB%1XADDR2           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR2(bus)));
+	HAL_PRINT("SMB%1XCTL3            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL3(bus)));
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+	HAL_PRINT("SMB%1XT_OUT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBT_OUT(bus)));
+#endif
+
+
+	/* Bank 0 Registers                                                                                    */
+	spin_lock_irqsave(&bus->bank_lock, bank_flags);
+	SMB_SelectBank(bus, SMB_BANK_0);
+
+	HAL_PRINT("SMB%1XADDR3           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR3(bus)));
+	HAL_PRINT("SMB%1XADDR7           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR7(bus)));
+	HAL_PRINT("SMB%1XADDR4           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR4(bus)));
+	HAL_PRINT("SMB%1XADDR8           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR8(bus)));
+	HAL_PRINT("SMB%1XADDR5           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR5(bus)));
+	HAL_PRINT("SMB%1XADDR9           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR9(bus)));
+	HAL_PRINT("SMB%1XADDR6           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR6(bus)));
+	HAL_PRINT("SMB%1XADDR10          = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBADDR10(bus)));
+	HAL_PRINT("SMB%1XCST2            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST2(bus)));
+	HAL_PRINT("SMB%1XCST3            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCST3(bus)));
+	HAL_PRINT("SMB%1XCTL4            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL4(bus)));
+	HAL_PRINT("SMB%1XCTL5            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBCTL5(bus)));
+	HAL_PRINT("SMB%1XSCLLT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSCLLT(bus)));
+	HAL_PRINT("SMB%1XFIF_CTL         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBFIF_CTL(bus)));
+	HAL_PRINT("SMB%1XSCLHT           = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBSCLHT(bus)));
+	HAL_PRINT("SMB%1X_VER            = 0x%02X\n", bus->module__num,
+		  REG_READ(SMB_VER(bus)));
+
+
+	/* Bank 1 Registers                                                                                    */
+
+	SMB_SelectBank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->bank_lock, bank_flags);
+
+	HAL_PRINT("SMB%1XFIF_CTS         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBFIF_CTS(bus)));
+	HAL_PRINT("SMB%1XTXF_CTL         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBTXF_CTL(bus)));
+#if defined (SMB_CAPABILITY_HW_PEC_SUPPORT)
+	HAL_PRINT("SMB%1XPEC             = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBPEC(bus)));
+#endif
+	HAL_PRINT("SMB%1XTXF_STS         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBTXF_STS(bus)));
+	HAL_PRINT("SMB%1XRXF_STS         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBRXF_STS(bus)));
+	HAL_PRINT("SMB%1XRXF_CTL         = 0x%02X\n", bus->module__num,
+		  REG_READ(SMBRXF_CTL(bus)));
+
+	HAL_PRINT("\n");
+}
+
+static void SMB_PrintVersion(void)
+{
+	HAL_PRINT("SMB         = %s\n", I2C_VERSION);
+}
+#endif //CONFIG_NPCM750_I2C_DEBUG_PRINT
+
+static void SMB_callback(nuvoton_i2c_bus_t *bus, SMB_STATE_IND_T op_status, u16 info)
+{
+	switch (op_status) {
+#if !defined SMB_MASTER_ONLY
+		extern u8  read_data_buf[PAGE_SIZE];
+		extern u16 read_size;
+		extern u8  write_data_buf[32];
+		extern u16 write_size;
+	case SMB_SLAVE_RCV_IND:
+		// Slave got an address match with direction bit clear so it should receive data
+		//     the interrupt must call SMB_StartSlaveReceive()
+		// info: the enum SMB_ADDR_T address match
+		SMB_StartSlaveReceive(bus, read_size, read_data_buf);
+		break;
+	case SMB_SLAVE_XMIT_IND:
+		// Slave got an address match with direction bit set so it should transmit data
+		//     the interrupt must call SMB_StartSlaveTransmit()
+		// info: the enum SMB_ADDR_T address match
+		SMB_StartSlaveTransmit(bus, write_size, write_data_buf);
+		break;
+	case SMB_SLAVE_DONE_IND:
+		// Slave done transmitting or receiving
+		// info:
+		//     on receive: number of actual bytes received
+		//     on transmit: number of actual bytes transmitted,
+		//                  when PEC is used 'info' should be (nwrite+1) which means that 'nwrite' bytes
+		//                     were sent + the PEC byte
+		//                     'nwrite' is the second parameter SMB_StartSlaveTransmit()
+		break;
+#endif // !defined SMB_MASTER_ONLY
+	case SMB_MASTER_DONE_IND:
+		// Master transaction finished and all transmit bytes were sent
+		// info: number of bytes actually received after the Master receive operation
+		//       (if Master didn't issue receive it should be 0)
+	case SMB_NO_DATA_IND:
+		// Notify that not all data was received on Master or Slave
+		// info:
+		//     on receive: number of actual bytes received
+		//                 when PEC is used even if 'info' is the expected number of bytes,
+		//                     it means that PEC error occured.
+		{
+			struct i2c_msg *msgs = bus->msgs;
+			int msgs_num = bus->msgs_num;
+
+			if (msgs[0].flags & I2C_M_RD)
+				msgs[0].len = info;
+			else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD)
+				msgs[1].len = info;
+
+			bus->cmd_err = 0;
+			complete(&bus->cmd_complete);
+		}
+		break;
+	case SMB_NACK_IND:
+		// MASTER transmit got a NAK before transmitting all bytes
+		// info: number of transmitted bytes
+		bus->cmd_err = -EAGAIN;
+		complete(&bus->cmd_complete);
+		break;
+	case SMB_BUS_ERR_IND:
+		// Bus error occured
+		// info: has no meaning
+		bus->cmd_err = -EIO;
+		complete(&bus->cmd_complete);
+		break;
+	case SMB_WAKE_UP_IND:
+		// SMBus wake up occured
+		// info: has no meaning
+		break;
+	default:
+		break;
+	}
+}
+
+
+static int __nuvoton_i2c_init(struct nuvoton_i2c_bus *bus,
+			      struct platform_device *pdev)
+{
+	u32 clk_freq;
+	int ret;
+
+	/* Initialize the internal data structures */
+	bus->operation_state = SMB_DISABLE;
+	bus->master_or_slave = SMB_SLAVE;
+#ifdef SMB_STALL_TIMEOUT_SUPPORT
+	bus->stall_counter   = 0;
+	bus->stall_threshold = DEFAULT_STALL_COUNT;
+#endif
+
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "bus-frequency", &clk_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Could not read bus-frequency property\n");
+		clk_freq = 100000;
+	}
+	I2C_DEBUG("clk_freq = %d\n", clk_freq);
+	ret = SMB_InitModule(bus, SMB_MASTER, clk_freq / 1000);
+	if (ret == false) {
+		dev_err(&pdev->dev,
+			"SMB_InitModule() failed\n");
+		return -1;
+	}
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+	SMB_EnableTimeout(bus, true);
+#endif //SMB_CAPABILITY_TIMEOUT_SUPPORT
+
+#ifdef CONFIG_I2C_SLAVE
+	/* If slave has already been registered, re-enable it. */
+	if (bus->slave)
+		__nuvoton_i2c_reg_slave(bus, bus->slave->addr);
+#endif /* CONFIG_I2C_SLAVE */
+
+	return 0;
+}
+
+
+static irqreturn_t nuvoton_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct nuvoton_i2c_bus *bus = dev_id;
+
+#ifdef SMB_SW_BYPASS_HW_ISSUE_SMB_STOP
+	npcm750_clk_GetTimeStamp(bus->interrupt_time_stamp);
+#endif
+	SMB_InterruptHandler(bus);
+
+#ifdef CONFIG_I2C_SLAVE
+	if (nuvoton_i2c_slave_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by slave.\n");
+		return IRQ_HANDLED;
+	}
+#endif /* CONFIG_I2C_SLAVE */
+
+	return IRQ_HANDLED;
+}
+
+
+static int nuvoton_i2c_master_xfer(struct i2c_adapter *adap,
+				   struct i2c_msg *msgs, int num)
+{
+	struct nuvoton_i2c_bus *bus = adap->algo_data;
+	struct i2c_msg *msg0, *msg1;
+	unsigned long time_left, flags;
+	u16 nwrite, nread;
+	u8 *write_data, *read_data;
+	u8 slave_addr;
+	int ret = 0;
+
+	//spin_lock_irqsave(&bus->lock, flags);
+	bus->cmd_err = 0;
+
+	if (num > 2 || num < 1) {
+		pr_err(" num = %d > 2.\n", num);
+		return -1;
+	}
+
+	msg0 = &msgs[0];
+	slave_addr = msg0->addr;
+	if (msg0->flags & I2C_M_RD) { // read
+		if (num == 2) {
+			pr_err(" num = 2 but first msg is read instead of "
+			       "write.\n");
+			return -1;
+		}
+		nwrite = 0;
+		write_data = NULL;
+		if (msg0->flags & I2C_M_RECV_LEN)
+			nread = SMB_BYTES_BLOCK_PROT;
+		else
+			nread = msg0->len;
+
+		read_data = msg0->buf;
+	} else { // write
+		nwrite = msg0->len;
+		write_data = msg0->buf;
+		nread = 0;
+		read_data = NULL;
+		if (num == 2) {
+			msg1 = &msgs[1];
+			if (slave_addr != msg1->addr) {
+				pr_err(" slave_addr == %02x but msg1->addr == "
+				       "%02x\n", slave_addr, msg1->addr);
+				return -1;
+			}
+			if ((msg1->flags & I2C_M_RD) == 0) {
+				pr_err(" num = 2 but both msg are write.\n");
+				return -1;
+			}
+			if (msg1->flags & I2C_M_RECV_LEN)
+				nread = SMB_BYTES_BLOCK_PROT;
+			else
+				nread = msg1->len;
+
+			read_data = msg1->buf;
+		}
+	}
+
+	bus->msgs = msgs;
+	bus->msgs_num = num;
+
+	if (nwrite == 0 && nread == 0)
+		nwrite = nread = SMB_BYTES_QUICK_PROT;
+
+	reinit_completion(&bus->cmd_complete);
+	SMB_StartMasterTransaction(bus, slave_addr, nwrite, nread, write_data,
+				   read_data, 0);
+	//spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_timeout(&bus->cmd_complete,
+						bus->adap.timeout);
+
+	if (time_left == 0)
+		ret = -ETIMEDOUT;
+	else
+		ret = bus->cmd_err;
+
+	spin_lock_irqsave(&bus->lock, flags);
+#ifdef CONFIG_NPCM750_I2C_DEBUG
+	if (bus->msgs[0].flags & I2C_M_RD)
+		nread = bus->msgs[0].len;
+	else if (bus->msgs_num == 2 && bus->msgs[1].flags & I2C_M_RD)
+		nread = bus->msgs[1].len;
+	if (nread && nread != SMB_BYTES_QUICK_PROT) {
+		int i;
+		char str[32 * 3 + 4];
+		char *s = str;
+
+		for (i = 0; (i < nread && i < 32); i++)
+			s += sprintf(s, "%02x ", read_data[i]);
+
+		printk("read_data  = %s\n", str);
+	}
+#endif
+
+
+	bus->msgs = NULL;
+	bus->msgs_num = 0;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	/* If nothing went wrong, return number of messages transferred. */
+	if (ret >= 0)
+		return num;
+	else
+		return ret;
+}
+
+static u32 nuvoton_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+#ifdef CONFIG_I2C_SLAVE
+static void __nuvoton_i2c_reg_slave(struct nuvoton_i2c_bus *bus, u16 slave_addr)
+{
+	u32 addr_reg_val, func_ctrl_reg_val;
+
+	/* Set slave addr. */
+	addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG);
+	addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
+	addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
+	writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG);
+
+	/* Turn on slave mode. */
+	func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+	writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+}
+
+static int nuvoton_i2c_reg_slave(struct i2c_client *client)
+{
+	struct nuvoton_i2c_bus *bus;
+	unsigned long flags;
+
+	bus = client->adapter->algo_data;
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	__nuvoton_i2c_reg_slave(bus, client->addr);
+
+	bus->slave = client;
+	bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return 0;
+}
+
+static int nuvoton_i2c_unreg_slave(struct i2c_client *client)
+{
+	struct nuvoton_i2c_bus *bus = client->adapter->algo_data;
+	u32 func_ctrl_reg_val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bus->lock, flags);
+
+	if (!bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Turn off slave mode. */
+	func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+	writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return 0;
+}
+#endif /* CONFIG_I2C_SLAVE */
+
+static const struct i2c_algorithm nuvoton_i2c_algo = {
+	.master_xfer	= nuvoton_i2c_master_xfer,
+	.functionality	= nuvoton_i2c_functionality,
+#ifdef CONFIG_I2C_SLAVE
+	.reg_slave	= nuvoton_i2c_reg_slave,
+	.unreg_slave	= nuvoton_i2c_unreg_slave,
+#endif /* CONFIG_I2C_SLAVE */
+};
+
+static int nuvoton_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct nuvoton_i2c_bus *bus;
+	struct resource *res;
+	struct clk *i2c_clk;
+	int ret;
+	static u32 first_boot = 1;
+	int module__num;
+
+	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return -ENOMEM;
+
+#ifdef CONFIG_OF
+	module__num = of_alias_get_id(pdev->dev.of_node, "i2c");
+	bus->module__num = module__num;
+
+	I2C_DEBUG("module__num = %d\n", module__num);
+
+	i2c_clk = devm_clk_get(&pdev->dev, NULL);
+
+	if (IS_ERR(i2c_clk)) {
+		pr_err(" I2C probe failed: can't read clk.\n");
+		return  -EPROBE_DEFER; // this error will cause the probing to run again after clk is ready.
+	}
+	//clk_prepare_enable(otp_clk);
+	bus->apb_clk = clk_get_rate(i2c_clk);
+	I2C_DEBUG("I2C APB clock is %d\n", bus->apb_clk);
+#endif //  CONFIG_OF
+
+	if (first_boot)
+		if (gcr_regmap == NULL) {
+			gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+			if (IS_ERR(gcr_regmap)) {
+				pr_err("%s: failed to find nuvoton,"
+				       "npcm750-gcr\n", __func__);
+				return IS_ERR(gcr_regmap);
+			}
+			regmap_write(gcr_regmap, I2CSEGCTL_OFFSET, I2CSEGCTL_VAL);
+			first_boot = 0;
+		}
+
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bus->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bus->base))
+		return PTR_ERR(bus->base);
+
+	I2C_DEBUG("base = %p\n", bus->base);
+
+	/* Initialize the I2C adapter */
+	spin_lock_init(&bus->lock);
+	spin_lock_init(&bus->bank_lock);
+	init_completion(&bus->cmd_complete);
+	bus->adap.owner = THIS_MODULE;
+	bus->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	bus->adap.retries = 0;
+	bus->adap.timeout = 50 * HZ / 1000;
+	bus->adap.algo = &nuvoton_i2c_algo;
+	bus->adap.algo_data = bus;
+	bus->adap.dev.parent = &pdev->dev;
+	bus->adap.dev.of_node = pdev->dev.of_node;
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c");
+
+	bus->dev = &pdev->dev;
+
+	ret = __nuvoton_i2c_init(bus, pdev);
+	if (ret < 0)
+		return ret;
+
+	bus->irq = platform_get_irq(pdev, 0);
+	if (bus->irq < 0) {
+		pr_err("I2C platform_get_irq error.");
+		return -ENODEV;
+	}
+	//bus->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	I2C_DEBUG("irq = %d\n", bus->irq);
+
+	ret = request_irq(bus->irq, nuvoton_i2c_bus_irq, 0,
+			  dev_name(&pdev->dev), (void *)bus);
+	if (ret)
+		return ret;
+
+	ret = i2c_add_adapter(&bus->adap);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, bus);
+
+	dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
+		 bus->adap.nr, bus->irq);
+
+	return 0;
+}
+
+static int nuvoton_i2c_remove_bus(struct platform_device *pdev)
+{
+	struct nuvoton_i2c_bus *bus = platform_get_drvdata(pdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bus->lock, flags);
+
+	/* Disable everything. */
+	SMB_Disable(bus);
+
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	i2c_del_adapter(&bus->adap);
+
+	return 0;
+}
+
+static const struct of_device_id nuvoton_i2c_bus_of_table[] = {
+	{ .compatible = "nuvoton,npcm750-i2c-bus", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, nuvoton_i2c_bus_of_table);
+
+static struct platform_driver nuvoton_i2c_bus_driver = {
+	.probe		= nuvoton_i2c_probe_bus,
+	.remove		= nuvoton_i2c_remove_bus,
+	.driver		= {
+		.name		= "nuvoton-i2c-bus",
+		.of_match_table	= nuvoton_i2c_bus_of_table,
+	},
+};
+module_platform_driver(nuvoton_i2c_bus_driver);
+
+MODULE_AUTHOR("Avi Fishman <avi.fishman at gmail.com>");
+MODULE_DESCRIPTION("Nuvoton I2C Bus Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(I2C_VERSION);
+
+
-- 
2.14.1



More information about the openbmc mailing list