Uwe Poeche has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/36062 )
Change subject: soc/intel/fsp_baytrail: use designware I2C driver ......................................................................
soc/intel/fsp_baytrail: use designware I2C driver
Refactor I2C driver for fsp_baytrail to match the coreboot supported I2C bus device structure. The internal I2C controllers are now handled by the generic PCI driver approach and generic i2c access is enabled. All the I2C specific parts were removed from lpss.c and have been implemented in the I2C driver.
With this patch I2C chip devices can now be used in devicetree.
TEST=Booted siemens/tcu3 and verified that access to PTN3460 worked.
Change-Id: I3b87bd7c27e4c1afcce7cd4225cca02599f43c60 Signed-off-by: Uwe Poeche uwe.poeche@siemens.com --- M src/mainboard/siemens/mc_tcu3/mainboard.c M src/mainboard/siemens/mc_tcu3/ptn3460.c M src/soc/intel/fsp_baytrail/Kconfig M src/soc/intel/fsp_baytrail/Makefile.inc M src/soc/intel/fsp_baytrail/chip.h M src/soc/intel/fsp_baytrail/i2c.c M src/soc/intel/fsp_baytrail/include/soc/i2c.h M src/soc/intel/fsp_baytrail/include/soc/iomap.h M src/soc/intel/fsp_baytrail/lpss.c 9 files changed, 313 insertions(+), 321 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/62/36062/1
diff --git a/src/mainboard/siemens/mc_tcu3/mainboard.c b/src/mainboard/siemens/mc_tcu3/mainboard.c index 9e12aa5..e77c0b1 100644 --- a/src/mainboard/siemens/mc_tcu3/mainboard.c +++ b/src/mainboard/siemens/mc_tcu3/mainboard.c @@ -70,9 +70,15 @@ */ static void mainboard_enable(struct device *dev) { + +} + +static void mainboard_final(void *chip_info) +{ setup_lcd_panel(); }
struct chip_operations mainboard_ops = { .enable_dev = mainboard_enable, + .final = mainboard_final, }; diff --git a/src/mainboard/siemens/mc_tcu3/ptn3460.c b/src/mainboard/siemens/mc_tcu3/ptn3460.c index 414baeb..cfaeee4 100644 --- a/src/mainboard/siemens/mc_tcu3/ptn3460.c +++ b/src/mainboard/siemens/mc_tcu3/ptn3460.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 @@ -18,7 +18,7 @@ #include <string.h> #include <delay.h> #include <types.h> - +#include <device/i2c_simple.h> #include "soc/i2c.h" #include "ptn3460.h"
@@ -34,31 +34,24 @@ uint8_t disp_con = 0, color_depth = 0; uint8_t edid_data[0x80]; uint8_t hwid[4], tcu31_hwid[4] = {7, 9, 2, 0}; + uint8_t i;
if (!hwi_block || hwilib_find_blocks(hwi_block) != CB_SUCCESS) { - printk(BIOS_ERR, "LCD: Info block "%s" not found!\n", - hwi_block); + printk(BIOS_ERR, "LCD: Info block "%s" not found!\n", hwi_block); return 1; }
- status = i2c_init(PTN_I2C_CONTROLLER); - if (status) - return (PTN_BUS_ERROR | status); - /* Get all needed information from hwinfo block */ if (hwilib_get_field(Edid, edid_data, 0x80) != sizeof(edid_data)) { - printk(BIOS_ERR, "LCD: No EDID data available in %s\n", - hwi_block); + printk(BIOS_ERR, "LCD: No EDID data available in %s\n", hwi_block); return 1; } if ((hwilib_get_field(PF_DisplCon, &disp_con, 1) != 1)) { - printk(BIOS_ERR, "LCD: Missing panel features from %s\n", - hwi_block); + printk(BIOS_ERR, "LCD: Missing panel features from %s\n", hwi_block); return 1; } if (hwilib_get_field(PF_Color_Depth, &color_depth, 1) != 1) { - printk(BIOS_ERR, "LCD: Missing panel features from %s\n", - hwi_block); + printk(BIOS_ERR, "LCD: Missing panel features from %s\n", hwi_block); return 1; } /* Here, all the desired information for setting up DP2LVDS converter*/ @@ -69,8 +62,8 @@ /* Select this table to be emulated */ ptn_select_edid(6); /* Read PTN configuration data */ - status = i2c_read(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF, - (u8*)&cfg, PTN_CONFIG_LEN); + status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF, (u8 *) &cfg, + sizeof(struct ptn_3460_config)); if (status) return (PTN_BUS_ERROR | status);
@@ -81,28 +74,29 @@ cfg.lvds_interface_ctrl1 |= 0x0b; /* Turn on dual LVDS lane and clock */ if (color_depth == PF_COLOR_DEPTH_6BIT) cfg.lvds_interface_ctrl1 |= 0x20; /* Use 18 bits per pixel */ - - cfg.lvds_interface_ctrl2 = 0x03; /* no clock spreading, 300 mV LVDS swing */ + cfg.lvds_interface_ctrl2 = 0x03; /* no clock spreading, 300 mV LVDS swing */ /* Swap LVDS even and odd lanes for HW-ID 7.9.2.0 only. */ if (hwilib_get_field(HWID, hwid, sizeof(hwid)) == sizeof(hwid) && !(memcmp(hwid, tcu31_hwid, sizeof(hwid)))) { cfg.lvds_interface_ctrl3 = 0x01; /* swap LVDS even and odd */ } else cfg.lvds_interface_ctrl3 = 0x00; /* no LVDS signal swap */ - cfg.t2_delay = 1; /* Delay T2 (VDD to LVDS active) by 16 ms */ - cfg.t3_timing = 10; /* 500 ms from LVDS to backlight active */ - cfg.t12_timing = 20; /* 1 second re-power delay */ - cfg.t4_timing = 3; /* 150 ms backlight off to LVDS inactive */ - cfg.t5_delay = 1; /* Delay T5 (LVDS to VDD inactive) by 16 ms */ - cfg.backlight_ctrl = 0; /* Enable backlight control */ + cfg.t2_delay = 1; /* Delay T2 (VDD to LVDS active) by 16 ms */ + cfg.t3_timing = 10; /* 500 ms from LVDS to backlight active */ + cfg.t12_timing = 20; /* 1 second re-power delay */ + cfg.t4_timing = 3; /* 150 ms backlight off to LVDS inactive */ + cfg.t5_delay = 1; /* Delay T5 (LVDS to VDD inactive) by 16 ms */ + cfg.backlight_ctrl = 0; /* Enable backlight control */
/* Write back configuration data to PTN3460 */ - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF, - (u8*)&cfg, PTN_CONFIG_LEN); - if (status) - return (PTN_BUS_ERROR | status); - else - return PTN_NO_ERROR; + for (i = 0; i < sizeof(struct ptn_3460_config); i++) { + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + i, + *(((uint8_t *) &cfg) + i)); + if (status) + return (PTN_BUS_ERROR | status); + } + + return PTN_NO_ERROR; }
/** \brief This functions reads one desired EDID data structure from PTN3460 @@ -117,14 +111,13 @@ if (edid_num > PTN_MAX_EDID_NUM) return PTN_INVALID_EDID; /* First enable access to the desired EDID table */ - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, - &edid_num, 1); + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, edid_num); if (status) return (PTN_BUS_ERROR | status);
/* Now we can simply read back EDID-data */ - status = i2c_read(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF, - data, PTN_EDID_LEN); + status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF, data, + PTN_EDID_LEN); if (status) return (PTN_BUS_ERROR | status); else @@ -139,22 +132,24 @@ int ptn3460_write_edid(u8 edid_num, u8 *data) { int status; + int i;
if (edid_num > PTN_MAX_EDID_NUM) return PTN_INVALID_EDID; + /* First enable access to the desired EDID table */ - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, - &edid_num, 1); + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, edid_num); if (status) return (PTN_BUS_ERROR | status);
/* Now we can simply write EDID-data to ptn3460 */ - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF, - data, PTN_EDID_LEN); - if (status) - return (PTN_BUS_ERROR | status); - else - return PTN_NO_ERROR; + for (i = 0; i < PTN_EDID_LEN; i++) { + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF + i, + data[i]); + if (status) + return (PTN_BUS_ERROR | status); + } + return PTN_NO_ERROR; }
/** \brief This functions selects one of 7 EDID-tables inside PTN3460 @@ -171,8 +166,7 @@ return PTN_INVALID_EDID; /* Enable emulation of the desired EDID table */ val = (edid_num << 1) | 1; - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 4, - &val, 1); + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 4, val); if (status) return (PTN_BUS_ERROR | status); else @@ -188,14 +182,19 @@ */ int ptn3460_flash_config(void) { - int status; + int status, i; struct ptn_3460_flash flash;
flash.cmd = 0x01; /* perform erase and flash cycle */ flash.magic = 0x7845; /* Magic number to protect flash operation */ flash.trigger = 0x56; /* This value starts flash operation */ - status = i2c_write(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_FLASH_CFG_OFF, - (u8*)&flash, PTN_FLASH_CFG_LEN); + + for (i = 0; i < sizeof(struct ptn_3460_flash); i++) { + status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_FLASH_CFG_OFF+i, + *(((uint8_t *) &flash) + i)); + if (status) + return (PTN_BUS_ERROR | status); + } if (status) { return (PTN_BUS_ERROR | status); } else { diff --git a/src/soc/intel/fsp_baytrail/Kconfig b/src/soc/intel/fsp_baytrail/Kconfig index 072df29..efe12da 100644 --- a/src/soc/intel/fsp_baytrail/Kconfig +++ b/src/soc/intel/fsp_baytrail/Kconfig @@ -44,6 +44,7 @@ select MICROCODE_BLOB_NOT_HOOKED_UP select INTEL_DESCRIPTOR_MODE_CAPABLE select HAVE_SPI_CONSOLE_SUPPORT + select DRIVERS_I2C_DESIGNWARE
# Microcode header files are delivered in FSP package select USES_MICROCODE_HEADER_FILES if HAVE_FSP_BIN @@ -103,6 +104,14 @@ string default "../intel/cpu/baytrail/microcode/M0130673322.h ../intel/cpu/baytrail/microcode/M0130679901.h ../intel/cpu/baytrail/microcode/M0230672228.h"
+config DRIVERS_I2C_DESIGNWARE_CLOCK_MHZ + int + default 133 + +config SOC_INTEL_I2C_DEV_MAX + int + default 7 + ## Baytrail Specific FSP Kconfig source src/soc/intel/fsp_baytrail/fsp/Kconfig
diff --git a/src/soc/intel/fsp_baytrail/Makefile.inc b/src/soc/intel/fsp_baytrail/Makefile.inc index fa71932..0d89832 100644 --- a/src/soc/intel/fsp_baytrail/Makefile.inc +++ b/src/soc/intel/fsp_baytrail/Makefile.inc @@ -3,7 +3,7 @@ # # Copyright (C) 2010 Google Inc. # Copyright (C) 2013-2014 Sage Electronic Engineering, LLC. -# Copyright (C) 2016 Siemens AG +# Copyright (C) 2016-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 @@ -34,6 +34,7 @@ romstage-y += pmutil.c romstage-y += spi.c romstage-y += tsc_freq.c +romstage-y += i2c.c
postcar-y += tsc_freq.c
diff --git a/src/soc/intel/fsp_baytrail/chip.h b/src/soc/intel/fsp_baytrail/chip.h index b73aa14..73e9f0d 100644 --- a/src/soc/intel/fsp_baytrail/chip.h +++ b/src/soc/intel/fsp_baytrail/chip.h @@ -20,6 +20,7 @@
#include <arch/acpi.h> #include <drivers/intel/fsp1_0/fsp_values.h> +#include <drivers/i2c/designware/dw_i2c.h>
/* The devicetree parser expects chip.h to reside directly in the path * specified by the devicetree. */ @@ -346,11 +347,13 @@ int lpe_codec_clk_freq; /* 19 or 25 are valid. */ int lpe_codec_clk_num; /* Platform clock pins. [0:5] are valid. */
+ /* Structure for designware I2C controller */ + struct dw_i2c_bus_config i2c[CONFIG_SOC_INTEL_I2C_DEV_MAX]; + /* ***** ACPI configuration ***** */ /* Options for these are in src/arch/x86/include/arch/acpi.h */ uint8_t fadt_pm_profile; uint16_t fadt_boot_arch; - };
#endif /* _FSP_BAYTRAIL_CHIP_H_ */ diff --git a/src/soc/intel/fsp_baytrail/i2c.c b/src/soc/intel/fsp_baytrail/i2c.c index 5f6ca467..c14fec7 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,284 @@ * GNU General Public License for more details. */
+#include <cbmem.h> +#include <device/device.h> +#include <device/pci_def.h> #include <device/pci.h> -#include <device/pci_ops.h> -#include <console/console.h> -#include <soc/baytrail.h> +#include <device/pci_ids.h> +#include <drivers/i2c/designware/dw_i2c.h> +#include <reg_script.h> #include <soc/pci_devs.h> -#include <soc/iosf.h> -#include <device/mmio.h> -#include <delay.h> +#include "chip.h" +#include <console/console.h> #include <soc/i2c.h> +#include <soc/iosf.h> +#include <soc/iomap.h> +#include <soc/nvs.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) +/* 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; - } - - /* 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; - } - - return I2C_SUCCESS; + if (bus <= 6) + return PCI_DEVFN(SIO1_DEV, bus + 1); + else + return -1; }
-/* 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) +/* Convert PCI device and function to I2C bus number */ +int dw_i2c_soc_dev_to_bus(struct device *dev) { - 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; - } - - /* 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; - } - - return I2C_SUCCESS; + 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; }
-/* 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) +/* Getting I2C bus configuration from devicetree config */ +const struct dw_i2c_bus_config *dw_i2c_get_soc_cfg(unsigned int bus) { - int i; - int status; + const struct soc_intel_fsp_baytrail_config *config; + const struct device *dev = pcidev_path_on_root(SOC_DEVFN_SOC);
- /* 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; + 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"); }
-/** \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) +#if !ENV_RAMSTAGE +static int lpss_i2c_early_init_bus(unsigned int bus) { - 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; + const struct dw_i2c_bus_config *config; + const struct device *tree_dev; + pci_devfn_t dev; + int devfn; + uintptr_t base;
- /* 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; + /* 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; }
- 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); - - /* 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; + /* 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; }
- /* Set memory base */ - pci_write_config32(dev, PCI_BASE_ADDRESS_0, (int)base_ptr); + /* 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; + }
- /* Enable memory space */ + /* 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_read_config32(dev, PCI_COMMAND) | 0x2)); + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- /* 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); + /* Take device out of reset */ + write32((void *)((uint32_t)base + I2C_SOFTWARE_RESET), I2C_RESET_APB | I2C_RESET_FUNC);
- /* Enable the I2C controller for operation */ - write32(base_ptr + I2C_ENABLE, 0x1); + /* Initialize the controller */ + if (dw_i2c_init(bus, config) < 0) { + printk(BIOS_ERR, "I2C%u failed to initialize\n", bus); + return -1; + }
- printk(BIOS_INFO, "I2C: Controller %d enabled.\n", bus); - return I2C_SUCCESS; + return 0; }
-/** \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) +uintptr_t dw_i2c_base_address(unsigned int bus) { - int i = 0; - char *base_ptr = NULL; - struct device *dev; - unsigned int val; - int stat; + int devfn; + pci_devfn_t dev; + uintptr_t base;
- /* 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; - } + /* Find device+function for this controller */ + devfn = dw_i2c_soc_bus_to_devfn(bus); + if (devfn < 0) + return 0;
- /* Ensure I2C controller is not active before setting slave address */ - stat = wait_for_idle(base_ptr); - if (stat != I2C_SUCCESS) - return stat; + /* Form a PCI address for this device */ + dev = PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn));
- /* clear any abort status from a previous transaction */ - read32(base_ptr + I2C_CLR_TX_ABRT); + /* Read the first base address for this device */ + base = ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0), 16);
- /* 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; - } - } - return I2C_SUCCESS; + /* Attempt to initialize bus if base is not set yet */ + if (!base && !lpss_i2c_early_init_bus(bus)) + base = ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0), 16); + return base; } +#else
-/** \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) +uintptr_t dw_i2c_base_address(unsigned int bus) { - int i; - char *base_ptr; + int devfn; struct device *dev; - unsigned int val; - int stat; + struct resource *res = NULL;
- /* 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; + /* 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 */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); }
- /* Ensure I2C controller is not active yet */ - stat = wait_for_idle(base_ptr); - if (stat) { - return stat; - } - - /* 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); - } - return I2C_SUCCESS; + if (res) + return res->base; + else + return (uintptr_t)NULL; } + +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; + } + + /* Save BAR0 and BAR1 to ACPI NVS */ + bar = find_resource(dev, PCI_BASE_ADDRESS_0); + if (bar) + gnvs->dev.lpss_bar0[nvs_index] = (u32)bar->base; + + bar = find_resource(dev, PCI_BASE_ADDRESS_1); + if (bar) + gnvs->dev.lpss_bar1[nvs_index] = (u32)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); +} + +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); +} + +static void dev_ctl_reg(struct device *dev, int *iosf_reg, int *nvs_index) +{ + int bus; + + 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; + } +} + +static void i2c_disable_resets(struct device *dev) +{ + uint32_t base; + + 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); +} + +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; + } + dev_enable_snoop_and_pm(dev, iosf_reg); + i2c_disable_resets(dev); + + if (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 + * its ready for initialization sequence. + */ +static void dw_i2c_device_init(struct device *dev) +{ + uintptr_t base_address; + int bus = dw_i2c_soc_dev_to_bus(dev); + + if (bus < 0) + return; + + base_address = dw_i2c_base_address(bus); + if (!base_address) + 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 diff --git a/src/soc/intel/fsp_baytrail/include/soc/i2c.h b/src/soc/intel/fsp_baytrail/include/soc/i2c.h index f0ae0b3..731ea85 100644 --- a/src/soc/intel/fsp_baytrail/include/soc/i2c.h +++ b/src/soc/intel/fsp_baytrail/include/soc/i2c.h @@ -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 @@ -85,6 +85,9 @@ #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 */ +#define I2C_SOFTWARE_RESET 0x804 +#define I2C_RESET_APB (1 << 1) +#define I2C_RESET_FUNC (1 << 0)
/* There are a bunch of interrupt clearing registers now which are not used! */ /* So proceed somewhat later with definition */ @@ -120,12 +123,4 @@ #define I2C_ERR 0x8000000 #define I2C_SUCCESS 0x0000000
- -#define I2C_TIMEOUT_US 2000 /* Use 2000 us as time */ - -/* 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/include/soc/iomap.h b/src/soc/intel/fsp_baytrail/include/soc/iomap.h index 11c01e3..d54d3fc 100644 --- a/src/soc/intel/fsp_baytrail/include/soc/iomap.h +++ b/src/soc/intel/fsp_baytrail/include/soc/iomap.h @@ -65,6 +65,8 @@
/* Temporary Base Address */ #define TEMP_BASE_ADDRESS 0xfd000000 +#define EARLY_I2C_BASE_ADDRESS 0xfe020000 +#define EARLY_I2C_BASE(x) (EARLY_I2C_BASE_ADDRESS + (0x2000 * (x)))
/* * IO Port bases. diff --git a/src/soc/intel/fsp_baytrail/lpss.c b/src/soc/intel/fsp_baytrail/lpss.c index 154a70a..2ad6fb3 100644 --- a/src/soc/intel/fsp_baytrail/lpss.c +++ b/src/soc/intel/fsp_baytrail/lpss.c @@ -89,20 +89,6 @@ switch (dev->path.pci.devfn) { SET_IOSF_REG(SIO_DMA1); break; - SET_IOSF_REG(I2C1); - break; - SET_IOSF_REG(I2C2); - break; - SET_IOSF_REG(I2C3); - break; - SET_IOSF_REG(I2C4); - break; - SET_IOSF_REG(I2C5); - break; - SET_IOSF_REG(I2C6); - break; - SET_IOSF_REG(I2C7); - break; SET_IOSF_REG(SIO_DMA2); break; SET_IOSF_REG(PWM1); @@ -118,33 +104,6 @@ } }
-static void i2c_disable_resets(struct device *dev) -{ - /* Release the I2C devices from reset. */ - static const struct reg_script ops[] = { - REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x804, 0x3), - REG_SCRIPT_END, - }; - -#define CASE_I2C(name_) \ - case PCI_DEVFN(name_ ## _DEV, name_ ## _FUNC) - - switch (dev->path.pci.devfn) { - CASE_I2C(I2C1): - CASE_I2C(I2C2): - CASE_I2C(I2C3): - CASE_I2C(I2C4): - CASE_I2C(I2C5): - CASE_I2C(I2C6): - CASE_I2C(I2C7): - printk(BIOS_DEBUG, "Releasing I2C device from reset.\n"); - reg_script_run_on_dev(dev, ops); - break; - default: - return; - } -} - static void lpss_init(struct device *dev) { struct soc_intel_fsp_baytrail_config *config = config_of(dev); @@ -160,7 +119,6 @@ return; } dev_enable_snoop_and_pm(dev, iosf_reg); - i2c_disable_resets(dev);
if (config->PcdLpssSioEnablePciMode == LPSS_PCI_MODE_DISABLE) dev_enable_acpi_mode(dev, iosf_reg, nvs_index); @@ -178,13 +136,6 @@
static const unsigned short pci_device_ids[] = { SIO_DMA1_DEVID, - I2C1_DEVID, - I2C2_DEVID, - I2C3_DEVID, - I2C4_DEVID, - I2C5_DEVID, - I2C6_DEVID, - I2C7_DEVID, SIO_DMA2_DEVID, PWM1_DEVID, PWM2_DEVID,