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>
This commit is contained in:
Daisuke Nojiri 2014-11-21 15:33:26 -08:00 committed by Patrick Georgi
parent 4488590fd2
commit 0594914dec
5 changed files with 1008 additions and 0 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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__