blob: f06989b8af76f2e0e61649faef5e6d57825d537c [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <console/console.h>
#include <soc/twsi.h>
#include <soc/clock.h>
#include <device/i2c.h>
#include <device/i2c_simple.h>
#include <delay.h>
#include <device/mmio.h>
#include <soc/addressmap.h>
#include <timer.h>
#define TWSI_THP 24
#define TWSI_SW_TWSI 0x1000
#define TWSI_TWSI_SW 0x1008
#define TWSI_INT 0x1010
#define TWSI_SW_TWSI_EXT 0x1018
union twsx_sw_twsi {
u64 u;
struct {
u64 data:32;
u64 eop_ia:3;
u64 ia:5;
u64 addr:10;
u64 scr:2;
u64 size:3;
u64 sovr:1;
u64 r:1;
u64 op:4;
u64 eia:1;
u64 slonly:1;
u64 v:1;
} s;
};
union twsx_sw_twsi_ext {
u64 u;
struct {
u64 data:32;
u64 ia:8;
u64 :24;
} s;
};
union twsx_int {
u64 u;
struct {
u64 st_int:1; /** TWSX_SW_TWSI register update int */
u64 ts_int:1; /** TWSX_TWSI_SW register update int */
u64 core_int:1; /** TWSI core interrupt, ignored for HLC */
u64 :5; /** Reserved */
u64 sda_ovr:1; /** SDA testing override */
u64 scl_ovr:1; /** SCL testing override */
u64 sda:1; /** SDA signal */
u64 scl:1; /** SCL signal */
u64 :52; /** Reserved */
} s;
};
enum {
TWSI_OP_WRITE = 0,
TWSI_OP_READ = 1,
};
enum {
TWSI_EOP_SLAVE_ADDR = 0,
TWSI_EOP_CLK_CTL = 3,
TWSI_SW_EOP_IA = 6,
};
enum {
TWSI_SLAVEADD = 0,
TWSI_DATA = 1,
TWSI_CTL = 2,
TWSI_CLKCTL = 3,
TWSI_STAT = 3,
TWSI_SLAVEADD_EXT = 4,
TWSI_RST = 7,
};
enum {
TWSI_CTL_AAK = (1 << 2),
TWSI_CTL_IFLG = (1 << 3),
TWSI_CTL_STP = (1 << 4),
TWSI_CTL_STA = (1 << 5),
TWSI_CTL_ENAB = (1 << 6),
TWSI_CTL_CE = (1 << 7),
};
enum {
/** Bus error */
TWSI_STAT_BUS_ERROR = 0x00,
/** Start condition transmitted */
TWSI_STAT_START = 0x08,
/** Repeat start condition transmitted */
TWSI_STAT_RSTART = 0x10,
/** Address + write bit transmitted, ACK received */
TWSI_STAT_TXADDR_ACK = 0x18,
/** Address + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR_NAK = 0x20,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_ACK = 0x28,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_NAK = 0x30,
/** Arbitration lost in address or data byte */
TWSI_STAT_TX_ARB_LOST = 0x38,
/** Address + read bit transmitted, ACK received */
TWSI_STAT_RXADDR_ACK = 0x40,
/** Address + read bit transmitted, /ACK received */
TWSI_STAT_RXADDR_NAK = 0x48,
/** Data byte received in master mode, ACK transmitted */
TWSI_STAT_RXDATA_ACK_SENT = 0x50,
/** Data byte received, NACK transmitted */
TWSI_STAT_RXDATA_NAK_SENT = 0x58,
/** Slave address received, sent ACK */
TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
/**
* Arbitration lost in address as master, slave address + write bit
* received, ACK transmitted
*/
TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
/** General call address received, ACK transmitted */
TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
/**
* Arbitration lost in address as master, general call address
* received, ACK transmitted
*/
TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
/** Data byte received after slave address received, ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
/** Data byte received after slave address received, /ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
/**
* Data byte received after general call address received, ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_ACK = 0x90,
/**
* Data byte received after general call address received, /ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_NAK = 0x98,
/** STOP or repeated START condition received in slave mode */
TWSI_STAT_STOP_MULTI_START = 0xA0,
/** Slave address + read bit received, ACK transmitted */
TWSI_STAT_SLAVE_RXADDR2_ACK = 0xA8,
/**
* Arbitration lost in address as master, slave address + read bit
* received, ACK transmitted
*/
TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xB0,
/** Data byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_ACK = 0xB8,
/** Data byte transmitted in slave mode, /ACK received */
TWSI_STAT_SLAVE_TXDATA_NAK = 0xC0,
/** Last byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xC8,
/** Second address byte + write bit transmitted, ACK received */
TWSI_STAT_TXADDR2DATA_ACK = 0xD0,
/** Second address byte + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR2DATA_NAK = 0xD8,
/** No relevant status information */
TWSI_STAT_IDLE = 0xF8
};
/**
* Returns true if we lost arbitration
*
* @param code status code
* @param final_read true if this is the final read operation
*
* @return true if arbitration has been lost, false if it hasn't been lost.
*/
static int twsi_i2c_lost_arb(u8 code, int final_read)
{
switch (code) {
/* Arbitration lost */
case TWSI_STAT_TX_ARB_LOST:
case TWSI_STAT_TX_ACK_ARB_LOST:
case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
case TWSI_STAT_RXDATA_ACK_ARB_LOST:
return -1;
/* Being addressed as slave, should back off and listen */
case TWSI_STAT_SLAVE_RXADDR_ACK:
case TWSI_STAT_RX_GEN_ADDR_ACK:
case TWSI_STAT_GEN_RXADDR_ACK:
case TWSI_STAT_GEN_RXADDR_NAK:
return -1;
/* Core busy as slave */
case TWSI_STAT_SLAVE_RXDATA_ACK:
case TWSI_STAT_SLAVE_RXDATA_NAK:
case TWSI_STAT_STOP_MULTI_START:
case TWSI_STAT_SLAVE_RXADDR2_ACK:
case TWSI_STAT_SLAVE_TXDATA_ACK:
case TWSI_STAT_SLAVE_TXDATA_NAK:
case TWSI_STAT_SLAVE_TXDATA_END_ACK:
return -1;
/* Ack allowed on pre-terminal bytes only */
case TWSI_STAT_RXDATA_ACK_SENT:
if (!final_read)
return 0;
return -1;
/* NAK allowed on terminal byte only */
case TWSI_STAT_RXDATA_NAK_SENT:
if (!final_read)
return 0;
return -1;
case TWSI_STAT_TXDATA_NAK:
case TWSI_STAT_TXADDR_NAK:
case TWSI_STAT_RXADDR_NAK:
case TWSI_STAT_TXADDR2DATA_NAK:
return -1;
}
return 0;
}
#define RST_BOOT_PNR_MUL(Val) ((Val >> 33) & 0x1F)
/**
* Writes to the MIO_TWS(0..5)_SW_TWSI register
*
* @param baseaddr Base address of i2c registers
* @param sw_twsi value to write
*
* @return 0 for success, otherwise error
*/
static u64 twsi_write_sw(void *baseaddr, union twsx_sw_twsi sw_twsi)
{
unsigned long timeout = 500000;
sw_twsi.s.r = 0;
sw_twsi.s.v = 1;
printk(BIOS_SPEW, "%s(%p, 0x%llx)\n", __func__, baseaddr, sw_twsi.u);
write64(baseaddr + TWSI_SW_TWSI, sw_twsi.u);
do {
sw_twsi.u = read64(baseaddr + TWSI_SW_TWSI);
timeout--;
} while (sw_twsi.s.v != 0 && timeout > 0);
if (sw_twsi.s.v)
printk(BIOS_ERR, "%s: timed out\n", __func__);
return sw_twsi.u;
}
/**
* Reads the MIO_TWS(0..5)_SW_TWSI register
*
* @param baseaddr Base address of i2c registers
* @param sw_twsi value for eia and op, etc. to read
*
* @return value of the register
*/
static u64 twsi_read_sw(void *baseaddr, union twsx_sw_twsi sw_twsi)
{
unsigned long timeout = 500000;
sw_twsi.s.r = 1;
sw_twsi.s.v = 1;
printk(BIOS_SPEW, "%s(%p, 0x%llx)\n", __func__, baseaddr, sw_twsi.u);
write64(baseaddr + TWSI_SW_TWSI, sw_twsi.u);
do {
sw_twsi.u = read64(baseaddr + TWSI_SW_TWSI);
timeout--;
} while (sw_twsi.s.v != 0 && timeout > 0);
if (sw_twsi.s.v)
printk(BIOS_ERR, "%s: Error writing 0x%llx\n", __func__,
sw_twsi.u);
printk(BIOS_SPEW, "%s: Returning 0x%llx\n", __func__, sw_twsi.u);
return sw_twsi.u;
}
/**
* Write control register
*
* @param baseaddr Base address for i2c registers
* @param data data to write
*/
static void twsi_write_ctl(void *baseaddr, const u8 data)
{
union twsx_sw_twsi twsi_sw;
printk(BIOS_SPEW, "%s(%p, 0x%x)\n", __func__, baseaddr, data);
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_CTL;
twsi_sw.s.data = data;
twsi_write_sw(baseaddr, twsi_sw);
}
/**
* Reads the TWSI Control Register
*
* @param[in] baseaddr Base address for i2c
*
* @return 8-bit TWSI control register
*/
static u32 twsi_read_ctl(void *baseaddr)
{
union twsx_sw_twsi sw_twsi;
sw_twsi.u = 0;
sw_twsi.s.op = TWSI_SW_EOP_IA;
sw_twsi.s.eop_ia = TWSI_CTL;
sw_twsi.u = twsi_read_sw(baseaddr, sw_twsi);
printk(BIOS_SPEW, "%s(%p): 0x%x\n", __func__, baseaddr, sw_twsi.s.data);
return sw_twsi.s.data;
}
/**
* Read i2c status register
*
* @param baseaddr Base address of i2c registers
*
* @return value of status register
*/
static u8 twsi_read_status(void *baseaddr)
{
union twsx_sw_twsi twsi_sw;
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_STAT;
return twsi_read_sw(baseaddr, twsi_sw);
}
/**
* Waits for an i2c operation to complete
*
* @param baseaddr Base address of registers
*
* @return 0 for success, 1 if timeout
*/
static int twsi_wait(void *baseaddr, struct stopwatch *timeout)
{
u8 twsi_ctl;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
do {
twsi_ctl = twsi_read_ctl(baseaddr);
twsi_ctl &= TWSI_CTL_IFLG;
} while (!twsi_ctl && !stopwatch_expired(timeout));
printk(BIOS_SPEW, " return: %u\n", !twsi_ctl);
return !twsi_ctl;
}
/**
* Sends an i2c stop condition
*
* @param baseaddr register base address
*
* @return 0 for success, -1 if error
*/
static int twsi_stop(void *baseaddr)
{
u8 stat;
twsi_write_ctl(baseaddr, TWSI_CTL_STP | TWSI_CTL_ENAB);
stat = twsi_read_status(baseaddr);
if (stat != TWSI_STAT_IDLE) {
printk(BIOS_ERR, "%s: Bad status on bus@%p\n", __func__,
baseaddr);
return -1;
}
return 0;
}
/**
* Manually clear the I2C bus and send a stop
*/
static void twsi_unblock(void *baseaddr)
{
int i;
union twsx_int int_reg;
int_reg.u = 0;
for (i = 0; i < 9; i++) {
int_reg.s.scl_ovr = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.s.scl_ovr = 1;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
}
int_reg.s.sda_ovr = 1;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.s.scl_ovr = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
int_reg.u = 0;
write64(baseaddr + TWSI_INT, int_reg.u);
udelay(5);
}
/**
* Unsticks the i2c bus
*
* @param baseaddr base address of registers
*/
static int twsi_start_unstick(void *baseaddr)
{
twsi_stop(baseaddr);
twsi_unblock(baseaddr);
return 0;
}
/**
* Sends an i2c start condition
*
* @param baseaddr base address of registers
*
* @return 0 for success, otherwise error
*/
static int twsi_start(void *baseaddr)
{
int result;
u8 stat;
struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
twsi_write_ctl(baseaddr, TWSI_CTL_STA | TWSI_CTL_ENAB);
result = twsi_wait(baseaddr, &timeout);
if (result) {
stat = twsi_read_status(baseaddr);
printk(BIOS_SPEW, "%s: result: 0x%x, status: 0x%x\n", __func__,
result, stat);
switch (stat) {
case TWSI_STAT_START:
case TWSI_STAT_RSTART:
return 0;
case TWSI_STAT_RXADDR_ACK:
default:
return twsi_start_unstick(baseaddr);
}
}
printk(BIOS_SPEW, "%s: success\n", __func__);
return 0;
}
/**
* Writes data to the i2c bus
*
* @param baseraddr register base address
* @param slave_addr address of slave to write to
* @param buffer Pointer to buffer to write
* @param length Number of bytes in buffer to write
*
* @return 0 for success, otherwise error
*/
static int twsi_write_data(void *baseaddr, const u8 slave_addr,
const u8 *buffer, const unsigned int length)
{
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, 0x%x)\n", __func__, baseaddr,
slave_addr, buffer, length);
stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: Could not start BUS transaction\n",
__func__);
return -1;
}
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
}
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = (u32)(slave_addr << 1) | TWSI_OP_WRITE;
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
printk(BIOS_SPEW, "%s: Waiting\n", __func__);
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing slave address 0x%x\n",
__func__, slave_addr);
return result;
}
result = twsi_read_status(baseaddr);
if ((result = twsi_read_status(baseaddr)) != TWSI_STAT_TXADDR_ACK) {
twsi_stop(baseaddr);
return twsi_i2c_lost_arb(result, 0);
}
while (curr < length) {
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = buffer[curr++];
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: Timed out writing data to 0x%x\n",
__func__, slave_addr);
return result;
}
}
printk(BIOS_SPEW, "%s: Stopping\n", __func__);
return twsi_stop(baseaddr);
}
/**
* Performs a read transaction on the i2c bus
*
* @param baseaddr Base address of twsi registers
* @param slave_addr i2c bus address to read from
* @param buffer buffer to read into
* @param length number of bytes to read
*
* @return 0 for success, otherwise error
*/
static int twsi_read_data(void *baseaddr, const u8 slave_addr,
u8 *buffer, const unsigned int length)
{
union twsx_sw_twsi twsi_sw;
unsigned int curr = 0;
int result;
struct stopwatch timeout;
printk(BIOS_SPEW, "%s(%p, 0x%x, %p, %u)\n", __func__, baseaddr,
slave_addr, buffer, length);
stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
result = twsi_start(baseaddr);
if (result) {
printk(BIOS_ERR, "%s: start failed\n", __func__);
return result;
}
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: wait failed\n", __func__);
return result;
}
twsi_sw.u = 0;
twsi_sw.s.op = TWSI_SW_EOP_IA;
twsi_sw.s.eop_ia = TWSI_DATA;
twsi_sw.s.data = (u32)(slave_addr << 1) | TWSI_OP_READ;
twsi_write_sw(baseaddr, twsi_sw);
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: waiting for sending addr failed\n", __func__);
return result;
}
result = twsi_read_status(baseaddr);
if (result != TWSI_STAT_RXADDR_ACK) {
twsi_stop(baseaddr);
return twsi_i2c_lost_arb(result, 0);
}
while (curr < length) {
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB |
((curr < length - 1) ? TWSI_CTL_AAK : 0));
result = twsi_wait(baseaddr, &timeout);
if (result) {
printk(BIOS_ERR, "%s: waiting for data failed\n",
__func__);
return result;
}
twsi_sw.u = twsi_read_sw(baseaddr, twsi_sw);
buffer[curr++] = twsi_sw.s.data;
}
twsi_stop(baseaddr);
return 0;
}
static int twsi_set_speed(void *baseaddr, const unsigned int speed)
{
u64 io_clock_hz;
int n_div;
int m_div;
union twsx_sw_twsi sw_twsi;
io_clock_hz = thunderx_get_io_clock();
/* Set the TWSI clock to a conservative TWSI_BUS_FREQ. Compute the
* clocks M divider based on the SCLK.
* TWSI freq = (core freq) / (20 x (M+1) x (thp+1) x 2^N)
* M = ((core freq) / (20 x (TWSI freq) x (thp+1) x 2^N)) - 1
*/
for (n_div = 0; n_div < 8; n_div++) {
m_div = io_clock_hz / (20 * speed * (TWSI_THP + 1));
m_div /= 1 << n_div;
m_div -= 1;
if (m_div < 16)
break;
}
if (m_div >= 16)
return -1;
sw_twsi.u = 0;
sw_twsi.s.v = 1;
sw_twsi.s.op = 0x6; /* See EOP field */
sw_twsi.s.r = 0; /* Select CLKCTL when R = 0 */
sw_twsi.s.eop_ia = 3; /* R=0 selects CLKCTL, R=1 selects STAT */
sw_twsi.s.data = ((m_div & 0xf) << 3) | ((n_div & 0x7) << 0);
twsi_write_sw(baseaddr, sw_twsi);
return 0;
}
int twsi_init(unsigned int bus, enum i2c_speed hz)
{
void *baseaddr = (void *)MIO_TWSx_PF_BAR0(bus);
if (!baseaddr)
return -1;
if (twsi_set_speed(baseaddr, hz) < 0)
return -1;
/* Enable TWSI, HLC disable, STOP, NAK */
twsi_write_ctl(baseaddr, TWSI_CTL_ENAB);
return 0;
}
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
int seg_count)
{
int result;
void *baseaddr = (void *)MIO_TWSx_PF_BAR0(bus);
if (!baseaddr)
return -1;
printk(BIOS_SPEW, "%s: %d messages\n", __func__, seg_count);
for (; seg_count > 0; seg_count--, segments++) {
if (segments->flags & I2C_M_RD) {
result = twsi_read_data(baseaddr, segments->slave,
segments->buf, segments->len);
} else {
result = twsi_write_data(baseaddr, segments->slave,
segments->buf, segments->len);
}
if (result) {
printk(BIOS_ERR, "%s: error transmitting data\n",
__func__);
return -1;
}
}
return 0;
}