ipq806x: add i2c driver

this change ports i2c and other relevant drivers from depthcharge for ipq806x.

BUG=chrome-os-partner:33647
BRANCH=ToT
TEST=Booted storm using vboot2

Change-Id: I3d9a431aa8adb9b91dbccdf031647dfadbafc24c
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: a0c615d0a49fd9c0ffa231353800882fff6ab90b
Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Change-Id: Id7cc3932ed4ae54f46336aaebde35e84125ebebd
Original-Reviewed-on: https://chromium-review.googlesource.com/229428
Original-Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Original-Tested-by: Vadim Bendebury <vbendeb@chromium.org>
Original-Commit-Queue: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: http://review.coreboot.org/9685
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Vadim Bendebury 2014-12-06 10:44:58 -08:00 committed by Patrick Georgi
parent fa00ae7de6
commit 6fe4e5e34c
10 changed files with 412 additions and 405 deletions

View File

@ -49,4 +49,12 @@ config DRAM_SIZE_MB
default 512 if BOARD_VARIANT_AP148
default 1024
config DRIVER_TPM_I2C_BUS
hex
default 0x1
config DRIVER_TPM_I2C_ADDR
hex
default 0x20
endif # BOARD_GOOGLE_STORM

View File

@ -22,6 +22,7 @@ bootblock-y += reset.c
verstage-y += cdp.c
verstage-y += chromeos.c
verstage-y += gsbi.c
verstage-y += memlayout.ld
verstage-y += reset.c

View File

@ -27,34 +27,32 @@
* SUCH DAMAGE.
*/
#ifndef __GSBI_TYPES_H__
#define __GSBI_TYPES_H__
#include <soc/gpio.h>
#include <soc/gsbi.h>
#include <soc/qup.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;
#define GPIO_FUNC_I2C 0x1
typedef enum {
GSBI_SUCCESS = 0,
GSBI_ID_ERROR,
GSBI_ERROR,
GSBI_UNSUPPORTED
} gsbi_return_t;
int gsbi_init_board(gsbi_id_t 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:
return 1;
}
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
return 0;
}

View File

@ -25,6 +25,9 @@ bootblock-$(CONFIG_DRIVERS_UART) += uart.c
verstage-y += clock.c
verstage-y += gpio.c
verstage-y += gsbi.c
verstage-y += i2c.c
verstage-y += qup.c
verstage-y += spi.c
verstage-y += timer.c
verstage-$(CONFIG_CONSOLE_SERIAL_IPQ806X) += uart.c

View File

@ -1,5 +1,5 @@
/*
* This file is part of the depthcharge project.
* This file is part of the coreboot project.
*
* Copyright (C) 2014 The Linux Foundation. All rights reserved.
*
@ -28,8 +28,8 @@
*/
#include <arch/io.h>
#include "drivers/gpio/ipq806x.h"
#include "ipq806x_gsbi.h"
#include <soc/gsbi.h>
#include <soc/gpio.h>
//TODO: To be implemented as part of the iomap.
static int gsbi_base[] = {
@ -51,8 +51,6 @@ static int gsbi_base[] = {
#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;
@ -76,27 +74,9 @@ gsbi_return_t gsbi_init(gsbi_id_t gsbi_id, gsbi_protocol_t protocol)
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: {
if (gsbi_init_board(gsbi_id)) {
ret = GSBI_UNSUPPORTED;
goto bail_out;
}
}
/*Select i2c protocol*/

View File

@ -1,5 +1,5 @@
/*
* This file is part of the depthcharge project.
* This file is part of the coreboot project.
*
* Copyright (C) 2014 The Linux Foundation. All rights reserved.
*
@ -27,43 +27,25 @@
* SUCH DAMAGE.
*/
#include <arch/io.h>
#include <assert.h>
#include <libpayload.h>
#include <console/console.h>
#include <delay.h>
#include <device/i2c.h>
#include <stdlib.h>
#include <string.h>
#include <soc/gsbi.h>
#include <soc/qup.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 const qup_config_t gsbi4_qup_config = {
QUP_MINICORE_I2C_MASTER,
100000,
24000000,
QUP_MODE_FIFO
};
static int i2c_read(uint32_t gsbi_id, uint8_t slave,
uint8_t *data, int data_len)
uint8_t *data, int data_len)
{
qup_data_t obj;
qup_return_t qup_ret = 0;
@ -100,44 +82,54 @@ static int i2c_write(uint32_t gsbi_id, uint8_t slave,
return 0;
}
static int i2c_transfer(struct I2cOps *me, I2cSeg *segments, int seg_count)
static int i2c_init(unsigned bus)
{
Ipq806xI2c *bus = container_of(me, Ipq806xI2c, ops);
I2cSeg *seg = segments;
static uint8_t initialized = 0;
unsigned gsbi_id = bus;
if (initialized)
return 0;
if (gsbi_init(gsbi_id, GSBI_PROTO_I2C_ONLY)) {
printk(BIOS_ERR, "failed to initialize gsbi\n");
return 1;
}
if (qup_init(gsbi_id, &gsbi4_qup_config)) {
printk(BIOS_ERR, "failed to initialize qup\n");
return 1;
}
if (qup_reset_i2c_master_status(gsbi_id)) {
printk(BIOS_ERR, "failed to reset i2c master status\n");
return 1;
}
initialized = 1;
return 0;
}
int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
{
struct i2c_seg *seg = segments;
int ret = 0;
if (!bus->initialized)
if (0 != i2c_init(bus->gsbi_id))
return 1;
if (i2c_init(bus))
return 1;
while (seg_count--) {
if (seg->read)
ret = i2c_read(bus->gsbi_id, seg->chip,
seg->buf, seg->len);
ret = i2c_read(bus, seg->chip, seg->buf, seg->len);
else
ret = i2c_write(bus->gsbi_id, seg->chip,
seg->buf, seg->len,
(seg_count ? 0 : 1));
ret = i2c_write(bus, seg->chip, seg->buf, seg->len,
(seg_count ? 0 : 1));
seg++;
}
if (QUP_SUCCESS != ret) {
qup_set_state(bus->gsbi_id, QUP_STATE_RESET);
if (ret) {
qup_set_state(bus, 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

@ -31,5 +31,33 @@
#define GSBI_HCLK_CTL_S 4
#define GSBI_HCLK_CTL_CLK_ENA 0x1
#endif
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);
int gsbi_init_board(gsbi_id_t gsbi_id);
#endif

View File

@ -107,4 +107,11 @@
#define UART2_DM_BASE 0x12490000
#define UART_GSBI2_BASE 0x12480000
#define GSBI_QUP1_BASE 0x12460000
#define GSBI_QUP2_BASE 0x124A0000
#define GSBI_QUP3_BASE 0x16280000
#define GSBI_QUP4_BASE 0x16380000
#define GSBI_QUP5_BASE 0x1A280000
#define GSBI_QUP6_BASE 0x16580000
#define GSBI_QUP7_BASE 0x16680000
#endif // __SOC_QUALCOMM_IPQ806X_IOMAP_H_

View File

@ -1,5 +1,5 @@
/*
* This file is part of the depthcharge project.
* This file is part of the coreboot project.
*
* Copyright (C) 2014 The Linux Foundation. All rights reserved.
*
@ -29,7 +29,8 @@
#ifndef __QUP_H__
#define __QUP_H__
#include "ipq806x_gsbi.h"
#include <soc/gsbi.h>
/* QUP block registers */
#define QUP_CONFIG 0x0
@ -117,6 +118,9 @@ typedef enum {
QUP_ERR_STATE_SET,
QUP_ERR_TIMEOUT,
QUP_ERR_UNSUPPORTED,
QUP_ERR_I2C_FAILED,
QUP_ERR_I2C_ARB_LOST,
QUP_ERR_I2C_BUS_ERROR,
QUP_ERR_I2C_INVALID_SLAVE_ADDR,
QUP_ERR_XFER_FAIL,
QUP_ERR_UNDEFINED,
@ -164,7 +168,7 @@ typedef struct {
*
* return: QUP_SUCCESS, if initialization succeeds.
*/
qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr);
qup_return_t qup_init(gsbi_id_t gsbi_id, const qup_config_t *config_ptr);
/*
* Set QUP state to run, pause, reset.

View File

@ -1,5 +1,5 @@
/*
* This file is part of the depthcharge project.
* This file is part of the coreboot project.
*
* Copyright (C) 2014 The Linux Foundation. All rights reserved.
*
@ -28,292 +28,283 @@
*/
#include <arch/io.h>
#include <libpayload.h>
#include "ipq806x_qup.h"
#include <console/console.h>
#include <delay.h>
#include <soc/iomap.h>
#include <stdlib.h>
#include <soc/qup.h>
#define TIMEOUT_CNT 100000
//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*/
GSBI_QUP1_BASE,
GSBI_QUP2_BASE,
GSBI_QUP3_BASE,
GSBI_QUP4_BASE,
GSBI_QUP5_BASE,
GSBI_QUP6_BASE,
GSBI_QUP7_BASE,
};
#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 QUP_ERR_XFER_FAIL;
if (reg_val & QUP_I2C_INVALID_READ_ADDR)
return QUP_ERR_I2C_INVALID_SLAVE_ADDR;
if (reg_val & QUP_I2C_FAILED_MASK)
return QUP_ERR_I2C_FAILED;
if (reg_val & QUP_I2C_ARB_LOST)
return QUP_ERR_I2C_ARB_LOST;
if (reg_val & QUP_I2C_BUS_ERROR)
return QUP_ERR_I2C_BUS_ERROR;
return ret;
return QUP_SUCCESS;
}
static int check_bit_state(uint32_t *reg, int wait_for)
{
unsigned int count = TIMEOUT_CNT;
while ((readl(reg) & (QUP_STATE_VALID_MASK | QUP_STATE_MASK)) !=
(QUP_STATE_VALID | wait_for)) {
if (count == 0)
return QUP_ERR_TIMEOUT;
count--;
udelay(1);
}
return QUP_SUCCESS;
}
/*
* Check whether GSBIn_QUP State is valid
*/
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;
return check_bit_state(QUP_ADDR(gsbi_id, QUP_STATE), wait_for);
}
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 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;
}
static qup_return_t qup_reset_master_status(gsbi_id_t gsbi_id)
{
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);
return QUP_SUCCESS;
}
static qup_return_t qup_fifo_wait_for(gsbi_id_t gsbi_id, uint32_t status)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
uint32_t start_ts;
uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
unsigned int count = TIMEOUT_CNT;
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;
while (!(readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status)) {
ret = qup_i2c_master_status(gsbi_id);
if (ret)
return ret;
if (count == 0)
return QUP_ERR_TIMEOUT;
count--;
}
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);
return QUP_SUCCESS;
}
writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)),
QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO));
static qup_return_t qup_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
unsigned int count = TIMEOUT_CNT;
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
while (readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status) {
ret = qup_i2c_master_status(gsbi_id);
if (ret)
return ret;
if (count == 0)
return QUP_ERR_TIMEOUT;
count--;
}
return QUP_SUCCESS;
}
static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
uint8_t stop_seq)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
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;
qup_reset_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++;
if (data_len) {
ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_FULL);
if (ret)
return ret;
}
/* 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));
}
*/
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;
ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY);
if (ret)
return ret;
qup_set_state(gsbi_id, QUP_STATE_PAUSE);
return QUP_SUCCESS;
}
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;
switch (mode) {
case QUP_MODE_FIFO:
ret = qup_i2c_write_fifo(gsbi_id, p_tx_obj, stop_seq);
break;
default:
ret = QUP_ERR_UNSUPPORTED;
}
bailout:
if (QUP_SUCCESS != ret) {
if (ret) {
qup_set_state(gsbi_id, QUP_STATE_RESET);
printf("%s() returns %s\n", __func__, get_error_string(ret));
printk(BIOS_ERR, "%s() failed (%d)\n", __func__, ret);
}
return ret;
}
static qup_return_t qup_i2c_read_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
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;
qup_reset_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));
ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY);
if (ret)
return ret;
writel(OUTPUT_SERVICE_FLAG, QUP_ADDR(gsbi_id, QUP_OPERATIONAL));
while (data_len) {
uint32_t data;
ret = qup_fifo_wait_for(gsbi_id, INPUT_SERVICE_FLAG);
if (ret)
return ret;
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--;
break;
} else {
/* Tag: MINACK: Invalid master input data.*/
break;
}
}
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);
return QUP_SUCCESS;
}
static qup_return_t qup_i2c_read(gsbi_id_t gsbi_id, uint8_t mode,
qup_data_t *p_tx_obj)
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;
case QUP_MODE_FIFO:
ret = qup_i2c_read_fifo(gsbi_id, p_tx_obj);
break;
default:
ret = QUP_ERR_UNSUPPORTED;
}
bailout:
if (QUP_SUCCESS != ret) {
if (ret) {
qup_set_state(gsbi_id, QUP_STATE_RESET);
printf("%s() returns %s\n", __func__, get_error_string(ret));
printk(BIOS_ERR, "%s() failed (%d)\n", __func__, ret);
}
return ret;
}
qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
qup_return_t qup_init(gsbi_id_t gsbi_id, const qup_config_t *config_ptr)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
uint32_t reg_val;
@ -323,11 +314,10 @@ qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
/*Wait till the reset takes effect */
ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET);
if (ret)
goto bailout;
if (QUP_SUCCESS != ret)
return ret;
/*Reset the config*/
/* Reset the config */
writel(0, QUP_ADDR(gsbi_id, QUP_CONFIG));
/*Program the config register*/
@ -335,16 +325,14 @@ qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
reg_val = 0x0F;
/*Set protocol*/
switch (config_ptr->protocol) {
case QUP_MINICORE_I2C_MASTER: {
case QUP_MINICORE_I2C_MASTER:
reg_val |= ((config_ptr->protocol &
QUP_MINI_CORE_PROTO_MASK) <<
QUP_MINI_CORE_PROTO_SHFT);
}
break;
default: {
default:
ret = QUP_ERR_UNSUPPORTED;
goto bailout;
}
}
writel(reg_val, QUP_ADDR(gsbi_id, QUP_CONFIG));
@ -353,18 +341,16 @@ qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
/*Set QUP IO Mode*/
switch (config_ptr->mode) {
case QUP_MODE_FIFO: {
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: {
default:
ret = QUP_ERR_UNSUPPORTED;
goto bailout;
}
}
writel(reg_val, QUP_ADDR(gsbi_id, QUP_IO_MODES));
@ -376,8 +362,8 @@ qup_return_t qup_init(gsbi_id_t gsbi_id, qup_config_t *config_ptr)
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));
if (ret)
printk(BIOS_ERR, "failed to init qup (%d)\n", ret);
return ret;
}
@ -394,8 +380,7 @@ qup_return_t qup_set_state(gsbi_id_t gsbi_id, uint32_t state)
* two writes of 10[binary]) are required for the
* transition to complete.
*/
if (QUP_STATE_PAUSE == curr_state &&
QUP_STATE_RESET == state) {
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 {
@ -403,18 +388,28 @@ qup_return_t qup_set_state(gsbi_id_t gsbi_id, uint32_t state)
}
ret = qup_wait_for_state(gsbi_id, state);
}
return ret;
}
qup_return_t qup_reset_i2c_master_status(gsbi_id_t gsbi_id)
static qup_return_t qup_i2c_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
uint8_t stop_seq)
{
/*
* 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 ret = QUP_ERR_UNDEFINED;
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;
printk(BIOS_DEBUG, "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++)
printk(BIOS_DEBUG, " %2.2x", p_tx_obj->p.iic.data[i]);
printk(BIOS_DEBUG, "\n");
}
return ret;
}
qup_return_t qup_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
@ -423,61 +418,52 @@ qup_return_t qup_send_data(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
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)) {
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");
}
case QUP_MINICORE_I2C_MASTER:
ret = qup_i2c_send_data(gsbi_id, p_tx_obj, stop_seq);
break;
}
default:
ret = QUP_ERR_UNSUPPORTED;
}
}
return ret;
}
static qup_return_t qup_i2c_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj)
{
qup_return_t ret = QUP_ERR_UNDEFINED;
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;
printk(BIOS_DEBUG, "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++)
printk(BIOS_DEBUG, " %2.2x", p_rx_obj->p.iic.data[i]);
printk(BIOS_DEBUG, "\n");
}
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)) {
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");
}
case QUP_MINICORE_I2C_MASTER:
ret = qup_i2c_recv_data(gsbi_id, p_rx_obj);
break;
}
default:
ret = QUP_ERR_UNSUPPORTED;
}
}
return ret;
}