summaryrefslogtreecommitdiff
path: root/src/soc/amd/common
diff options
context:
space:
mode:
authorKarthikeyan Ramasubramanian <kramasub@google.com>2021-03-08 23:23:50 -0700
committerMartin Roth <martinroth@google.com>2021-03-22 03:40:23 +0000
commit0dbea48d46013c004014a024ad8717d049e67c8d (patch)
treea379bc38d7af31ac93c1b8d08a5e081ee2eb185d /src/soc/amd/common
parent1a5d279120a93986f4272072b6c55c81b82bafe7 (diff)
soc/amd/common: Introduce I2C driver common to all AMD SoCs
I2C driver is replicated in each generation of AMD SoCs. Introduce a common I2C driver that can be used across all the AMD SoCs. To begin with, peripheral reset functionality is moved into this common driver. SoC specific I2C driver passes the SCL pin configuration in order for the common driver to reset the peripherals. More functionality can be moved here in subsequent changes. Also sb_reset_i2c_slaves() is renamed as sb_reset_i2c_peripherals() as an effort towards using inclusive language. BUG=None TEST=Build Dalboz and Grunt. Boot to OS in Dalboz. Ensure that the I2C peripherals are detected as earlier in Dalboz. localhost ~ # i2cdetect -y 0 Warning: Can't use SMBus Quick Write command, will skip some addresses 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 10: 20: 30: -- -- -- -- -- -- -- -- 40: 50: 50 51 -- -- -- -- -- -- 58 59 -- -- -- -- -- -- 60: 70: localhost ~ # i2cdetect -y 1 Warning: Can't use SMBus Quick Write command, will skip some addresses 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 10: 20: 30: -- -- -- -- -- -- -- -- 40: 50: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: 70: Change-Id: I9f735dcfe8375abdc88ff06e8c4f8a6b741bc085 Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com> Suggested-by: Kyosti Malkki <kyosti.malkki@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/51404 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Mathew King <mathewk@chromium.org> Reviewed-by: Furquan Shaikh <furquan@google.com>
Diffstat (limited to 'src/soc/amd/common')
-rw-r--r--src/soc/amd/common/block/i2c/Kconfig4
-rw-r--r--src/soc/amd/common/block/i2c/Makefile.inc1
-rw-r--r--src/soc/amd/common/block/i2c/i2c.c82
-rw-r--r--src/soc/amd/common/block/include/amdblocks/i2c.h36
4 files changed, 123 insertions, 0 deletions
diff --git a/src/soc/amd/common/block/i2c/Kconfig b/src/soc/amd/common/block/i2c/Kconfig
new file mode 100644
index 0000000000..5d8498c6af
--- /dev/null
+++ b/src/soc/amd/common/block/i2c/Kconfig
@@ -0,0 +1,4 @@
+config SOC_AMD_COMMON_BLOCK_I2C
+ bool
+ help
+ Select this option to add FCH I2C controller functions to the build.
diff --git a/src/soc/amd/common/block/i2c/Makefile.inc b/src/soc/amd/common/block/i2c/Makefile.inc
new file mode 100644
index 0000000000..8af77965ce
--- /dev/null
+++ b/src/soc/amd/common/block/i2c/Makefile.inc
@@ -0,0 +1 @@
+all-$(CONFIG_SOC_AMD_COMMON_BLOCK_I2C) += i2c.c
diff --git a/src/soc/amd/common/block/i2c/i2c.c b/src/soc/amd/common/block/i2c/i2c.c
new file mode 100644
index 0000000000..59e885d807
--- /dev/null
+++ b/src/soc/amd/common/block/i2c/i2c.c
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <assert.h>
+#include <delay.h>
+#include <amdblocks/acpimmio.h>
+#include <amdblocks/gpio_banks.h>
+#include <amdblocks/gpio_defs.h>
+#include <amdblocks/i2c.h>
+
+#define MAX_PIN_COUNT 4
+
+struct common_i2c_save {
+ uint32_t control_value;
+ uint8_t mux_value;
+};
+
+/*
+ * To program I2C pins without destroying their programming, the registers
+ * that will be changed need to be saved first.
+ */
+static void save_i2c_pin_registers(uint8_t gpio, struct common_i2c_save *save_table)
+{
+ save_table->mux_value = iomux_read8(gpio);
+ save_table->control_value = gpio_read32(gpio);
+}
+
+static void restore_i2c_pin_registers(uint8_t gpio, struct common_i2c_save *save_table)
+{
+ /* Write and flush posted writes. */
+ iomux_write8(gpio, save_table->mux_value);
+ iomux_read8(gpio);
+ gpio_write32(gpio, save_table->control_value);
+ gpio_read32(gpio);
+}
+
+static void drive_scl(const struct soc_i2c_peripheral_reset_info *reset_info, uint32_t val)
+{
+ uint8_t j;
+
+ for (j = 0; j < reset_info->num_pins; j++) {
+ if (reset_info->i2c_scl_reset_mask & reset_info->i2c_scl[j].pin_mask)
+ gpio_write32(reset_info->i2c_scl[j].pin.gpio, val);
+ }
+
+ gpio_read32(0); /* Flush posted write */
+ /*
+ * TODO(b/183010197): 4usec gets 85KHz for 1 pin, 70KHz for 4 pins. Ensure this delay
+ * works fine for all SoCs and make this delay configurable if required.
+ */
+ udelay(4);
+}
+
+void sb_reset_i2c_peripherals(const struct soc_i2c_peripheral_reset_info *reset_info)
+{
+ struct common_i2c_save save_table[MAX_PIN_COUNT];
+ uint8_t i;
+
+ if (!reset_info || !reset_info->i2c_scl || !reset_info->num_pins ||
+ !reset_info->i2c_scl_reset_mask)
+ return;
+
+ assert(reset_info->num_pins <= MAX_PIN_COUNT);
+
+ /* Save and reprogram I2C SCL pins */
+ for (i = 0; i < reset_info->num_pins; i++) {
+ save_i2c_pin_registers(reset_info->i2c_scl[i].pin.gpio, &save_table[i]);
+ program_gpios(&reset_info->i2c_scl[i].pin, 1);
+ }
+
+ /*
+ * Toggle SCL back and forth 9 times under 100KHz. A single read is
+ * needed after the writes to force the posted write to complete.
+ */
+ for (i = 0; i < 9; i++) {
+ drive_scl(reset_info, GPIO_OUTPUT_OUT_HIGH);
+ drive_scl(reset_info, GPIO_OUTPUT_OUT_LOW);
+ }
+
+ /* Restore I2C pins. */
+ for (i = 0; i < reset_info->num_pins; i++)
+ restore_i2c_pin_registers(reset_info->i2c_scl[i].pin.gpio, &save_table[i]);
+}
diff --git a/src/soc/amd/common/block/include/amdblocks/i2c.h b/src/soc/amd/common/block/include/amdblocks/i2c.h
new file mode 100644
index 0000000000..9fa203bf57
--- /dev/null
+++ b/src/soc/amd/common/block/include/amdblocks/i2c.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef AMD_COMMON_BLOCK_I2C_H
+#define AMD_COMMON_BLOCK_I2C_H
+
+#include <amdblocks/gpio_banks.h>
+#include <types.h>
+
+/**
+ * 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.
+ * @pin_mask: Bit Mask of a single I2C bus that needs to be reset.
+ */
+struct soc_i2c_scl_pin {
+ struct soc_amd_gpio pin;
+ uint8_t pin_mask;
+};
+
+/**
+ * Information about I2C peripherals that need to be reset.
+ * @i2c_scl_reset_mask: Bit mask of I2C buses that need to be reset based on the device tree
+ * configuration.
+ * @i2c_scl: SoC specific I2C SCL pins that need to be bit-banged as part of reset
+ * procedure.
+ * @num_pins: Number of pins defined in @i2c_scl.
+ */
+struct soc_i2c_peripheral_reset_info {
+ uint8_t i2c_scl_reset_mask;
+ const struct soc_i2c_scl_pin *i2c_scl;
+ uint32_t num_pins;
+};
+
+/* Reset I2C peripherals. */
+void sb_reset_i2c_peripherals(const struct soc_i2c_peripheral_reset_info *reset_info);
+
+#endif /* AMD_COMMON_BLOCK_I2C_H */