aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/fsp_baytrail/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel/fsp_baytrail/i2c.c')
-rw-r--r--src/soc/intel/fsp_baytrail/i2c.c456
1 files changed, 240 insertions, 216 deletions
diff --git a/src/soc/intel/fsp_baytrail/i2c.c b/src/soc/intel/fsp_baytrail/i2c.c
index 5f6ca467ea..37ce2d0b21 100644
--- a/src/soc/intel/fsp_baytrail/i2c.c
+++ b/src/soc/intel/fsp_baytrail/i2c.c
@@ -1,7 +1,7 @@
/*
* This file is part of the coreboot project.
*
- * Copyright (C) 2014 Siemens AG
+ * Copyright (C) 2014-2019 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
@@ -13,258 +13,282 @@
* GNU General Public License for more details.
*/
-#include <device/pci.h>
-#include <device/pci_ops.h>
+#include <cbmem.h>
#include <console/console.h>
-#include <soc/baytrail.h>
-#include <soc/pci_devs.h>
-#include <soc/iosf.h>
-#include <device/mmio.h>
-#include <delay.h>
+#include <device/device.h>
+#include <device/pci_def.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <drivers/i2c/designware/dw_i2c.h>
#include <soc/i2c.h>
+#include <soc/iosf.h>
+#include <soc/iomap.h>
+#include <soc/nvs.h>
+#include <soc/pci_devs.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)
+#include "chip.h"
+
+/* Convert I2C bus number to PCI device and function */
+int dw_i2c_soc_bus_to_devfn(unsigned int bus)
{
- int i;
- u32 as;
-
- as = read32(base_adr + I2C_ABORT_SOURCE) & 0x1ffff;
- if (as) {
- /* Reading back I2C_CLR_TX_ABRT resets abort lock on TX FIFO */
- i = read32(base_adr + I2C_CLR_TX_ABRT);
- return I2C_ERR_ABORT | as;
- }
+ if (bus <= 6)
+ return PCI_DEVFN(SIO1_DEV, bus + 1);
+ else
+ return -1;
+}
+
+/* Convert PCI device and function to I2C bus number */
+int dw_i2c_soc_dev_to_bus(struct device *dev)
+{
+ pci_devfn_t devfn = dev->path.pci.devfn;
+ if ((devfn >= SOC_DEVFN_I2C1) && (devfn <= SOC_DEVFN_I2C7))
+ return PCI_FUNC(devfn) - 1;
+ else
+ return -1;
+}
+
+/* Getting I2C bus configuration from devicetree config */
+const struct dw_i2c_bus_config *dw_i2c_get_soc_cfg(unsigned int bus)
+{
+ const struct soc_intel_fsp_baytrail_config *config;
+ const struct device *dev = pcidev_path_on_root(SOC_DEVFN_SOC);
- /* Wait here for a free slot in TX-FIFO */
- i = I2C_TIMEOUT_US;
- while (!(read32(base_adr + I2C_STATUS) & I2C_TFNF)) {
- udelay(1);
- if (!--i)
- return I2C_ERR_TIMEOUT;
+ if (dev && dev->chip_info) {
+ config = dev->chip_info;
+ return &config->i2c[bus];
}
- return I2C_SUCCESS;
+ die("Could not find SA_DEV_ROOT devicetree config!\n");
}
-/* 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)
+#if !ENV_RAMSTAGE
+static int lpss_i2c_early_init_bus(unsigned int bus)
{
- int i;
- u32 as;
-
- as = read32(base_adr + I2C_ABORT_SOURCE) & 0x1ffff;
- if (as) {
- /* Reading back I2C_CLR_TX_ABRT resets abort lock on TX FIFO */
- i = read32(base_adr + I2C_CLR_TX_ABRT);
- return I2C_ERR_ABORT | as;
+ const struct dw_i2c_bus_config *config;
+ const struct device *tree_dev;
+ pci_devfn_t dev;
+ int devfn;
+ uintptr_t base;
+
+ /* Find the PCI device for this bus controller */
+ devfn = dw_i2c_soc_bus_to_devfn(bus);
+ if (devfn < 0) {
+ printk(BIOS_ERR, "I2C%u device not found\n", bus);
+ return -1;
+ }
+
+ /* Look up the controller device in the devicetree */
+ dev = PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ tree_dev = pcidev_path_on_root(devfn);
+ if (!tree_dev || !tree_dev->enabled) {
+ printk(BIOS_ERR, "I2C%u device not enabled\n", bus);
+ return -1;
}
- /* Wait here for a received entry in RX-FIFO */
- i = I2C_TIMEOUT_US;
- while (!(read32(base_adr + I2C_STATUS) & I2C_RFNE)) {
- udelay(1);
- if (!--i)
- return I2C_ERR_TIMEOUT;
+ /* Skip if not enabled for early init */
+ config = dw_i2c_get_soc_cfg(bus);
+ if (!config || !config->early_init) {
+ printk(BIOS_DEBUG, "I2C%u not enabled for early init\n", bus);
+ return -1;
+ }
+
+ /* Prepare early base address for access before memory */
+ base = EARLY_I2C_BASE(bus);
+ pci_write_config32(dev, PCI_BASE_ADDRESS_0, base);
+ pci_write_config32(dev, PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+
+ /* Take device out of reset */
+ write32((void *)((uint32_t)base + I2C_SOFTWARE_RESET), I2C_RESET_APB | I2C_RESET_FUNC);
+
+ /* Initialize the controller */
+ if (dw_i2c_init(bus, config) < 0) {
+ printk(BIOS_ERR, "I2C%u failed to initialize\n", bus);
+ return -1;
}
- return I2C_SUCCESS;
+ return 0;
}
-/* 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)
+uintptr_t dw_i2c_base_address(unsigned int bus)
{
- int i;
- int status;
-
- /* For IDLE, increase timeout by ten times */
- i = I2C_TIMEOUT_US * 10;
- status = read32(base_adr + I2C_STATUS);
- while (((status & I2C_MST_ACTIVITY) || (!(status & I2C_TFE)))) {
- status = read32(base_adr + I2C_STATUS);
- udelay(1);
- if (!--i)
- return I2C_ERR_TIMEOUT;
- }
+ int devfn;
+ pci_devfn_t dev;
+ uintptr_t base;
+
+ /* Find device+function for this controller */
+ devfn = dw_i2c_soc_bus_to_devfn(bus);
+ if (devfn < 0)
+ return 0;
- return I2C_SUCCESS;
+ /* Form a PCI address for this device */
+ dev = PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+ /* Read the first base address for this device */
+ base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & 0xfffffff0;
+
+ /* Attempt to initialize bus if base is not set yet */
+ if (!base && !lpss_i2c_early_init_bus(bus))
+ base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & 0xfffffff0;
+ return base;
}
+#else
-/** \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)
+uintptr_t dw_i2c_base_address(unsigned int bus)
{
+ int devfn;
struct device *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;
+ struct resource *bar = NULL;
+
+ /* bus -> devfn */
+ devfn = dw_i2c_soc_bus_to_devfn(bus);
+
+ if (devfn < 0)
+ return (uintptr_t)NULL;
+
+ /* devfn -> dev */
+ dev = pcidev_path_on_root(devfn);
+ if (dev && dev->enabled) {
+ /* dev -> bar0 */
+ bar = find_resource(dev, PCI_BASE_ADDRESS_0);
}
- base_ptr = (char*)base_adr[bus];
- /* Set the I2C-device the user wants to use */
- dev = pcidev_on_root(PCH_DEV_SLOT_I2C1, bus + 1);
+ if (bar)
+ return bar->base;
+ else
+ return (uintptr_t)NULL;
+}
- /* 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;
+static void i2c_enable_acpi_mode(struct device *dev, int iosf_reg, int nvs_index)
+{
+ struct resource *bar;
+ global_nvs_t *gnvs;
+ uint32_t val;
+
+ /* Find ACPI NVS to update BARs */
+ gnvs = (global_nvs_t *)cbmem_find(CBMEM_ID_ACPI_GNVS);
+ if (!gnvs) {
+ printk(BIOS_ERR, "Unable to locate Global NVS\n");
+ return;
}
- /* Set memory base */
- pci_write_config32(dev, PCI_BASE_ADDRESS_0, (int)base_ptr);
+ /* Save BAR0 and BAR1 to ACPI NVS */
+ bar = find_resource(dev, PCI_BASE_ADDRESS_0);
+ if (bar)
+ gnvs->dev.lpss_bar0[nvs_index] = (uint32_t)bar->base;
+
+ bar = find_resource(dev, PCI_BASE_ADDRESS_1);
+ if (bar)
+ gnvs->dev.lpss_bar1[nvs_index] = (uint32_t)bar->base;
+
+ /* Device is enabled in ACPI mode */
+ gnvs->dev.lpss_en[nvs_index] = 1;
+
+ /* Put device in ACPI mode */
+ val = iosf_lpss_read(iosf_reg);
+ val |= (LPSS_CTL_PCI_CFG_DIS | LPSS_CTL_ACPI_INT_EN);
+ iosf_lpss_write(iosf_reg, val);
+ val = pci_read_config32(dev, PCI_COMMAND);
+ val |= PCI_COMMAND_INT_DISABLE;
+ pci_write_config32(dev, PCI_COMMAND, val);
+}
- /* Enable memory space */
- pci_write_config32(dev, PCI_COMMAND,
- (pci_read_config32(dev, PCI_COMMAND) | 0x2));
-
- /* Set up some settings of I2C controller */
- write32(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 */
- write32(base_ptr + I2C_SS_SCL_HCNT, 0x1f4);
- write32(base_ptr + I2C_SS_SCL_LCNT, 0x1f4);
- /* For 400 kHz, the counter value is 0x7d */
- write32(base_ptr + I2C_FS_SCL_HCNT, 0x7d);
- write32(base_ptr + I2C_FS_SCL_LCNT, 0x7d);
- /* no interrupts in BIOS */
- write32(base_ptr + I2C_INTR_MASK, 0);
-
- /* Enable the I2C controller for operation */
- write32(base_ptr + I2C_ENABLE, 0x1);
-
- printk(BIOS_INFO, "I2C: Controller %d enabled.\n", bus);
- return I2C_SUCCESS;
+static void dev_enable_snoop_and_pm(struct device *dev, int iosf_reg)
+{
+ uint32_t val;
+
+ val = iosf_lpss_read(iosf_reg);
+ val &= ~(LPSS_CTL_SNOOP | LPSS_CTL_NOSNOOP);
+ val |= (LPSS_CTL_SNOOP | LPSS_CTL_PM_CAP_PRSNT);
+ iosf_lpss_write(iosf_reg, val);
}
-/** \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)
+static void dev_ctl_reg(struct device *dev, int *iosf_reg, int *nvs_index)
{
- int i = 0;
- char *base_ptr = NULL;
- struct device *dev;
- unsigned int val;
- int stat;
-
- /* Get base address of desired I2C-controller */
- dev = pcidev_on_root(PCH_DEV_SLOT_I2C1, 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;
- }
+ int bus;
- /* Ensure I2C controller is not active before setting slave address */
- stat = wait_for_idle(base_ptr);
- if (stat != I2C_SUCCESS)
- return stat;
-
- /* clear any abort status from a previous transaction */
- read32(base_ptr + I2C_CLR_TX_ABRT);
-
- /* Now we can program the desired slave address and start transfer */
- write32(base_ptr + I2C_TARGET_ADR, chip & 0xff);
- /* Send address inside slave to read from */
- write32(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 */
- write32(base_ptr + I2C_DATA_CMD, val);
- stat = wait_rx_fifo(base_ptr);
- if (stat)
- return stat;
- buf[i] = read32(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;
- }
+ bus = dw_i2c_soc_dev_to_bus(dev);
+ if (bus >= 0) {
+ *iosf_reg = LPSS_I2C1_CTL + (bus * 8);
+ *nvs_index = bus + 1;
+ } else {
+
+ *iosf_reg = -1;
+ *nvs_index = -1;
}
- 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)
+static void i2c_disable_resets(struct device *dev)
{
- int i;
- char *base_ptr;
- struct device *dev;
- unsigned int val;
- int stat;
-
- /* Get base address of desired I2C-controller */
- dev = pcidev_on_root(PCH_DEV_SLOT_I2C1, bus + 1);
- base_ptr = (char *)pci_read_config32(dev, PCI_BASE_ADDRESS_0);
- if (base_ptr == NULL) {
- return I2C_ERR_INVALID_ADR;
- }
+ uint32_t base;
- /* Ensure I2C controller is not active yet */
- stat = wait_for_idle(base_ptr);
- if (stat) {
- return stat;
- }
+ printk(BIOS_DEBUG, "Releasing I2C device from reset.\n");
+ base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & 0xfffffff0;
+ write32((void *)(base + I2C_SOFTWARE_RESET), I2C_RESET_APB | I2C_RESET_FUNC);
+}
- /* clear any abort status from a previous transaction */
- read32(base_ptr + I2C_CLR_TX_ABRT);
-
- /* Program slave address to use for this transfer */
- write32(base_ptr + I2C_TARGET_ADR, chip & 0xff);
-
- /* Send address inside slave to write data to */
- write32(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;
- }
- write32(base_ptr + I2C_DATA_CMD, val);
+static void i2c_lpss_init(struct device *dev)
+{
+ struct soc_intel_fsp_baytrail_config *config = dev->chip_info;
+ int iosf_reg, nvs_index;
+
+ dev_ctl_reg(dev, &iosf_reg, &nvs_index);
+
+ if (iosf_reg < 0) {
+ int slot = PCI_SLOT(dev->path.pci.devfn);
+ int func = PCI_FUNC(dev->path.pci.devfn);
+ printk(BIOS_DEBUG, "Could not find iosf_reg for %02x.%01x\n",
+ slot, func);
+ return;
}
- return I2C_SUCCESS;
+ dev_enable_snoop_and_pm(dev, iosf_reg);
+ i2c_disable_resets(dev);
+
+ if (config && (config->PcdLpssSioEnablePciMode == LPSS_PCI_MODE_DISABLE))
+ i2c_enable_acpi_mode(dev, iosf_reg, nvs_index);
}
+/*
+ * This function ensures that the device is actually out of reset and
+ * it is ready for initialization sequence.
+ */
+static void dw_i2c_device_init(struct device *dev)
+{
+ int bus = dw_i2c_soc_dev_to_bus(dev);
+
+ if (bus < 0)
+ return;
+
+ if (!dw_i2c_base_address(bus))
+ return;
+ i2c_lpss_init(dev);
+ dw_i2c_dev_init(dev);
+}
+
+static struct device_operations i2c_dev_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .scan_bus = scan_smbus,
+ .ops_i2c_bus = &dw_i2c_bus_ops,
+ .ops_pci = &pci_dev_ops_pci,
+ .init = dw_i2c_device_init,
+ .acpi_fill_ssdt_generator = dw_i2c_acpi_fill_ssdt,
+};
+
+static const unsigned short pci_device_ids[] = {
+ I2C1_DEVID,
+ I2C2_DEVID,
+ I2C3_DEVID,
+ I2C4_DEVID,
+ I2C5_DEVID,
+ I2C6_DEVID,
+ I2C7_DEVID,
+ 0
+};
+
+static const struct pci_driver pch_i2c __pci_driver = {
+ .ops = &i2c_dev_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = pci_device_ids,
+};
+#endif