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 512 if BOARD_VARIANT_AP148
default 1024 default 1024
config DRIVER_TPM_I2C_BUS
hex
default 0x1
config DRIVER_TPM_I2C_ADDR
hex
default 0x20
endif # BOARD_GOOGLE_STORM endif # BOARD_GOOGLE_STORM

View File

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

View File

@ -27,34 +27,32 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#ifndef __GSBI_TYPES_H__ #include <soc/gpio.h>
#define __GSBI_TYPES_H__ #include <soc/gsbi.h>
#include <soc/qup.h>
typedef enum { #define GPIO_FUNC_I2C 0x1
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 { int gsbi_init_board(gsbi_id_t gsbi_id)
GSBI_SUCCESS = 0, {
GSBI_ID_ERROR, switch (gsbi_id) {
GSBI_ERROR, case GSBI_ID_4:
GSBI_UNSUPPORTED /* Configure GPIOs 13 - SCL, 12 - SDA, 2mA gpio_en */
} gsbi_return_t; 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 { return 0;
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

@ -25,6 +25,9 @@ bootblock-$(CONFIG_DRIVERS_UART) += uart.c
verstage-y += clock.c verstage-y += clock.c
verstage-y += gpio.c verstage-y += gpio.c
verstage-y += gsbi.c
verstage-y += i2c.c
verstage-y += qup.c
verstage-y += spi.c verstage-y += spi.c
verstage-y += timer.c verstage-y += timer.c
verstage-$(CONFIG_CONSOLE_SERIAL_IPQ806X) += uart.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. * Copyright (C) 2014 The Linux Foundation. All rights reserved.
* *
@ -28,8 +28,8 @@
*/ */
#include <arch/io.h> #include <arch/io.h>
#include "drivers/gpio/ipq806x.h" #include <soc/gsbi.h>
#include "ipq806x_gsbi.h" #include <soc/gpio.h>
//TODO: To be implemented as part of the iomap. //TODO: To be implemented as part of the iomap.
static int gsbi_base[] = { static int gsbi_base[] = {
@ -51,8 +51,6 @@ static int gsbi_base[] = {
#define GSBI_APPS_NS_OFFSET 0x4 #define GSBI_APPS_NS_OFFSET 0x4
#define GSBI_APPS_MAX_OFFSET 0xff #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) gsbi_return_t gsbi_init(gsbi_id_t gsbi_id, gsbi_protocol_t protocol)
{ {
unsigned i = 0; 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)); writel(0, GSBI_RESET(gsbi_id));
switch (gsbi_id) { if (gsbi_init_board(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; ret = GSBI_UNSUPPORTED;
goto bail_out; goto bail_out;
}
} }
/*Select i2c protocol*/ /*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. * Copyright (C) 2014 The Linux Foundation. All rights reserved.
* *
@ -27,43 +27,25 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <arch/io.h>
#include <assert.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" static const qup_config_t gsbi4_qup_config = {
#include "drivers/bus/i2c/i2c.h" QUP_MINICORE_I2C_MASTER,
#include "drivers/bus/i2c/ipq806x_qup.h" 100000,
#include "drivers/bus/i2c/ipq806x_gsbi.h" 24000000,
#include "drivers/bus/i2c/ipq806x.h" QUP_MODE_FIFO
};
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, 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_data_t obj;
qup_return_t qup_ret = 0; qup_return_t qup_ret = 0;
@ -100,44 +82,54 @@ static int i2c_write(uint32_t gsbi_id, uint8_t slave,
return 0; 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); static uint8_t initialized = 0;
I2cSeg *seg = segments; 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; int ret = 0;
if (!bus->initialized) if (i2c_init(bus))
if (0 != i2c_init(bus->gsbi_id)) return 1;
return 1;
while (seg_count--) { while (seg_count--) {
if (seg->read) if (seg->read)
ret = i2c_read(bus->gsbi_id, seg->chip, ret = i2c_read(bus, seg->chip, seg->buf, seg->len);
seg->buf, seg->len);
else else
ret = i2c_write(bus->gsbi_id, seg->chip, ret = i2c_write(bus, seg->chip, seg->buf, seg->len,
seg->buf, seg->len, (seg_count ? 0 : 1));
(seg_count ? 0 : 1));
seg++; seg++;
} }
if (QUP_SUCCESS != ret) { if (ret) {
qup_set_state(bus->gsbi_id, QUP_STATE_RESET); qup_set_state(bus, QUP_STATE_RESET);
return 1; return 1;
} }
return 0; 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_S 4
#define GSBI_HCLK_CTL_CLK_ENA 0x1 #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 UART2_DM_BASE 0x12490000
#define UART_GSBI2_BASE 0x12480000 #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_ #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. * Copyright (C) 2014 The Linux Foundation. All rights reserved.
* *
@ -29,7 +29,8 @@
#ifndef __QUP_H__ #ifndef __QUP_H__
#define __QUP_H__ #define __QUP_H__
#include "ipq806x_gsbi.h"
#include <soc/gsbi.h>
/* QUP block registers */ /* QUP block registers */
#define QUP_CONFIG 0x0 #define QUP_CONFIG 0x0
@ -117,6 +118,9 @@ typedef enum {
QUP_ERR_STATE_SET, QUP_ERR_STATE_SET,
QUP_ERR_TIMEOUT, QUP_ERR_TIMEOUT,
QUP_ERR_UNSUPPORTED, 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_I2C_INVALID_SLAVE_ADDR,
QUP_ERR_XFER_FAIL, QUP_ERR_XFER_FAIL,
QUP_ERR_UNDEFINED, QUP_ERR_UNDEFINED,
@ -164,7 +168,7 @@ typedef struct {
* *
* return: QUP_SUCCESS, if initialization succeeds. * 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. * 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. * Copyright (C) 2014 The Linux Foundation. All rights reserved.
* *
@ -28,292 +28,283 @@
*/ */
#include <arch/io.h> #include <arch/io.h>
#include <libpayload.h> #include <console/console.h>
#include "ipq806x_qup.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. //TODO: refactor the following array to iomap driver.
static unsigned gsbi_qup_base[] = { static unsigned gsbi_qup_base[] = {
0x12460000, /*gsbi 1*/ GSBI_QUP1_BASE,
0x124A0000, /*gsbi 2*/ GSBI_QUP2_BASE,
0x16280000, /*gsbi 3*/ GSBI_QUP3_BASE,
0x16380000, /*gsbi 4*/ GSBI_QUP4_BASE,
0x1A280000, /*gsbi 5*/ GSBI_QUP5_BASE,
0x16580000, /*gsbi 6*/ GSBI_QUP6_BASE,
0x16680000, /*gsbi 7*/ GSBI_QUP7_BASE,
}; };
#define QUP_ADDR(gsbi_num, reg) ((void *)((gsbi_qup_base[gsbi_num-1]) + (reg))) #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) 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)); uint32_t reg_val = readl(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS));
if (readl(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS))) if (readl(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS)))
ret = QUP_ERR_XFER_FAIL; return QUP_ERR_XFER_FAIL;
else if (reg_val & QUP_I2C_INVALID_READ_ADDR) if (reg_val & QUP_I2C_INVALID_READ_ADDR)
ret = QUP_ERR_I2C_INVALID_SLAVE_ADDR; return QUP_ERR_I2C_INVALID_SLAVE_ADDR;
else if (reg_val & (QUP_I2C_FAILED_MASK | if (reg_val & QUP_I2C_FAILED_MASK)
QUP_I2C_ARB_LOST | return QUP_ERR_I2C_FAILED;
QUP_I2C_BUS_ERROR)) if (reg_val & QUP_I2C_ARB_LOST)
ret = QUP_ERR_XFER_FAIL; 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) static qup_return_t qup_wait_for_state(gsbi_id_t gsbi_id, unsigned wait_for)
{ {
qup_return_t ret = QUP_ERR_STATE_SET; return check_bit_state(QUP_ADDR(gsbi_id, QUP_STATE), wait_for);
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_return_t qup_reset_i2c_master_status(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;
}
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; qup_return_t ret = QUP_ERR_UNDEFINED;
uint32_t start_ts; unsigned int count = TIMEOUT_CNT;
uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
switch (mode) { while (!(readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status)) {
case QUP_MODE_FIFO: { ret = qup_i2c_master_status(gsbi_id);
uint8_t addr = p_tx_obj->p.iic.addr; if (ret)
uint8_t *data_ptr = p_tx_obj->p.iic.data; return ret;
unsigned data_len = p_tx_obj->p.iic.data_len; if (count == 0)
unsigned idx = 0; return QUP_ERR_TIMEOUT;
count--;
}
writel(0x7C, QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS)); return QUP_SUCCESS;
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)), static qup_return_t qup_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status)
QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO)); {
qup_return_t ret = QUP_ERR_UNDEFINED;
unsigned int count = TIMEOUT_CNT;
while (data_len) { while (readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & status) {
if (data_len == 1 && stop_seq) { ret = qup_i2c_master_status(gsbi_id);
writel((QUP_I2C_STOP_SEQ | if (ret)
QUP_I2C_DATA(data_ptr[idx])), return ret;
QUP_ADDR(gsbi_id, if (count == 0)
QUP_OUTPUT_FIFO)); return QUP_ERR_TIMEOUT;
} else { count--;
writel((QUP_I2C_DATA_SEQ | }
QUP_I2C_DATA(data_ptr[idx])),
QUP_ADDR(gsbi_id, return QUP_SUCCESS;
QUP_OUTPUT_FIFO)); }
}
data_len--; static qup_return_t qup_i2c_write_fifo(gsbi_id_t gsbi_id, qup_data_t *p_tx_obj,
idx++; uint8_t stop_seq)
start_ts = timer_raw_value(); {
while (data_len && readl(QUP_ADDR(gsbi_id, qup_return_t ret = QUP_ERR_UNDEFINED;
QUP_OPERATIONAL)) & uint8_t addr = p_tx_obj->p.iic.addr;
OUTPUT_FIFO_FULL) { uint8_t *data_ptr = p_tx_obj->p.iic.data;
ret = qup_i2c_master_status(gsbi_id); unsigned data_len = p_tx_obj->p.iic.data_len;
if (QUP_SUCCESS != ret) unsigned idx = 0;
goto bailout;
if (start_ts < (timer_raw_value() - d)) { qup_reset_master_status(gsbi_id);
ret = QUP_ERR_TIMEOUT; qup_set_state(gsbi_id, QUP_STATE_RUN);
goto bailout;
} writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)),
} QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO));
/* Hardware sets the OUTPUT_SERVICE_FLAG flag to 1 when
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 OUTPUT_FIFO_NOT_EMPTY flag in the QUP_OPERATIONAL
register changes from 1 to 0, indicating that software register changes from 1 to 0, indicating that software
can write more data to the output FIFO. Software should can write more data to the output FIFO. Software should
set OUTPUT_SERVICE_FLAG to 1 to clear it to 0, which set OUTPUT_SERVICE_FLAG to 1 to clear it to 0, which
means that software knows to return to fill the output means that software knows to return to fill the output
FIFO with data. FIFO with data.
*/ */
if (readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) & if (readl(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) &
OUTPUT_SERVICE_FLAG) { OUTPUT_SERVICE_FLAG) {
writel(OUTPUT_SERVICE_FLAG, writel(OUTPUT_SERVICE_FLAG,
QUP_ADDR(gsbi_id, QUP_ADDR(gsbi_id, QUP_OPERATIONAL));
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: default:
ret = QUP_ERR_UNSUPPORTED; ret = QUP_ERR_UNSUPPORTED;
} }
bailout: if (ret) {
if (QUP_SUCCESS != ret) {
qup_set_state(gsbi_id, QUP_STATE_RESET); 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; 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, 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; qup_return_t ret = QUP_ERR_UNDEFINED;
uint32_t start_ts;
uint32_t d = MAX_DELAY_MS * timer_hz() / 1000;
switch (mode) { switch (mode) {
case QUP_MODE_FIFO: { case QUP_MODE_FIFO:
uint8_t addr = p_tx_obj->p.iic.addr; ret = qup_i2c_read_fifo(gsbi_id, p_tx_obj);
uint8_t *data_ptr = p_tx_obj->p.iic.data; break;
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: default:
ret = QUP_ERR_UNSUPPORTED; ret = QUP_ERR_UNSUPPORTED;
} }
bailout: if (ret) {
if (QUP_SUCCESS != ret) {
qup_set_state(gsbi_id, QUP_STATE_RESET); 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; 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; qup_return_t ret = QUP_ERR_UNDEFINED;
uint32_t reg_val; 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 */ /*Wait till the reset takes effect */
ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET); ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET);
if (ret)
goto bailout;
if (QUP_SUCCESS != ret) /* Reset the config */
return ret;
/*Reset the config*/
writel(0, QUP_ADDR(gsbi_id, QUP_CONFIG)); writel(0, QUP_ADDR(gsbi_id, QUP_CONFIG));
/*Program the config register*/ /*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; reg_val = 0x0F;
/*Set protocol*/ /*Set protocol*/
switch (config_ptr->protocol) { switch (config_ptr->protocol) {
case QUP_MINICORE_I2C_MASTER: { case QUP_MINICORE_I2C_MASTER:
reg_val |= ((config_ptr->protocol & reg_val |= ((config_ptr->protocol &
QUP_MINI_CORE_PROTO_MASK) << QUP_MINI_CORE_PROTO_MASK) <<
QUP_MINI_CORE_PROTO_SHFT); QUP_MINI_CORE_PROTO_SHFT);
}
break; break;
default: { default:
ret = QUP_ERR_UNSUPPORTED; ret = QUP_ERR_UNSUPPORTED;
goto bailout; goto bailout;
}
} }
writel(reg_val, QUP_ADDR(gsbi_id, QUP_CONFIG)); 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*/ /*Set QUP IO Mode*/
switch (config_ptr->mode) { switch (config_ptr->mode) {
case QUP_MODE_FIFO: { case QUP_MODE_FIFO:
reg_val = QUP_OUTPUT_BIT_SHIFT_EN | reg_val = QUP_OUTPUT_BIT_SHIFT_EN |
((config_ptr->mode & QUP_MODE_MASK) << ((config_ptr->mode & QUP_MODE_MASK) <<
QUP_OUTPUT_MODE_SHFT) | QUP_OUTPUT_MODE_SHFT) |
((config_ptr->mode & QUP_MODE_MASK) << ((config_ptr->mode & QUP_MODE_MASK) <<
QUP_INPUT_MODE_SHFT); QUP_INPUT_MODE_SHFT);
}
break; break;
default: { default:
ret = QUP_ERR_UNSUPPORTED; ret = QUP_ERR_UNSUPPORTED;
goto bailout; goto bailout;
}
} }
writel(reg_val, QUP_ADDR(gsbi_id, QUP_IO_MODES)); 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)); writel(reg_val, QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL));
bailout: bailout:
if (QUP_SUCCESS != ret) if (ret)
printf("%s() returns %s\n", __func__, get_error_string(ret)); printk(BIOS_ERR, "failed to init qup (%d)\n", ret);
return 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 * two writes of 10[binary]) are required for the
* transition to complete. * transition to complete.
*/ */
if (QUP_STATE_PAUSE == curr_state && if (QUP_STATE_PAUSE == curr_state && QUP_STATE_RESET == state) {
QUP_STATE_RESET == state) {
writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE)); writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE));
writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE)); writel(0x2, QUP_ADDR(gsbi_id, QUP_STATE));
} else { } 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); ret = qup_wait_for_state(gsbi_id, state);
} }
return ret; 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)
{ {
/* qup_return_t ret = QUP_ERR_UNDEFINED;
* Writing a one clears the status bits. uint8_t mode = (readl(QUP_ADDR(gsbi_id, QUP_IO_MODES)) >>
* Bit31-25, Bit1 and Bit0 are reserved. QUP_OUTPUT_MODE_SHFT) & QUP_MODE_MASK;
*/
//TODO: Define each status bit. OR all status bits in a single macro. ret = qup_i2c_write(gsbi_id, mode, p_tx_obj, stop_seq);
writel(0x3FFFFFC, QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS)); if (0) {
return QUP_SUCCESS; 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, 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; qup_return_t ret = QUP_ERR_UNDEFINED;
if (p_tx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >> if (p_tx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >>
QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) {
QUP_MINI_CORE_PROTO_MASK)) {
switch (p_tx_obj->protocol) { switch (p_tx_obj->protocol) {
case QUP_MINICORE_I2C_MASTER: { case QUP_MINICORE_I2C_MASTER:
uint8_t mode = (readl(QUP_ADDR(gsbi_id, ret = qup_i2c_send_data(gsbi_id, p_tx_obj, stop_seq);
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; break;
}
default: default:
ret = QUP_ERR_UNSUPPORTED; 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; return ret;
} }
qup_return_t qup_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj) qup_return_t qup_recv_data(gsbi_id_t gsbi_id, qup_data_t *p_rx_obj)
{ {
qup_return_t ret = QUP_ERR_UNDEFINED; qup_return_t ret = QUP_ERR_UNDEFINED;
if (p_rx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >> if (p_rx_obj->protocol == ((readl(QUP_ADDR(gsbi_id, QUP_CONFIG)) >>
QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_SHFT) & QUP_MINI_CORE_PROTO_MASK)) {
QUP_MINI_CORE_PROTO_MASK)) {
switch (p_rx_obj->protocol) { switch (p_rx_obj->protocol) {
case QUP_MINICORE_I2C_MASTER: { case QUP_MINICORE_I2C_MASTER:
uint8_t mode = (readl(QUP_ADDR(gsbi_id, ret = qup_i2c_recv_data(gsbi_id, p_rx_obj);
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; break;
}
default: default:
ret = QUP_ERR_UNSUPPORTED; ret = QUP_ERR_UNSUPPORTED;
} }
} }
return ret; return ret;
} }