[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)