From f4825e5c12fe1129c7862a61b91bb7e60f1e9b45 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Thu, 5 Jun 2025 09:12:24 +0200 Subject: [PATCH] soc/amd/common: Add I3C driver Add an I3C driver that allows to use the I3C HW from the OS. It does: - Power on/off the I3C HW - Configures the IOMUX Add the SoC specific AOAC devices and GPIO pins to reconfigure the GPIO for I3C HW. New log messages are seen in coreboot: [DEBUG] MMIO: fedd2000 disabled TEST: The I3C driver loads on amd/glinda using Ubuntu 25.04. Change-Id: Ibca20e2a4f0cb0e6006cfa47fd4addbe27504645 Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/c/coreboot/+/87960 Tested-by: build bot (Jenkins) Reviewed-by: Matt DeVillier Reviewed-by: Felix Held --- src/soc/amd/common/block/i2c/i3c.c | 86 ++++++++++++++++--- .../amd/common/block/include/amdblocks/i2c.h | 1 + src/soc/amd/genoa_poc/i3c.c | 9 +- src/soc/amd/glinda/i3c.c | 9 +- src/soc/amd/mendocino/i3c.c | 9 +- src/soc/amd/phoenix/i3c.c | 9 +- src/soc/amd/turin_poc/i3c.c | 9 +- 7 files changed, 101 insertions(+), 31 deletions(-) diff --git a/src/soc/amd/common/block/i2c/i3c.c b/src/soc/amd/common/block/i2c/i3c.c index 43eae71b67..4fbe6ae37b 100644 --- a/src/soc/amd/common/block/i2c/i3c.c +++ b/src/soc/amd/common/block/i2c/i3c.c @@ -1,28 +1,57 @@ /* SPDX-License-Identifier: GPL-2.0-only */ - +#include #include +#include #include #include #include #include -#if CONFIG(HAVE_ACPI_TABLES) -static const char *i3c_acpi_name(const struct device *dev) +static int get_i3c_bus_for_dev(const struct device *dev, + const struct soc_i3c_ctrlr_info *ctrlr, + const size_t num_ctrlrs) { - size_t i; - size_t num_ctrlrs; - const struct soc_i3c_ctrlr_info *ctrlr = soc_get_i3c_ctrlr_info(&num_ctrlrs); + if (!dev || !ctrlr) + return -1; if (!(uintptr_t)dev->path.mmio.addr) { printk(BIOS_ERR, "NULL MMIO address at %s\n", __func__); - return NULL; + return -1; } - for (i = 0; i < num_ctrlrs; i++) { - if ((uintptr_t)dev->path.mmio.addr == ctrlr[i].bar) - return ctrlr[i].acpi_name; + for (int i = 0; i < num_ctrlrs; i++) { + if (dev->path.mmio.addr != ctrlr[i].bar) + continue; + return i; } - printk(BIOS_ERR, "%s: Could not find %lu\n", __func__, (uintptr_t)dev->path.mmio.addr); + printk(BIOS_ERR, "%s: Could not find i3c bus for %s\n", __func__, dev_path(dev)); + + return -1; +} + +static const struct soc_i3c_ctrlr_info * +get_i3c_info_for_dev(const struct device *dev) +{ + size_t num_ctrlrs; + const struct soc_i3c_ctrlr_info *ctrlr = soc_get_i3c_ctrlr_info(&num_ctrlrs); + const int bus = get_i3c_bus_for_dev(dev, ctrlr, num_ctrlrs); + + if (ctrlr && bus >= 0 && bus < num_ctrlrs) + return &ctrlr[bus]; + + printk(BIOS_ERR, "%s: Could not find soc_i3c_ctrlr_info for %s\n", __func__, dev_path(dev)); + + return NULL; +} + +#if CONFIG(HAVE_ACPI_TABLES) +static const char *i3c_acpi_name(const struct device *dev) +{ + const struct soc_i3c_ctrlr_info *info = get_i3c_info_for_dev(dev); + if (info) + return info->acpi_name; + + printk(BIOS_ERR, "%s: Could not find soc_i3c_ctrlr_info for %s\n", __func__, dev_path(dev)); return NULL; } @@ -34,12 +63,47 @@ static void i3c_acpi_fill_ssdt(const struct device *dev) } #endif +/* Even though this is called enable, it gets called for both enabled and disabled devices. */ +static void i3c_enable(struct device *dev) +{ + const struct soc_i3c_ctrlr_info *info = get_i3c_info_for_dev(dev); + if (!info) + return; + + if (dev->enabled) { + power_on_aoac_device(info->aoac_device); + wait_for_aoac_enabled(info->aoac_device); + } else { + power_off_aoac_device(info->aoac_device); + } +} + +static void i3c_init(struct device *dev) +{ + size_t num_ctrlrs, num_buses; + const struct soc_i3c_ctrlr_info *ctrlr = soc_get_i3c_ctrlr_info(&num_ctrlrs); + const int bus = get_i3c_bus_for_dev(dev, ctrlr, num_ctrlrs); + if (bus < 0) + return; + + const struct dw_i2c_bus_config *cfg = soc_get_i2c_bus_config(&num_buses); + + if (cfg) + soc_i2c_misc_init(bus, cfg); +} + static void i3c_read_resources(struct device *dev) { mmio_range(dev, 0, dev->path.mmio.addr, 4 * KiB); } +/* + * Currently there's no I3C driver, thus the I3C hardware cannot be used pre ramstage. + * It's sufficient to power switch the I3C controllers in the enable method. + */ struct device_operations soc_amd_i3c_mmio_ops = { + .enable = i3c_enable, + .init = i3c_init, .read_resources = i3c_read_resources, .set_resources = noop_set_resources, #if CONFIG(HAVE_ACPI_TABLES) diff --git a/src/soc/amd/common/block/include/amdblocks/i2c.h b/src/soc/amd/common/block/include/amdblocks/i2c.h index fc1a03217a..553561b7fc 100644 --- a/src/soc/amd/common/block/include/amdblocks/i2c.h +++ b/src/soc/amd/common/block/include/amdblocks/i2c.h @@ -74,6 +74,7 @@ struct i2c_pad_control { struct soc_i3c_ctrlr_info { uintptr_t bar; const char *acpi_name; + unsigned int aoac_device; }; void fch_i2c_pad_init(unsigned int bus, diff --git a/src/soc/amd/genoa_poc/i3c.c b/src/soc/amd/genoa_poc/i3c.c index e694623177..0e041942b6 100644 --- a/src/soc/amd/genoa_poc/i3c.c +++ b/src/soc/amd/genoa_poc/i3c.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include +#include #include #include static const struct soc_i3c_ctrlr_info i3c_ctrlr[I3C_CTRLR_COUNT] = { - { APU_I3C0_BASE, "I3C0" }, - { APU_I3C1_BASE, "I3C1" }, - { APU_I3C2_BASE, "I3C2" }, - { APU_I3C3_BASE, "I3C3" } + { APU_I3C0_BASE, "I3C0", FCH_AOAC_DEV_I3C0}, + { APU_I3C1_BASE, "I3C1", FCH_AOAC_DEV_I3C1}, + { APU_I3C2_BASE, "I3C2", FCH_AOAC_DEV_I3C2}, + { APU_I3C3_BASE, "I3C3", FCH_AOAC_DEV_I3C3} }; const struct soc_i3c_ctrlr_info *soc_get_i3c_ctrlr_info(size_t *num_ctrlrs) diff --git a/src/soc/amd/glinda/i3c.c b/src/soc/amd/glinda/i3c.c index e694623177..0e041942b6 100644 --- a/src/soc/amd/glinda/i3c.c +++ b/src/soc/amd/glinda/i3c.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include +#include #include #include static const struct soc_i3c_ctrlr_info i3c_ctrlr[I3C_CTRLR_COUNT] = { - { APU_I3C0_BASE, "I3C0" }, - { APU_I3C1_BASE, "I3C1" }, - { APU_I3C2_BASE, "I3C2" }, - { APU_I3C3_BASE, "I3C3" } + { APU_I3C0_BASE, "I3C0", FCH_AOAC_DEV_I3C0}, + { APU_I3C1_BASE, "I3C1", FCH_AOAC_DEV_I3C1}, + { APU_I3C2_BASE, "I3C2", FCH_AOAC_DEV_I3C2}, + { APU_I3C3_BASE, "I3C3", FCH_AOAC_DEV_I3C3} }; const struct soc_i3c_ctrlr_info *soc_get_i3c_ctrlr_info(size_t *num_ctrlrs) diff --git a/src/soc/amd/mendocino/i3c.c b/src/soc/amd/mendocino/i3c.c index e694623177..0e041942b6 100644 --- a/src/soc/amd/mendocino/i3c.c +++ b/src/soc/amd/mendocino/i3c.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include +#include #include #include static const struct soc_i3c_ctrlr_info i3c_ctrlr[I3C_CTRLR_COUNT] = { - { APU_I3C0_BASE, "I3C0" }, - { APU_I3C1_BASE, "I3C1" }, - { APU_I3C2_BASE, "I3C2" }, - { APU_I3C3_BASE, "I3C3" } + { APU_I3C0_BASE, "I3C0", FCH_AOAC_DEV_I3C0}, + { APU_I3C1_BASE, "I3C1", FCH_AOAC_DEV_I3C1}, + { APU_I3C2_BASE, "I3C2", FCH_AOAC_DEV_I3C2}, + { APU_I3C3_BASE, "I3C3", FCH_AOAC_DEV_I3C3} }; const struct soc_i3c_ctrlr_info *soc_get_i3c_ctrlr_info(size_t *num_ctrlrs) diff --git a/src/soc/amd/phoenix/i3c.c b/src/soc/amd/phoenix/i3c.c index e694623177..0e041942b6 100644 --- a/src/soc/amd/phoenix/i3c.c +++ b/src/soc/amd/phoenix/i3c.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include +#include #include #include static const struct soc_i3c_ctrlr_info i3c_ctrlr[I3C_CTRLR_COUNT] = { - { APU_I3C0_BASE, "I3C0" }, - { APU_I3C1_BASE, "I3C1" }, - { APU_I3C2_BASE, "I3C2" }, - { APU_I3C3_BASE, "I3C3" } + { APU_I3C0_BASE, "I3C0", FCH_AOAC_DEV_I3C0}, + { APU_I3C1_BASE, "I3C1", FCH_AOAC_DEV_I3C1}, + { APU_I3C2_BASE, "I3C2", FCH_AOAC_DEV_I3C2}, + { APU_I3C3_BASE, "I3C3", FCH_AOAC_DEV_I3C3} }; const struct soc_i3c_ctrlr_info *soc_get_i3c_ctrlr_info(size_t *num_ctrlrs) diff --git a/src/soc/amd/turin_poc/i3c.c b/src/soc/amd/turin_poc/i3c.c index e694623177..0e041942b6 100644 --- a/src/soc/amd/turin_poc/i3c.c +++ b/src/soc/amd/turin_poc/i3c.c @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include +#include #include #include static const struct soc_i3c_ctrlr_info i3c_ctrlr[I3C_CTRLR_COUNT] = { - { APU_I3C0_BASE, "I3C0" }, - { APU_I3C1_BASE, "I3C1" }, - { APU_I3C2_BASE, "I3C2" }, - { APU_I3C3_BASE, "I3C3" } + { APU_I3C0_BASE, "I3C0", FCH_AOAC_DEV_I3C0}, + { APU_I3C1_BASE, "I3C1", FCH_AOAC_DEV_I3C1}, + { APU_I3C2_BASE, "I3C2", FCH_AOAC_DEV_I3C2}, + { APU_I3C3_BASE, "I3C3", FCH_AOAC_DEV_I3C3} }; const struct soc_i3c_ctrlr_info *soc_get_i3c_ctrlr_info(size_t *num_ctrlrs)