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@siemens.com>
Reviewed-on: http://review.coreboot.org/8401
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Werner Zeh 2015-02-10 13:02:34 +01:00 committed by Patrick Georgi
parent b5a374d58b
commit 0f9c9de35a
3 changed files with 396 additions and 0 deletions

View File

@ -54,6 +54,7 @@ smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c
ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smm.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smm.c
ramstage-y += placeholders.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/
CPPFLAGS_common += -I$(src)/soc/intel/fsp_baytrail/fsp CPPFLAGS_common += -I$(src)/soc/intel/fsp_baytrail/fsp

View File

@ -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>
#include <stdlib.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_ERR 0x8000000
#define I2C_SUCCESS 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__ */

View File

@ -0,0 +1,257 @@
/*
* 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 (read32(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))) {
udelay(1);
if (!--i)
return I2C_ERR_TIMEOUT;
}
return I2C_SUCCESS;
}
/* 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 (read32(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))) {
udelay(1);
if (!--i)
return I2C_ERR_TIMEOUT;
}
return I2C_SUCCESS;
}
/* 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)))) {
status = *((volatile unsigned int *)(base_adr + I2C_STATUS));
udelay(1);
if (!--i)
return I2C_ERR_TIMEOUT;
}
return I2C_SUCCESS;
}
/** \brief Enables I2C-controller, sets up BAR and timing parameters
* @param bus Number of the I2C-controller to use (0...6)
* @return I2C_SUCCESS 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;
/* Ensure the desired device is valid */
if (bus > ARRAY_SIZE(base_adr)) {
printk(BIOS_ERR, "I2C: Only I2C controllers 0...6 are available.\n");
return I2C_ERR;
}
base_ptr = (char*)base_adr[bus];
/* 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 I2C_ERR;
}
/* 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_SUCCESS;
}
/** \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_SUCCESS 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_SUCCESS)
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_SUCCESS;
}
/** \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_SUCCESS 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_SUCCESS;
}