9418476524
This patch is a manual cleanup of all the rubble left by coccinelle waltzing through our code base. It's generally not very good with line breaks and sometimes even eats comments, so this patch is my best attempt at putting it all back together. Also finally remove those hated writel()-style macros from the headers. BRANCH=none BUG=chromium:444723 TEST=None (depends on next patch) Change-Id: Id572f69c420c35577701feb154faa5aaf79cd13e Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 817402a80ab77083728b55aed74b3b4202ba7f1d Original-Change-Id: I3b0dcd6fe09fc4e3b83ee491625d6dced98e3047 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/254865 Reviewed-on: http://review.coreboot.org/9837 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
475 lines
13 KiB
C
475 lines
13 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2014 - 2015 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 <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[] = {
|
|
(unsigned)GSBI_QUP1_BASE,
|
|
(unsigned)GSBI_QUP2_BASE,
|
|
(unsigned)GSBI_QUP3_BASE,
|
|
(unsigned)GSBI_QUP4_BASE,
|
|
(unsigned)GSBI_QUP5_BASE,
|
|
(unsigned)GSBI_QUP6_BASE,
|
|
(unsigned)GSBI_QUP7_BASE,
|
|
};
|
|
|
|
#define QUP_ADDR(gsbi_num, reg) ((void *)((gsbi_qup_base[gsbi_num-1]) + (reg)))
|
|
|
|
static qup_return_t qup_i2c_master_status(gsbi_id_t gsbi_id)
|
|
{
|
|
uint32_t reg_val = read32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS));
|
|
|
|
if (read32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS)))
|
|
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;
|
|
if (reg_val & QUP_I2C_INVALID_WRITE)
|
|
return QUP_ERR_I2C_INVALID_WRITE;
|
|
if (reg_val & QUP_I2C_PACKET_NACK)
|
|
return QUP_ERR_I2C_NACK;
|
|
if (reg_val & QUP_I2C_INVALID_TAG)
|
|
return QUP_ERR_I2C_INVALID_TAG;
|
|
|
|
return QUP_SUCCESS;
|
|
}
|
|
|
|
static int check_bit_state(uint32_t *reg, int wait_for)
|
|
{
|
|
unsigned int count = TIMEOUT_CNT;
|
|
|
|
while ((read32(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)
|
|
{
|
|
return check_bit_state(QUP_ADDR(gsbi_id, QUP_STATE), wait_for);
|
|
}
|
|
|
|
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.
|
|
write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_STATUS), 0x3FFFFFC);
|
|
return QUP_SUCCESS;
|
|
}
|
|
|
|
static qup_return_t qup_reset_master_status(gsbi_id_t gsbi_id)
|
|
{
|
|
write32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS), 0x7C);
|
|
write32(QUP_ADDR(gsbi_id, QUP_ERROR_FLAGS_EN), 0x7C);
|
|
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;
|
|
unsigned int count = TIMEOUT_CNT;
|
|
|
|
while (!(read32(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_fifo_wait_while(gsbi_id_t gsbi_id, uint32_t status)
|
|
{
|
|
qup_return_t ret = QUP_ERR_UNDEFINED;
|
|
unsigned int count = TIMEOUT_CNT;
|
|
|
|
while (read32(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);
|
|
|
|
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
|
|
(QUP_I2C_START_SEQ | QUP_I2C_ADDR(addr)));
|
|
|
|
while (data_len) {
|
|
if (data_len == 1 && stop_seq) {
|
|
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
|
|
QUP_I2C_STOP_SEQ | QUP_I2C_DATA(data_ptr[idx]));
|
|
} else {
|
|
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
|
|
QUP_I2C_DATA_SEQ | QUP_I2C_DATA(data_ptr[idx]));
|
|
}
|
|
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 (read32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL)) &
|
|
OUTPUT_SERVICE_FLAG) {
|
|
write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL),
|
|
OUTPUT_SERVICE_FLAG);
|
|
}
|
|
}
|
|
|
|
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_i2c_master_status(gsbi_id);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (ret) {
|
|
qup_set_state(gsbi_id, QUP_STATE_RESET);
|
|
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);
|
|
|
|
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
|
|
QUP_I2C_START_SEQ | (QUP_I2C_ADDR(addr) | QUP_I2C_SLAVE_READ));
|
|
|
|
write32(QUP_ADDR(gsbi_id, QUP_OUTPUT_FIFO),
|
|
QUP_I2C_RECV_SEQ | data_len);
|
|
|
|
ret = qup_fifo_wait_while(gsbi_id, OUTPUT_FIFO_NOT_EMPTY);
|
|
if (ret)
|
|
return ret;
|
|
|
|
write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), OUTPUT_SERVICE_FLAG);
|
|
|
|
while (data_len) {
|
|
uint32_t data;
|
|
|
|
ret = qup_fifo_wait_for(gsbi_id, INPUT_SERVICE_FLAG);
|
|
if (ret)
|
|
return ret;
|
|
|
|
data = read32(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--;
|
|
write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL),
|
|
INPUT_SERVICE_FLAG);
|
|
} 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;
|
|
}
|
|
}
|
|
|
|
write32(QUP_ADDR(gsbi_id, QUP_OPERATIONAL), INPUT_SERVICE_FLAG);
|
|
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_return_t ret = QUP_ERR_UNDEFINED;
|
|
|
|
switch (mode) {
|
|
case QUP_MODE_FIFO:
|
|
ret = qup_i2c_read_fifo(gsbi_id, p_tx_obj);
|
|
break;
|
|
default:
|
|
ret = QUP_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
if (ret) {
|
|
qup_set_state(gsbi_id, QUP_STATE_RESET);
|
|
printk(BIOS_ERR, "%s() failed (%d)\n", __func__, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Reset the QUP core.*/
|
|
write32(QUP_ADDR(gsbi_id, QUP_SW_RESET), 0x1);
|
|
|
|
/*Wait till the reset takes effect */
|
|
ret = qup_wait_for_state(gsbi_id, QUP_STATE_RESET);
|
|
if (ret)
|
|
goto bailout;
|
|
|
|
/* Reset the config */
|
|
write32(QUP_ADDR(gsbi_id, QUP_CONFIG), 0);
|
|
|
|
/*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;
|
|
}
|
|
write32(QUP_ADDR(gsbi_id, QUP_CONFIG), reg_val);
|
|
|
|
/*Reset i2c clk cntl register*/
|
|
write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL), 0);
|
|
|
|
/*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;
|
|
}
|
|
write32(QUP_ADDR(gsbi_id, QUP_IO_MODES), reg_val);
|
|
|
|
/*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);
|
|
write32(QUP_ADDR(gsbi_id, QUP_I2C_MASTER_CLK_CTL), reg_val);
|
|
|
|
bailout:
|
|
if (ret)
|
|
printk(BIOS_ERR, "failed to init qup (%d)\n", 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 = read32(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) {
|
|
write32(QUP_ADDR(gsbi_id, QUP_STATE), 0x2);
|
|
write32(QUP_ADDR(gsbi_id, QUP_STATE), 0x2);
|
|
} else {
|
|
write32(QUP_ADDR(gsbi_id, QUP_STATE), state);
|
|
}
|
|
ret = qup_wait_for_state(gsbi_id, state);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
uint8_t mode = (read32(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,
|
|
uint8_t stop_seq)
|
|
{
|
|
qup_return_t ret = QUP_ERR_UNDEFINED;
|
|
|
|
if (p_tx_obj->protocol == ((read32(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:
|
|
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 = (read32(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 == ((read32(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:
|
|
ret = qup_i2c_recv_data(gsbi_id, p_rx_obj);
|
|
break;
|
|
default:
|
|
ret = QUP_ERR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|