aboutsummaryrefslogtreecommitdiff
path: root/src/soc
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2014-11-21 15:33:26 -0800
committerPatrick Georgi <pgeorgi@google.com>2015-04-13 17:36:55 +0200
commit0594914decf27fb600127f9541e756a2a2383d49 (patch)
tree2b11b8c4735e5446e6ef8bb3dccc4e394a9aa4e0 /src/soc
parent4488590fd2bd475f9a5a8354c3ebd7e70316afef (diff)
ipq806x: copy i2c, qup, and gsbi drivers from depthcharge
this is a preparation for porting these drivers to coreboot. the code will be modified by the following patches. BUG=chrome-os-partner:33647 BRANCH=ToT TEST=None Change-Id: I2baeed5b6130ace2515d6e28115f8d1008004976 Signed-off-by: Stefan Reinauer <reinauer@chromium.org> Original-Commit-Id: 7c03a186a599be9d274c6fcdea1906529cc117d7 Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Original-Change-Id: I9f3428ef02d2ba15ae63c99b10fe0605dd595313 Original-Reviewed-on: https://chromium-review.googlesource.com/231461 Original-Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Original-Commit-Queue: Vadim Bendebury <vbendeb@chromium.org> Original-Tested-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: http://review.coreboot.org/9582 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Diffstat (limited to 'src/soc')
-rw-r--r--src/soc/qualcomm/ipq806x/gsbi.c115
-rw-r--r--src/soc/qualcomm/ipq806x/gsbi.h60
-rw-r--r--src/soc/qualcomm/ipq806x/i2c.c143
-rw-r--r--src/soc/qualcomm/ipq806x/qup.c483
-rw-r--r--src/soc/qualcomm/ipq806x/qup.h207
5 files changed, 1008 insertions, 0 deletions
diff --git a/src/soc/qualcomm/ipq806x/gsbi.c b/src/soc/qualcomm/ipq806x/gsbi.c
new file mode 100644
index 0000000000..be75d9a043
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/gsbi.c
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the depthcharge project.
+ *
+ * Copyright (C) 2014 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 <arch/io.h>
+#include "drivers/gpio/ipq806x.h"
+#include "ipq806x_gsbi.h"
+
+//TODO: To be implemented as part of the iomap.
+static int gsbi_base[] = {
+ 0x12440000, /*GSBI1*/
+ 0x12480000, /*GSBI2*/
+ 0x16200000, /*GSBI3*/
+ 0x16300000, /*GSBI4*/
+ 0x1A200000, /*GSBI5*/
+ 0x16500000, /*GSBI6*/
+ 0x16600000 /*GSBI7*/
+};
+
+#define QUP_APPS_ADDR(N, os) ((void *)((0x009029C8+os)+(32*(N-1))))
+#define GSBI_HCLK_CTL(N) ((void *)(0x009029C0 + (32*(N-1))))
+#define GSBI_RESET(N) ((void *)(0x009029DC + (32*(N-1))))
+#define GSBI_CTL(N) ((void *)(gsbi_base[N-1]))
+
+#define GSBI_APPS_MD_OFFSET 0x0
+#define GSBI_APPS_NS_OFFSET 0x4
+#define GSBI_APPS_MAX_OFFSET 0xff
+
+#define GPIO_FUNC_I2C 0x1
+
+gsbi_return_t gsbi_init(gsbi_id_t gsbi_id, gsbi_protocol_t protocol)
+{
+ unsigned i = 0;
+ unsigned qup_apps_ini[] = {
+ GSBI_APPS_NS_OFFSET, 0xf80b43,
+ GSBI_APPS_NS_OFFSET, 0xfc095b,
+ GSBI_APPS_NS_OFFSET, 0xfc015b,
+ GSBI_APPS_NS_OFFSET, 0xfc005b,
+ GSBI_APPS_NS_OFFSET, 0xA05,
+ GSBI_APPS_NS_OFFSET, 0x185,
+ GSBI_APPS_MD_OFFSET, 0x100fb,
+ GSBI_APPS_NS_OFFSET, 0xA05,
+ GSBI_APPS_NS_OFFSET, 0xfc015b,
+ GSBI_APPS_NS_OFFSET, 0xfc015b,
+ GSBI_APPS_NS_OFFSET, 0xfc095b,
+ GSBI_APPS_NS_OFFSET, 0xfc0b5b,
+ GSBI_APPS_MAX_OFFSET, 0x0
+ };
+
+ gsbi_return_t ret = GSBI_SUCCESS;
+
+ writel(0, GSBI_RESET(gsbi_id));
+
+ switch (gsbi_id) {
+ case GSBI_ID_4: {
+ /* Configure GPIOs 13 - SCL, 12 - SDA, 2mA gpio_en */
+ gpio_tlmm_config_set(12, GPIO_FUNC_I2C,
+ GPIO_NO_PULL, GPIO_2MA, 1);
+ gpio_tlmm_config_set(13, GPIO_FUNC_I2C,
+ GPIO_NO_PULL, GPIO_2MA, 1);
+ }
+ break;
+ case GSBI_ID_1: {
+ /* Configure GPIOs 54 - SCL, 53 - SDA, 2mA gpio_en */
+ gpio_tlmm_config_set(54, GPIO_FUNC_I2C,
+ GPIO_NO_PULL, GPIO_2MA, 1);
+ gpio_tlmm_config_set(53, GPIO_FUNC_I2C,
+ GPIO_NO_PULL, GPIO_2MA, 1);
+ }
+ break;
+ default: {
+ ret = GSBI_UNSUPPORTED;
+ goto bail_out;
+ }
+ }
+
+ /*Select i2c protocol*/
+ writel((2 << 4), GSBI_CTL(gsbi_id));
+
+ //TODO: Make use of clock API when available instead of the hardcoding.
+ /* Clock set to 24Mhz */
+ for (i = 0; GSBI_APPS_MAX_OFFSET != qup_apps_ini[i]; i += 2)
+ writel(qup_apps_ini[i+1],
+ QUP_APPS_ADDR(gsbi_id, qup_apps_ini[i]));
+
+ writel(((1 << 6)|(1 << 4)), GSBI_HCLK_CTL(gsbi_id));
+
+bail_out:
+ return ret;
+}
diff --git a/src/soc/qualcomm/ipq806x/gsbi.h b/src/soc/qualcomm/ipq806x/gsbi.h
new file mode 100644
index 0000000000..0da7a4796e
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/gsbi.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the depthcharge project.
+ *
+ * Copyright (C) 2014 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 __GSBI_TYPES_H__
+#define __GSBI_TYPES_H__
+
+typedef enum {
+ GSBI_ID_1 = 1,
+ GSBI_ID_2,
+ GSBI_ID_3,
+ GSBI_ID_4,
+ GSBI_ID_5,
+ GSBI_ID_6,
+ GSBI_ID_7,
+} gsbi_id_t;
+
+typedef enum {
+ GSBI_SUCCESS = 0,
+ GSBI_ID_ERROR,
+ GSBI_ERROR,
+ GSBI_UNSUPPORTED
+} gsbi_return_t;
+
+typedef enum {
+ GSBI_PROTO_I2C_UIM = 1,
+ GSBI_PROTO_I2C_ONLY,
+ GSBI_PROTO_SPI_ONLY,
+ GSBI_PROTO_UART_FLOW_CTL,
+ GSBI_PROTO_UIM,
+ GSBI_PROTO_I2C_UART,
+} gsbi_protocol_t;
+
+gsbi_return_t gsbi_init(gsbi_id_t gsbi_id, gsbi_protocol_t protocol);
+#endif
diff --git a/src/soc/qualcomm/ipq806x/i2c.c b/src/soc/qualcomm/ipq806x/i2c.c
new file mode 100644
index 0000000000..9bae822a8e
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/i2c.c
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the depthcharge project.
+ *
+ * Copyright (C) 2014 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 <assert.h>
+#include <libpayload.h>
+
+#include "base/container_of.h"
+#include "drivers/bus/i2c/i2c.h"
+#include "drivers/bus/i2c/ipq806x_qup.h"
+#include "drivers/bus/i2c/ipq806x_gsbi.h"
+#include "drivers/bus/i2c/ipq806x.h"
+
+static int i2c_init(unsigned gsbi_id)
+{
+ gsbi_return_t gsbi_ret = 0;
+ qup_return_t qup_ret = 0;
+ qup_config_t gsbi4_qup_config = {
+ QUP_MINICORE_I2C_MASTER,
+ 100000,
+ 24000000,
+ QUP_MODE_FIFO
+ };
+
+ gsbi_ret = gsbi_init(gsbi_id, GSBI_PROTO_I2C_ONLY);
+ if (GSBI_SUCCESS != gsbi_ret)
+ return 1;
+
+ qup_ret = qup_init(gsbi_id, &gsbi4_qup_config);
+ if (QUP_SUCCESS != qup_ret)
+ return 1;
+
+ qup_ret = qup_reset_i2c_master_status(gsbi_id);
+ if (QUP_SUCCESS != qup_ret)
+ return 1;
+
+ return 0;
+}
+
+static int i2c_read(uint32_t gsbi_id, uint8_t slave,
+ uint8_t *data, int data_len)
+{
+ qup_data_t obj;
+ qup_return_t qup_ret = 0;
+
+ memset(&obj, 0, sizeof(obj));
+ obj.protocol = QUP_MINICORE_I2C_MASTER;
+ obj.p.iic.addr = slave;
+ obj.p.iic.data_len = data_len;
+ obj.p.iic.data = data;
+ qup_ret = qup_recv_data(gsbi_id, &obj);
+
+ if (QUP_SUCCESS != qup_ret)
+ return 1;
+ else
+ return 0;
+}
+
+static int i2c_write(uint32_t gsbi_id, uint8_t slave,
+ uint8_t *data, int data_len, uint8_t stop_seq)
+{
+ qup_data_t obj;
+ qup_return_t qup_ret = 0;
+
+ memset(&obj, 0, sizeof(obj));
+ obj.protocol = QUP_MINICORE_I2C_MASTER;
+ obj.p.iic.addr = slave;
+ obj.p.iic.data_len = data_len;
+ obj.p.iic.data = data;
+ qup_ret = qup_send_data(gsbi_id, &obj, stop_seq);
+
+ if (QUP_SUCCESS != qup_ret)
+ return 1;
+ else
+ return 0;
+}
+
+static int i2c_transfer(struct I2cOps *me, I2cSeg *segments, int seg_count)
+{
+ Ipq806xI2c *bus = container_of(me, Ipq806xI2c, ops);
+ I2cSeg *seg = segments;
+ int ret = 0;
+
+ if (!bus->initialized)
+ if (0 != i2c_init(bus->gsbi_id))
+ return 1;
+
+ while (seg_count--) {
+ if (seg->read)
+ ret = i2c_read(bus->gsbi_id, seg->chip,
+ seg->buf, seg->len);
+ else
+ ret = i2c_write(bus->gsbi_id, seg->chip,
+ seg->buf, seg->len,
+ (seg_count ? 0 : 1));
+ seg++;
+ }
+
+ if (QUP_SUCCESS != ret) {
+ qup_set_state(bus->gsbi_id, QUP_STATE_RESET);
+ return 1;
+ }
+
+ return 0;
+}
+
+Ipq806xI2c *new_ipq806x_i2c(unsigned gsbi_id)
+{
+ Ipq806xI2c *bus = 0;
+
+ if (!i2c_init(gsbi_id)) {
+ bus = xzalloc(sizeof(*bus));
+ bus->gsbi_id = gsbi_id;
+ bus->initialized = 1;
+ bus->ops.transfer = &i2c_transfer;
+ }
+ return bus;
+}
diff --git a/src/soc/qualcomm/ipq806x/qup.c b/src/soc/qualcomm/ipq806x/qup.c
new file mode 100644
index 0000000000..5ac7f07e58
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/qup.c
@@ -0,0 +1,483 @@
+/*
+ * This file is part of the depthcharge project.
+ *
+ * Copyright (C) 2014 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 <arch/io.h>
+#include <libpayload.h>
+#include "ipq806x_qup.h"
+
+//TODO: refactor the following array to iomap driver.
+static unsigned gsbi_qup_base[] = {
+ 0x12460000, /*gsbi 1*/
+ 0x124A0000, /*gsbi 2*/
+ 0x16280000, /*gsbi 3*/
+ 0x16380000, /*gsbi 4*/
+ 0x1A280000, /*gsbi 5*/
+ 0x16580000, /*gsbi 6*/
+ 0x16680000, /*gsbi 7*/
+};
+
+#define QUP_ADDR(gsbi_num, reg) ((void *)((gsbi_qup_base[gsbi_num-1]) + (reg)))
+#define MAX_DELAY_MS 100
+
+static char *get_error_string(qup_return_t error)
+{
+ char *msg;
+ switch (error) {
+ case QUP_ERR_BAD_PARAM:
+ msg = "bad parameter";
+ break;
+ case QUP_ERR_STATE_SET:
+ msg = "setting state failed";
+ break;
+ case QUP_ERR_TIMEOUT:
+ msg = "timeout";
+ break;
+ case QUP_ERR_UNSUPPORTED:
+ msg = "unsupported";
+ break;
+ case QUP_ERR_I2C_INVALID_SLAVE_ADDR:
+ msg = "invalid slave address";
+ break;
+ case QUP_ERR_XFER_FAIL:
+ msg = "transfer failed";
+ break;
+ case QUP_ERR_UNDEFINED:
+ default:
+ msg = "undefined";
+ break;
+ }
+ return msg;
+}
+
+static qup_return_t qup_i2c_master_status(gsbi_id_t gsbi_id)
+{
+ qup_return_t ret = QUP_SUCCESS;
+ uint32_t reg_val = readl(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS));
+
+ if (readl(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS)))
+ ret = QUP_ERR_XFER_FAIL;
+ else if (reg_val & QUP_I2C_INVALID_READ_ADDR)
+ ret = QUP_ERR_I2C_INVALID_SLAVE_ADDR;
+ else if (reg_val & (QUP_I2C_FAILED_MASK |
+ QUP_I2C_ARB_LOST |
+ QUP_I2C_BUS_ERROR))
+ ret = QUP_ERR_XFER_FAIL;
+
+ return ret;
+}
+
+static qup_return_t qup_wait_for_state(gsbi_id_t gsbi_id, unsigned wait_for)
+{
+ qup_return_t ret = QUP_ERR_STATE_SET;
+ uint32_t val = 0;
+ uint32_t start_ts;
+ uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
+ uint8_t final_state = 0;
+
+ start_ts = timer_raw_value();
+ do {
+ val = readl(QUP_ADDR(gsbi_id, QUP_STATE));
+ final_state = ((val & (QUP_STATE_VALID_MASK|QUP_STATE_MASK))
+ == (QUP_STATE_VALID|wait_for));
+ } while ((!final_state) && (start_ts > (timer_raw_value() - d)));
+
+ if (final_state)
+ ret = QUP_SUCCESS;
+
+ return ret;
+}
+
+static qup_return_t qup_i2c_write(gsbi_id_t gsbi_id, uint8_t mode,
+ qup_data_t *p_tx_obj, uint8_t stop_seq)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+ uint32_t start_ts;
+ uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
+
+ switch (mode) {
+ case QUP_MODE_FIFO: {
+ uint8_t addr = p_tx_obj->p.iic.addr;
+ uint8_t *data_ptr = p_tx_obj->p.iic.data;
+ unsigned data_len = p_tx_obj->p.iic.data_len;
+ unsigned idx = 0;
+
+ writel(0x7C, QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS));
+ writel(0x7C, QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS_EN));
+ qup_reset_i2c_master_status(gsbi_id);
+ qup_set_state(gsbi_id, QUP_STATE_RUN);
+
+ writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)),
+ QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO));
+
+ while (data_len) {
+ if (data_len == 1 && stop_seq) {
+ writel((QUP_I2C_STOP_SEQ |
+ QUP_I2C_DATA(data_ptr[idx])),
+ QUP_ADDR(gsbi_id,
+ QUP_OUTPUT_FIFO));
+ } else {
+ writel((QUP_I2C_DATA_SEQ |
+ QUP_I2C_DATA(data_ptr[idx])),
+ QUP_ADDR(gsbi_id,
+ QUP_OUTPUT_FIFO));
+ }
+ data_len--;
+ idx++;
+ start_ts = timer_raw_value();
+ while (data_len && readl(QUP_ADDR(gsbi_id,
+ QUP_OPERATIONAL)) &
+ OUTPUT_FIFO_FULL) {
+ ret = qup_i2c_master_status(gsbi_id);
+ if (QUP_SUCCESS != ret)
+ goto bailout;
+ if (start_ts < (timer_raw_value() - d)) {
+ ret = QUP_ERR_TIMEOUT;
+ goto bailout;
+ }
+ }
+ /* Hardware sets the OUTPUT_SERVICE_FLAG flag to 1 when
+ OUTPUT_FIFO_NOT_EMPTY flag in the QUP_OPERATIONAL
+ register changes from 1 to 0, indicating that software
+ can write more data to the output FIFO. Software should
+ set OUTPUT_SERVICE_FLAG to 1 to clear it to 0, which
+ means that software knows to return to fill the output
+ FIFO with data.
+ */
+ if (readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) &
+ OUTPUT_SERVICE_FLAG) {
+ writel(OUTPUT_SERVICE_FLAG,
+ QUP_ADDR(gsbi_id,
+ QUP_OPERATIONAL));
+ }
+ }
+
+ start_ts = timer_raw_value();
+ while (((readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL))) &
+ OUTPUT_FIFO_NOT_EMPTY)) {
+ ret = qup_i2c_master_status(gsbi_id);
+ if (QUP_SUCCESS != ret)
+ goto bailout;
+ if (start_ts < (timer_raw_value() - d)) {
+ ret = QUP_ERR_TIMEOUT;
+ goto bailout;
+ }
+ }
+
+ qup_set_state(gsbi_id, QUP_STATE_PAUSE);
+ ret = QUP_SUCCESS;
+ }
+ break;
+
+ default:
+ ret = QUP_ERR_UNSUPPORTED;
+ }
+
+bailout:
+ if (QUP_SUCCESS != ret) {
+ qup_set_state(gsbi_id, QUP_STATE_RESET);
+ printf("%s() returns %s\n", __func__, get_error_string(ret));
+ }
+
+ return ret;
+}
+
+static qup_return_t qup_i2c_read(gsbi_id_t gsbi_id, uint8_t mode,
+ qup_data_t *p_tx_obj)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+ uint32_t start_ts;
+ uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
+
+ switch (mode) {
+ case QUP_MODE_FIFO: {
+ uint8_t addr = p_tx_obj->p.iic.addr;
+ uint8_t *data_ptr = p_tx_obj->p.iic.data;
+ unsigned data_len = p_tx_obj->p.iic.data_len;
+ unsigned idx = 0;
+
+ writel(0x7C, QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS));
+ writel(0x7C, QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS_EN));
+ qup_reset_i2c_master_status(gsbi_id);
+ qup_set_state(gsbi_id, QUP_STATE_RUN);
+
+ writel((QUP_I2C_START_SEQ |
+ (QUP_I2C_ADDR(addr)|
+ QUP_I2C_SLAVE_READ)),
+ QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO));
+
+ writel((QUP_I2C_RECV_SEQ | data_len),
+ QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO));
+
+ start_ts = timer_raw_value();
+ while ((readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) &
+ OUTPUT_FIFO_NOT_EMPTY)) {
+ ret = qup_i2c_master_status(gsbi_id);
+ if (QUP_SUCCESS != ret)
+ goto bailout;
+ if (start_ts < (timer_raw_value() - d)) {
+ ret = QUP_ERR_TIMEOUT;
+ goto bailout;
+ }
+ }
+
+ writel(OUTPUT_SERVICE_FLAG,
+ QUP_ADDR(gsbi_id, QUP_OPERATIONAL));
+
+ while (data_len) {
+ unsigned data;
+ start_ts = timer_raw_value();
+ while ((!((readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL))) &
+ INPUT_SERVICE_FLAG))) {
+ ret = qup_i2c_master_status(gsbi_id);
+ if (QUP_SUCCESS != ret)
+ goto bailout;
+ if (start_ts < (timer_raw_value() - d)) {
+ ret = QUP_ERR_TIMEOUT;
+ goto bailout;
+ }
+ }
+
+ data = readl(QUP_ADDR(gsbi_id, QUP_INPUT_FIFO));
+
+ /*Process tag and corresponding data value.
+ For I2C master mini-core, data in FIFO is composed of
+ 16 bits and is divided into an 8-bit tag for the upper
+ bits and 8-bit data for the lower bits.
+ The 8-bit tag indicates whether the byte is the last
+ byte, or if a bus error happened during the receipt of
+ the byte.*/
+ if ((QUP_I2C_MI_TAG(data)) == QUP_I2C_MIDATA_SEQ) {
+ /* Tag: MIDATA = Master input data.*/
+ data_ptr[idx] = QUP_I2C_DATA(data);
+ idx++;
+ data_len--;
+ writel(INPUT_SERVICE_FLAG, QUP_ADDR(gsbi_id,
+ QUP_OPERATIONAL));
+ } else if (QUP_I2C_MI_TAG(data) ==
+ QUP_I2C_MISTOP_SEQ) {
+ /* Tag: MISTOP: Last byte of master input. */
+ data_ptr[idx] = QUP_I2C_DATA(data);
+ idx++;
+ data_len--;
+ goto recv_done;
+ } else {
+ /* Tag: MINACK: Invalid master input data.*/
+ goto recv_done;
+ }
+ }
+recv_done:
+ writel(INPUT_SERVICE_FLAG,
+ QUP_ADDR(gsbi_id, QUP_OPERATIONAL));
+ p_tx_obj->p.iic.data_len = idx;
+ qup_set_state(gsbi_id, QUP_STATE_PAUSE);
+ ret = QUP_SUCCESS;
+ }
+ break;
+
+ default:
+ ret = QUP_ERR_UNSUPPORTED;
+ }
+
+bailout:
+ if (QUP_SUCCESS != ret) {
+ qup_set_state(gsbi_id, QUP_STATE_RESET);
+ printf("%s() returns %s\n", __func__, get_error_string(ret));
+ }
+
+ return ret;
+}
+
+qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+ uint32_t reg_val;
+
+ /* Reset the QUP core.*/
+ writel(0x1, QUP_ADDR(gsbi_id, QUP_SW_RESET));
+
+ /*Wait till the reset takes effect */
+ ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET);
+
+ if (QUP_SUCCESS != ret)
+ return ret;
+
+ /*Reset the config*/
+ writel(0, QUP_ADDR(gsbi_id, QUP_CONFIG));
+
+ /*Program the config register*/
+ /*Set N value*/
+ reg_val = 0x0F;
+ /*Set protocol*/
+ switch (config_ptr->protocol) {
+ case QUP_MINICORE_I2C_MASTER: {
+ reg_val |= ((config_ptr->protocol &
+ QUP_MINI_CORE_PROTO_MASK) <<
+ QUP_MINI_CORE_PROTO_SHFT);
+ }
+ break;
+ default: {
+ ret = QUP_ERR_UNSUPPORTED;
+ goto bailout;
+ }
+ }
+ writel(reg_val, QUP_ADDR(gsbi_id, QUP_CONFIG));
+
+ /*Reset i2c clk cntl register*/
+ writel(0, QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL));
+
+ /*Set QUP IO Mode*/
+ switch (config_ptr->mode) {
+ case QUP_MODE_FIFO: {
+ reg_val = QUP_OUTPUT_BIT_SHIFT_EN |
+ ((config_ptr->mode & QUP_MODE_MASK) <<
+ QUP_OUTPUT_MODE_SHFT) |
+ ((config_ptr->mode & QUP_MODE_MASK) <<
+ QUP_INPUT_MODE_SHFT);
+ }
+ break;
+ default: {
+ ret = QUP_ERR_UNSUPPORTED;
+ goto bailout;
+ }
+ }
+ writel(reg_val, QUP_ADDR(gsbi_id, QUP_IO_MODES));
+
+ /*Set i2c clk cntl*/
+ reg_val = (QUP_DIVIDER_MIN_VAL << QUP_HS_DIVIDER_SHFT);
+ reg_val |= ((((config_ptr->src_frequency / config_ptr->clk_frequency)
+ / 2) - QUP_DIVIDER_MIN_VAL) &
+ QUP_FS_DIVIDER_MASK);
+ writel(reg_val, QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL));
+
+bailout:
+ if (QUP_SUCCESS != ret)
+ printf("%s() returns %s\n", __func__, get_error_string(ret));
+
+ return ret;
+}
+
+qup_return_t qup_set_state(gsbi_id_t gsbi_id, uint32_t state)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+ unsigned curr_state = readl(QUP_ADDR(gsbi_id, QUP_STATE));
+
+ if ((state >= QUP_STATE_RESET && state <= QUP_STATE_PAUSE)
+ && (curr_state & QUP_STATE_VALID_MASK)) {
+ /*
+ * For PAUSE_STATE to RESET_STATE transition,
+ * two writes of 10[binary]) are required for the
+ * transition to complete.
+ */
+ if (QUP_STATE_PAUSE == curr_state &&
+ QUP_STATE_RESET == state) {
+ writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE));
+ writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE));
+ } else {
+ writel(state, QUP_ADDR(gsbi_id, QUP_STATE));
+ }
+ ret = qup_wait_for_state(gsbi_id, state);
+ }
+ return ret;
+}
+
+qup_return_t qup_reset_i2c_master_status(gsbi_id_t gsbi_id)
+{
+ /*
+ * Writing a one clears the status bits.
+ * Bit31-25, Bit1 and Bit0 are reserved.
+ */
+ //TODO: Define each status bit. OR all status bits in a single macro.
+ writel(0x3FFFFFC, QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS));
+ return QUP_SUCCESS;
+}
+
+qup_return_t qup_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
+ uint8_t stop_seq)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+
+ if (p_tx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >>
+ QUP_MINI_CORE_PROTO_SHFT) &
+ QUP_MINI_CORE_PROTO_MASK)) {
+ switch (p_tx_obj->protocol) {
+ case QUP_MINICORE_I2C_MASTER: {
+ uint8_t mode = (readl(QUP_ADDR(gsbi_id,
+ QUP_IO_MODES)) >>
+ QUP_OUTPUT_MODE_SHFT) &
+ QUP_MODE_MASK;
+ ret = qup_i2c_write(gsbi_id, mode, p_tx_obj, stop_seq);
+ if (0) {
+ int i;
+ printf("i2c tx bus %d device %2.2x:",
+ gsbi_id, p_tx_obj->p.iic.addr);
+ for (i = 0; i < p_tx_obj->p.iic.data_len; i++)
+ printf(" %2.2x",
+ p_tx_obj->p.iic.data[i]);
+ printf("\n");
+ }
+ break;
+ }
+
+ default:
+ ret = QUP_ERR_UNSUPPORTED;
+ }
+ }
+ return ret;
+}
+
+qup_return_t qup_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj)
+{
+ qup_return_t ret = QUP_ERR_UNDEFINED;
+ if (p_rx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >>
+ QUP_MINI_CORE_PROTO_SHFT) &
+ QUP_MINI_CORE_PROTO_MASK)) {
+ switch (p_rx_obj->protocol) {
+ case QUP_MINICORE_I2C_MASTER: {
+ uint8_t mode = (readl(QUP_ADDR(gsbi_id,
+ QUP_IO_MODES)) >>
+ QUP_INPUT_MODE_SHFT) &
+ QUP_MODE_MASK;
+ ret = qup_i2c_read(gsbi_id, mode, p_rx_obj);
+ if (0) {
+ int i;
+ printf("i2c rxed on bus %d device %2.2x:",
+ gsbi_id, p_rx_obj->p.iic.addr);
+ for (i = 0; i < p_rx_obj->p.iic.data_len; i++)
+ printf(" %2.2x",
+ p_rx_obj->p.iic.data[i]);
+ printf("\n");
+ }
+ break;
+ }
+ default:
+ ret = QUP_ERR_UNSUPPORTED;
+ }
+ }
+ return ret;
+}
diff --git a/src/soc/qualcomm/ipq806x/qup.h b/src/soc/qualcomm/ipq806x/qup.h
new file mode 100644
index 0000000000..f848cf6413
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/qup.h
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the depthcharge project.
+ *
+ * Copyright (C) 2014 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 __QUP_H__
+#define __QUP_H__
+#include "ipq806x_gsbi.h"
+
+/* QUP block registers */
+#define QUP_CONFIG 0x0
+#define QUP_STATE 0x4
+#define QUP_IO_MODES 0x8
+#define QUP_SW_RESET 0xc
+#define QUP_TIME_OUT 0x10
+#define QUP_TIME_OUT_CURRENT 0x14
+#define QUP_OPERATIONAL 0x18
+#define QUP_ERROR_FLAGS 0x1c
+#define QUP_ERROR_FLAGS_EN 0x20
+#define QUP_TEST_CTRL 0x24
+#define QUP_MX_OUTPUT_COUNT 0x100
+#define QUP_MX_OUTPUT_CNT_CURRENT 0x104
+#define QUP_OUTPUT_DEBUG 0x108
+#define QUP_OUTPUT_FIFO_WORD_CNT 0x10c
+#define QUP_OUTPUT_FIFO 0x110
+#define QUP_MX_WRITE_COUNT 0x150
+#define QUP_WRITE_CNT_CURRENT 0x154
+#define QUP_MX_INPUT_COUNT 0x200
+#define QUP_READ_COUNT 0x208
+#define QUP_MX_READ_CNT_CURRENT 0x20c
+#define QUP_INPUT_DEBUG 0x210
+#define QUP_INPUT_FIFO_WORD_CNT 0x214
+#define QUP_INPUT_FIFO 0x218
+#define QUP_I2C_MASTER_CLK_CTL 0x400
+#define QUP_I2C_MASTER_STATUS 0x404
+
+#define OUTPUT_FIFO_FULL (1<<6)
+#define INPUT_FIFO_NOT_EMPTY (1<<5)
+#define OUTPUT_FIFO_NOT_EMPTY (1<<4)
+#define INPUT_SERVICE_FLAG (1<<9)
+#define OUTPUT_SERVICE_FLAG (1<<8)
+#define QUP_OUTPUT_BIT_SHIFT_EN (1<<16)
+
+#define QUP_MODE_MASK (0x03)
+#define QUP_OUTPUT_MODE_SHFT (10)
+#define QUP_INPUT_MODE_SHFT (12)
+
+#define QUP_FS_DIVIDER_MASK (0xFF)
+
+#define QUP_MINI_CORE_PROTO_SHFT (8)
+#define QUP_MINI_CORE_PROTO_MASK (0x0F)
+
+/* Mini-core states */
+#define QUP_STATE_RESET 0x0
+#define QUP_STATE_RUN 0x1
+#define QUP_STATE_PAUSE 0x3
+#define QUP_STATE_VALID (1<<2)
+#define QUP_STATE_MASK 0x3
+#define QUP_STATE_VALID_MASK (1<<2)
+
+/* Tags for output FIFO */
+#define QUP_I2C_1CLK_NOOP_SEQ 0x1 /*MSB 8-bit NOP, LSB 8-bits 1 clk.*/
+#define QUP_I2C_START_SEQ (0x1 << 8)
+#define QUP_I2C_DATA_SEQ (0x2 << 8)
+#define QUP_I2C_STOP_SEQ (0x3 << 8)
+#define QUP_I2C_RECV_SEQ (0x4 << 8)
+
+/* Tags for input FIFO */
+#define QUP_I2C_MIDATA_SEQ (0x5 << 8)
+#define QUP_I2C_MISTOP_SEQ (0x6 << 8)
+#define QUP_I2C_MINACK_SEQ (0x7 << 8)
+
+#define QUP_I2C_ADDR(x) ((x & 0xFF) << 1)
+#define QUP_I2C_DATA(x) (x & 0xFF)
+#define QUP_I2C_MI_TAG(x) (x & 0xFF00)
+#define QUP_I2C_SLAVE_READ (0x1)
+
+/*Bit vals for I2C_MASTER_CLK_CTL register */
+#define QUP_HS_DIVIDER_SHFT (8)
+#define QUP_DIVIDER_MIN_VAL (0x3)
+
+/* Bit masks for I2C_MASTER_STATUS register */
+#define QUP_I2C_INVALID_READ_SEQ (1 << 25)
+#define QUP_I2C_INVALID_READ_ADDR (1 << 24)
+#define QUP_I2C_INVALID_TAG (1 << 23)
+#define QUP_I2C_FAILED_MASK (0x3 << 6)
+#define QUP_I2C_ARB_LOST (1 << 4)
+#define QUP_I2C_BUS_ERROR (1 << 2)
+
+typedef enum {
+ QUP_SUCCESS = 0,
+ QUP_ERR_BAD_PARAM,
+ QUP_ERR_STATE_SET,
+ QUP_ERR_TIMEOUT,
+ QUP_ERR_UNSUPPORTED,
+ QUP_ERR_I2C_INVALID_SLAVE_ADDR,
+ QUP_ERR_XFER_FAIL,
+ QUP_ERR_UNDEFINED,
+} qup_return_t;
+
+typedef enum {
+ QUP_MINICORE_SPI = 1,
+ QUP_MINICORE_I2C_MASTER,
+ QUP_MINICORE_I2C_SLAVE
+} qup_protocol_t;
+
+typedef enum {
+ QUP_MODE_FIFO = 0,
+ QUP_MODE_BLOCK,
+ QUP_MODE_DATAMOVER,
+} qup_mode_t;
+
+typedef struct {
+ qup_protocol_t protocol;
+ unsigned clk_frequency;
+ unsigned src_frequency;
+ qup_mode_t mode;
+} qup_config_t;
+
+typedef struct {
+ qup_protocol_t protocol;
+ union {
+ struct {
+ uint8_t addr;
+ uint8_t *data;
+ unsigned data_len;
+ } iic;
+ struct {
+ void *in;
+ void *out;
+ unsigned size;
+ } spi;
+ } p;
+} qup_data_t;
+
+/*
+ * Initialize GSBI QUP block for FIFO I2C transfers.
+ * gsbi_id[IN]: GSBI for which QUP is to be initialized.
+ * config_ptr[IN]: configurations parameters for the QUP.
+ *
+ * return: QUP_SUCCESS, if initialization succeeds.
+ */
+qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr);
+
+/*
+ * Set QUP state to run, pause, reset.
+ * gsbi_id[IN]: GSBI block for which QUP state is to be set.
+ * state[IN]: New state to transition to.
+ *
+ * return: QUP_SUCCESS, if state transition succeeds.
+ */
+qup_return_t qup_set_state(gsbi_id_t gsbi_id, uint32_t state);
+
+/*
+ * Reset the status bits set during an i2c transfer.
+ * gsbi_id[IN]: GSBI block for which i2c status bits are to be cleared.
+ *
+ * return: QUP_SUCCESS, if status bits are cleared successfully.
+ */
+qup_return_t qup_reset_i2c_master_status(gsbi_id_t gsbi_id);
+
+/*
+ * Send data to the peripheral on the bus.
+ * gsbi_id[IN]: GSBI block for which data is to be sent.
+ * p_tx_obj[IN]: Data to be sent to the slave on the bus.
+ * stop_seq[IN]: When set to non-zero QUP engine sends i2c stop sequnce.
+ *
+ * return: QUP_SUCCESS, when data is sent successfully to the peripheral.
+ */
+qup_return_t qup_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
+ uint8_t stop_seq);
+
+/*
+ * Receive data from peripheral on the bus.
+ * gsbi_id[IN]: GSBI block from which data is to be received.
+ * p_tx_obj[IN]: length of data to be received, slave address.
+ * [OUT]: buffer filled with data from slave.
+ *
+ * return: QUP_SUCCESS, when data is received successfully.
+ */
+qup_return_t qup_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj);
+
+#endif //__QUP_H__