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 <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/87960
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
This commit is contained in:
Patrick Rudolph 2025-06-05 09:12:24 +02:00 committed by Felix Held
commit f4825e5c12
7 changed files with 101 additions and 31 deletions

View file

@ -1,28 +1,57 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <device/mmio.h>
#include <acpi/acpigen.h>
#include <amdblocks/aoac.h>
#include <amdblocks/i2c.h>
#include <console/console.h>
#include <device/device.h>
#include <types.h>
#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)

View file

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

View file

@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/i2c.h>
#include <soc/aoac_defs.h>
#include <soc/iomap.h>
#include <types.h>
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)

View file

@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/i2c.h>
#include <soc/aoac_defs.h>
#include <soc/iomap.h>
#include <types.h>
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)

View file

@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/i2c.h>
#include <soc/aoac_defs.h>
#include <soc/iomap.h>
#include <types.h>
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)

View file

@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/i2c.h>
#include <soc/aoac_defs.h>
#include <soc/iomap.h>
#include <types.h>
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)

View file

@ -1,14 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/i2c.h>
#include <soc/aoac_defs.h>
#include <soc/iomap.h>
#include <types.h>
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)