summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2016-06-27 10:57:13 -0700
committerDuncan Laurie <dlaurie@chromium.org>2016-07-02 01:18:22 +0200
commitff8bce0a5f53652d4d26cb501159e8711f79eb9b (patch)
treea66528533f3f26c959f7a2eed1b7c1d707900af4
parent02fcc887829bc0bf4e98f591de2a199d6a69f2ba (diff)
soc/intel/apollolake: Add support for LPSS I2C driver
Support the I2C interfaces on this SOC using the Intel common lpss_i2c driver. The controllers are supported in pre-ram environments by setting a temporary base address in bootblock and in ramstage using the naturally enumerated base address. The base speed of this controller is 133MHz and the SCL/SDA timing values that are reported to the OS are calculated using that clock. This was tested on a google/reef board doing I2C transactions to the trackpad both in verstage and in ramstage. Change-Id: I0a9d62cd1007caa95cdf4754f30c30aaff9f78f9 Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: https://review.coreboot.org/15480 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-rw-r--r--src/soc/intel/apollolake/Kconfig8
-rw-r--r--src/soc/intel/apollolake/Makefile.inc3
-rw-r--r--src/soc/intel/apollolake/chip.h17
-rw-r--r--src/soc/intel/apollolake/i2c.c126
-rw-r--r--src/soc/intel/apollolake/i2c_early.c116
-rw-r--r--src/soc/intel/apollolake/include/soc/i2c.h50
-rw-r--r--src/soc/intel/apollolake/include/soc/iomap.h5
7 files changed, 323 insertions, 2 deletions
diff --git a/src/soc/intel/apollolake/Kconfig b/src/soc/intel/apollolake/Kconfig
index 8e33015afd..cce06ebbfe 100644
--- a/src/soc/intel/apollolake/Kconfig
+++ b/src/soc/intel/apollolake/Kconfig
@@ -41,8 +41,9 @@ config CPU_SPECIFIC_OPTIONS
select RELOCATABLE_RAMSTAGE # Build fails if this is not selected
select SMM_TSEG
select SOC_INTEL_COMMON
- select SOC_INTEL_COMMON_SMI
select SOC_INTEL_COMMON_ACPI
+ select SOC_INTEL_COMMON_LPSS_I2C
+ select SOC_INTEL_COMMON_SMI
select SPI_FLASH
select UDELAY_TSC
select TSC_CONSTANT_RATE
@@ -96,6 +97,11 @@ config CPU_ADDR_BITS
int
default 36
+config SOC_INTEL_COMMON_LPSS_I2C_CLOCK_MHZ
+ depends on SOC_INTEL_COMMON_LPSS_I2C
+ int
+ default 133
+
config CONSOLE_UART_BASE_ADDRESS
depends on CONSOLE_SERIAL
hex "MMIO base address for UART"
diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc
index 030e35cc1a..a64ee78328 100644
--- a/src/soc/intel/apollolake/Makefile.inc
+++ b/src/soc/intel/apollolake/Makefile.inc
@@ -23,6 +23,7 @@ bootblock-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
romstage-y += car.c
romstage-$(CONFIG_PLATFORM_USES_FSP2_0) += romstage.c
romstage-y += gpio.c
+romstage-y += i2c_early.c
romstage-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
romstage-y += lpc_lib.c
romstage-y += memmap.c
@@ -45,6 +46,7 @@ ramstage-y += cpu.c
ramstage-y += chip.c
ramstage-y += gpio.c
ramstage-y += graphics.c
+ramstage-y += i2c.c
ramstage-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
ramstage-y += lpc.c
ramstage-y += lpc_lib.c
@@ -69,6 +71,7 @@ postcar-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
postcar-y += tsc_freq.c
verstage-y += car.c
+verstage-y += i2c_early.c
verstage-y += memmap.c
verstage-y += mmap_boot.c
verstage-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
diff --git a/src/soc/intel/apollolake/chip.h b/src/soc/intel/apollolake/chip.h
index ef82c53dee..c83f9739fd 100644
--- a/src/soc/intel/apollolake/chip.h
+++ b/src/soc/intel/apollolake/chip.h
@@ -18,7 +18,21 @@
#ifndef _SOC_APOLLOLAKE_CHIP_H_
#define _SOC_APOLLOLAKE_CHIP_H_
+#include <soc/gpio.h>
+#include <soc/intel/common/lpss_i2c.h>
+#include <device/i2c.h>
+
#define CLKREQ_DISABLED 0xf
+#define APOLLOLAKE_I2C_DEV_MAX 8
+
+struct apollolake_i2c_config {
+ /* Bus should be enabled prior to ramstage with temporary base */
+ int early_init;
+ /* Bus speed in Hz, default is I2C_SPEED_FAST (400 KHz) */
+ enum i2c_speed speed;
+ /* Specific bus speed configuration */
+ struct lpss_i2c_speed_config speed_config[LPSS_I2C_SPEED_CONFIG_COUNT];
+};
/* Serial IRQ control. SERIRQ_QUIET is the default (0). */
enum serirq_mode {
@@ -79,6 +93,9 @@ struct soc_intel_apollolake_config {
/* Integrated Sensor Hub */
uint8_t integrated_sensor_hub_enable;
+
+ /* I2C bus configuration */
+ struct apollolake_i2c_config i2c[APOLLOLAKE_I2C_DEV_MAX];
};
#endif /* _SOC_APOLLOLAKE_CHIP_H_ */
diff --git a/src/soc/intel/apollolake/i2c.c b/src/soc/intel/apollolake/i2c.c
new file mode 100644
index 0000000000..1c16a06058
--- /dev/null
+++ b/src/soc/intel/apollolake/i2c.c
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+#include <arch/acpi_device.h>
+#include <arch/acpigen.h>
+#include <device/device.h>
+#include <device/i2c.h>
+#include <device/pci.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <soc/i2c.h>
+#include <soc/intel/common/lpss_i2c.h>
+#include <soc/pci_devs.h>
+#include <soc/pci_ids.h>
+#include "chip.h"
+
+uintptr_t lpss_i2c_base_address(unsigned bus)
+{
+ unsigned devfn;
+ struct device *dev;
+ struct resource *res;
+
+ /* bus -> devfn */
+ devfn = i2c_bus_to_devfn(bus);
+ if (devfn >= 0) {
+ /* devfn -> dev */
+ dev = dev_find_slot(0, devfn);
+ if (dev) {
+ /* dev -> bar0 */
+ res = find_resource(dev, PCI_BASE_ADDRESS_0);
+ if (res)
+ return res->base;
+ }
+ }
+
+ return (uintptr_t)NULL;
+}
+
+static int i2c_dev_to_bus(struct device *dev)
+{
+ return i2c_devfn_to_bus(dev->path.pci.devfn);
+}
+
+/*
+ * The device should already be enabled and out of reset,
+ * either from early init in coreboot or FSP-S.
+ */
+static void i2c_dev_init(struct device *dev)
+{
+ struct soc_intel_apollolake_config *config = dev->chip_info;
+ const struct lpss_i2c_speed_config *sptr;
+ enum i2c_speed speed;
+ int i, bus = i2c_dev_to_bus(dev);
+
+ if (!config || bus < 0)
+ return;
+
+ speed = config->i2c[bus].speed ? : I2C_SPEED_FAST;
+ lpss_i2c_init(bus, speed);
+
+ /* Apply custom speed config if it has been set by the board */
+ for (i = 0; i < LPSS_I2C_SPEED_CONFIG_COUNT; i++) {
+ sptr = &config->i2c[bus].speed_config[i];
+ if (sptr->speed == speed) {
+ lpss_i2c_set_speed_config(bus, sptr);
+ break;
+ }
+ }
+}
+
+static void i2c_fill_ssdt(struct device *dev)
+{
+ struct soc_intel_apollolake_config *config = dev->chip_info;
+ int bus = i2c_dev_to_bus(dev);
+
+ if (!config || bus < 0)
+ return;
+
+ acpigen_write_scope(acpi_device_path(dev));
+ lpss_i2c_acpi_fill_ssdt(config->i2c[bus].speed_config);
+ acpigen_pop_len();
+}
+
+static struct i2c_bus_operations i2c_bus_ops = {
+ .dev_to_bus = &i2c_dev_to_bus,
+};
+
+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 = &i2c_bus_ops,
+ .init = &i2c_dev_init,
+ .acpi_fill_ssdt_generator = &i2c_fill_ssdt,
+};
+
+static const unsigned short pci_device_ids[] = {
+ PCI_DEVICE_ID_APOLLOLAKE_I2C0,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C1,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C2,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C3,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C4,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C5,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C6,
+ PCI_DEVICE_ID_APOLLOLAKE_I2C7,
+ 0,
+};
+
+static const struct pci_driver pch_i2c __pci_driver = {
+ .ops = &i2c_dev_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = pci_device_ids,
+};
diff --git a/src/soc/intel/apollolake/i2c_early.c b/src/soc/intel/apollolake/i2c_early.c
new file mode 100644
index 0000000000..968e99394a
--- /dev/null
+++ b/src/soc/intel/apollolake/i2c_early.c
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <commonlib/helpers.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/i2c.h>
+#include <device/pci_def.h>
+#include <soc/intel/common/lpss_i2c.h>
+#include <soc/i2c.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+#include "chip.h"
+
+static int i2c_early_init_bus(unsigned bus)
+{
+ ROMSTAGE_CONST struct soc_intel_apollolake_config *config;
+ ROMSTAGE_CONST struct device *tree_dev;
+ const struct lpss_i2c_speed_config *sptr;
+ enum i2c_speed speed;
+ pci_devfn_t dev;
+ unsigned devfn;
+ uintptr_t base;
+ uint32_t value;
+ void *reg;
+
+ /* Find the PCI device for this bus controller */
+ devfn = i2c_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 = dev_find_slot(0, devfn);
+ if (!tree_dev || !tree_dev->enabled) {
+ printk(BIOS_ERR, "I2C%u device not enabled\n", bus);
+ return -1;
+ }
+
+ /* Skip if not enabled for early init */
+ config = tree_dev->chip_info;
+ if (!config || !config->i2c[bus].early_init) {
+ printk(BIOS_ERR, "I2C%u not enabled for early init\n", bus);
+ return -1;
+ }
+
+ /* Prepare early base address for access before memory */
+ base = PRERAM_I2C_BASE_ADDRESS(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 */
+ reg = (void *)(base + I2C_LPSS_REG_RESET);
+ value = read32(reg);
+ value |= I2C_LPSS_RESET_RELEASE_HC;
+ write32(reg, value);
+
+ /* Initialize the controller */
+ speed = config->i2c[bus].speed ? : I2C_SPEED_FAST;
+ if (lpss_i2c_init(bus, speed) < 0) {
+ printk(BIOS_ERR, "I2C%u failed to initialize\n", bus);
+ return -1;
+ }
+
+ /* Apply custom speed config if it has been set by the board */
+ for (value = 0; value < LPSS_I2C_SPEED_CONFIG_COUNT; value++) {
+ sptr = &config->i2c[bus].speed_config[value];
+ if (sptr->speed == speed) {
+ lpss_i2c_set_speed_config(bus, sptr);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+uintptr_t lpss_i2c_base_address(unsigned bus)
+{
+ unsigned devfn;
+ pci_devfn_t dev;
+ uintptr_t base;
+
+ /* Find device+function for this controller */
+ devfn = i2c_bus_to_devfn(bus);
+ if (devfn < 0)
+ return (uintptr_t)NULL;
+
+ /* 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 = ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0), 16);
+
+ /* Attempt to initialize bus if base is not set yet */
+ if (!base && !i2c_early_init_bus(bus))
+ base = ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0),
+ 16);
+
+ return base;
+}
diff --git a/src/soc/intel/apollolake/include/soc/i2c.h b/src/soc/intel/apollolake/include/soc/i2c.h
new file mode 100644
index 0000000000..da700f210d
--- /dev/null
+++ b/src/soc/intel/apollolake/include/soc/i2c.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 Google Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef _SOC_APOLLOLAKE_I2C_H_
+#define _SOC_APOLLOLAKE_I2C_H_
+
+#include <device/pci_def.h>
+#include <soc/pci_devs.h>
+
+/* I2C Controller Reset in MMIO private region */
+#define I2C_LPSS_REG_RESET 0x204
+#define I2C_LPSS_RESET_RELEASE_HC ((1 << 1) | (1 << 0))
+#define I2C_LPSS_RESET_RELEASE_IDMA (1 << 2)
+
+/* Convert I2C bus number to PCI device and function */
+static inline int i2c_bus_to_devfn(unsigned bus)
+{
+ if (bus >= 0 && bus <= 3)
+ return PCI_DEVFN(LPSS_DEV_SLOT_I2C_D0, bus);
+ else if (bus >= 4 && bus <= 7)
+ return PCI_DEVFN(LPSS_DEV_SLOT_I2C_D1, (bus - 4));
+ else
+ return -1;
+}
+
+/* Convert PCI device and function to I2C bus number */
+static inline int i2c_devfn_to_bus(unsigned devfn)
+{
+ if (PCI_SLOT(devfn) == LPSS_DEV_SLOT_I2C_D0)
+ return PCI_FUNC(devfn);
+ else if (PCI_SLOT(devfn) == LPSS_DEV_SLOT_I2C_D1)
+ return PCI_FUNC(devfn) + 4;
+ else
+ return -1;
+}
+
+#endif /* _SOC_APOLLOLAKE_I2C_H_ */
diff --git a/src/soc/intel/apollolake/include/soc/iomap.h b/src/soc/intel/apollolake/include/soc/iomap.h
index 716c2a604f..e9caecac7a 100644
--- a/src/soc/intel/apollolake/include/soc/iomap.h
+++ b/src/soc/intel/apollolake/include/soc/iomap.h
@@ -31,6 +31,9 @@
#define PMC_BAR1 0xfe044000
/* Temporary BAR for SPI until PCI enumeration assigns a BAR in ramstage. */
-#define PRERAM_SPI_BASE_ADDRESS 0xfe010000
+#define PRERAM_SPI_BASE_ADDRESS 0xfe010000
+
+/* Temporary BAR for early I2C bus access */
+#define PRERAM_I2C_BASE_ADDRESS(x) (0xfe020000 + (0x1000 * (x)))
#endif /* _SOC_APOLLOLAKE_IOMAP_H_ */