[coreboot-gerrit] Change in coreboot[master]: soc/cavium: Add twsi

Patrick Rudolph (Code Review) gerrit at coreboot.org
Wed Feb 14 15:28:59 CET 2018


Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/23751


Change subject: soc/cavium: Add twsi
......................................................................

soc/cavium: Add twsi

Add TWSI.
Ported from BDK uboot tree.

Change-Id: I9f85d024b3ffd0a0c4376a8880d14db9e3570d79
Signed-off-by: Patrick Rudolph <patrick.rudolph at 9elements.com>
---
M src/soc/cavium/cn81xx/Makefile.inc
A src/soc/cavium/cn81xx/twsi.c
M src/soc/cavium/common/Makefile.inc
D src/soc/cavium/common/i2c.c
A src/soc/cavium/common/include/soc/twsi.h
A src/soc/cavium/common/twsi.c
6 files changed, 764 insertions(+), 331 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/51/23751/1

diff --git a/src/soc/cavium/cn81xx/Makefile.inc b/src/soc/cavium/cn81xx/Makefile.inc
index d2bd94b..fbba930 100644
--- a/src/soc/cavium/cn81xx/Makefile.inc
+++ b/src/soc/cavium/cn81xx/Makefile.inc
@@ -28,12 +28,15 @@
 bootblock-y += bootblock.c
 bootblock-y += clock.c
 #bootblock-y += gpio.c
+bootblock-y += twsi.c
 bootblock-y += l2c.c
 bootblock-y += mmu_operations.c
 #bootblock-y += sdram.c
 bootblock-y += timer.c
 bootblock-y += ../common/wdt.c
 
+romstage-y += twsi.c
+
 romstage-y += sdram.c
 romstage-y += ../common/bdk/libdram/libdram.c
 romstage-y += ../common/bdk/libbdk-arch/bdk-csr.c
@@ -98,6 +101,7 @@
 ramstage-y += ../common/cbmem.c
 ramstage-y += sdram.c
 ramstage-y += spi.c
+ramstage-y += twsi.c
 ramstage-$(CONFIG_DRIVERS_UART) += uart.c
 ##ramstage-y += clock.c
 #ramstage-y += ../common/gpio.c
diff --git a/src/soc/cavium/cn81xx/twsi.c b/src/soc/cavium/cn81xx/twsi.c
new file mode 100644
index 0000000..a87de79
--- /dev/null
+++ b/src/soc/cavium/cn81xx/twsi.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present  Facebook, Inc.
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <stddef.h>
+#include <assert.h>
+#include <soc/twsi.h>
+
+static void *const twsi_bus[] = {
+	(void *const)0x87E0D0000000ULL,
+	(void *const)0x87E0D1000000ULL,
+};
+
+void *twsi_get_baseaddr(const size_t bus)
+{
+	assert (bus < ARRAY_SIZE(twsi_bus));
+	if (bus >= ARRAY_SIZE(twsi_bus))
+		return NULL;
+	return twsi_bus[bus];
+}
diff --git a/src/soc/cavium/common/Makefile.inc b/src/soc/cavium/common/Makefile.inc
index 2ed9a5e..6777dfc 100644
--- a/src/soc/cavium/common/Makefile.inc
+++ b/src/soc/cavium/common/Makefile.inc
@@ -16,5 +16,15 @@
 ifeq ($(CONFIG_SOC_CAVIUM_COMMON),y)
 
 bootblock-$(CONFIG_BOOTBLOCK_CUSTOM) += bootblock.c
+bootblock-y += twsi.c
+
+
+
+romstage-y += twsi.c
+
+
+ramstage-y += twsi.c
+
+CPPFLAGS_common += -Isrc/soc/cavium/common/include
 
 endif
diff --git a/src/soc/cavium/common/i2c.c b/src/soc/cavium/common/i2c.c
deleted file mode 100644
index 6c1bdfa..0000000
--- a/src/soc/cavium/common/i2c.c
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * This file is part of the coreboot project.
- *
- * Copyright 2017-present Facebook, 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.
- */
-
-/* FIXME: scaffolding based on rk3399 */
-
-#include <arch/io.h>
-#include <assert.h>
-#include <cbfs.h>
-#include <console/console.h>
-#include <delay.h>
-//#include <device/i2c_simple.h>
-#include <soc/addressmap.h>
-//#include <soc/grf.h>
-#include <soc/soc.h>
-#include <soc/i2c.h>
-//#include <soc/clock.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define RETRY_COUNT	3
-/* 100000us = 100ms */
-#define I2C_TIMEOUT_US	100000
-#define I2C_BUS_MAX	6
-#define I2C_NOACK	2
-#define I2C_TIMEOUT	3
-
-#define i2c_info(x...) do {if (0) printk(BIOS_DEBUG, x); } while (0)
-
-#if 0
-struct rk_i2c_regs {
-	u32 i2c_con;
-	u32 i2c_clkdiv;
-	u32 i2c_mrxaddr;
-	u32 i2c_mrxraddr;
-	u32 i2c_mtxcnt;
-	u32 i2c_mrxcnt;
-	u32 i2c_ien;
-	u32 i2c_ipd;
-	u32 i2c_fcnt;
-	u32 reserved0[(0x100 - 0x24) / 4];
-	u32 txdata[8];
-	u32 reserved1[(0x200 - 0x120) / 4];
-	u32 rxdata[8];
-};
-
-static const uintptr_t i2c_bus[] = IC_BASES;
-
-/* Con register bits. */
-#define I2C_ACT2NAK			(1<<6)
-#define I2C_NAK				(1<<5)
-#define I2C_STOP				(1<<4)
-#define I2C_START			(1<<3)
-#define I2C_MODE_TX			(0<<1)
-#define I2C_MODE_TRX		(1<<1)
-#define I2C_MODE_RX			(2<<1)
-#define I2C_EN				(1<<0)
-
-#define I2C_8BIT	(1<<24)
-#define I2C_16BIT	(3<<24)
-#define I2C_24BIT	(7<<24)
-
-/* Mtxcnt register bits. */
-#define I2C_CNT(cnt)		((cnt) & 0x3F)
-
-#define I2C_NAKRCVI	(1<<6)
-#define I2C_STOPI	(1<<5)
-#define I2C_STARTI	(1<<4)
-#define I2C_MBRFI	(1<<3)
-#define I2C_MBTFI	(1<<2)
-#define I2C_BRFI	(1<<1)
-#define I2C_BTFI	(1<<0)
-#define I2C_CLEANI	0x7F
-#endif
-
-static int i2c_send_start(struct cavium_i2c_regs *reg_addr)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int res = 0;
-	int timeout = I2C_TIMEOUT_US;
-
-	i2c_info("I2c Start::Send Start bit\n");
-	write32(&reg_addr->i2c_ipd, I2C_CLEANI);
-	write32(&reg_addr->i2c_con, I2C_EN | I2C_START);
-	while (timeout--) {
-		if (read32(&reg_addr->i2c_ipd) & I2C_STARTI)
-			break;
-		udelay(1);
-	}
-
-	if (timeout <= 0) {
-		printk(BIOS_ERR, "I2C Start::Send Start Bit Timeout\n");
-		res = I2C_TIMEOUT;
-	}
-
-	return res;
-#endif
-}
-
-static int i2c_send_stop(struct cavium_i2c_regs *reg_addr)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int res = 0;
-	int timeout = I2C_TIMEOUT_US;
-
-	i2c_info("I2c Stop::Send Stop bit\n");
-	write32(&reg_addr->i2c_ipd, I2C_CLEANI);
-	write32(&reg_addr->i2c_con, I2C_EN | I2C_STOP);
-	while (timeout--) {
-		if (read32(&reg_addr->i2c_ipd) & I2C_STOPI)
-			break;
-		udelay(1);
-	}
-	write32(&reg_addr->i2c_con, 0);
-	if (timeout <= 0) {
-		printk(BIOS_ERR, "I2C Stop::Send Stop Bit Timeout\n");
-		res = I2C_TIMEOUT;
-	}
-
-	return res;
-#endif
-}
-
-static int i2c_read(struct cavium_i2c_regs *reg_addr, struct i2c_msg segment)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int res = 0;
-	uint8_t *data = segment.buf;
-	int timeout = I2C_TIMEOUT_US;
-	unsigned int bytes_remaining = segment.len;
-	unsigned int bytes_transferred = 0;
-	unsigned int words_transferred = 0;
-	unsigned int rxdata = 0;
-	unsigned int con = 0;
-	unsigned int i, j;
-
-	write32(&reg_addr->i2c_mrxaddr, I2C_8BIT | segment.slave << 1 | 1);
-	write32(&reg_addr->i2c_mrxraddr, 0);
-	con = I2C_MODE_TRX | I2C_EN | I2C_ACT2NAK;
-	while (bytes_remaining) {
-		bytes_transferred = MIN(bytes_remaining, 32);
-		bytes_remaining -= bytes_transferred;
-		if (!bytes_remaining)
-			con |= I2C_EN | I2C_NAK;
-		words_transferred = ALIGN_UP(bytes_transferred, 4) / 4;
-
-		write32(&reg_addr->i2c_ipd, I2C_CLEANI);
-		write32(&reg_addr->i2c_con, con);
-		write32(&reg_addr->i2c_mrxcnt, bytes_transferred);
-
-		timeout = I2C_TIMEOUT_US;
-		while (timeout--) {
-			if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
-				write32(&reg_addr->i2c_mrxcnt, 0);
-				write32(&reg_addr->i2c_con, 0);
-				return I2C_NOACK;
-			}
-			if (read32(&reg_addr->i2c_ipd) & I2C_MBRFI)
-				break;
-			udelay(1);
-		}
-		if (timeout <= 0) {
-			printk(BIOS_ERR, "I2C Read::Recv Data Timeout\n");
-			write32(&reg_addr->i2c_mrxcnt, 0);
-			write32(&reg_addr->i2c_con, 0);
-			return I2C_TIMEOUT;
-		}
-
-		for (i = 0; i < words_transferred; i++) {
-			rxdata = read32(&reg_addr->rxdata[i]);
-			i2c_info("I2c Read::RXDATA[%d] = 0x%x\n", i, rxdata);
-			for (j = 0; j < 4; j++) {
-				if ((i * 4 + j) == bytes_transferred)
-					break;
-				*data++ = (rxdata >> (j * 8)) & 0xff;
-			}
-		}
-		con = I2C_MODE_RX | I2C_EN | I2C_ACT2NAK;
-	}
-	return res;
-#endif
-}
-
-static int i2c_write(struct rk_i2c_regs *reg_addr, struct i2c_msg segment)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int res = 0;
-	uint8_t *data = segment.buf;
-	int timeout = I2C_TIMEOUT_US;
-	int bytes_remaining = segment.len + 1;
-	int bytes_transferred = 0;
-	int words_transferred = 0;
-	unsigned int i;
-	unsigned int j = 1;
-	u32 txdata = 0;
-
-	txdata |= (segment.slave << 1);
-	while (bytes_remaining) {
-		bytes_transferred = MIN(bytes_remaining, 32);
-		words_transferred = ALIGN_UP(bytes_transferred, 4) / 4;
-		for (i = 0; i < words_transferred; i++) {
-			do {
-				if ((i * 4 + j) == bytes_transferred)
-					break;
-				txdata |= (*data++) << (j * 8);
-			} while (++j < 4);
-			write32(&reg_addr->txdata[i], txdata);
-			j = 0;
-			i2c_info("I2c Write::TXDATA[%d] = 0x%x\n", i, txdata);
-			txdata = 0;
-		}
-
-		write32(&reg_addr->i2c_ipd, I2C_CLEANI);
-		write32(&reg_addr->i2c_con,
-			I2C_EN | I2C_MODE_TX | I2C_ACT2NAK);
-		write32(&reg_addr->i2c_mtxcnt, bytes_transferred);
-
-		timeout = I2C_TIMEOUT_US;
-		while (timeout--) {
-			if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
-				write32(&reg_addr->i2c_mtxcnt, 0);
-				write32(&reg_addr->i2c_con, 0);
-				return I2C_NOACK;
-			}
-			if (read32(&reg_addr->i2c_ipd) & I2C_MBTFI)
-				break;
-			udelay(1);
-		}
-
-		if (timeout <= 0) {
-			printk(BIOS_ERR, "I2C Write::Send Data Timeout\n");
-			write32(&reg_addr->i2c_mtxcnt, 0);
-			write32(&reg_addr->i2c_con, 0);
-			return I2C_TIMEOUT;
-		}
-
-		bytes_remaining -= bytes_transferred;
-	}
-	return res;
-#endif
-}
-
-static int i2c_do_xfer(void *reg_addr, struct i2c_msg segment)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int res = 0;
-
-	if (i2c_send_start(reg_addr))
-		return I2C_TIMEOUT;
-	if (segment.flags & I2C_M_RD)
-		res = i2c_read(reg_addr, segment);
-	else
-		res = i2c_write(reg_addr, segment);
-	return i2c_send_stop(reg_addr) || res;
-#endif
-}
-
-int platform_i2c_transfer(unsigned bus, struct i2c_msg *segments,
-			  int seg_count)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	int i;
-	int res = 0;
-	struct rk_i2c_regs *regs = (struct rk_i2c_regs *)(i2c_bus[bus]);
-	struct i2c_msg *seg = segments;
-
-	for (i = 0; i < seg_count; i++, seg++) {
-		res = i2c_do_xfer(regs, *seg);
-		if (res)
-			break;
-	}
-	return res;
-#endif
-}
-
-void i2c_init(unsigned int bus, unsigned int hz)
-{
-	/* FIXME: stub */
-	printk(BIOS_ERR, "%s: implement this.\n", __func__);
-	return -1;
-#if 0
-	unsigned int clk_div;
-	unsigned int divl;
-	unsigned int divh;
-	unsigned int i2c_src_clk;
-	unsigned int i2c_clk;
-	struct rk_i2c_regs *regs = (struct rk_i2c_regs *)(i2c_bus[bus]);
-
-	i2c_src_clk = rkclk_i2c_clock_for_bus(bus);
-
-	/* SCL Divisor = 8*(CLKDIVL + 1 + CLKDIVH + 1)
-	   SCL = PCLK / SCLK Divisor */
-	clk_div = div_round_up(i2c_src_clk, hz * 8);
-	divh = clk_div * 3 / 7 - 1;
-	divl = clk_div - divh - 2;
-	i2c_clk = i2c_src_clk / (8 * (divl + 1 + divh + 1));
-	printk(BIOS_DEBUG, "I2C bus %u: %uHz (divh = %u, divl = %u)\n",
-	       bus, i2c_clk, divh, divl);
-	assert((divh < 65536) && (divl < 65536) && hz - i2c_clk < 15*KHz);
-	write32(&regs->i2c_clkdiv, (divh << 16) | (divl << 0));
-#endif
-}
diff --git a/src/soc/cavium/common/include/soc/twsi.h b/src/soc/cavium/common/include/soc/twsi.h
new file mode 100644
index 0000000..a9f23ad
--- /dev/null
+++ b/src/soc/cavium/common/include/soc/twsi.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2018-present  Facebook, Inc.
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <types.h>
+#include <device/i2c.h>
+
+#ifndef __SOC_CAVIUM_CN81XX_INCLUDE_SOC_TWSI_H
+#define __SOC_CAVIUM_CN81XX_INCLUDE_SOC_TWSI_H
+
+int twsi_init(unsigned int bus, enum i2c_speed hz);
+void *twsi_get_baseaddr(const size_t bus);
+
+#endif
diff --git a/src/soc/cavium/common/twsi.c b/src/soc/cavium/common/twsi.c
new file mode 100644
index 0000000..e2ea838
--- /dev/null
+++ b/src/soc/cavium/common/twsi.c
@@ -0,0 +1,716 @@
+/*
+ * Copyright 2016          Cavium, Inc. <support at cavium.com>
+ * Copyright 2018-present  Facebook, Inc.
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <console/console.h>
+#include <soc/twsi.h>
+#include <device/i2c.h>
+#include <device/i2c_simple.h>
+#include <assert.h>
+#include <delay.h>
+#include <arch/io.h>
+
+
+#define RST_BOOT	((void *const)0x87e006001600ll)
+#define PLL_REF_CLK	50000000	/* 50 MHz */
+
+#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 rst_boot {
+	u64 u;
+	struct {
+		u64 rboot_pin:1;
+		u64 rboot:1;
+		u64 lboot:10;
+		u64 lboot_ext23:6;
+		u64 lboot_ext45:6;
+		u64 reserved_24_29:6;
+		u64 lboot_oci:3;
+		u64 pnr_mul:6;
+		u64 reserved_39_39:1;
+		u64 c_mul:7;
+		u64 reserved_47_54:8;
+		u64 dis_scan:1;
+		u64 dis_huk:1;
+		u64 vrm_err:1;
+		u64 jt_tstmode:1;
+		u64 ckill_ppdis:1;
+		u64 trusted_mode:1;
+		u64 ejtagdis:1;
+		u64 jtcsrdis:1;
+		u64 chipkill:1;
+	} s;
+};
+
+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_DEBUG, "%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_DEBUG, "%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_DEBUG, "%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_DEBUG, "%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)
+{
+	int io_clock_hz;
+	int n_div;
+	int m_div;
+	union twsx_sw_twsi sw_twsi;
+	union rst_boot rst_boot;
+
+	printk(BIOS_DEBUG, "%s(%p, %u)\n", __func__, baseaddr, speed);
+	rst_boot.u = read64(RST_BOOT);
+
+	io_clock_hz = rst_boot.s.pnr_mul * PLL_REF_CLK;
+
+	/* 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 = twsi_get_baseaddr(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 = twsi_get_baseaddr(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;
+}

-- 
To view, visit https://review.coreboot.org/23751
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9f85d024b3ffd0a0c4376a8880d14db9e3570d79
Gerrit-Change-Number: 23751
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <patrick.rudolph at 9elements.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180214/105dcd06/attachment-0001.html>


More information about the coreboot-gerrit mailing list