From a27af5b395bdea390032e6ed65ed4e01893932db Mon Sep 17 00:00:00 2001
From: Ricardo de Almeida Gonzaga <ricardo.gonzaga@intel.com>
Date: Wed, 30 Sep 2015 09:32:16 -0300
Subject: [PATCH] x86: Add Intel Quark X1000 I2C support

This patch adds the i2c.c, i2c.h and i2c-registers.h files,
which support access to I2C controller configuration register
through a function interface.
---
 cpu/x86/Makefile.x86_quarkX1000            |   2 +-
 cpu/x86/drivers/quarkX1000/i2c-registers.h | 156 +++++++
 cpu/x86/drivers/quarkX1000/i2c.c           | 512 +++++++++++++++++++++
 cpu/x86/drivers/quarkX1000/i2c.h           |  67 +++
 4 files changed, 736 insertions(+), 1 deletion(-)
 create mode 100644 cpu/x86/drivers/quarkX1000/i2c-registers.h
 create mode 100644 cpu/x86/drivers/quarkX1000/i2c.c
 create mode 100644 cpu/x86/drivers/quarkX1000/i2c.h

diff --git a/cpu/x86/Makefile.x86_quarkX1000 b/cpu/x86/Makefile.x86_quarkX1000
index 2ef0b00e9..8e78fd95e 100644
--- a/cpu/x86/Makefile.x86_quarkX1000
+++ b/cpu/x86/Makefile.x86_quarkX1000
@@ -2,7 +2,7 @@ include $(CONTIKI)/cpu/x86/Makefile.x86_common
 
 CONTIKI_CPU_DIRS += drivers/legacy_pc drivers/quarkX1000 init/legacy_pc
 
-CONTIKI_SOURCEFILES += bootstrap_quarkX1000.S rtc.c pit.c pic.c irq.c nmi.c pci.c uart-16x50.c uart.c gpio.c
+CONTIKI_SOURCEFILES += bootstrap_quarkX1000.S rtc.c pit.c pic.c irq.c nmi.c pci.c uart-16x50.c uart.c gpio.c i2c.c
 
 CFLAGS += -m32 -march=i586 -mtune=i586
 LDFLAGS += -m32 -Xlinker -T -Xlinker $(CONTIKI)/cpu/x86/quarkX1000.ld
diff --git a/cpu/x86/drivers/quarkX1000/i2c-registers.h b/cpu/x86/drivers/quarkX1000/i2c-registers.h
new file mode 100644
index 000000000..7b9e4cec0
--- /dev/null
+++ b/cpu/x86/drivers/quarkX1000/i2c-registers.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_
+#define CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_
+
+#define QUARKX1000_IC_CON            0x00
+#define QUARKX1000_IC_TAR            0x04
+#define QUARKX1000_IC_DATA_CMD       0x10
+#define QUARKX1000_IC_SS_SCL_HCNT    0x14
+#define QUARKX1000_IC_SS_SCL_LCNT    0x18
+#define QUARKX1000_IC_FS_SCL_HCNT    0x1C
+#define QUARKX1000_IC_FS_SCL_LCNT    0x20
+#define QUARKX1000_IC_INTR_STAT      0x2C
+#define QUARKX1000_IC_INTR_MASK      0x30
+#define QUARKX1000_IC_RAW_INTR_STAT  0x34
+#define QUARKX1000_IC_RX_TL          0x38
+#define QUARKX1000_IC_TX_TL          0x3C
+#define QUARKX1000_IC_CLR_INTR       0x40
+#define QUARKX1000_IC_CLR_RX_UNDER   0x44
+#define QUARKX1000_IC_CLR_RX_OVER    0x48
+#define QUARKX1000_IC_CLR_TX_OVER    0x4C
+#define QUARKX1000_IC_CLR_RD_REQ     0x50
+#define QUARKX1000_IC_CLR_TX_ABRT    0x54
+#define QUARKX1000_IC_CLR_ACTIVITY   0x5C
+#define QUARKX1000_IC_CLR_STOP_DET   0x60
+#define QUARKX1000_IC_CLR_START_DET  0x64
+#define QUARKX1000_IC_ENABLE         0x6C
+#define QUARKX1000_IC_STATUS         0x70
+#define QUARKX1000_IC_TXFLR          0x74
+#define QUARKX1000_IC_RXFLR          0x78
+#define QUARKX1000_IC_SDA_HOLD       0x7C
+#define QUARKX1000_IC_TX_ABRT_SOURCE 0x80
+#define QUARKX1000_IC_ENABLE_STATUS  0x9C
+#define QUARKX1000_IC_FS_SPKLEN      0xA0
+
+/* IC_CON */
+#define QUARKX1000_IC_CON_MASTER_MODE_SHIFT         0
+#define QUARKX1000_IC_CON_MASTER_MODE_MASK       0x01
+#define QUARKX1000_IC_CON_SPEED_SHIFT               1
+#define QUARKX1000_IC_CON_SPEED_MASK             0x06
+#define QUARKX1000_IC_CON_10BITADDR_MASTER_SHIFT    4
+#define QUARKX1000_IC_CON_10BITADDR_MASTER_MASK  0x10
+#define QUARKX1000_IC_CON_RESTART_EN_SHIFT          5
+#define QUARKX1000_IC_CON_RESTART_EN_MASK        0x20
+
+/* IC_TAR */
+#define QUARKX1000_IC_TAR_SHIFT     0
+#define QUARKX1000_IC_TAR_MASK  0x3FF
+
+/* IC_DATA_CMD */
+#define QUARKX1000_IC_DATA_CMD_DAT_SHIFT         0
+#define QUARKX1000_IC_DATA_CMD_DAT_MASK      0x0FF
+#define QUARKX1000_IC_DATA_CMD_CMD_SHIFT         8
+#define QUARKX1000_IC_DATA_CMD_CMD_MASK      0x100
+#define QUARKX1000_IC_DATA_CMD_STOP_SHIFT        9
+#define QUARKX1000_IC_DATA_CMD_STOP_MASK     0x200
+#define QUARKX1000_IC_DATA_CMD_RESTART_SHIFT    10
+#define QUARKX1000_IC_DATA_CMD_RESTART_MASK  0x400
+
+/* IC_SS_SCL_HCNT */
+#define QUARKX1000_IC_SS_SCL_HCNT_SHIFT      0
+#define QUARKX1000_IC_SS_SCL_HCNT_MASK  0xFFFF
+
+/* IC_SS_SCL_LCNT */
+#define QUARKX1000_IC_SS_SCL_LCNT_SHIFT      0
+#define QUARKX1000_IC_SS_SCL_LCNT_MASK  0xFFFF
+
+/* IC_FS_SCL_HCNT */
+#define QUARKX1000_IC_FS_SCL_HCNT_SHIFT      0
+#define QUARKX1000_IC_FS_SCL_HCNT_MASK  0xFFFF
+
+/* IC_FS_SCL_LCNT */
+#define QUARKX1000_IC_FS_SCL_LCNT_SHIFT      0
+#define QUARKX1000_IC_FS_SCL_LCNT_MASK  0xFFFF
+
+/* IC_INTR_STAT */
+#define QUARKX1000_IC_INTR_STAT_RX_UNDER_SHIFT      0
+#define QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK   0x001
+#define QUARKX1000_IC_INTR_STAT_RX_OVER_SHIFT       1
+#define QUARKX1000_IC_INTR_STAT_RX_OVER_MASK    0x002
+#define QUARKX1000_IC_INTR_STAT_RX_FULL_SHIFT       2
+#define QUARKX1000_IC_INTR_STAT_RX_FULL_MASK    0x004
+#define QUARKX1000_IC_INTR_STAT_TX_OVER_SHIFT       3
+#define QUARKX1000_IC_INTR_STAT_TX_OVER_MASK    0x008
+#define QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT      4
+#define QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK   0x010
+#define QUARKX1000_IC_INTR_STAT_RD_REQ_SHIFT        5
+#define QUARKX1000_IC_INTR_STAT_RD_REQ_MASK     0x020
+#define QUARKX1000_IC_INTR_STAT_TX_ABRT_SHIFT       6
+#define QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK    0x040
+#define QUARKX1000_IC_INTR_STAT_ACTIVITY_SHIFT      8
+#define QUARKX1000_IC_INTR_STAT_ACTIVITY_MASK   0x100
+#define QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT      9
+#define QUARKX1000_IC_INTR_STAT_STOP_DET_MASK   0x200
+#define QUARKX1000_IC_INTR_STAT_START_DET_SHIFT    10
+#define QUARKX1000_IC_INTR_STAT_START_DET_MASK  0x400
+
+/* IC_ENABLE */
+#define QUARKX1000_IC_ENABLE_SHIFT    0
+#define QUARKX1000_IC_ENABLE_MASK  0x01
+
+/* IC_STATUS */
+#define QUARKX1000_IC_STATUS_ACTIVITY_SHIFT        0
+#define QUARKX1000_IC_STATUS_ACTIVITY_MASK      0x01
+#define QUARKX1000_IC_STATUS_TFNF_SHIFT            1
+#define QUARKX1000_IC_STATUS_TFNF_MASK          0x02
+#define QUARKX1000_IC_STATUS_TFE_SHIFT             2
+#define QUARKX1000_IC_STATUS_TFE_MASK           0x04
+#define QUARKX1000_IC_STATUS_RFNE_SHIFT            3
+#define QUARKX1000_IC_STATUS_RFNE_MASK          0x08
+#define QUARKX1000_IC_STATUS_RFF_SHIFT             4
+#define QUARKX1000_IC_STATUS_RFF_MASK           0x10
+#define QUARKX1000_IC_STATUS_MST_ACTIVITY_SHIFT    5
+#define QUARKX1000_IC_STATUS_MST_ACTIVITY_MASK  0x20
+
+/* IC_TXFLR */
+#define QUARKX1000_IC_TXFLR_SHIFT    0
+#define QUARKX1000_IC_TXFLR_MASK  0x1F
+
+/* IC_RXFLR */
+#define QUARKX1000_IC_RXFLR_SHIFT    0
+#define QUARKX1000_IC_RXFLR_MASK  0x1F
+
+/* IC_FS_SPKLEN */
+#define QUARKX1000_IC_FS_SPKLEN_SHIFT    0
+#define QUARKX1000_IC_FS_SPKLEN_MASK  0xFF
+
+#endif /* CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_ */
diff --git a/cpu/x86/drivers/quarkX1000/i2c.c b/cpu/x86/drivers/quarkX1000/i2c.c
new file mode 100644
index 000000000..bf7b6110a
--- /dev/null
+++ b/cpu/x86/drivers/quarkX1000/i2c.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "contiki.h"
+#include "i2c.h"
+#include "i2c-registers.h"
+#include "interrupt.h"
+#include "pic.h"
+
+#define I2C_CLOCK_SPEED 25 /* kHz */
+#define I2C_FIFO_DEPTH  16
+
+#define I2C_STD_HCNT (I2C_CLOCK_SPEED * 4)
+#define I2C_STD_LCNT (I2C_CLOCK_SPEED * 5)
+#define I2C_FS_HCNT  (I2C_CLOCK_SPEED)
+#define I2C_FS_LCNT  (I2C_CLOCK_SPEED)
+
+#define I2C_FS_SPKLEN_LCNT_OFFSET 8
+#define I2C_FS_SPKLEN_HCNT_OFFSET 6
+
+#define I2C_POLLING_TIMEOUT (CLOCK_SECOND / 10)
+
+#define I2C_IRQ 9
+#define I2C_INT PIC_INT(I2C_IRQ)
+
+typedef enum {
+  I2C_DIRECTION_READ,
+  I2C_DIRECTION_WRITE
+} I2C_DIRECTION;
+
+struct i2c_internal_data {
+  struct quarkX1000_i2c_config config;
+
+  pci_driver_t pci;
+
+  I2C_DIRECTION direction;
+
+  uint8_t rx_len;
+  uint8_t *rx_buffer;
+  uint8_t tx_len;
+  uint8_t *tx_buffer;
+  uint8_t rx_tx_len;
+
+  uint32_t hcnt;
+  uint32_t lcnt;
+};
+
+static struct i2c_internal_data device;
+
+static uint32_t
+read(uint32_t base_addr, uint32_t offset)
+{
+  return *(uint32_t*)(base_addr + offset);
+}
+
+static void
+write(uint32_t base_addr, uint32_t offset, uint32_t val)
+{
+  *(uint32_t*)(base_addr + offset) = val;
+}
+
+static uint32_t
+get_value(uint32_t base_addr, uint32_t offset, uint32_t mask, uint32_t shift)
+{
+  uint32_t register_value = *(uint32_t*)(base_addr + offset);
+
+  register_value &= ~(0xFFFFFFFF - mask);
+
+  return register_value >> shift;
+}
+
+static void
+set_value(uint32_t base_addr, uint32_t offset, uint32_t mask, uint32_t shift, uint32_t value)
+{
+  volatile uint32_t *register_value = (volatile uint32_t*)(base_addr + offset);
+
+  *register_value &= ~mask;
+  *register_value |= value << shift;
+}
+
+static void
+i2c_data_read(void)
+{
+  uint8_t i, rx_cnt;
+
+  if (device.rx_len == 0)
+    return;
+
+  rx_cnt = get_value(device.pci.mmio, QUARKX1000_IC_RXFLR,
+    QUARKX1000_IC_RXFLR_MASK, QUARKX1000_IC_RXFLR_SHIFT);
+
+  if (rx_cnt > device.rx_len)
+    rx_cnt = device.rx_len;
+
+  for (i = 0; i < rx_cnt; i++) {
+    device.rx_buffer[i] = get_value(device.pci.mmio, QUARKX1000_IC_DATA_CMD,
+      QUARKX1000_IC_DATA_CMD_DAT_MASK, QUARKX1000_IC_DATA_CMD_DAT_SHIFT);
+  }
+
+  device.rx_buffer += i;
+  device.rx_len -= i;
+}
+
+static void
+i2c_data_send(void)
+{
+  uint32_t data = 0;
+  uint8_t i, tx_cnt;
+
+  if (device.rx_tx_len == 0)
+    return;
+
+  tx_cnt = I2C_FIFO_DEPTH - get_value(device.pci.mmio, QUARKX1000_IC_TXFLR,
+    QUARKX1000_IC_TXFLR_MASK, QUARKX1000_IC_TXFLR_SHIFT);
+
+  if (tx_cnt > device.rx_tx_len)
+    tx_cnt = device.rx_tx_len;
+
+  for (i = 0; i < tx_cnt; i++) {
+    if (device.tx_len > 0) {
+      data = device.tx_buffer[i];
+
+      if (device.tx_len == 1)
+        data |= (device.rx_len > 0) ? QUARKX1000_IC_DATA_CMD_RESTART_MASK : QUARKX1000_IC_DATA_CMD_STOP_MASK;
+
+      device.tx_len -= 1;
+    } else {
+      data = QUARKX1000_IC_DATA_CMD_CMD_MASK;
+
+      if (device.rx_tx_len == 1)
+        data |= QUARKX1000_IC_DATA_CMD_STOP_MASK;
+    }
+
+    write(device.pci.mmio, QUARKX1000_IC_DATA_CMD, data);
+    device.rx_tx_len -= 1;
+  }
+
+  device.tx_buffer += i;
+}
+
+static void
+i2c_isr(void)
+{
+  if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK) {
+    i2c_data_read();
+
+    write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
+    read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
+
+    if (device.direction == I2C_DIRECTION_WRITE) {
+      if (device.config.cb_tx)
+        device.config.cb_tx();
+    } else {
+      if (device.config.cb_rx)
+        device.config.cb_rx();
+    }
+  }
+
+  if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK) {
+    i2c_data_send();
+    if (device.rx_tx_len <= 0) {
+      set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+        QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 0);
+      set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+        QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
+    }
+  }
+
+  if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_RX_FULL_MASK)
+    i2c_data_read();
+
+  if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & (QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK
+    | QUARKX1000_IC_INTR_STAT_TX_OVER_MASK | QUARKX1000_IC_INTR_STAT_RX_OVER_MASK
+    | QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK)) {
+    write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
+    read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
+
+    if (device.config.cb_err)
+      device.config.cb_err();
+  }
+}
+
+int
+quarkX1000_i2c_configure(struct quarkX1000_i2c_config *config)
+{
+  uint32_t hcnt, lcnt;
+  uint8_t ic_fs_spklen;
+
+  device.config.speed = config->speed;
+  device.config.addressing_mode = config->addressing_mode;
+  device.config.cb_rx = config->cb_rx;
+  device.config.cb_tx = config->cb_tx;
+  device.config.cb_err = config->cb_err;
+
+  if (device.config.speed == QUARKX1000_I2C_SPEED_STANDARD) {
+    lcnt = I2C_STD_LCNT;
+    hcnt = I2C_STD_HCNT;
+  } else {
+    lcnt = I2C_FS_LCNT;
+    hcnt = I2C_FS_HCNT;
+  }
+
+  ic_fs_spklen = get_value(device.pci.mmio, QUARKX1000_IC_FS_SPKLEN,
+    QUARKX1000_IC_FS_SPKLEN_MASK, QUARKX1000_IC_FS_SPKLEN_SHIFT);
+
+  /* We adjust the Low Count and High Count based on the Spike Suppression Limit */
+  device.lcnt = (lcnt < (ic_fs_spklen + I2C_FS_SPKLEN_LCNT_OFFSET)) ? ic_fs_spklen + I2C_FS_SPKLEN_LCNT_OFFSET : lcnt;
+  device.hcnt = (hcnt < (ic_fs_spklen + I2C_FS_SPKLEN_HCNT_OFFSET)) ? ic_fs_spklen + I2C_FS_SPKLEN_HCNT_OFFSET : hcnt;
+
+  /* Clear interrupts. */
+  read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
+
+  return 0;
+}
+
+static int
+i2c_setup(void)
+{
+  /* Clear all values */
+  write(device.pci.mmio, QUARKX1000_IC_CON, 0);
+
+  /* Clear interrupts */
+  read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
+
+  /* Quark X1000 SoC I2C only supports master mode. */
+  set_value(device.pci.mmio, QUARKX1000_IC_CON,
+    QUARKX1000_IC_CON_MASTER_MODE_MASK, QUARKX1000_IC_CON_MASTER_MODE_SHIFT, 1);
+
+  /* Set restart enable */
+  set_value(device.pci.mmio, QUARKX1000_IC_CON,
+    QUARKX1000_IC_CON_RESTART_EN_MASK, QUARKX1000_IC_CON_RESTART_EN_SHIFT, 1);
+
+  /* Set addressing mode */
+  if (device.config.addressing_mode == QUARKX1000_I2C_ADDR_MODE_10BIT) {
+    set_value(device.pci.mmio, QUARKX1000_IC_CON,
+      QUARKX1000_IC_CON_10BITADDR_MASTER_MASK, QUARKX1000_IC_CON_10BITADDR_MASTER_SHIFT, 1);
+  }
+
+  if (device.config.speed == QUARKX1000_I2C_SPEED_STANDARD) {
+    set_value(device.pci.mmio, QUARKX1000_IC_SS_SCL_LCNT,
+      QUARKX1000_IC_SS_SCL_LCNT_MASK, QUARKX1000_IC_SS_SCL_LCNT_SHIFT, device.lcnt);
+    set_value(device.pci.mmio, QUARKX1000_IC_SS_SCL_HCNT,
+      QUARKX1000_IC_SS_SCL_HCNT_MASK, QUARKX1000_IC_SS_SCL_HCNT_SHIFT, device.hcnt);
+    set_value(device.pci.mmio, QUARKX1000_IC_CON,
+      QUARKX1000_IC_CON_SPEED_MASK, QUARKX1000_IC_CON_SPEED_SHIFT, 0x1);
+  } else {
+    set_value(device.pci.mmio, QUARKX1000_IC_FS_SCL_LCNT,
+      QUARKX1000_IC_FS_SCL_LCNT_MASK, QUARKX1000_IC_FS_SCL_LCNT_SHIFT, device.lcnt);
+    set_value(device.pci.mmio, QUARKX1000_IC_FS_SCL_HCNT,
+      QUARKX1000_IC_FS_SCL_HCNT_MASK, QUARKX1000_IC_FS_SCL_HCNT_SHIFT, device.hcnt);
+    set_value(device.pci.mmio, QUARKX1000_IC_CON,
+      QUARKX1000_IC_CON_SPEED_MASK, QUARKX1000_IC_CON_SPEED_SHIFT, 0x2);
+  }
+
+  return 0;
+}
+
+static void
+i2c_operation_setup(uint8_t *write_buf, uint8_t write_len,
+  uint8_t *read_buf,  uint8_t read_len, uint16_t addr)
+{
+  device.rx_len = read_len;
+  device.rx_buffer = read_buf;
+  device.tx_len = write_len;
+  device.tx_buffer = write_buf;
+  device.rx_tx_len = device.rx_len + device.tx_len;
+
+  /* Disable controller */
+  set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+    QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+
+  i2c_setup();
+
+  /* Disable interrupts */
+  write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
+
+  /* Clear interrupts */
+  read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
+
+  /* Set address of target slave */
+  set_value(device.pci.mmio, QUARKX1000_IC_TAR,
+    QUARKX1000_IC_TAR_MASK, QUARKX1000_IC_TAR_SHIFT, addr);
+}
+
+/* This is an interrupt based operation */
+static int
+i2c_operation(uint8_t *write_buf, uint8_t write_len,
+  uint8_t *read_buf,  uint8_t read_len, uint16_t addr)
+{
+  if (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK)
+    return -1;
+
+  i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
+
+  /* Enable master TX and RX interrupts */
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_TX_OVER_MASK, QUARKX1000_IC_INTR_STAT_TX_OVER_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK, QUARKX1000_IC_INTR_STAT_TX_ABRT_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK, QUARKX1000_IC_INTR_STAT_RX_UNDER_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_RX_OVER_MASK, QUARKX1000_IC_INTR_STAT_RX_OVER_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_RX_FULL_MASK, QUARKX1000_IC_INTR_STAT_RX_FULL_SHIFT, 1);
+  set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
+    QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
+
+  /* Enable controller */
+  set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+    QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 1);
+
+  return 0;
+}
+
+/* This is an interrupt based write */
+int
+quarkX1000_i2c_write(uint8_t *buf, uint8_t len, uint16_t addr)
+{
+  device.direction = I2C_DIRECTION_WRITE;
+  return i2c_operation(buf, len, 0, 0, addr);
+}
+
+/* This is an interrupt based read */
+int
+quarkX1000_i2c_read(uint8_t *buf, uint8_t len, uint16_t addr)
+{
+  device.direction = I2C_DIRECTION_READ;
+  return i2c_operation(0, 0, buf, len, addr);
+}
+
+static int
+i2c_polling_operation(uint8_t *write_buf, uint8_t write_len,
+  uint8_t *read_buf,  uint8_t read_len, uint16_t addr)
+{
+  uint32_t start_time, intr_mask_stat;
+
+  if (!(read(device.pci.mmio, QUARKX1000_IC_CON) & QUARKX1000_IC_CON_MASTER_MODE_MASK))
+    return -1;
+
+  /* Wait i2c idle */
+  start_time = clock_seconds();
+  while (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
+    if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+      return -1;
+    }
+  }
+
+  /* Get interrupt mask to restore in the end of polling operation */
+  intr_mask_stat = read(device.pci.mmio, QUARKX1000_IC_INTR_MASK);
+
+  i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
+
+  /* Enable controller */
+  set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+    QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 1);
+
+  /* Transmit */
+  if (device.tx_len != 0) {
+    while (device.tx_len > 0) {
+      start_time = clock_seconds();
+      while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFNF_MASK)) {
+        if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+          set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+            QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+          return -1;
+        }
+      }
+      i2c_data_send();
+    }
+
+    start_time = clock_seconds();
+    while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFE_MASK)) {
+      if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+        set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+          QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+        return -1;
+      }
+    }
+  }
+
+  i2c_data_send();
+
+  /* Receive */
+  if (device.rx_len != 0) {
+    while (device.rx_len > 0) {
+      start_time = clock_seconds();
+      while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_RFNE_MASK)) {
+        if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+          set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+            QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+          return -1;
+        }
+      }
+      i2c_data_read();
+    }
+  }
+
+  /* Stop Det */
+  start_time = clock_seconds();
+  while (!(read(device.pci.mmio, QUARKX1000_IC_RAW_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK)) {
+    if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+      set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+        QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+      return -1;
+    }
+  }
+  read(device.pci.mmio, QUARKX1000_IC_CLR_STOP_DET);
+
+  /* Wait i2c idle */
+  start_time = clock_seconds();
+  while (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
+    if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
+      set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+        QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+      return -1;
+    }
+  }
+
+  /* Disable controller */
+  set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
+    QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
+
+  /* Restore interrupt mask */
+  write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, intr_mask_stat);
+
+  return 0;
+}
+
+int
+quarkX1000_i2c_polling_write(uint8_t *buf, uint8_t len, uint16_t addr)
+{
+  device.direction = I2C_DIRECTION_WRITE;
+  return i2c_polling_operation(buf, len, 0, 0, addr);
+}
+
+int
+quarkX1000_i2c_polling_read(uint8_t *buf, uint8_t len, uint16_t addr)
+{
+  device.direction = I2C_DIRECTION_READ;
+  return i2c_polling_operation(0, 0, buf, len ,addr);
+}
+
+int
+quarkX1000_i2c_is_available(void)
+{
+  return device.pci.mmio ? 1 : 0;
+}
+
+static void
+i2c_handler()
+{
+  i2c_isr();
+
+  pic_eoi(I2C_IRQ);
+}
+
+int
+quarkX1000_i2c_init(void)
+{
+  pci_config_addr_t pci_addr;
+
+  pci_addr.raw = 0;
+  pci_addr.bus = 0;
+  pci_addr.dev = 21;
+  pci_addr.func = 2;
+  pci_addr.reg_off = PCI_CONFIG_REG_BAR0;
+
+  pci_command_enable(pci_addr, PCI_CMD_1_MEM_SPACE_EN);
+
+  SET_INTERRUPT_HANDLER(I2C_INT, 0, i2c_handler);
+
+  if (pci_irq_agent_set_pirq(IRQAGENT3, INTC, PIRQC) < 0)
+    return -1;
+
+  pci_pirq_set_irq(PIRQC, I2C_IRQ, 1);
+
+  pci_init(&device.pci, pci_addr, 0);
+
+  pic_unmask_irq(I2C_IRQ);
+
+  return 0;
+}
diff --git a/cpu/x86/drivers/quarkX1000/i2c.h b/cpu/x86/drivers/quarkX1000/i2c.h
new file mode 100644
index 000000000..b5292524f
--- /dev/null
+++ b/cpu/x86/drivers/quarkX1000/i2c.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CPU_X86_DRIVERS_QUARKX1000_I2C_H_
+#define CPU_X86_DRIVERS_QUARKX1000_I2C_H_
+
+#include "pci.h"
+
+typedef enum {
+  QUARKX1000_I2C_SPEED_STANDARD,
+  QUARKX1000_I2C_SPEED_FAST
+} QUARKX1000_I2C_SPEED;
+
+typedef enum {
+  QUARKX1000_I2C_ADDR_MODE_7BIT,
+  QUARKX1000_I2C_ADDR_MODE_10BIT
+} QUARKX1000_I2C_ADDR_MODE;
+
+typedef void (*quarkX1000_i2c_callback)(void);
+
+struct quarkX1000_i2c_config {
+  QUARKX1000_I2C_SPEED speed;
+  QUARKX1000_I2C_ADDR_MODE addressing_mode;
+
+  quarkX1000_i2c_callback cb_rx;
+  quarkX1000_i2c_callback cb_tx;
+  quarkX1000_i2c_callback cb_err;
+};
+
+int quarkX1000_i2c_init(void);
+int quarkX1000_i2c_configure(struct quarkX1000_i2c_config *config);
+int quarkX1000_i2c_is_available(void);
+
+int quarkX1000_i2c_read(uint8_t *buf, uint8_t len, uint16_t addr);
+int quarkX1000_i2c_write(uint8_t *buf, uint8_t len, uint16_t addr);
+
+int quarkX1000_i2c_polling_read(uint8_t *buf, uint8_t len, uint16_t addr);
+int quarkX1000_i2c_polling_write(uint8_t *buf, uint8_t len, uint16_t addr);
+
+#endif /* CPU_X86_DRIVERS_QUARKX1000_I2C_H_ */