[coreboot-gerrit] Patch set updated for coreboot: soc/intel/apollolake: Add support for LPSS I2C driver

Duncan Laurie (dlaurie@chromium.org) gerrit at coreboot.org
Fri Jul 1 19:43:45 CEST 2016


Duncan Laurie (dlaurie at chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15480

-gerrit

commit 04f7c8e19572deb97515ff392b6c9e6b0be5dfde
Author: Duncan Laurie <dlaurie at chromium.org>
Date:   Mon Jun 27 10:57:13 2016 -0700

    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 at chromium.org>
---
 src/soc/intel/apollolake/Kconfig             |   8 +-
 src/soc/intel/apollolake/Makefile.inc        |   3 +
 src/soc/intel/apollolake/chip.h              |  17 ++++
 src/soc/intel/apollolake/i2c.c               | 126 +++++++++++++++++++++++++++
 src/soc/intel/apollolake/i2c_early.c         | 116 ++++++++++++++++++++++++
 src/soc/intel/apollolake/include/soc/i2c.h   |  50 +++++++++++
 src/soc/intel/apollolake/include/soc/iomap.h |   5 +-
 7 files changed, 323 insertions(+), 2 deletions(-)

diff --git a/src/soc/intel/apollolake/Kconfig b/src/soc/intel/apollolake/Kconfig
index 8e33015..cce06eb 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 030e35c..a64ee78 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 ef82c53..c83f973 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 0000000..1c16a06
--- /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 0000000..968e993
--- /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 0000000..da700f2
--- /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 716c2a6..e9caeca 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_ */



More information about the coreboot-gerrit mailing list