summaryrefslogtreecommitdiff
path: root/src/soc/cavium/cn81xx/twsi.c
diff options
context:
space:
mode:
authorDavid Hendricks <dhendricks@fb.com>2017-12-01 20:49:48 -0800
committerPatrick Rudolph <siro@das-labor.org>2018-07-10 07:01:57 +0000
commit8cbd569f74d8929387730e45b0d6e993b1365c02 (patch)
treeca6414a4d81e37280887b0da0f1a6120a50f0a3a /src/soc/cavium/cn81xx/twsi.c
parent03d31427338ba59d3a354ac1beb3b0c153471768 (diff)
downloadcoreboot-8cbd569f74d8929387730e45b0d6e993b1365c02.tar.xz
cavium: Add CN81xx SoC and eval board support
This adds Cavium CN81xx SoC and SFF EVB files. Code is based off of Cavium's Octeon-TX SDK: https://github.com/Cavium-Open-Source-Distributions/OCTEON-TX-SDK BDK coreboot differences: bootblock: - Get rid of BDK header - Add Kconfig for link address - Move CAR setup code into assembly - Move unaligned memory access enable into assembly - Implement custom bootblock entry function - Add CLIB and CSIB blobs romstage: - Use minimal DRAM init only devicetree: - Convert FTD to static C file containing key value pairs Tested on CN81xx: - Boots to payload - Tested with GNU/Linux 4.16.3 - All hardware is usable (after applying additional commits) Implemented in future commits: - Vboot integration - MMU suuport - L2 Cache handling - ATF from external repo - Devicetree patching - Extended DRAM testing - UART init Not working: - Booting a payload - Booting upstream ATF TODO: - Configuration straps Change-Id: I47b4412d29203b45aee49bfa026c1d86ef7ce688 Signed-off-by: David Hendricks <dhendricks@fb.com> Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-on: https://review.coreboot.org/23037 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: David Hendricks <david.hendricks@gmail.com>
Diffstat (limited to 'src/soc/cavium/cn81xx/twsi.c')
-rw-r--r--src/soc/cavium/cn81xx/twsi.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/src/soc/cavium/cn81xx/twsi.c b/src/soc/cavium/cn81xx/twsi.c
new file mode 100644
index 0000000000..e86cac7c8d
--- /dev/null
+++ b/src/soc/cavium/cn81xx/twsi.c
@@ -0,0 +1,696 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Facebook, Inc.
+ * Copyright 2003-2017 Cavium Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 <assert.h>
+#include <delay.h>
+#include <arch/io.h>
+#include <soc/addressmap.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)
+{
+ unsigned long timeout = 500000;
+ u8 twsi_ctl;
+
+ printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
+ do {
+ twsi_ctl = twsi_read_ctl(baseaddr);
+ twsi_ctl &= TWSI_CTL_IFLG;
+ timeout--;
+ } while (!twsi_ctl && timeout > 0);
+
+ 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;
+
+ printk(BIOS_SPEW, "%s(%p)\n", __func__, baseaddr);
+ twsi_write_ctl(baseaddr, TWSI_CTL_STA | TWSI_CTL_ENAB);
+ result = twsi_wait(baseaddr);
+ 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;
+
+ printk(BIOS_SPEW, "%s(%p, 0x%x, %p, 0x%x)\n", __func__, baseaddr,
+ slave_addr, buffer, length);
+ result = twsi_start(baseaddr);
+ if (result) {
+ printk(BIOS_ERR, "%s: Could not start BUS transaction\n",
+ __func__);
+ return -1;
+ }
+
+ result = twsi_wait(baseaddr);
+ 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);
+ 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);
+ 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;
+
+ printk(BIOS_SPEW, "%s(%p, 0x%x, %p, %u)\n", __func__, baseaddr,
+ slave_addr, buffer, length);
+ result = twsi_start(baseaddr);
+ if (result) {
+ printk(BIOS_ERR, "%s: start failed\n", __func__);
+ return result;
+ }
+
+ result = twsi_wait(baseaddr);
+ 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);
+ 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);
+ 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 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;
+}