[coreboot-gerrit] Patch set updated for coreboot: 3e5c780 fsp_baytrail: Add I2C driver

Werner Zeh (werner.zeh@siemens.com) gerrit at coreboot.org
Mon Feb 23 06:44:41 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 3e5c780377f9b0020378850dbfe040ff563e782a
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 | 136 +++++++++++++++
 src/soc/intel/fsp_baytrail/i2c.c          | 267 ++++++++++++++++++++++++++++++
 3 files changed, 404 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..a89f091
--- /dev/null
+++ b/src/soc/intel/fsp_baytrail/baytrail/i2c.h
@@ -0,0 +1,136 @@
+/*
+ * 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 are not used! */
+/* 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 NACK */
+#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_US		2000	/* Use 2000 us as time */
+
+/* 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..700bba1
--- /dev/null
+++ b/src/soc/intel/fsp_baytrail/i2c.c
@@ -0,0 +1,267 @@
+/*
+ * 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_US;
+	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_US;
+	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_US * 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.\n");
+		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!\n", 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 yet */
+	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 */
+			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