[msm8x60] Add support to read platform id from EPROM

This code adds support for I2C driver. I2C driver is used to read
EPROM for detecting product id of msm8660 device.

Change-Id: I66bdfb827048ba9ac97d17a05bb138019f2d5be9
diff --git a/platform/msm8x60/acpuclock.c b/platform/msm8x60/acpuclock.c
index fbf0c58..4e5695d 100755
--- a/platform/msm8x60/acpuclock.c
+++ b/platform/msm8x60/acpuclock.c
@@ -32,6 +32,48 @@
 #include <platform/iomap.h>
 #include <reg.h>
 
+/* Set rate and enable the clock */
+void clock_config(uint32_t ns,
+        uint32_t md,
+        uint32_t ns_addr,
+        uint32_t md_addr)
+{
+    unsigned int val = 0;
+
+    /* Activate the reset for the M/N Counter */
+    val = 1 << 7;
+    writel(val, ns_addr);
+
+    /* Write the MD value into the MD register */
+    writel(md, md_addr);
+
+    /* Write the ns value, and active reset for M/N Counter, again */
+    val = 1 << 7;
+    val |= ns;
+    writel(val, ns_addr);
+
+    /* De-activate the reset for M/N Counter */
+    val = 1 << 7;
+    val = ~val;
+    val = val & readl(ns_addr);
+    writel(val, ns_addr);
+
+    /* Enable the M/N Counter */
+    val = 1 << 8;
+    val = val | readl(ns_addr);
+    writel(val, ns_addr);
+
+    /* Enable the Clock Root */
+    val = 1 << 11;
+    val = val | readl(ns_addr);
+    writel(val, ns_addr);
+
+    /* Enable the Clock Branch */
+    val = 1 << 9;
+    val = val | readl(ns_addr);
+    writel(val, ns_addr);
+}
+
 void acpu_clock_init (void)
 {
 }
diff --git a/platform/msm8x60/gpio.c b/platform/msm8x60/gpio.c
new file mode 100644
index 0000000..021678e
--- /dev/null
+++ b/platform/msm8x60/gpio.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <debug.h>
+#include <reg.h>
+#include <platform/iomap.h>
+#include <dev/gpio.h>
+
+#include <platform/gpio_hw.h>
+
+void gpio_tlmm_config(uint32_t gpio, uint8_t func,
+                      uint8_t dir, uint8_t pull,
+                      uint8_t drvstr, uint32_t enable)
+{
+    unsigned int val = 0;
+    val |= pull;
+    val |= func << 2;
+    val |= drvstr << 6;
+    val |= enable << 9;
+    unsigned int *addr = (unsigned int *)GPIO_BASE_ADDR(gpio);
+    writel(val, addr);
+    return;
+}
diff --git a/platform/msm8x60/include/platform/gpio_hw.h b/platform/msm8x60/include/platform/gpio_hw.h
new file mode 100644
index 0000000..14f0762
--- /dev/null
+++ b/platform/msm8x60/include/platform/gpio_hw.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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 __PLATFORM_MSM8X60_GPIO_HW_H
+#define __PLATFORM_MSM8X60_GPIO_HW_H
+
+#define TLMM_BASE_ADDR (0x800000)
+#define GPIO_BASE (0x1000)
+#define GPIO_BASE_ADDR(x) (TLMM_BASE_ADDR + GPIO_BASE + (x)*0x10)
+
+/* GPIO TLMM: Direction */
+#define GPIO_INPUT      0
+#define GPIO_OUTPUT     1
+
+/* GPIO TLMM: Pullup/Pulldown */
+#define GPIO_NO_PULL    0
+#define GPIO_PULL_DOWN  1
+#define GPIO_KEEPER     2
+#define GPIO_PULL_UP    3
+
+/* GPIO TLMM: Drive Strength */
+#define GPIO_2MA        0
+#define GPIO_4MA        1
+#define GPIO_6MA        2
+#define GPIO_8MA        3
+#define GPIO_10MA       4
+#define GPIO_12MA       5
+#define GPIO_14MA       6
+#define GPIO_16MA       7
+
+/* GPIO TLMM: Status */
+#define GPIO_ENABLE     0
+#define GPIO_DISABLE    1
+
+#endif
diff --git a/platform/msm8x60/include/platform/iomap.h b/platform/msm8x60/include/platform/iomap.h
index e53f24f..e7d23db 100755
--- a/platform/msm8x60/include/platform/iomap.h
+++ b/platform/msm8x60/include/platform/iomap.h
@@ -84,6 +84,50 @@
 #define GPIO_CFG157_ADDR    0x008019D0
 #define GPIO_CFG158_ADDR    0x008019E0
 
+#define GSBI1_BASE  (0x16000000)
+#define GSBI2_BASE  (0x16100000)
+#define GSBI3_BASE  (0x16200000)
+#define GSBI4_BASE  (0x16300000)
+#define GSBI5_BASE  (0x16400000)
+#define GSBI6_BASE  (0x16500000)
+#define GSBI7_BASE  (0x16600000)
+#define GSBI8_BASE  (0x19800000)
+#define GSBI9_BASE  (0x19900000)
+#define GSBI10_BASE (0x19A00000)
+#define GSBI11_BASE (0x19B00000)
+#define GSBI12_BASE (0x19C00000)
+
+#define GSBI1_QUP_BASE  (GSBI1_BASE  + 0x80000)
+#define GSBI2_QUP_BASE  (GSBI2_BASE  + 0x80000)
+#define GSBI3_QUP_BASE  (GSBI3_BASE  + 0x80000)
+#define GSBI4_QUP_BASE  (GSBI4_BASE  + 0x80000)
+#define GSBI5_QUP_BASE  (GSBI5_BASE  + 0x80000)
+#define GSBI6_QUP_BASE  (GSBI6_BASE  + 0x80000)
+#define GSBI7_QUP_BASE  (GSBI7_BASE  + 0x80000)
+#define GSBI8_QUP_BASE  (GSBI8_BASE  + 0x80000)
+#define GSBI9_QUP_BASE  (GSBI9_BASE  + 0x80000)
+#define GSBI10_QUP_BASE (GSBI10_BASE + 0x80000)
+#define GSBI11_QUP_BASE (GSBI11_BASE + 0x80000)
+#define GSBI12_QUP_BASE (GSBI12_BASE + 0x80000)
+
+#define GSBI_CTL_PROTOCOL_CODE_I2C (0x20)
+
+#define CLK_CTL_BASE    0x00900000
+
+#define GSBIn_HCLK_CTL(n)   ((CLK_CTL_BASE) + 0x29C0 + (32 * ((n) - 1)))
+#define GSBIn_HCLK_FS(n)    ((CLK_CTL_BASE) + 0x29C4 + (32 * ((n) - 1)))
+#define GSBIn_QUP_APPS_MD(n)    ((CLK_CTL_BASE) + 0x29C8 + (32 * ((n) - 1)))
+#define GSBIn_QUP_APPS_NS(n)    ((CLK_CTL_BASE) + 0x29CC + (32 * ((n) - 1)))
+
+/* Defines for the GPIO EXPANDER chip, SX1509QIULTRT */
+#define GPIO_EXPANDER_REG_OPEN_DRAIN_A  (0x0B)
+#define GPIO_EXPANDER_REG_DIR_B     (0x0E)
+#define GPIO_EXPANDER_REG_DIR_A     (0x0F)
+#define GPIO_EXPANDER_REG_DATA_B    (0x10)
+#define GPIO_EXPANDER_REG_DATA_A    (0x11)
+#define CORE_GPIO_EXPANDER_I2C_ADDRESS  (0x3E)
+#define EEPROM_I2C_ADDRESS              (0x52)
+
 #define EBI2_CHIP_SELECT_CFG0    0x1A100000
 #define EBI2_XMEM_CS3_CFG1       0x1A110034
 
diff --git a/platform/msm8x60/include/platform/irqs.h b/platform/msm8x60/include/platform/irqs.h
index 2cfc93a..7fbf37e 100755
--- a/platform/msm8x60/include/platform/irqs.h
+++ b/platform/msm8x60/include/platform/irqs.h
@@ -39,8 +39,29 @@
 #define USB1_HS_BAM_IRQ             (GIC_SPI_START + 94)
 #define USB2_IRQ                    (GIC_SPI_START + 141)
 #define USB1_IRQ                    (GIC_SPI_START + 142)
-
-
+#define GSBI1_QUP_IRQ               (GIC_SPI_START + 147)
+#define GSBI2_UART_IRQ              (GIC_SPI_START + 148)
+#define GSBI2_QUP_IRQ               (GIC_SPI_START + 149)
+#define GSBI3_UART_IRQ              (GIC_SPI_START + 150)
+#define GSBI3_QUP_IRQ               (GIC_SPI_START + 151)
+#define GSBI4_UART_IRQ              (GIC_SPI_START + 152)
+#define GSBI4_QUP_IRQ               (GIC_SPI_START + 153)
+#define GSBI5_UART_IRQ              (GIC_SPI_START + 154)
+#define GSBI5_QUP_IRQ               (GIC_SPI_START + 155)
+#define GSBI6_UART_IRQ              (GIC_SPI_START + 156)
+#define GSBI6_QUP_IRQ               (GIC_SPI_START + 157)
+#define GSBI7_UART_IRQ              (GIC_SPI_START + 158)
+#define GSBI7_QUP_IRQ               (GIC_SPI_START + 159)
+#define GSBI8_UART_IRQ              (GIC_SPI_START + 160)
+#define GSBI8_QUP_IRQ               (GIC_SPI_START + 161)
+#define GSBI9_UART_IRQ              (GIC_SPI_START + 189)
+#define GSBI9_QUP_IRQ               (GIC_SPI_START + 190)
+#define GSBI10_UART_IRQ             (GIC_SPI_START + 191)
+#define GSBI10_QUP_IRQ              (GIC_SPI_START + 192)
+#define GSBI11_UART_IRQ             (GIC_SPI_START + 193)
+#define GSBI11_QUP_IRQ              (GIC_SPI_START + 194)
+#define GSBI12_UART_IRQ             (GIC_SPI_START + 195)
+#define GSBI12_QUP_IRQ              (GIC_SPI_START + 196)
 
 /* Retrofit universal macro names */
 #define INT_USB_HS                  USB1_HS_IRQ
diff --git a/platform/msm8x60/platform.c b/platform/msm8x60/platform.c
index 7fe6f31..28217de 100755
--- a/platform/msm8x60/platform.c
+++ b/platform/msm8x60/platform.c
@@ -37,6 +37,27 @@
 #include <kernel/thread.h>
 #include <platform/debug.h>
 #include <platform/iomap.h>
+#include <i2c_qup.h>
+
+#define CONVERT_ENDIAN_U32(val)                   \
+    ((((uint32_t)(val) & 0x000000FF) << 24) |     \
+     (((uint32_t)(val) & 0x0000FF00) << 8)  |     \
+     (((uint32_t)(val) & 0x00FF0000) >> 8)  |     \
+     (((uint32_t)(val) & 0xFF000000) >> 24))
+
+#define CONVERT_ENDIAN_U16(val)              \
+    ((((uint16_t)(val) & 0x00FF) << 8) |     \
+     (((uint16_t)(val) & 0xFF00) >> 8))
+
+/* Configuration Data Table */
+#define CDT_MAGIC_NUMBER    0x43445400
+struct cdt_header
+{
+    uint32_t magic;   /* Magic number */
+    uint16_t version; /* Version number */
+    uint32_t reserved1;
+    uint32_t reserved2;
+}__attribute__((packed));
 
 void platform_init_interrupts(void);
 void platform_init_timer();
@@ -68,3 +89,46 @@
     writel(0x0, 0x00902D80); //SCSS_CPU1CORE_RESET
     writel(0x3, 0x00902E64); //SCSS_DBG_STATUS_CORE_PWRDUP
 }
+
+static struct qup_i2c_dev* dev = NULL;
+
+uint32_t eprom_read (uint16_t addr, uint8_t count) {
+    uint32_t ret = 0;
+    if(!dev){
+        return ret;
+    }
+    /* Create a i2c_msg buffer, that is used to put the controller into
+     * read mode and then to read some data.
+     */
+    struct i2c_msg msg_buf[] = {
+        {EEPROM_I2C_ADDRESS, I2C_M_WR, 2, &addr},
+        {EEPROM_I2C_ADDRESS, I2C_M_RD, count, &ret}
+    };
+
+    qup_i2c_xfer(dev, msg_buf, 2);
+    return ret;
+}
+
+/* Read EEPROM to find out product id. Return 0 in case of failure */
+uint32_t platform_id_read (void)
+{
+    uint32_t id = 0;
+    uint16_t offset = 0;
+    dev = qup_i2c_init(GSBI8_BASE, 100000, 24000000);
+    if(!dev){
+        return id;
+    }
+    /* Check if EPROM is valid */
+    if (CONVERT_ENDIAN_U32(eprom_read(0, 4)) == CDT_MAGIC_NUMBER)
+    {
+        /* Get offset for platform ID info from Meta Data block 0 */
+        offset = eprom_read(CONVERT_ENDIAN_U16(0 +
+            sizeof(struct cdt_header)), 2);
+        /* Read platform ID */
+        id = eprom_read(CONVERT_ENDIAN_U16(offset), 4);
+        id = CONVERT_ENDIAN_U32(id);
+        id = (id & 0x00FF0000) >> 16;
+    }
+    return id;
+}
+
diff --git a/platform/msm8x60/rules.mk b/platform/msm8x60/rules.mk
index 859e91f..a732f16 100755
--- a/platform/msm8x60/rules.mk
+++ b/platform/msm8x60/rules.mk
@@ -19,7 +19,8 @@
 	$(LOCAL_DIR)/platform.o \
 	$(LOCAL_DIR)/interrupts.o \
 	$(LOCAL_DIR)/acpuclock.o \
-	$(LOCAL_DIR)/mmc_init.o
+	$(LOCAL_DIR)/mmc_init.o \
+	$(LOCAL_DIR)/gpio.o
 
 LINKER_SCRIPT += $(BUILDDIR)/system-onesegment.ld
 
diff --git a/platform/msm_shared/i2c_qup.c b/platform/msm_shared/i2c_qup.c
new file mode 100644
index 0000000..92e355f
--- /dev/null
+++ b/platform/msm_shared/i2c_qup.c
@@ -0,0 +1,840 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ */
+
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */
+
+#include <debug.h>
+#include <arch/arm.h>
+#include <reg.h>
+#include <kernel/thread.h>
+
+#include <i2c_qup.h>
+#include <platform/irqs.h>
+#include <platform/iomap.h>
+#include <platform/gpio_hw.h>
+
+static struct qup_i2c_dev *dev_addr = NULL;
+
+/* QUP Registers */
+enum {
+    QUP_CONFIG = 0x0,
+    QUP_STATE = 0x4,
+    QUP_IO_MODE = 0x8,
+    QUP_SW_RESET = 0xC,
+    QUP_OPERATIONAL = 0x18,
+    QUP_ERROR_FLAGS = 0x1C,
+    QUP_ERROR_FLAGS_EN = 0x20,
+    QUP_MX_READ_CNT = 0x208,
+    QUP_MX_INPUT_CNT = 0x200,
+    QUP_MX_WR_CNT = 0x100,
+    QUP_OUT_DEBUG = 0x108,
+    QUP_OUT_FIFO_CNT = 0x10C,
+    QUP_OUT_FIFO_BASE = 0x110,
+    QUP_IN_READ_CUR = 0x20C,
+    QUP_IN_DEBUG = 0x210,
+    QUP_IN_FIFO_CNT = 0x214,
+    QUP_IN_FIFO_BASE = 0x218,
+    QUP_I2C_CLK_CTL = 0x400,
+    QUP_I2C_STATUS = 0x404,
+};
+
+/* QUP States and reset values */
+enum {
+    QUP_RESET_STATE = 0,
+    QUP_RUN_STATE = 1U,
+    QUP_STATE_MASK = 3U,
+    QUP_PAUSE_STATE = 3U,
+    QUP_STATE_VALID = 1U << 2,
+    QUP_I2C_MAST_GEN = 1U << 4,
+    QUP_OPERATIONAL_RESET = 0xFF0,
+    QUP_I2C_STATUS_RESET = 0xFFFFFC,
+};
+
+/* QUP OPERATIONAL FLAGS */
+enum {
+    QUP_OUT_SVC_FLAG = 1U << 8,
+    QUP_IN_SVC_FLAG = 1U << 9,
+    QUP_MX_INPUT_DONE = 1U << 11,
+};
+
+/* I2C mini core related values */
+enum {
+    I2C_MINI_CORE = 2U << 8,
+    I2C_N_VAL = 0xF,
+
+};
+
+/* Packing Unpacking words in FIFOs , and IO modes*/
+enum {
+    QUP_WR_BLK_MODE = 1U << 10,
+    QUP_RD_BLK_MODE = 1U << 12,
+    QUP_UNPACK_EN = 1U << 14,
+    QUP_PACK_EN = 1U << 15,
+};
+
+/* QUP tags */
+enum {
+    QUP_OUT_NOP = 0,
+    QUP_OUT_START = 1U << 8,
+    QUP_OUT_DATA = 2U << 8,
+    QUP_OUT_STOP = 3U << 8,
+    QUP_OUT_REC = 4U << 8,
+    QUP_IN_DATA = 5U << 8,
+    QUP_IN_STOP = 6U << 8,
+    QUP_IN_NACK = 7U << 8,
+};
+
+/* Status, Error flags */
+enum {
+    I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
+    I2C_STATUS_BUS_ACTIVE = 1U << 8,
+    I2C_STATUS_ERROR_MASK = 0x38000FC,
+    QUP_I2C_NACK_FLAG = 1U << 3,
+    QUP_IN_NOT_EMPTY = 1U << 5,
+    QUP_STATUS_ERROR_FLAGS = 0x7C,
+};
+
+void set_i2c_clk(struct qup_i2c_dev *dev)
+{
+    uint32_t md = 0;
+    uint32_t ns = 0;
+
+    switch (dev->src_clk_freq) {
+    case 24000000:
+        ns = I2C_APPS_CLK_NS_24MHz;
+        md = I2C_APPS_CLK_MD_24MHz;
+        break;
+    default:
+        return;
+    }
+    /* Enable the GSBI8 HCLK */
+    writel((GSBI8_HCLK_CTL_CLK_ENA << GSBI8_HCLK_CTL_S),
+           GSBIn_HCLK_CTL(dev->gsbi_number));
+    clock_config(ns,
+                 md,
+                 GSBIn_QUP_APPS_NS(dev->gsbi_number),
+                 GSBIn_QUP_APPS_MD(dev->gsbi_number));
+}
+
+void i2c_gpio_cfg(uint32_t base)
+{
+    switch (base) {
+    case GSBI8_BASE:
+        gpio_tlmm_config(64, 1, GPIO_OUTPUT, GPIO_NO_PULL,
+                         GPIO_2MA, GPIO_DISABLE);
+        gpio_tlmm_config(65, 1, GPIO_OUTPUT, GPIO_NO_PULL,
+                         GPIO_2MA, GPIO_DISABLE);
+        break;
+    default:
+        break;
+    }
+}
+
+#ifdef DEBUG
+static void qup_print_status(struct qup_i2c_dev *dev)
+{
+    unsigned val;
+    val = readl(dev->base + QUP_CONFIG);
+    dprintf(INFO, "Qup config is :0x%x\n", val);
+    val = readl(dev->base + QUP_STATE);
+    dprintf(INFO, "Qup state is :0x%x\n", val);
+    val = readl(dev->base + QUP_IO_MODE);
+    dprintf(INFO, "Qup mode is :0x%x\n", val);
+}
+#else
+static inline void qup_print_status(struct qup_i2c_dev *dev)
+{
+}
+#endif
+
+void i2c_gpio_cfg(unsigned base);
+
+static irqreturn_t qup_i2c_interrupt(void)
+{
+    struct qup_i2c_dev *dev = dev_addr;
+    if (!dev) {
+        dprintf(CRITICAL,
+                "dev_addr is NULL, that means i2c_qup_init failed...\n");
+        return IRQ_FAIL;
+    }
+    unsigned status = readl(dev->base + QUP_I2C_STATUS);
+    unsigned status1 = readl(dev->base + QUP_ERROR_FLAGS);
+    unsigned op_flgs = readl(dev->base + QUP_OPERATIONAL);
+    int err = 0;
+
+    if (!dev->msg)
+        return IRQ_HANDLED;
+
+    if (status & I2C_STATUS_ERROR_MASK) {
+        dprintf(CRITICAL, "QUP: I2C status flags :0x%x \n", status);
+        err = -status;
+        /* Clear Error interrupt if it's a level triggered interrupt */
+        if (dev->num_irqs == 1) {
+            writel(QUP_RESET_STATE, dev->base + QUP_STATE);
+        }
+        goto intr_done;
+    }
+
+    if (status1 & 0x7F) {
+        dprintf(CRITICAL, "QUP: QUP status flags :0x%x\n", status1);
+        err = -status1;
+        /* Clear Error interrupt if it's a level triggered interrupt */
+        if (dev->num_irqs == 1)
+            writel((status1 & QUP_STATUS_ERROR_FLAGS),
+                   dev->base + QUP_ERROR_FLAGS);
+        goto intr_done;
+    }
+
+    if (op_flgs & QUP_OUT_SVC_FLAG)
+        writel(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
+    if (dev->msg->flags == I2C_M_RD) {
+        if ((op_flgs & QUP_MX_INPUT_DONE) || (op_flgs & QUP_IN_SVC_FLAG))
+            writel(QUP_IN_SVC_FLAG, dev->base + QUP_OPERATIONAL);
+        else
+            return IRQ_HANDLED;
+    }
+
+intr_done:
+    dev->err = err;
+    return IRQ_HANDLED;
+}
+
+static int qup_i2c_poll_writeready(struct qup_i2c_dev *dev)
+{
+    unsigned retries = 0;
+
+    while (retries != 2000) {
+        unsigned status = readl(dev->base + QUP_I2C_STATUS);
+
+        if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
+            if (!(status & I2C_STATUS_BUS_ACTIVE))
+                return 0;
+            else                /* 1-bit delay before we check for bus busy */
+                udelay(dev->one_bit_t);
+        }
+        if (retries++ == 1000)
+            udelay(100);
+    }
+    qup_print_status(dev);
+    return -ETIMEDOUT;
+}
+
+static int qup_i2c_poll_state(struct qup_i2c_dev *dev, unsigned state)
+{
+    unsigned retries = 0;
+
+    dprintf(CRITICAL, "Polling Status for state:0x%x\n", state);
+
+    while (retries != 2000) {
+        unsigned status = readl(dev->base + QUP_STATE);
+
+        if ((status & (QUP_STATE_VALID | state)) == (QUP_STATE_VALID | state))
+            return 0;
+        else if (retries++ == 1000)
+            udelay(100);
+    }
+    return -ETIMEDOUT;
+}
+
+#ifdef DEBUG
+static void qup_verify_fifo(struct qup_i2c_dev *dev, unsigned val,
+                            unsigned addr, int rdwr)
+{
+    if (rdwr)
+        dprintf(INFO, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+    else
+        dprintf(INFO, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
+}
+#else
+static inline void qup_verify_fifo(struct qup_i2c_dev *dev, unsigned val,
+                                   unsigned addr, int rdwr)
+{
+}
+#endif
+
+static void
+qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
+               unsigned carry_over)
+{
+    uint16_t addr = (msg->addr << 1) | 1;
+    /* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field is
+       treated as 256 byte read. */
+    uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);
+
+    if (*idx % 4) {
+        writel(carry_over | ((QUP_OUT_START | addr) << 16),
+               dev->base + QUP_OUT_FIFO_BASE);
+
+        qup_verify_fifo(dev, carry_over |
+                        ((QUP_OUT_START | addr) << 16), (unsigned)dev->base
+                        + QUP_OUT_FIFO_BASE + (*idx - 2), 1);
+        writel((QUP_OUT_REC | rd_len), dev->base + QUP_OUT_FIFO_BASE);
+
+        qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
+                        (unsigned)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2),
+                        1);
+    } else {
+        writel(((QUP_OUT_REC | rd_len) << 16) |
+               QUP_OUT_START | addr, dev->base + QUP_OUT_FIFO_BASE);
+
+        qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
+                        QUP_OUT_START | addr,
+                        (unsigned)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
+    }
+    *idx += 4;
+}
+
+static void
+qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
+                int *idx, unsigned *carry_over)
+{
+    int entries = dev->cnt;
+    int empty_sl = dev->wr_sz - ((*idx) >> 1);
+    int i = 0;
+    unsigned val = 0;
+    unsigned last_entry = 0;
+    uint16_t addr = msg->addr << 1;
+
+    if (dev->pos == 0) {
+        if (*idx % 4) {
+            writel(*carry_over | ((QUP_OUT_START | addr) << 16),
+                   dev->base + QUP_OUT_FIFO_BASE);
+
+            qup_verify_fifo(dev, *carry_over | QUP_OUT_DATA << 16 |
+                            addr << 16, (unsigned)dev->base +
+                            QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+        } else
+            val = QUP_OUT_START | addr;
+        *idx += 2;
+        i++;
+        entries++;
+    } else {
+        /* Avoid setp time issue by adding 1 NOP when number of bytes are more
+           than FIFO/BLOCK size. setup time issue can't appear otherwise since
+           next byte to be written will always be ready */
+        val = (QUP_OUT_NOP | 1);
+        *idx += 2;
+        i++;
+        entries++;
+    }
+    if (entries > empty_sl)
+        entries = empty_sl;
+
+    for (; i < (entries - 1); i++) {
+        if (*idx % 4) {
+            writel(val | ((QUP_OUT_DATA |
+                           msg->buf[dev->pos]) << 16),
+                   dev->base + QUP_OUT_FIFO_BASE);
+
+            qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
+                            msg->buf[dev->pos] << 16, (unsigned)dev->base +
+                            QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+        } else
+            val = QUP_OUT_DATA | msg->buf[dev->pos];
+        (*idx) += 2;
+        dev->pos++;
+    }
+    if (dev->pos < (msg->len - 1))
+        last_entry = QUP_OUT_DATA;
+    else if (rem > 1)           /* not last array entry */
+        last_entry = QUP_OUT_DATA;
+    else
+        last_entry = QUP_OUT_STOP;
+    if ((*idx % 4) == 0) {
+        /*
+         * If read-start and read-command end up in different fifos, it
+         * may result in extra-byte being read due to extra-read cycle.
+         * Avoid that by inserting NOP as the last entry of fifo only
+         * if write command(s) leave 1 space in fifo.
+         */
+        if (rem > 1) {
+            struct i2c_msg *next = msg + 1;
+            if (next->addr == msg->addr && (next->flags | I2C_M_RD)
+                && *idx == ((dev->wr_sz * 2) - 4)) {
+                writel(((last_entry | msg->buf[dev->pos]) |
+                        ((1 | QUP_OUT_NOP) << 16)),
+                       dev->base + QUP_OUT_FIFO_BASE);
+                *idx += 2;
+            } else
+                *carry_over = (last_entry | msg->buf[dev->pos]);
+        } else {
+            writel((last_entry | msg->buf[dev->pos]),
+                   dev->base + QUP_OUT_FIFO_BASE);
+
+            qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
+                            (unsigned)dev->base + QUP_OUT_FIFO_BASE +
+                            (*idx), 0);
+        }
+    } else {
+        writel(val | ((last_entry | msg->buf[dev->pos]) << 16),
+               dev->base + QUP_OUT_FIFO_BASE);
+
+        qup_verify_fifo(dev, val | (last_entry << 16) |
+                        (msg->buf[dev->pos] << 16), (unsigned)dev->base +
+                        QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
+    }
+
+    *idx += 2;
+    dev->pos++;
+    dev->cnt = msg->len - dev->pos;
+}
+
+static int qup_update_state(struct qup_i2c_dev *dev, unsigned state)
+{
+    if (qup_i2c_poll_state(dev, 0) != 0)
+        return -EIO;
+    writel(state, dev->base + QUP_STATE);
+    if (qup_i2c_poll_state(dev, state) != 0)
+        return -EIO;
+    return 0;
+}
+
+static int qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
+{
+    unsigned wr_mode = (dev->wr_sz < dev->out_fifo_sz) ? QUP_WR_BLK_MODE : 0;
+    if (rd_len > 256) {
+        dprintf(INFO, "HW doesn't support READs > 256 bytes\n");
+        return -EPROTONOSUPPORT;
+    }
+    if (rd_len <= dev->in_fifo_sz) {
+        writel(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
+        writel(rd_len, dev->base + QUP_MX_READ_CNT);
+    } else {
+        writel(wr_mode | QUP_RD_BLK_MODE |
+               QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
+        writel(rd_len, dev->base + QUP_MX_INPUT_CNT);
+    }
+    return 0;
+}
+
+static int qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
+{
+    int total_len = 0;
+    int ret = 0;
+    if (dev->msg->len >= (dev->out_fifo_sz - 1)) {
+        total_len = dev->msg->len + 1 + (dev->msg->len / (dev->out_blk_sz - 1));
+        writel(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
+               dev->base + QUP_IO_MODE);
+        dev->wr_sz = dev->out_blk_sz;
+    } else
+        writel(QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
+
+    if (rem > 1) {
+        struct i2c_msg *next = dev->msg + 1;
+        if (next->addr == dev->msg->addr && next->flags == I2C_M_RD) {
+            ret = qup_set_read_mode(dev, next->len);
+            /* make sure read start & read command are in 1 blk */
+            if ((total_len % dev->out_blk_sz) == (dev->out_blk_sz - 1))
+                total_len += 3;
+            else
+                total_len += 2;
+        }
+    }
+    /* WRITE COUNT register valid/used only in block mode */
+    if (dev->wr_sz == dev->out_blk_sz)
+        writel(total_len, dev->base + QUP_MX_WR_CNT);
+    return ret;
+}
+
+int qup_i2c_xfer(struct qup_i2c_dev *dev, struct i2c_msg msgs[], int num)
+{
+    int ret;
+    int rem = num;
+    int err;
+
+    if (dev->suspended) {
+        return -EIO;
+    }
+
+    /* Set the GSBIn_QUP_APPS_CLK to 24MHz, then below figure out what speed to
+       run I2C_MASTER_CORE at. */
+    if (dev->clk_state == 0) {
+        if (dev->clk_ctl == 0) {
+            set_i2c_clk(dev);
+        }
+    }
+    /* Initialize QUP registers during first transfer */
+    if (dev->clk_ctl == 0) {
+        int fs_div;
+        int hs_div;
+        unsigned fifo_reg;
+        /* Configure the GSBI Protocol Code for i2c */
+        writel((GSBI_PROTOCOL_CODE_I2C <<
+                GSBI_CTRL_REG_PROTOCOL_CODE_S), dev->base);
+
+        fs_div = ((dev->src_clk_freq / dev->clk_freq) / 2) - 3;
+        hs_div = 3;
+        dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+        fifo_reg = readl(dev->base + QUP_IO_MODE);
+        if (fifo_reg & 0x3)
+            dev->out_blk_sz = (fifo_reg & 0x3) * 16;
+        else
+            dev->out_blk_sz = 16;
+        if (fifo_reg & 0x60)
+            dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
+        else
+            dev->in_blk_sz = 16;
+        /*
+         * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+         * associated with each byte written/received
+         */
+        dev->out_blk_sz /= 2;
+        dev->in_blk_sz /= 2;
+        dev->out_fifo_sz = dev->out_blk_sz * (2 << ((fifo_reg & 0x1C) >> 2));
+        dev->in_fifo_sz = dev->in_blk_sz * (2 << ((fifo_reg & 0x380) >> 7));
+        dprintf(CRITICAL, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
+                dev->in_blk_sz, dev->in_fifo_sz,
+                dev->out_blk_sz, dev->out_fifo_sz);
+    }
+
+    unmask_interrupt(dev->qup_irq);
+    writel(1, dev->base + QUP_SW_RESET);
+    ret = qup_i2c_poll_state(dev, QUP_RESET_STATE);
+    if (ret) {
+        dprintf(INFO, "QUP Busy:Trying to recover\n");
+        goto out_err;
+    }
+
+    /* Initialize QUP registers */
+    writel(0, dev->base + QUP_CONFIG);
+    writel(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
+    writel(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
+
+    writel(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
+
+    /* Initialize I2C mini core registers */
+    writel(0, dev->base + QUP_I2C_CLK_CTL);
+    writel(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
+
+    dev->cnt = msgs->len;
+    dev->pos = 0;
+    dev->msg = msgs;
+    while (rem) {
+        int filled = FALSE;
+
+        dev->wr_sz = dev->out_fifo_sz;
+        dev->err = 0;
+
+        if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN) != 0) {
+            ret = -EIO;
+            goto out_err;
+        }
+
+        qup_print_status(dev);
+        /* HW limits Read upto 256 bytes in 1 read without stop */
+        if (dev->msg->flags & I2C_M_RD) {
+            ret = qup_set_read_mode(dev, dev->cnt);
+            if (ret != 0)
+                goto out_err;
+        } else {
+            ret = qup_set_wr_mode(dev, rem);
+            if (ret != 0)
+                goto out_err;
+            /* Don't fill block till we get interrupt */
+            if (dev->wr_sz == dev->out_blk_sz)
+                filled = TRUE;
+        }
+
+        err = qup_update_state(dev, QUP_RUN_STATE);
+        if (err < 0) {
+            ret = err;
+            goto out_err;
+        }
+
+        qup_print_status(dev);
+        writel(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
+
+        do {
+            int idx = 0;
+            unsigned carry_over = 0;
+
+            /* Transition to PAUSE state only possible from RUN */
+            err = qup_update_state(dev, QUP_PAUSE_STATE);
+            if (err < 0) {
+                ret = err;
+                goto out_err;
+            }
+
+            qup_print_status(dev);
+            /* This operation is Write, check the next operation and decide
+               mode */
+            while (filled == FALSE) {
+                if ((msgs->flags & I2C_M_RD) && (dev->cnt == msgs->len))
+                    qup_issue_read(dev, msgs, &idx, carry_over);
+                else if (!(msgs->flags & I2C_M_RD))
+                    qup_issue_write(dev, msgs, rem, &idx, &carry_over);
+                if (idx >= (dev->wr_sz << 1))
+                    filled = TRUE;
+                /* Start new message */
+                if (filled == FALSE) {
+                    if (msgs->flags & I2C_M_RD)
+                        filled = TRUE;
+                    else if (rem > 1) {
+                        /* Only combine operations with same address */
+                        struct i2c_msg *next = msgs + 1;
+                        if (next->addr != msgs->addr || next->flags == 0)
+                            filled = TRUE;
+                        else {
+                            rem--;
+                            msgs++;
+                            dev->msg = msgs;
+                            dev->pos = 0;
+                            dev->cnt = msgs->len;
+                        }
+                    } else
+                        filled = TRUE;
+                }
+            }
+            err = qup_update_state(dev, QUP_RUN_STATE);
+            if (err < 0) {
+                ret = err;
+                goto out_err;
+            }
+            dprintf(CRITICAL, "idx:%d, rem:%d, num:%d, mode:%d\n",
+                    idx, rem, num, dev->mode);
+
+            qup_print_status(dev);
+            if (dev->err) {
+                if (dev->err & QUP_I2C_NACK_FLAG) {
+                    dprintf(CRITICAL,
+                            "I2C slave addr:0x%x not connected\n",
+                            dev->msg->addr);
+                } else {
+                    dprintf(INFO, "QUP data xfer error %d\n", dev->err);
+                }
+                ret = dev->err;
+                goto out_err;
+            }
+            if (dev->msg->flags & I2C_M_RD) {
+                int i;
+                unsigned dval = 0;
+                for (i = 0; dev->pos < dev->msg->len; i++, dev->pos++) {
+                    unsigned rd_status = readl(dev->base + QUP_OPERATIONAL);
+                    if (i % 2 == 0) {
+                        if ((rd_status & QUP_IN_NOT_EMPTY) == 0)
+                            break;
+                        dval = readl(dev->base + QUP_IN_FIFO_BASE);
+                        dev->msg->buf[dev->pos] = dval & 0xFF;
+                    } else
+                        dev->msg->buf[dev->pos] = ((dval & 0xFF0000) >> 16);
+                }
+                dev->cnt -= i;
+            } else
+                filled = FALSE; /* refill output FIFO */
+        } while (dev->cnt > 0);
+        if (dev->cnt == 0) {
+            rem--;
+            msgs++;
+            if (rem) {
+                dev->pos = 0;
+                dev->cnt = msgs->len;
+                dev->msg = msgs;
+            }
+        }
+        /* Wait for I2C bus to be idle */
+        ret = qup_i2c_poll_writeready(dev);
+        if (ret) {
+            dprintf(INFO, "Error waiting for write ready\n");
+            goto out_err;
+        }
+    }
+
+    ret = num;
+out_err:
+    dev->msg = NULL;
+    dev->pos = 0;
+    dev->err = 0;
+    dev->cnt = 0;
+    mask_interrupt(dev->qup_irq);
+    return ret;
+}
+
+static int set_gsbi_number(struct qup_i2c_dev *dev)
+{
+    switch (dev->base) {
+    case GSBI1_QUP_BASE:
+        dev->gsbi_number = 1;
+        break;
+    case GSBI2_QUP_BASE:
+        dev->gsbi_number = 2;
+        break;
+    case GSBI3_QUP_BASE:
+        dev->gsbi_number = 3;
+        break;
+    case GSBI4_QUP_BASE:
+        dev->gsbi_number = 4;
+        break;
+    case GSBI5_QUP_BASE:
+        dev->gsbi_number = 5;
+        break;
+    case GSBI6_QUP_BASE:
+        dev->gsbi_number = 6;
+        break;
+    case GSBI7_QUP_BASE:
+        dev->gsbi_number = 7;
+        break;
+    case GSBI8_QUP_BASE:
+        dev->gsbi_number = 8;
+        break;
+    case GSBI9_QUP_BASE:
+        dev->gsbi_number = 9;
+        break;
+    case GSBI10_QUP_BASE:
+        dev->gsbi_number = 10;
+        break;
+    case GSBI11_QUP_BASE:
+        dev->gsbi_number = 11;
+        break;
+    case GSBI12_QUP_BASE:
+        dev->gsbi_number = 12;
+        break;
+    default:
+        return 1;
+    }
+    return 0;
+}
+
+static int set_qup_irq(struct qup_i2c_dev *dev)
+{
+    switch (dev->base) {
+    case GSBI1_QUP_BASE:
+        dev->qup_irq = GSBI1_QUP_IRQ;
+        break;
+    case GSBI2_QUP_BASE:
+        dev->qup_irq = GSBI2_QUP_IRQ;
+        break;
+    case GSBI3_QUP_BASE:
+        dev->qup_irq = GSBI3_QUP_IRQ;
+        break;
+    case GSBI4_QUP_BASE:
+        dev->qup_irq = GSBI4_QUP_IRQ;
+        break;
+    case GSBI5_QUP_BASE:
+        dev->qup_irq = GSBI5_QUP_IRQ;
+        break;
+    case GSBI6_QUP_BASE:
+        dev->qup_irq = GSBI6_QUP_IRQ;
+        break;
+    case GSBI7_QUP_BASE:
+        dev->qup_irq = GSBI7_QUP_IRQ;
+        break;
+    case GSBI8_QUP_BASE:
+        dev->qup_irq = GSBI8_QUP_IRQ;
+        break;
+    case GSBI9_QUP_BASE:
+        dev->qup_irq = GSBI9_QUP_IRQ;
+        break;
+    case GSBI10_QUP_BASE:
+        dev->qup_irq = GSBI10_QUP_IRQ;
+        break;
+    case GSBI11_QUP_BASE:
+        dev->qup_irq = GSBI11_QUP_IRQ;
+        break;
+    case GSBI12_QUP_BASE:
+        dev->qup_irq = GSBI12_QUP_IRQ;
+        break;
+    default:
+        return 1;
+    }
+    return 0;
+}
+
+struct qup_i2c_dev *qup_i2c_init(unsigned base,
+                                 unsigned clk_freq, unsigned src_clk_freq)
+{
+    struct qup_i2c_dev *dev;
+    if (dev_addr != NULL) {
+        return dev_addr;
+    }
+
+    dev = malloc(sizeof(struct qup_i2c_dev));
+    if (!dev) {
+        return NULL;
+    }
+    dev = memset(dev, 0, sizeof(struct qup_i2c_dev));
+
+    /* This must be done for qup_i2c_interrupt to work. */
+    dev_addr = dev;
+
+    /* Initialize the GPIO for GSBIn as i2c */
+    i2c_gpio_cfg(base);
+
+    /* Configure GSBIn in i2c mode */
+    writel(GSBI_CTL_PROTOCOL_CODE_I2C, base);
+
+    /* Set the base address for GSBIn QUP The reason we add 0x80000 is to make
+       the GSBIn base address be the GSBIn QUP base address, which is what the
+       i2c driver wants. */
+    dev->base = base + 0x80000;
+
+    /* Set clk_freq and src_clk_freq for i2c. */
+    dev->clk_freq = clk_freq;
+    dev->src_clk_freq = src_clk_freq;
+
+    dev->num_irqs = 1;
+
+    dev->one_bit_t = USEC_PER_SEC / dev->clk_freq;
+    dev->clk_ctl = 0;
+
+    /* Set the IRQ number for GSBIn_BASE address */
+    if (set_qup_irq(dev)) {
+        dprintf(INFO,
+                "Could not find a valid QUP IRQ value based on GSBIn_BASE: %d\n",
+                base);
+        dprintf(INFO, "Please double check the GSBIn_BASE address.\n");
+        return NULL;
+    }
+
+    /* Set the GSBI number based on GSBIn_BASE address */
+    if (set_gsbi_number(dev)) {
+        dprintf(INFO, "Could not find a valid GSBI # based on GSBIn_BASE: %d\n",
+                base);
+        dprintf(INFO, "Please double check the GSBIn_BASE address.\n");
+        return NULL;
+    }
+
+    /* Register the GSBIn QUP IRQ */
+    register_int_handler(dev->qup_irq, qup_i2c_interrupt, 0);
+
+    /* Then disable it */
+    mask_interrupt(dev->qup_irq);
+
+    return dev;
+}
+
+int qup_i2c_deinit(struct qup_i2c_dev *dev)
+{
+    /* Disable the qup_irq */
+    mask_interrupt(dev->qup_irq);
+    /* Free the memory used for dev */
+    free(dev);
+    return 0;
+}
diff --git a/platform/msm_shared/include/i2c_qup.h b/platform/msm_shared/include/i2c_qup.h
new file mode 100644
index 0000000..2c0baa6
--- /dev/null
+++ b/platform/msm_shared/include/i2c_qup.h
@@ -0,0 +1,254 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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  __I2C_QUP__
+#define  __I2C_QUP__
+
+/**
+ * struct i2c_msg - an I2C transaction segment beginning with START
+ * @addr: Slave address, either seven or ten bits.  When this is a ten
+ *  bit address, I2C_M_TEN must be set in @flags and the adapter
+ *  must support I2C_FUNC_10BIT_ADDR.
+ * @flags: I2C_M_RD is handled by all adapters.  No other flags may be
+ *  provided unless the adapter exported the relevant I2C_FUNC_*
+ *  flags through i2c_check_functionality().
+ * @len: Number of data bytes in @buf being read from or written to the
+ *  I2C slave address.  For read transactions where I2C_M_RECV_LEN
+ *  is set, the caller guarantees that this buffer can hold up to
+ *  32 bytes in addition to the initial length byte sent by the
+ *  slave (plus, if used, the SMBus PEC); and this value will be
+ *  incremented by the number of block data bytes received.
+ * @buf: The buffer into which data is read, or from which it's written.
+ *
+ * An i2c_msg is the low level representation of one segment of an I2C
+ * transaction.  It is visible to drivers in the @i2c_transfer() procedure,
+ * to userspace from i2c-dev, and to I2C adapter drivers through the
+ * @i2c_adapter.@master_xfer() method.
+ *
+ * Except when I2C "protocol mangling" is used, all I2C adapters implement
+ * the standard rules for I2C transactions.  Each transaction begins with a
+ * START.  That is followed by the slave address, and a bit encoding read
+ * versus write.  Then follow all the data bytes, possibly including a byte
+ * with SMBus PEC.  The transfer terminates with a NAK, or when all those
+ * bytes have been transferred and ACKed.  If this is the last message in a
+ * group, it is followed by a STOP.  Otherwise it is followed by the next
+ * @i2c_msg transaction segment, beginning with a (repeated) START.
+ *
+ * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
+ * passing certain @flags may have changed those standard protocol behaviors.
+ * Those flags are only for use with broken/nonconforming slaves, and with
+ * adapters which are known to support the specific mangling options they
+ * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
+ */
+struct i2c_msg {
+    unsigned short addr;        /* slave address */
+    unsigned short flags;
+#define I2C_M_TEN           0x0010  /* this is a ten bit chip address */
+#define I2C_M_WR            0x0000  /* write data, from master to slave */
+#define I2C_M_RD            0x0001  /* read data, from slave to master */
+#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
+    unsigned short len;         /* msg length */
+    unsigned char *buf;         /* pointer to msg data */
+};
+
+struct qup_i2c_dev {
+    unsigned int base;
+    unsigned int gsbi_number;
+    int qup_irq;
+    int num_irqs;
+    struct i2c_msg *msg;
+    int pos;
+    int cnt;
+    int err;
+    int mode;
+    int clk_ctl;
+    int clk_freq;
+    int src_clk_freq;
+    int one_bit_t;
+    int out_fifo_sz;
+    int in_fifo_sz;
+    int out_blk_sz;
+    int in_blk_sz;
+    int wr_sz;
+    int suspended;
+    int clk_state;
+};
+
+/* Function Definitions */
+struct qup_i2c_dev *qup_i2c_init(unsigned base,
+                                 unsigned clk_freq, unsigned src_clk_freq);
+int qup_i2c_deinit(struct qup_i2c_dev *dev);
+int qup_i2c_xfer(struct qup_i2c_dev *dev, struct i2c_msg msgs[], int num);
+
+struct device {
+    struct device *parent;
+    const char *init_name;      /* initial name of the device */
+    void (*release) (struct device * dev);
+};
+
+/**
+ * enum irqreturn
+ * @IRQ_NONE        interrupt was not from this device
+ * @IRQ_HANDLED     interrupt was handled by this device
+ * @IRQ_WAKE_THREAD handler requests to wake the handler thread
+ */
+enum irqreturn {
+    IRQ_NONE,
+    IRQ_HANDLED,
+    IRQ_WAKE_THREAD,
+    IRQ_FAIL,
+};
+
+typedef enum irqreturn irqreturn_t;
+
+#define I2C_SMBUS_BLOCK_MAX 32
+union i2c_smbus_data {
+    unsigned char byte;
+    unsigned short word;
+    unsigned char block[I2C_SMBUS_BLOCK_MAX + 2];
+};
+
+/*
+ * i2c_adapter is the structure used to identify a physical i2c bus along
+ * with the access algorithms necessary to access it.
+ */
+struct i2c_adapter {
+    struct module *owner;
+    unsigned int id;
+    unsigned int class;         /* classes to allow probing for */
+    const struct i2c_algorithm *algo;   /* the algorithm to access the bus */
+    void *algo_data;
+    /* data fields that are valid for all devices */
+    unsigned int level;         /* nesting level for lockdep */
+    int timeout;                /* in jiffies */
+    int retries;
+    struct device dev;          /* the adapter device */
+    int nr;
+    char name[48];
+};
+
+/*
+ * The following structs are for those who like to implement new bus drivers:
+ * i2c_algorithm is the interface to a class of hardware solutions which can
+ * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
+ * to name two of the most common.
+ */
+struct i2c_algorithm {
+    /* If an adapter algorithm can't do I2C-level access, set master_xfer to
+       NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If
+       set to NULL, the SMBus protocol is simulated using common I2C messages */
+    /* master_xfer should return the number of messages successfully processed,
+       or a negative value on error */
+    int (*master_xfer) (struct i2c_adapter * adap, struct i2c_msg * msgs,
+                        int num);
+    int (*smbus_xfer) (struct i2c_adapter * adap, unsigned short addr,
+                       unsigned short flags, char read_write,
+                       unsigned char command, int size,
+                       union i2c_smbus_data * data);
+
+    /* To determine what the adapter supports */
+    unsigned int (*functionality) (struct i2c_adapter *);
+};
+
+#define EIO         5
+#define ENOMEM      12
+#define EBUSY       16
+#define ENODEV      19
+#define ENOSYS      38
+#define EPROTONOSUPPORT 93
+#define ETIMEDOUT   110
+
+#define FALSE  0
+#define TRUE   1
+
+#define USEC_PER_SEC   1000000L
+
+#define IRQF_TRIGGER_NONE   0x00000000
+#define IRQF_TRIGGER_RISING 0x00000001
+#define IRQF_TRIGGER_FALLING    0x00000002
+#define IRQF_TRIGGER_HIGH   0x00000004
+#define IRQF_TRIGGER_LOW    0x00000008
+#define IRQF_TRIGGER_MASK   (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
+        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
+#define IRQF_TRIGGER_PROBE  0x00000010
+
+/* To determine what functionality is present */
+
+#define I2C_FUNC_I2C            0x00000001
+#define I2C_FUNC_10BIT_ADDR     0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING  0x00000004  /* I2C_M_NOSTART etc. */
+#define I2C_FUNC_SMBUS_PEC      0x00000008
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL  0x00008000  /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK        0x00010000
+#define I2C_FUNC_SMBUS_READ_BYTE    0x00020000
+#define I2C_FUNC_SMBUS_WRITE_BYTE   0x00040000
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA   0x00080000
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA  0x00100000
+#define I2C_FUNC_SMBUS_READ_WORD_DATA   0x00200000
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA  0x00400000
+#define I2C_FUNC_SMBUS_PROC_CALL    0x00800000
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA  0x01000000
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK   0x04000000  /* I2C-like block xfer */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK  0x08000000  /* w/ 1-byte reg. addr. */
+
+#define I2C_FUNC_SMBUS_BYTE     (I2C_FUNC_SMBUS_READ_BYTE | \
+        I2C_FUNC_SMBUS_WRITE_BYTE)
+#define I2C_FUNC_SMBUS_BYTE_DATA    (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+        I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
+#define I2C_FUNC_SMBUS_WORD_DATA    (I2C_FUNC_SMBUS_READ_WORD_DATA | \
+        I2C_FUNC_SMBUS_WRITE_WORD_DATA)
+#define I2C_FUNC_SMBUS_BLOCK_DATA   (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+        I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
+#define I2C_FUNC_SMBUS_I2C_BLOCK    (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+        I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
+
+#define I2C_FUNC_SMBUS_EMUL     (I2C_FUNC_SMBUS_QUICK | \
+        I2C_FUNC_SMBUS_BYTE | \
+        I2C_FUNC_SMBUS_BYTE_DATA | \
+        I2C_FUNC_SMBUS_WORD_DATA | \
+        I2C_FUNC_SMBUS_PROC_CALL | \
+        I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+        I2C_FUNC_SMBUS_I2C_BLOCK | \
+        I2C_FUNC_SMBUS_PEC)
+
+/* GSBI/I2C QUP APPS CLK definitions */
+#define I2C_APPS_CLK_MD_24MHz       0x000100FB
+#define I2C_APPS_CLK_NS_24MHz       0x00FC005B
+
+#define GSBI8_HCLK_CTL_S                (4)
+#define GSBI8_HCLK_CTL_CLK_ENA          (0x1)
+
+#define GSBI_CTRL_REG_PROTOCOL_CODE_S   (4)
+#define GSBI_PROTOCOL_CODE_I2C          (0x2)
+
+#endif /* __I2C_QUP__ */
diff --git a/platform/msm_shared/proc_comm.c b/platform/msm_shared/proc_comm.c
index d341346..f20e550 100644
--- a/platform/msm_shared/proc_comm.c
+++ b/platform/msm_shared/proc_comm.c
@@ -32,7 +32,7 @@
 #include <reg.h>
 
 #include <platform/iomap.h>
-
+#ifndef PLATFORM_MSM8X60
 #define ACPU_CLK           0  /* Applications processor clock */
 #define ADM_CLK            1  /* Applications data mover clock */
 #define ADSP_CLK           2  /* ADSP clock */
@@ -353,4 +353,4 @@
     int enable = 0;
     return msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable);
 }
-
+#endif
diff --git a/platform/msm_shared/rules.mk b/platform/msm_shared/rules.mk
index 6348d5a..b1795ea 100644
--- a/platform/msm_shared/rules.mk
+++ b/platform/msm_shared/rules.mk
@@ -21,3 +21,8 @@
 	$(LOCAL_DIR)/mddi.o \
 	$(LOCAL_DIR)/mmc.o
 
+ifeq ($(PLATFORM),msm8x60)
+	OBJS += $(LOCAL_DIR)/i2c_qup.o
+endif
+
+
diff --git a/target/msm8660_surf/init.c b/target/msm8660_surf/init.c
index f1e1b94..e407974 100755
--- a/target/msm8660_surf/init.c
+++ b/target/msm8660_surf/init.c
@@ -41,7 +41,7 @@
 
 #define LINUX_MACHTYPE_8660_SURF    1009002
 #define LINUX_MACHTYPE_8660_FFA     1009003
-
+#define LINUX_MACHTYPE_8660_FLUID   1009004
 
 void keypad_init(void);
 
@@ -49,6 +49,7 @@
 int target_is_emmc_boot(void);
 void debug_led_write(char);
 char debug_led_read();
+uint32_t platform_id_read (void);
 
 void target_init(void)
 {
@@ -72,17 +73,28 @@
 
 unsigned board_machtype(void)
 {
-    /* Writing to Debug LED register and reading back to auto detect
-       SURF and FFA. If we read back, it is SURF */
-    debug_led_write(0xA5);
-
-    if((debug_led_read() & 0xFF) == 0xA5)
+    unsigned id = platform_id_read();
+    switch(id)
     {
-        debug_led_write(0);
-        return LINUX_MACHTYPE_8660_SURF;
-    }
-    else
-        return LINUX_MACHTYPE_8660_FFA;
+        case 0x1:
+            return LINUX_MACHTYPE_8660_SURF;
+        case 0x2:
+            return LINUX_MACHTYPE_8660_FFA;
+        case 0x3:
+            return LINUX_MACHTYPE_8660_FLUID;
+        default:
+            /* Writing to Debug LED register and reading back to auto detect
+            SURF and FFA. If we read back, it is SURF */
+            debug_led_write(0xA5);
+
+            if((debug_led_read() & 0xFF) == 0xA5)
+            {
+                debug_led_write(0);
+                return LINUX_MACHTYPE_8660_SURF;
+            }
+            else
+                return LINUX_MACHTYPE_8660_FFA;
+    };
 }
 
 void reboot_device(unsigned reboot_reason)