diff options
Diffstat (limited to 'src/soc/amd/common/block')
-rw-r--r-- | src/soc/amd/common/block/i2c/i2c.c | 120 | ||||
-rw-r--r-- | src/soc/amd/common/block/include/amdblocks/i2c.h | 34 |
2 files changed, 153 insertions, 1 deletions
diff --git a/src/soc/amd/common/block/i2c/i2c.c b/src/soc/amd/common/block/i2c/i2c.c index 59e885d807..95e25798d2 100644 --- a/src/soc/amd/common/block/i2c/i2c.c +++ b/src/soc/amd/common/block/i2c/i2c.c @@ -1,14 +1,132 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +#include <acpi/acpi.h> #include <assert.h> -#include <delay.h> #include <amdblocks/acpimmio.h> #include <amdblocks/gpio_banks.h> #include <amdblocks/gpio_defs.h> #include <amdblocks/i2c.h> +#include <console/console.h> +#include <delay.h> +#include <device/device.h> +#include <device/i2c.h> +#include <device/mmio.h> +#include <drivers/i2c/designware/dw_i2c.h> #define MAX_PIN_COUNT 4 +uintptr_t dw_i2c_base_address(unsigned int bus) +{ + size_t num_ctrlrs; + const struct soc_i2c_ctrlr_info *ctrlr = soc_get_i2c_ctrlr_info(&num_ctrlrs); + + if (bus >= num_ctrlrs) { + printk(BIOS_ERR, "Bus ID %d is >= number of I2C controllers %zu\n", + bus, num_ctrlrs); + return 0; + } + + return ctrlr[bus].bar; +} + +const struct dw_i2c_bus_config *dw_i2c_get_soc_cfg(unsigned int bus) +{ + size_t num_buses = 0; + const struct dw_i2c_bus_config *cfg = soc_get_i2c_bus_config(&num_buses); + + if (bus >= num_buses) { + printk(BIOS_ERR, "Bus ID %d is >= number of I2C buses %zu\n", bus, num_buses); + return NULL; + } + + return &cfg[bus]; +} + +static const char *i2c_acpi_name(const struct device *dev) +{ + size_t i; + size_t num_ctrlrs; + const struct soc_i2c_ctrlr_info *ctrlr = soc_get_i2c_ctrlr_info(&num_ctrlrs); + + if (!(uintptr_t)dev->path.mmio.addr) + die("NULL MMIO address at %s\n", __func__); + + for (i = 0; i < num_ctrlrs; i++) { + if ((uintptr_t)dev->path.mmio.addr == ctrlr[i].bar) + return ctrlr[i].acpi_name; + } + printk(BIOS_ERR, "%s: Could not find %lu\n", __func__, (uintptr_t)dev->path.mmio.addr); + return NULL; +} + +int dw_i2c_soc_dev_to_bus(const struct device *dev) +{ + size_t i; + size_t num_ctrlrs; + const struct soc_i2c_ctrlr_info *ctrlr = soc_get_i2c_ctrlr_info(&num_ctrlrs); + + if (!(uintptr_t)dev->path.mmio.addr) + die("NULL MMIO address at %s\n", __func__); + + for (i = 0; i < num_ctrlrs; i++) { + if ((uintptr_t)dev->path.mmio.addr == ctrlr[i].bar) + return i; + } + printk(BIOS_ERR, "%s: Could not find %lu\n", __func__, (uintptr_t)dev->path.mmio.addr); + return -1; +} + +void __weak soc_i2c_misc_init(unsigned int bus, const struct dw_i2c_bus_config *cfg) +{ + /* Nothing by default. */ +} + +static void dw_i2c_soc_init(bool is_early_init) +{ + unsigned int bus; + size_t num_buses = 0, num_ctrlrs = 0; + const struct dw_i2c_bus_config *cfg = soc_get_i2c_bus_config(&num_buses); + const struct soc_i2c_ctrlr_info *ctrlr = soc_get_i2c_ctrlr_info(&num_ctrlrs); + + /* Ensure that the number of controllers in devicetree and SoC match. */ + assert(num_buses == num_ctrlrs); + + for (bus = 0; bus < num_buses; bus++, cfg++, ctrlr++) { + /* + * Skip initialization when controller is in peripheral mode or base address + * is not configured or is not the expected stage to initialize. + */ + if (ctrlr->mode == I2C_PERIPHERAL_MODE || !ctrlr->bar || + cfg->early_init != is_early_init) + continue; + + if (dw_i2c_init(bus, cfg)) + printk(BIOS_ERR, "Failed to init i2c bus %d\n", bus); + continue; + + soc_i2c_misc_init(bus, cfg); + } +} + +void i2c_soc_early_init(void) +{ + dw_i2c_soc_init(true); +} + +void i2c_soc_init(void) +{ + dw_i2c_soc_init(false); +} + +struct device_operations soc_amd_i2c_mmio_ops = { + /* TODO(kramasub): Move I2C resource info here. */ + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .scan_bus = scan_smbus, + .acpi_name = i2c_acpi_name, + .acpi_fill_ssdt = dw_i2c_acpi_fill_ssdt, +}; + struct common_i2c_save { uint32_t control_value; uint8_t mux_value; diff --git a/src/soc/amd/common/block/include/amdblocks/i2c.h b/src/soc/amd/common/block/include/amdblocks/i2c.h index 9fa203bf57..497de6f28e 100644 --- a/src/soc/amd/common/block/include/amdblocks/i2c.h +++ b/src/soc/amd/common/block/include/amdblocks/i2c.h @@ -4,8 +4,27 @@ #define AMD_COMMON_BLOCK_I2C_H #include <amdblocks/gpio_banks.h> +#include <device/i2c.h> +#include <drivers/i2c/designware/dw_i2c.h> #include <types.h> +/* Enum to identify in which mode the I2C controller is operating. */ +enum i2c_ctrlr_mode { + I2C_MASTER_MODE, + I2C_PERIPHERAL_MODE, +}; + +/** + * Data structure to hold SoC I2C controller information + * @bar: MMIO base address for the I2C bus. + * @acpi_name: ACPI Name corresponding to the I2C bus. + */ +struct soc_i2c_ctrlr_info { + enum i2c_ctrlr_mode mode; + uintptr_t bar; + const char *acpi_name; +}; + /** * Data structure to identify GPIO to be toggled to reset peripherals on an I2C bus. * @pin: GPIO corresponding to I2C SCL that needs to be toggled/bit-banged. @@ -30,6 +49,21 @@ struct soc_i2c_peripheral_reset_info { uint32_t num_pins; }; +/* Helper function to perform misc I2C configuration specific to SoC. */ +void soc_i2c_misc_init(unsigned int bus, const struct dw_i2c_bus_config *cfg); + +/* Getter function to get the SoC I2C Controller Information. */ +const struct soc_i2c_ctrlr_info *soc_get_i2c_ctrlr_info(size_t *num_ctrlrs); + +/* Getter function to get the SoC I2C bus configuration. */ +const struct dw_i2c_bus_config *soc_get_i2c_bus_config(size_t *num_buses); + +/* Initialize all the i2c buses that are marked with early init. */ +void i2c_soc_early_init(void); + +/* Initialize all the i2c buses that are not marked with early init. */ +void i2c_soc_init(void); + /* Reset I2C peripherals. */ void sb_reset_i2c_peripherals(const struct soc_i2c_peripheral_reset_info *reset_info); |