Werner Zeh (werner.zeh@siemens.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/8401
-gerrit
commit ed0f4138e8c6a01bf031cbf0565881dde6b84ddd Author: Werner Zeh werner.zeh@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@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; +}