summaryrefslogtreecommitdiff
path: root/src/soc/amd/stoneyridge
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/stoneyridge
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/stoneyridge')
-rw-r--r--src/soc/amd/stoneyridge/Kconfig1
-rw-r--r--src/soc/amd/stoneyridge/bootblock.c26
-rw-r--r--src/soc/amd/stoneyridge/chip.h4
-rw-r--r--src/soc/amd/stoneyridge/i2c.c91
-rw-r--r--src/soc/amd/stoneyridge/include/soc/i2c.h7
5 files changed, 28 insertions, 101 deletions
diff --git a/src/soc/amd/stoneyridge/Kconfig b/src/soc/amd/stoneyridge/Kconfig
index dcd6b8f146..5f1b65a476 100644
--- a/src/soc/amd/stoneyridge/Kconfig
+++ b/src/soc/amd/stoneyridge/Kconfig
@@ -31,6 +31,7 @@ config CPU_SPECIFIC_OPTIONS
select SOC_AMD_COMMON_BLOCK_BANKED_GPIOS
select SOC_AMD_COMMON_BLOCK_CAR
select SOC_AMD_COMMON_BLOCK_HDA
+ select SOC_AMD_COMMON_BLOCK_I2C
select SOC_AMD_COMMON_BLOCK_IOMMU
select SOC_AMD_COMMON_BLOCK_LPC
select SOC_AMD_COMMON_BLOCK_PCI
diff --git a/src/soc/amd/stoneyridge/bootblock.c b/src/soc/amd/stoneyridge/bootblock.c
index 248ee773a4..fc4284f502 100644
--- a/src/soc/amd/stoneyridge/bootblock.c
+++ b/src/soc/amd/stoneyridge/bootblock.c
@@ -12,12 +12,15 @@
#include <amdblocks/agesawrapper_call.h>
#include <amdblocks/amd_pci_mmconf.h>
#include <amdblocks/biosram.h>
+#include <amdblocks/i2c.h>
#include <soc/pci_devs.h>
#include <soc/cpu.h>
#include <soc/southbridge.h>
#include <timestamp.h>
#include <halt.h>
+#include "chip.h"
+
#if CONFIG_PI_AGESA_TEMP_RAM_BASE < 0x100000
#error "Error: CONFIG_PI_AGESA_TEMP_RAM_BASE must be >= 1MB"
#endif
@@ -25,6 +28,14 @@
#error "Error: CONFIG_PI_AGESA_CAR_HEAP_BASE must be >= 1MB"
#endif
+/* This table is for the initial conversion of all SCL pins to input with no pull. */
+static const struct soc_i2c_scl_pin i2c_scl_pins[] = {
+ { PAD_GPI(I2C0_SCL_PIN, PULL_NONE), GPIO_I2C0_SCL },
+ { PAD_GPI(I2C1_SCL_PIN, PULL_NONE), GPIO_I2C1_SCL },
+ { PAD_GPI(I2C2_SCL_PIN, PULL_NONE), GPIO_I2C2_SCL },
+ { PAD_GPI(I2C3_SCL_PIN, PULL_NONE), GPIO_I2C3_SCL },
+};
+
/* Set the MMIO Configuration Base Address, Bus Range, and misc MTRRs. */
static void amd_initmmio(void)
{
@@ -53,6 +64,17 @@ static void amd_initmmio(void)
CONFIG_PI_AGESA_HEAP_SIZE, MTRR_TYPE_UNCACHEABLE);
}
+static void reset_i2c_peripherals(void)
+{
+ const struct soc_amd_stoneyridge_config *cfg = config_of_soc();
+ struct soc_i2c_peripheral_reset_info reset_info;
+
+ reset_info.i2c_scl_reset_mask = cfg->i2c_scl_reset & GPIO_I2C_MASK;
+ reset_info.i2c_scl = i2c_scl_pins;
+ reset_info.num_pins = ARRAY_SIZE(i2c_scl_pins);
+ sb_reset_i2c_peripherals(&reset_info);
+}
+
asmlinkage void bootblock_c_entry(uint64_t base_timestamp)
{
enable_pci_mmconf();
@@ -75,14 +97,14 @@ asmlinkage void bootblock_c_entry(uint64_t base_timestamp)
void bootblock_soc_early_init(void)
{
/*
- * This call (sb_reset_i2c_slaves) was originally early at
+ * This call (sb_reset_i2c_peripherals) was originally early at
* bootblock_c_entry, but had to be moved here. There was an
* unexplained delay in the middle of the i2c transaction when
* we had it in bootblock_c_entry. Moving it to this point
* (or adding delays) fixes the issue. It seems like the processor
* just pauses but we don't know why.
*/
- sb_reset_i2c_slaves();
+ reset_i2c_peripherals();
bootblock_fch_early_init();
post_code(0x90);
}
diff --git a/src/soc/amd/stoneyridge/chip.h b/src/soc/amd/stoneyridge/chip.h
index 07103a5714..82c54371c5 100644
--- a/src/soc/amd/stoneyridge/chip.h
+++ b/src/soc/amd/stoneyridge/chip.h
@@ -43,9 +43,9 @@ struct soc_amd_stoneyridge_config {
size_t uma_size;
/*
- * If sb_reset_i2c_slaves() is called, this devicetree register
+ * If sb_reset_i2c_peripherals() is called, this devicetree register
* defines which I2C SCL will be toggled 9 times at 100 KHz.
- * For example, should we need I2C0 and I2C3 have their slave
+ * For example, should we need I2C0 and I2C3 have their peripheral
* devices reseted by toggling SCL, use:
*
* register i2c_scl_reset = (GPIO_I2C0_SCL | GPIO_I2C3_SCL)
diff --git a/src/soc/amd/stoneyridge/i2c.c b/src/soc/amd/stoneyridge/i2c.c
index 295a833046..7b0bc77e59 100644
--- a/src/soc/amd/stoneyridge/i2c.c
+++ b/src/soc/amd/stoneyridge/i2c.c
@@ -7,6 +7,7 @@
#include <device/device.h>
#include <drivers/i2c/designware/dw_i2c.h>
#include <amdblocks/acpimmio.h>
+#include <amdblocks/i2c.h>
#include <soc/iomap.h>
#include <soc/pci_devs.h>
#include <soc/southbridge.h>
@@ -115,93 +116,3 @@ struct device_operations stoneyridge_i2c_mmio_ops = {
.acpi_name = i2c_acpi_name,
.acpi_fill_ssdt = dw_i2c_acpi_fill_ssdt,
};
-
-/*
- * I2C pins are open drain with external pull up, so in order to bit bang them
- * all, SCL pins must become GPIO inputs with no pull, then they need to be
- * toggled between input-no-pull and output-low. This table is for the initial
- * conversion of all SCL pins to input with no pull.
- */
-static const struct soc_amd_gpio i2c_2_gpi[] = {
- PAD_GPI(I2C0_SCL_PIN, PULL_NONE),
- PAD_GPI(I2C1_SCL_PIN, PULL_NONE),
- PAD_GPI(I2C2_SCL_PIN, PULL_NONE),
- PAD_GPI(I2C3_SCL_PIN, PULL_NONE),
-};
-#define saved_pins_count ARRAY_SIZE(i2c_2_gpi)
-
-/*
- * 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 soc_amd_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 soc_amd_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);
-}
-
-/* Slaves to be reset are controlled by devicetree register i2c_scl_reset */
-void sb_reset_i2c_slaves(void)
-{
- const struct soc_amd_stoneyridge_config *cfg;
- const struct device *dev = pcidev_path_on_root(GNB_DEVFN);
- struct soc_amd_i2c_save save_table[saved_pins_count];
- uint8_t i, j, control;
-
- if (!dev || !dev->chip_info)
- return;
- cfg = dev->chip_info;
- control = cfg->i2c_scl_reset & GPIO_I2C_MASK;
- if (control == 0)
- return;
-
- /* Save and reprogram I2C SCL pins */
- for (i = 0; i < saved_pins_count; i++)
- save_i2c_pin_registers(i2c_2_gpi[i].gpio, &save_table[i]);
- program_gpios(i2c_2_gpi, saved_pins_count);
-
- /*
- * 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 (j = 0; j < 9; j++) {
- if (control & GPIO_I2C0_SCL)
- gpio_write32(I2C0_SCL_PIN, GPIO_OUTPUT_ENABLE);
- if (control & GPIO_I2C1_SCL)
- gpio_write32(I2C1_SCL_PIN, GPIO_OUTPUT_ENABLE);
- if (control & GPIO_I2C2_SCL)
- gpio_write32(I2C2_SCL_PIN, GPIO_OUTPUT_ENABLE);
- if (control & GPIO_I2C3_SCL)
- gpio_write32(I2C3_SCL_PIN, GPIO_OUTPUT_ENABLE);
-
- gpio_read32(0); /* Flush posted write */
- udelay(4); /* 4usec gets 85KHz for 1 pin, 70KHz for 4 pins */
-
- if (control & GPIO_I2C0_SCL)
- gpio_write32(I2C0_SCL_PIN, 0);
- if (control & GPIO_I2C1_SCL)
- gpio_write32(I2C1_SCL_PIN, 0);
- if (control & GPIO_I2C2_SCL)
- gpio_write32(I2C2_SCL_PIN, 0);
- if (control & GPIO_I2C3_SCL)
- gpio_write32(I2C3_SCL_PIN, 0);
-
- gpio_read32(0); /* Flush posted write */
- udelay(4);
- }
-
- /* Restore I2C pins. */
- for (i = 0; i < saved_pins_count; i++)
- restore_i2c_pin_registers(i2c_2_gpi[i].gpio, &save_table[i]);
-}
diff --git a/src/soc/amd/stoneyridge/include/soc/i2c.h b/src/soc/amd/stoneyridge/include/soc/i2c.h
index 0b61329fef..b16084b5ad 100644
--- a/src/soc/amd/stoneyridge/include/soc/i2c.h
+++ b/src/soc/amd/stoneyridge/include/soc/i2c.h
@@ -6,11 +6,6 @@
#include <types.h>
#include <soc/gpio.h>
-struct soc_amd_i2c_save {
- uint32_t control_value;
- uint8_t mux_value;
-};
-
#define GPIO_I2C0_SCL BIT(0)
#define GPIO_I2C1_SCL BIT(1)
#define GPIO_I2C2_SCL BIT(2)
@@ -27,6 +22,4 @@ struct soc_amd_i2c_save {
#define I2C2_SCL_PIN_IOMUX_GPIOxx GPIO_113_IOMUX_GPIOxx
#define I2C3_SCL_PIN_IOMUX_GPIOxx GPIO_19_IOMUX_GPIOxx
-void sb_reset_i2c_slaves(void);
-
#endif /* AMD_STONEYRIDGE_I2C_H */