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.
As orientation for the I2C code the actual solution from
soc/intel/apollolake I2C was taken. All the I2C specific parts were
removed from lpss.c and have been implemented in the I2C driver.
Future merge to soc/intel/common/block/i2c/i2c.c would be possible.

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>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36062
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
This commit is contained in:
Uwe Poeche 2019-09-24 09:44:58 +02:00 committed by Patrick Georgi
parent 285975dbba
commit ed309e58b0
9 changed files with 307 additions and 415 deletions

View File

@ -69,10 +69,16 @@ enum cb_err mainboard_get_mac_address(struct device *dev, uint8_t mac[6])
* This is the earliest point to add customization.
*/
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,
};

View File

@ -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,6 +34,7 @@ int ptn3460_init(char *hwi_block)
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",
@ -41,10 +42,6 @@ int ptn3460_init(char *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",
@ -69,8 +66,8 @@ int ptn3460_init(char *hwi_block)
/* 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,7 +78,6 @@ int ptn3460_init(char *hwi_block)
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 */
/* Swap LVDS even and odd lanes for HW-ID 7.9.2.0 only. */
if (hwilib_get_field(HWID, hwid, sizeof(hwid)) == sizeof(hwid) &&
@ -89,20 +85,22 @@ int ptn3460_init(char *hwi_block)
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 +115,13 @@ int ptn3460_read_edid(u8 edid_num, u8 *data)
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 +136,24 @@ int ptn3460_read_edid(u8 edid_num, u8 *data)
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 +170,7 @@ int ptn_select_edid (u8 edid_num)
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 +186,19 @@ int ptn_select_edid (u8 edid_num)
*/
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 {

View File

@ -44,6 +44,7 @@ config CPU_SPECIFIC_OPTIONS
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 @@ config CPU_MICROCODE_HEADER_FILES
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

View File

@ -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 += memmap.c
romstage-y += pmutil.c
romstage-y += spi.c
romstage-y += tsc_freq.c
romstage-y += i2c.c
postcar-y += tsc_freq.c

View File

@ -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,6 +347,9 @@ struct soc_intel_fsp_baytrail_config {
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;

View File

@ -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;
}
/* 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 = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & 0xfffffff0;
/* 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 = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & 0xfffffff0;
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 *bar = 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 */
bar = 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 (bar)
return bar->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] = (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);
}
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 && (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

View File

@ -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
@ -16,116 +16,8 @@
#ifndef __SOC_INTEL_FSP_BAYTRAIL_I2C_H__
#define __SOC_INTEL_FSP_BAYTRAIL_I2C_H__
#include <device/pci_def.h>
#include <stdlib.h>
/* SMBus controller settings in PCI configuration space */
#define I2C_PCI_VENDOR_ID 0x8086
#define I2C0_PCI_DEV_ID 0x0f41
#define I2C1_PCI_DEV_ID 0x0f42
#define I2C2_PCI_DEV_ID 0x0f43
#define I2C3_PCI_DEV_ID 0x0f44
#define I2C4_PCI_DEV_ID 0x0f45
#define I2C5_PCI_DEV_ID 0x0f46
#define I2C6_PCI_DEV_ID 0x0f47
#define I2C0_MEM_BASE 0xd0921000
#define I2C1_MEM_BASE 0xd0923000
#define I2C2_MEM_BASE 0xd0925000
#define I2C3_MEM_BASE 0xd0927000
#define I2C4_MEM_BASE 0xd0929000
#define I2C5_MEM_BASE 0xd092b000
#define I2C6_MEM_BASE 0xd092d000
#define I2C_STANDARD_MODE 0x1
#define I2C_FAST_MODE 0x2
/* Define relevant registers in PCI space */
#define I2C_PCI_COMMAND 0x4
#define I2C_PCI_STATUS 0x6
/* Define memory mapped registers */
#define I2C_CTRL 0x0
#define I2C_SLAVE_DISABLE 0x40
#define I2C_RESTART_EN 0x20
#define I2C_ADR_MODE 0x10
#define I2C_SPEED_MASK 0x6
#define I2C_STD_MODE 0x1
#define I2C_FAST_MODE 0x2
#define I2C_MASTER_ENABLE 0x1
#define I2C_TARGET_ADR 0x4
#define I2C_TARGET_ADR_MASK 0x3ff
#define I2C_DATA_CMD 0x10
#define I2C_RESTART 0x400
#define I2C_STOP 0x200
#define I2C_RW_CMD 0x100
#define I2C_SS_SCL_HCNT 0x14 /* Counter for high period for 100 kHz SCL */
#define I2C_SS_SCL_LCNT 0x18 /* Counter for low period for 100 kHz SCL */
#define I2C_FS_SCL_HCNT 0x1c /* Counter for high period for 400 kHz SCL */
#define I2C_FS_SCL_LCNT 0x20 /* Counter for low period for 400 kHz SCL */
#define I2C_INTR_STAT 0x2c /* Interrupt status register, read only */
#define I2C_INTR_MASK 0x30 /* Interrupt mask register */
#define I2C_RAW_INTR_STAT 0x34 /* Raw interrupt status, read only */
#define I2C_START_DETECT 0x400
#define I2C_STOP_DETECT 0x200
#define I2C_ACTIVITY 0x100
#define I2C_TX_ABORT 0x40
#define I2C_RD_REQ 0x20 /* Read request in slave mode */
#define I2C_TX_EMPTY 0x10
#define I2C_TX_OVERFLOW 0x8
#define I2C_RX_FULL 0x4
#define I2C_RX_OVERFLOW 0x2
#define I2C_RX_UNDERFLOW 0x1
#define I2C_RX_TL 0x38 /* Rx FIFO threshold level 0..255 */
#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 */
/* There are a bunch of interrupt clearing registers now which are not used! */
/* So proceed somewhat later with definition */
#define I2C_ENABLE 0x6c /* 0: disable I2C controller, 1: enable */
#define I2C_STATUS 0x70
#define I2C_MST_ACTIVITY 0x20 /* Master FSM activity */
#define I2C_RFF 0x10 /* Receive FIFO completely full */
#define I2C_RFNE 0x8 /* Receive FIFO not empty */
#define I2C_TFE 0x4 /* Transmit FIFO completely empty */
#define I2C_TFNF 0x2 /* Transmit FIFO not full */
#define I2C_ACTIVE 0x1 /* 1: I2C currently in operation */
#define I2C_TXFLR 0x74 /* Current transmit FIFO level */
#define I2C_RXFLR 0x78 /* Current receive FIFO level */
#define I2C_SDA_HOLD 0x7c /* Data hold time after SCL goes low */
#define I2C_ABORT_SOURCE 0x80
#define I2C_ARB_LOST 0x1000 /* Arbitration lost */
#define I2C_MASTER_DIS 0x800 /* Master was disabled by user */
#define I2C_10B_RD_NORSTRT 0x400 /* 10 bit address read and RESTART disabled */
#define I2C_SBYTE_NORSTRT 0x200 /* START with RESTART disabled */
#define I2C_START_ACKDET 0x80 /* START byte was acknowledged */
#define I2C_TX_DATA_NOACK 0x8 /* TX data not acknowledged */
#define I2C_10B_ADR2_NOACK 0x4 /* Second address byte in 10 bit mode NACK */
#define I2C_10B_ADR1_NOACK 0x2 /* First address byte in 10 bit NACK */
#define I2C_7B_ADDR_NACK 0x1 /* 7 bit address byte not acknowledged */
#define I2C_ENABLE_STATUS 0x9c
/* Define some status and error values */
#define I2C_ERR_INVALID_ADR 0x1000000
#define I2C_ERR_TIMEOUT 0x2000000
#define I2C_ERR_ABORT 0x4000000
#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);
#define I2C_SOFTWARE_RESET 0x804
#define I2C_RESET_APB (1 << 1)
#define I2C_RESET_FUNC (1 << 0)
#endif /* __SOC_INTEL_FSP_BAYTRAIL_I2C_H__ */

View File

@ -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.

View File

@ -89,20 +89,6 @@ static void dev_ctl_reg(struct device *dev, int *iosf_reg, int *nvs_index)
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 dev_ctl_reg(struct device *dev, int *iosf_reg, int *nvs_index)
}
}
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 @@ static void lpss_init(struct device *dev)
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 struct device_operations device_ops = {
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,