summaryrefslogtreecommitdiff
path: root/src/soc/amd/common/block
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/amd/common/block')
-rw-r--r--src/soc/amd/common/block/i2c/i2c.c120
-rw-r--r--src/soc/amd/common/block/include/amdblocks/i2c.h34
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);