diff --git a/src/soc/intel/skylake/Kconfig b/src/soc/intel/skylake/Kconfig index 9278cf1aa2..a134d1c500 100644 --- a/src/soc/intel/skylake/Kconfig +++ b/src/soc/intel/skylake/Kconfig @@ -37,6 +37,7 @@ config CPU_SPECIFIC_OPTIONS select RELOCATABLE_RAMSTAGE select SOC_INTEL_COMMON select SOC_INTEL_COMMON_ACPI_WAKE_SOURCE + select SOC_INTEL_COMMON_LPSS_I2C select SOC_INTEL_COMMON_RESET select SMM_TSEG select SMP @@ -72,6 +73,10 @@ config CPU_ADDR_BITS int default 36 +config SOC_INTEL_COMMON_LPSS_I2C_CLOCK_MHZ + int + default 120 + config DCACHE_RAM_BASE hex "Base address of cache-as-RAM" default 0xfef00000 diff --git a/src/soc/intel/skylake/chip.h b/src/soc/intel/skylake/chip.h index 1d3113f481..fe03e59451 100644 --- a/src/soc/intel/skylake/chip.h +++ b/src/soc/intel/skylake/chip.h @@ -20,6 +20,7 @@ #define _SOC_CHIP_H_ #include +#include #include #include #include @@ -39,6 +40,10 @@ enum skylake_i2c_voltage { struct skylake_i2c_config { /* Bus voltage level, default is 3.3V */ enum skylake_i2c_voltage voltage; + /* Bus speed in Hz, default is I2C_SPEED_FAST (400 KHz) */ + enum i2c_speed speed; + /* Bus should be enabled prior to ramstage with temporary base */ + int early_init; }; struct soc_intel_skylake_config { diff --git a/src/soc/intel/skylake/i2c.c b/src/soc/intel/skylake/i2c.c index 76e7bf1015..64d39cddb2 100644 --- a/src/soc/intel/skylake/i2c.c +++ b/src/soc/intel/skylake/i2c.c @@ -14,16 +14,66 @@ */ #include +#include #include #include +#include #include +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 SiliconInit in FSP. + */ +static void i2c_dev_init(struct device *dev) +{ + struct soc_intel_skylake_config *config = dev->chip_info; + int bus = i2c_dev_to_bus(dev); + + if (!config || bus < 0) + return; + + lpss_i2c_init(bus, config->i2c[bus].speed ? : I2C_SPEED_FAST); +} + +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_pci = &soc_pci_ops, + .ops_i2c_bus = &i2c_bus_ops, + .init = &i2c_dev_init, }; static const unsigned short pci_device_ids[] = { diff --git a/src/soc/intel/skylake/include/soc/iomap.h b/src/soc/intel/skylake/include/soc/iomap.h index 038fe7da70..feba3027e7 100644 --- a/src/soc/intel/skylake/include/soc/iomap.h +++ b/src/soc/intel/skylake/include/soc/iomap.h @@ -29,6 +29,9 @@ #define UART_DEBUG_BASE_ADDRESS 0xfe034000 #define UART_DEBUG_BASE_SIZE 0x1000 +#define EARLY_I2C_BASE_ADDRESS 0xfe040000 +#define EARLY_I2C_BASE(x) (EARLY_I2C_BASE_ADDRESS + (0x1000 * (x))) + #define MCH_BASE_ADDRESS 0xfed10000 #define MCH_BASE_SIZE 0x8000 diff --git a/src/soc/intel/skylake/include/soc/pci_devs.h b/src/soc/intel/skylake/include/soc/pci_devs.h index 74fd1c57b6..476e2c6a56 100644 --- a/src/soc/intel/skylake/include/soc/pci_devs.h +++ b/src/soc/intel/skylake/include/soc/pci_devs.h @@ -17,6 +17,7 @@ #ifndef _SOC_PCI_DEVS_H_ #define _SOC_PCI_DEVS_H_ +#include #include #define _SA_DEVFN(slot) PCI_DEVFN(SA_DEV_SLOT_ ## slot, 0) @@ -147,4 +148,32 @@ #define PCH_DEV_SPI _PCH_DEV(LPC, 5) #define PCH_DEV_GBE _PCH_DEV(LPC, 6) +/* Convert I2C bus number to PCI device and function */ +static inline int i2c_bus_to_devfn(unsigned bus) +{ + switch (bus) { + case 0: return PCH_DEVFN_I2C0; + case 1: return PCH_DEVFN_I2C1; + case 2: return PCH_DEVFN_I2C2; + case 3: return PCH_DEVFN_I2C3; + case 4: return PCH_DEVFN_I2C4; + case 5: return PCH_DEVFN_I2C5; + } + return -1; +} + +/* Convert PCI device and function to I2C bus number */ +static inline int i2c_devfn_to_bus(unsigned devfn) +{ + switch (devfn) { + case PCH_DEVFN_I2C0: return 0; + case PCH_DEVFN_I2C1: return 1; + case PCH_DEVFN_I2C2: return 2; + case PCH_DEVFN_I2C3: return 3; + case PCH_DEVFN_I2C4: return 4; + case PCH_DEVFN_I2C5: return 5; + } + return -1; +} + #endif diff --git a/src/soc/intel/skylake/include/soc/romstage.h b/src/soc/intel/skylake/include/soc/romstage.h index 7fab8ce549..56bace15a7 100644 --- a/src/soc/intel/skylake/include/soc/romstage.h +++ b/src/soc/intel/skylake/include/soc/romstage.h @@ -19,6 +19,7 @@ #include +void i2c_early_init(void); void systemagent_early_init(void); void pch_early_init(void); void pch_uart_init(void); diff --git a/src/soc/intel/skylake/romstage/Makefile.inc b/src/soc/intel/skylake/romstage/Makefile.inc index 194091f811..6ae81378a4 100644 --- a/src/soc/intel/skylake/romstage/Makefile.inc +++ b/src/soc/intel/skylake/romstage/Makefile.inc @@ -1,4 +1,5 @@ verstage-y += cpu.c +verstage-y += i2c.c verstage-y += pch.c verstage-y += power_state.c verstage-y += report_platform.c @@ -9,6 +10,7 @@ verstage-y += systemagent.c verstage-y += uart.c romstage-y += cpu.c +romstage-y += i2c.c romstage-y += pch.c romstage-y += power_state.c romstage-y += report_platform.c diff --git a/src/soc/intel/skylake/romstage/i2c.c b/src/soc/intel/skylake/romstage/i2c.c new file mode 100644 index 0000000000..64e692420e --- /dev/null +++ b/src/soc/intel/skylake/romstage/i2c.c @@ -0,0 +1,97 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" + +uintptr_t lpss_i2c_base_address(unsigned bus) +{ + unsigned devfn; + pci_devfn_t dev; + + /* Find device+function for this controller */ + devfn = i2c_bus_to_devfn(bus); + if (devfn < 0) + return 0; + + /* 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 */ + return ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0), 16); +} + +static void i2c_early_init_bus(unsigned bus) +{ + ROMSTAGE_CONST struct soc_intel_skylake_config *config; + ROMSTAGE_CONST struct device *tree_dev; + 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) + return; + + /* 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) + return; + + /* Skip if not enabled for early init */ + config = tree_dev->chip_info; + if (!config) + return; + if (!config->i2c[bus].early_init) + return; + + /* 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 */ + reg = (void *)(base + SIO_REG_PPR_RESETS); + value = read32(reg); + value |= SIO_REG_PPR_RESETS_FUNC | SIO_REG_PPR_RESETS_APB | + SIO_REG_PPR_RESETS_IDMA; + write32(reg, value); + + /* Initialize the controller */ + lpss_i2c_init(bus, config->i2c[bus].speed ? : I2C_SPEED_FAST); +} + +void i2c_early_init(void) +{ + int bus; + + /* Initialize I2C controllers that are enabled in devicetree */ + for (bus = 0; bus < SKYLAKE_I2C_DEV_MAX; bus++) + i2c_early_init_bus(bus); +} diff --git a/src/soc/intel/skylake/romstage/romstage.c b/src/soc/intel/skylake/romstage/romstage.c index 9c61095214..8375ccde76 100644 --- a/src/soc/intel/skylake/romstage/romstage.c +++ b/src/soc/intel/skylake/romstage/romstage.c @@ -64,6 +64,7 @@ void car_soc_post_console_init(void) report_platform_info(); set_max_freq(); pch_early_init(); + i2c_early_init(); } int get_sw_write_protect_state(void)