[coreboot-gerrit] Patch set updated for coreboot: 2dc2d85 fsp_baytrail: Add I2C driver

Werner Zeh (werner.zeh@siemens.com) gerrit at coreboot.org
Wed Feb 11 07:35:13 CET 2015


Werner Zeh (werner.zeh at siemens.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/8401

-gerrit

commit 2dc2d85176d62bcc19fe04a8aefe0a3080372347
Author: Werner Zeh <werner.zeh at siemens.com>
Date:   Tue Feb 10 13:02:34 2015 +0100

    fsp_baytrail: Add I2C driver
    
    Add a driver wich can handle the internal I2C controllers
    of Baytrail SoC. This driver is not suitable for the
    SMBUS-controller.
    
    Change-Id: I841c3991a2fb0f8b92b8e59ec02d62f5866f5bdf
    Signed-off-by: Werner Zeh <werner.zeh at siemens.com>
---
 src/soc/intel/fsp_baytrail/Makefile.inc   |   1 +
 src/soc/intel/fsp_baytrail/baytrail/i2c.h | 138 +++++++++++++++
 src/soc/intel/fsp_baytrail/i2c.c          | 269 ++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)

diff --git a/src/soc/intel/fsp_baytrail/Makefile.inc b/src/soc/intel/fsp_baytrail/Makefile.inc
index 3896e85..2dfb9cb 100644
--- a/src/soc/intel/fsp_baytrail/Makefile.inc
+++ b/src/soc/intel/fsp_baytrail/Makefile.inc
@@ -54,6 +54,7 @@ smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c
 ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smm.c
 
 ramstage-y += placeholders.c
+ramstage-y += i2c.c
 
 CPPFLAGS_common += -I$(src)/soc/intel/fsp_baytrail/
 CPPFLAGS_common += -I$(src)/soc/intel/fsp_baytrail/fsp
diff --git a/src/soc/intel/fsp_baytrail/baytrail/i2c.h b/src/soc/intel/fsp_baytrail/baytrail/i2c.h
new file mode 100644
index 0000000..3799fbb
--- /dev/null
+++ b/src/soc/intel/fsp_baytrail/baytrail/i2c.h
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Siemens AG
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef __SOC_INTEL_FSP_BAYTRAIL_I2C_H__
+#define __SOC_INTEL_FSP_BAYTRAIL_I2C_H__
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+
+/* SMBus controller settings in PCI configuration space*/
+#define I2C_PCI_VENDOR_ID	0x8086
+#define I2C0_PCI_DEV_ID		0x0f41
+#define I2C1_PCI_DEV_ID		0x0f42
+#define I2C2_PCI_DEV_ID		0x0f43
+#define I2C3_PCI_DEV_ID		0x0f44
+#define I2C4_PCI_DEV_ID		0x0f45
+#define I2C5_PCI_DEV_ID		0x0f46
+#define I2C6_PCI_DEV_ID		0x0f47
+
+#define I2C0_MEM_BASE		0xd0921000
+#define I2C1_MEM_BASE		0xd0923000
+#define I2C2_MEM_BASE		0xd0925000
+#define I2C3_MEM_BASE		0xd0927000
+#define I2C4_MEM_BASE		0xd0929000
+#define I2C5_MEM_BASE		0xd092b000
+#define I2C6_MEM_BASE		0xd092d000
+
+#define I2C_STANDARD_MODE	0x1
+#define I2C_FAST_MODE		0x2
+
+/* Define relevant registers in PCI space.*/
+#define I2C_PCI_COMMAND		0x4
+#define I2C_PCI_STATUS		0x6
+
+/* Define memory mapped registers.*/
+#define I2C_CTRL			0x0
+#define  I2C_SLAVE_DISABLE	0x40
+#define  I2C_RESTART_EN		0x20
+#define  I2C_ADR_MODE		0x10
+#define  I2C_SPEED_MASK		0x6
+#define   I2C_STD_MODE		0x1
+#define   I2C_FAST_MODE		0x2
+#define  I2C_MASTER_ENABLE	0x1
+
+#define I2C_TARGET_ADR		0x4
+#define I2C_TARGET_ADR_MASK	0x3ff
+
+#define I2C_DATA_CMD		0x10
+#define  I2C_RESTART		0x400
+#define  I2C_STOP			0x200
+#define  I2C_RW_CMD			0x100
+
+#define I2C_SS_SCL_HCNT		0x14	/* Counter for high period for 100 kHz SCL*/
+#define I2C_SS_SCL_LCNT		0x18	/* Counter for low period for 100 kHz SCL*/
+#define I2C_FS_SCL_HCNT		0x1c	/* Counter for high period for 400 kHz SCL*/
+#define I2C_FS_SCL_LCNT		0x20	/* Counter for low period for 400 kHz SCL*/
+
+#define I2C_INTR_STAT		0x2c	/* Interrupt status register, read only*/
+#define I2C_INTR_MASK		0x30	/*Interrupt mask register*/
+#define I2C_RAW_INTR_STAT	0x34	/* Raw interrupt status, read only*/
+#define  I2C_START_DETECT	0x400
+#define  I2C_STOP_DETECT	0x200
+#define  I2C_ACTIVITY		0x100
+#define  I2C_TX_ABORT		0x40
+#define  I2C_RD_REQ			0x20	/* Read request in slave mode*/
+#define  I2C_TX_EMPTY		0x10
+#define  I2C_TX_OVERFLOW	0x8
+#define  I2C_RX_FULL		0x4
+#define  I2C_RX_OVERFLOW	0x2
+#define  I2C_RX_UNDERFLOW	0x1
+
+#define I2C_RX_TL			0x38	/* Rx FIFO threshold level 0..255*/
+#define I2C_TX_TL			0x3c	/* Tx FIFO threshold level 0..255*/
+#define I2C_CLR_INTR		0x40	/* Clear all events with a read*/
+#define I2C_CLR_TX_ABRT		0x54	/* Clear TX-Abort event with a read*/
+
+/*There are a bunch of interrupt clearing registers now which I do not use!*/
+/*So proceed somewhat later with definition*/
+#define I2C_ENABLE			0x6c	/* 0: disable I2C controller, 1: enable*/
+#define I2C_STATUS			0x70
+#define  I2C_MST_ACTIVITY	0x20	/* Master FSM activity*/
+#define  I2C_RFF			0x10	/* Receive FIFO completely full*/
+#define  I2C_RFNE			0x8		/* Receive FIFO not empty*/
+#define  I2C_TFE			0x4		/* Transmit FIFO completely empty*/
+#define  I2C_TFNF			0x2		/* Transmit FIFO not full*/
+#define  I2C_ACTIVE			0x1		/* 1: I2C currently in operation*/
+
+#define I2C_TXFLR			0x74	/* Current transmit FIFO level*/
+#define I2C_RXFLR			0x78	/* Current receive FIFO level*/
+#define I2C_SDA_HOLD		0x7c	/* Data hold time after SCL goes low*/
+#define I2C_ABORT_SOURCE	0x80
+#define  I2C_ARB_LOST		0x1000	/* Arbitration lost*/
+#define  I2C_MASTER_DIS		0x800	/* Master was disabled by user*/
+#define  I2C_10B_RD_NORSTRT	0x400	/* 10 bit address read and RESTART disabled*/
+#define  I2C_SBYTE_NORSTRT	0x200	/* START with RESTART disabled*/
+#define  I2C_START_ACKDET	0x80	/* START byte was acknowledged*/
+#define  I2C_TX_DATA_NOACK	0x8		/* TX data not acknowledged*/
+#define  I2C_10B_ADR2_NOACK	0x4		/* Second address byte in 10 bit mode NACK*/
+#define  I2C_10B_ADR1_NOACK	0x2		/* First address byte in 10 bit not ACK*/
+#define  I2C_7B_ADDR_NACK	0x1		/* 7 bit address byte not acknowledged*/
+
+#define I2C_ENABLE_STATUS	0x9c
+
+/* Define some status and error values*/
+#define I2C_ERR_INVALID_ADR	0x1000000
+#define I2C_ERR_TIMEOUT		0x2000000
+#define I2C_ERR_ABORT		0x4000000
+#define I2C_NO_ERROR		0x0000000
+
+
+#define I2C_TIMEOUT			2000		/* Use 2000 us as timeout for the bus*/
+
+/* Prototype section*/
+int i2c_init(unsigned bus);
+int i2c_read(unsigned bus, unsigned chip, unsigned addr,
+				uint8_t *buf, unsigned len);
+int i2c_write(unsigned bus, unsigned chip, unsigned addr,
+		const uint8_t *buf, unsigned len);
+
+#endif	/* __SOC_INTEL_FSP_BAYTRAIL_I2C_H__ */
diff --git a/src/soc/intel/fsp_baytrail/i2c.c b/src/soc/intel/fsp_baytrail/i2c.c
new file mode 100644
index 0000000..05c21a3
--- /dev/null
+++ b/src/soc/intel/fsp_baytrail/i2c.c
@@ -0,0 +1,269 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Siemens AG
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <device/pci.h>
+#include <baytrail/baytrail.h>
+#include <baytrail/pci_devs.h>
+#include <baytrail/iosf.h>
+#include <delay.h>
+#include <baytrail/i2c.h>
+
+/* Wait for the transmit FIFO till there is at least one slot empty.
+ * FIFO stall due to transmit abort will be checked and resolved
+ */
+static int wait_tx_fifo(char *base_adr) {
+	int i;
+
+	if (*((unsigned int *)(base_adr + I2C_ABORT_SOURCE)) & 0x1ffff) {
+		/*Reading back I2C_CLR_TX_ABRT resets abort lock on TX FIFO*/
+		i = *((volatile unsigned int *)(base_adr + I2C_CLR_TX_ABRT));
+		return I2C_ERR_ABORT |
+				(*((unsigned int *)(base_adr + I2C_ABORT_SOURCE)) & 0x1ffff);
+	}
+
+	/*Wait here for a free slot in TX-FIFO */
+	i = I2C_TIMEOUT;
+	while ((!(*((volatile unsigned int *)(base_adr + I2C_STATUS)) & I2C_TFNF))
+			&& (i)) {
+		udelay(1);
+		i--;
+	}
+
+	if (i == 0) {
+		return I2C_ERR_TIMEOUT;
+	} else {
+		return I2C_NO_ERROR;
+	}
+}
+
+/* Wait for the receive FIFO till there is at least one valid entry to read.
+ * FIFO stall due to transmit abort will be checked and resolved
+ */
+static int wait_rx_fifo(char *base_adr) {
+	int i;
+
+	if (*((unsigned int *)(base_adr + I2C_ABORT_SOURCE)) & 0x1ffff) {
+		/*Reading back I2C_CLR_TX_ABRT resets abort lock on TX FIFO*/
+		i = *((volatile unsigned int *)(base_adr + I2C_CLR_TX_ABRT));
+		return I2C_ERR_ABORT |
+				(*((unsigned int *)(base_adr + I2C_ABORT_SOURCE)) & 0x1ffff);
+	}
+
+	/*Wait here for a received entry in RX-FIFO */
+	i = I2C_TIMEOUT;
+	while ((!(*((volatile unsigned int *)(base_adr + I2C_STATUS)) & I2C_RFNE))
+			&& (i)) {
+		udelay(1);
+		i--;
+	}
+
+	if (i == 0) {
+		return I2C_ERR_TIMEOUT;
+	} else {
+		return I2C_NO_ERROR;
+	}
+}
+
+/* When there will be a fast switch between send and receive, one have
+ * to wait until the first operation is completely finished
+ * before starting the second operation
+ */
+static int wait_for_idle(char *base_adr)
+{
+	int i;
+	volatile int status;
+
+	/*For IDLE, increase timeout by ten times*/
+	i = I2C_TIMEOUT * 10;
+	status = *((volatile unsigned int *)(base_adr + I2C_STATUS));
+	while(((status & I2C_MST_ACTIVITY) || (!(status & I2C_TFE))) && (i)) {
+		status = *((volatile unsigned int *)(base_adr + I2C_STATUS));
+		udelay(1);
+		i--;
+	}
+
+	if (i == 0) {
+		return I2C_ERR_TIMEOUT;
+	} else {
+		return I2C_NO_ERROR;
+	}
+}
+
+/** \brief Enables I2C-controller, sets up BAR and timing parameters
+ * @param bus		Number of the I2C-controller to use (0...6)
+ * @return			0 on success, otherwise error code
+ */
+int i2c_init(unsigned bus)
+{
+	device_t dev;
+	int base_adr[7] = {I2C0_MEM_BASE, I2C1_MEM_BASE, I2C2_MEM_BASE,
+					   I2C3_MEM_BASE, I2C4_MEM_BASE, I2C5_MEM_BASE,
+					   I2C6_MEM_BASE};
+	char *base_ptr = (char*)base_adr[bus];
+	/* Ensure the desired device is valid*/
+	if (bus > 6) {
+		printk (BIOS_ERR, "I2C: Only I2C controllers 0...6 are available");
+		return 1;
+	}
+
+	/*Set the I2C-device the user wants to use */
+	dev = dev_find_slot(0, PCI_DEVFN(I2C1_DEV, bus + 1));
+
+	/* Ensure we have the right PCI device */
+	if ((pci_read_config16(dev, 0x0) != I2C_PCI_VENDOR_ID) ||
+		(pci_read_config16(dev, 0x2) != (I2C0_PCI_DEV_ID + bus))) {
+			printk(BIOS_ERR, "I2C: Controller %d not found!", bus);
+			return 2;
+	}
+
+	/* Set memory base*/
+	pci_write_config32(dev, PCI_BASE_ADDRESS_0,(int)base_ptr);
+
+	/* Enable memory space*/
+	pci_write_config32(dev, PCI_COMMAND,
+						(pci_read_config32(dev, PCI_COMMAND) | 0x2));
+
+	/*Set up some settings of I2C controller*/
+	*((unsigned int *)(base_ptr + I2C_CTRL)) = (I2C_RESTART_EN
+												| (I2C_STANDARD_MODE << 1)
+												| I2C_MASTER_ENABLE);
+	/*Adjust frequency for standard mode to 100 kHz*/
+	/*The counter value can be computed by N=100MHz/2/I2C_CLK*/
+	/*Thus, for 100 kHz I2C_CLK, N is 0x1F4*/
+	*((unsigned int *)(base_ptr + I2C_SS_SCL_HCNT)) = 0x1f4;
+	*((unsigned int *)(base_ptr + I2C_SS_SCL_LCNT)) = 0x1f4;
+	/*For 400 kHz, the counter value is 0x7d*/
+	*((unsigned int *)(base_ptr + I2C_FS_SCL_HCNT)) = 0x7d;
+	*((unsigned int *)(base_ptr + I2C_FS_SCL_LCNT)) = 0x7d;
+
+	/* Enable the I2C controller for operation*/
+	*((unsigned int *)(base_ptr + I2C_ENABLE)) = 0x1;
+
+	printk(BIOS_INFO, "I2C: Controller %d enabled.\n", bus);
+	return I2C_NO_ERROR;
+}
+
+/** \brief Read bytes over I2C-Bus from a slave. This function tries only one
+ *         time to transmit data. In case of an error (abort) error code is
+ *         returned. Retransmission has to be done from caller!
+ * @param bus		Number of the I2C-controller to use (0...6)
+ * @param chip		7 Bit of the slave address on I2C bus
+ * @param addr		Address inside slave where to read from
+ * @param *buf		Pointer to the buffer where to store read data
+ * @param len		Number of bytes to read
+ * @return			I2C_NO_ERROR when read was successful, otherwise error code
+ */
+int i2c_read(unsigned bus, unsigned chip, unsigned addr,
+			uint8_t *buf, unsigned len)
+{
+	int i = 0;
+	char *base_ptr = NULL;
+	device_t dev;
+	unsigned int val;
+	int stat;
+
+	/*Get base address of desired I2C-controller*/
+	dev = dev_find_slot(0, PCI_DEVFN(I2C1_DEV, bus + 1));
+	base_ptr = (char *)pci_read_config32(dev, PCI_BASE_ADDRESS_0);
+	if (base_ptr == NULL) {
+		printk(BIOS_INFO, "I2C: Invalid Base address\n");
+		return I2C_ERR_INVALID_ADR;
+	}
+
+	/*Ensure I2C controller is not active before setting slave address*/
+	stat = wait_for_idle(base_ptr);
+	if (stat != I2C_NO_ERROR) {
+		return stat;
+	}
+	/* Now we can program the desired slave address and start transfer*/
+	*((unsigned int *)(base_ptr + I2C_TARGET_ADR)) = (chip & 0xff);
+	/* Send address inside slave to read from*/
+	*((unsigned int *)(base_ptr + I2C_DATA_CMD)) = (addr & 0xff);
+
+	/* For the next byte we need a repeated start condition*/
+	val = I2C_RW_CMD | I2C_RESTART;
+	/* Now we can read desired amount of data over I2C*/
+	for (i = 0; i < len; i++) {
+		/*A read is initiated by writing dummy data to the DATA-register*/
+		*((unsigned int *)(base_ptr + I2C_DATA_CMD)) = val;
+		stat = wait_rx_fifo(base_ptr);
+		if (stat) {
+			return stat;
+		}
+		buf[i] = (*((unsigned int *)(base_ptr + I2C_DATA_CMD))) & 0xff;
+		val = I2C_RW_CMD;
+		if (i == (len -2)) {
+			/* For the last byte we need a stop condition to be generated*/
+			val |= I2C_STOP;
+		}
+	}
+	return I2C_NO_ERROR;
+}
+
+/** \brief Write bytes over I2C-Bus from a slave. This function tries only one
+ *         time to transmit data. In case of an error (abort) error code is
+ *         returned. Retransmission has to be done from caller!
+ * @param bus		Number of the I2C-controller to use (0...6)
+ * @param chip		7 Bit of the slave address on I2C bus
+ * @param addr		Address inside slave where to write to
+ * @param *buf		Pointer to the buffer where data to write is stored
+ * @param len		Number of bytes to write
+ * @return			I2C_NO_ERROR when read was successful, otherwise error code
+ */
+int i2c_write(unsigned bus, unsigned chip, unsigned addr,
+			const uint8_t *buf, unsigned len)
+{
+	int i;
+	char *base_ptr;
+	device_t dev;
+	unsigned int val;
+	int stat;
+
+	/*Get base address of desired I2C-controller*/
+	dev = dev_find_slot(0, PCI_DEVFN(I2C1_DEV, bus + 1));
+	base_ptr = (char *)pci_read_config32(dev, PCI_BASE_ADDRESS_0);
+	if (base_ptr == NULL) {
+		return I2C_ERR_INVALID_ADR;
+	}
+
+	/*Ensure I2C controller is not active jet*/
+	stat = wait_for_idle(base_ptr);
+	if (stat) {
+		return stat;
+	}
+	/*Program slave address to use for this transfer*/
+	*((unsigned int *)(base_ptr + I2C_TARGET_ADR)) = (chip & 0xff);
+
+	/* Send address inside slave to write data to*/
+	*((unsigned int *)(base_ptr + I2C_DATA_CMD)) = (addr & 0xff);
+
+	for (i = 0; i < len; i++) {
+		val = (unsigned int)(buf[i] & 0xff);	/* Take only 8 bits*/
+		if (i == (len -1)) {
+			/* For the last byte we need a stop condition to be generated*/
+			val |= I2C_STOP;
+		}
+		stat = wait_tx_fifo(base_ptr);
+		if (stat) {
+			return stat;
+		}
+		*((unsigned int *)(base_ptr + I2C_DATA_CMD)) = val;
+	}
+	return I2C_NO_ERROR;
+}



More information about the coreboot-gerrit mailing list