Attention is currently required from: Karthikeyan Ramasubramanian. Hello Karthikeyan Ramasubramanian,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/51404
to review the following change.
Change subject: soc/amd/common: Introduce I2C driver common to all AMD SoCs ......................................................................
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 the subsequent changes.
BUG=None TEST=Build Dalboz and Grunt.
Change-Id: I9f735dcfe8375abdc88ff06e8c4f8a6b741bc085 Signed-off-by: Karthikeyan Ramasubramanian kramasub@google.com --- A src/soc/amd/common/block/i2c/Kconfig A src/soc/amd/common/block/i2c/Makefile.inc A src/soc/amd/common/block/i2c/i2c.c A src/soc/amd/common/block/include/amdblocks/i2c.h M src/soc/amd/picasso/Kconfig M src/soc/amd/picasso/chip.h M src/soc/amd/picasso/early_fch.c M src/soc/amd/picasso/i2c.c M src/soc/amd/picasso/include/soc/i2c.h M src/soc/amd/stoneyridge/Kconfig M src/soc/amd/stoneyridge/bootblock.c M src/soc/amd/stoneyridge/chip.h M src/soc/amd/stoneyridge/i2c.c M src/soc/amd/stoneyridge/include/soc/i2c.h 14 files changed, 176 insertions(+), 177 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/04/51404/1
diff --git a/src/soc/amd/common/block/i2c/Kconfig b/src/soc/amd/common/block/i2c/Kconfig new file mode 100644 index 0000000..5d8498c --- /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 0000000..0898895 --- /dev/null +++ b/src/soc/amd/common/block/i2c/Makefile.inc @@ -0,0 +1,8 @@ +ifeq ($(CONFIG_SOC_AMD_COMMON_BLOCK_I2C),y) + +bootblock-y += i2c.c +verstage-y += i2c.c +romstage-y += i2c.c +ramstage-y += i2c.c + +endif # CONFIG_SOC_AMD_COMMON_BLOCK_I2C 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 0000000..ff9f34f --- /dev/null +++ b/src/soc/amd/common/block/i2c/i2c.c @@ -0,0 +1,86 @@ +/* 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 soc_amd_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 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); +} + +void sb_reset_i2c_peripherals(void) +{ + struct soc_amd_i2c_peripheral_reset_info reset_info; + struct soc_amd_i2c_save save_table[MAX_PIN_COUNT]; + uint8_t i, j, control; + + soc_amd_get_i2c_peripheral_reset_info(&reset_info); + if (!reset_info.i2c_scl || !reset_info.num_pins) + return; + + assert(reset_info.num_pins <= MAX_PIN_COUNT); + + control = reset_info.i2c_scl_reset & reset_info.i2c_scl_reset_mask; + if (!control) + return; + + /* 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++) { + for (j = 0; j < reset_info.num_pins; j++) { + if (control & reset_info.i2c_scl[i].pin_mask) + gpio_write32(reset_info.i2c_scl[i].pin.gpio, + GPIO_OUTPUT_ENABLE); + } + + gpio_read32(0); /* Flush posted write */ + udelay(4); /* 4usec gets 85KHz for 1 pin, 70KHz for 4 pins */ + + for (j = 0; j < reset_info.num_pins; j++) { + if (control & reset_info.i2c_scl[i].pin_mask) + gpio_write32(reset_info.i2c_scl[i].pin.gpio, 0); + } + + gpio_read32(0); /* Flush posted write */ + udelay(4); + } + + /* 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 0000000..7c9cfed --- /dev/null +++ b/src/soc/amd/common/block/include/amdblocks/i2c.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <types.h> +#include <amdblocks/gpio_banks.h> + +#ifndef AMD_COMMON_BLOCK_I2C_H +#define AMD_COMMON_BLOCK_I2C_H + +struct soc_amd_i2c_scl_pin { + struct soc_amd_gpio pin; + uint8_t pin_mask; +}; + +/** + * Information about I2C peripherals that need to be reset. + * @i2c_scl_reset: Reset configuration from the devicetree. + * @i2c_scl_reset_mask: SoC specific bit-mask identifying all relevant I2C SCL pins. + * @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_amd_i2c_peripheral_reset_info { + uint8_t i2c_scl_reset; + uint8_t i2c_scl_reset_mask; + const struct soc_amd_i2c_scl_pin *i2c_scl; + uint32_t num_pins; +}; + +/** + * Get information about I2C peripherals that need to be reset. + * @reset_info: In/Out parameter for the SoC specific I2C driver to populate + * the reset info. + */ +void soc_amd_get_i2c_peripheral_reset_info( + struct soc_amd_i2c_peripheral_reset_info *reset_info); + +/* Reset I2C peripherals. */ +void sb_reset_i2c_peripherals(void); + +#endif /* AMD_COMMON_BLOCK_I2C_H */ diff --git a/src/soc/amd/picasso/Kconfig b/src/soc/amd/picasso/Kconfig index ea9ee5b..1fca390 100644 --- a/src/soc/amd/picasso/Kconfig +++ b/src/soc/amd/picasso/Kconfig @@ -34,6 +34,7 @@ select SOC_AMD_COMMON_BLOCK_GRAPHICS select SOC_AMD_COMMON_BLOCK_HAS_ESPI 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_NONCAR diff --git a/src/soc/amd/picasso/chip.h b/src/soc/amd/picasso/chip.h index 549f033..2944b2a 100644 --- a/src/soc/amd/picasso/chip.h +++ b/src/soc/amd/picasso/chip.h @@ -97,9 +97,9 @@ struct soc_amd_picasso_config { struct soc_amd_common_config common_config; /* - * 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/picasso/early_fch.c b/src/soc/amd/picasso/early_fch.c index a9118ad..4f7b074 100644 --- a/src/soc/amd/picasso/early_fch.c +++ b/src/soc/amd/picasso/early_fch.c @@ -2,6 +2,7 @@
#include <amdblocks/acpimmio.h> #include <amdblocks/espi.h> +#include <amdblocks/i2c.h> #include <amdblocks/lpc.h> #include <amdblocks/smbus.h> #include <amdblocks/spi.h> @@ -31,7 +32,7 @@ fch_enable_cf9_io(); fch_enable_legacy_io(); enable_aoac_devices(); - sb_reset_i2c_slaves(); + sb_reset_i2c_peripherals();
/* * On reset Range_0 defaults to enabled. We want to start with a clean diff --git a/src/soc/amd/picasso/i2c.c b/src/soc/amd/picasso/i2c.c index af4c41f..10de4a6 100644 --- a/src/soc/amd/picasso/i2c.c +++ b/src/soc/amd/picasso/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/i2c.h> #include <soc/iomap.h> #include <soc/pci_devs.h> @@ -142,80 +143,20 @@ .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(I2C2_SCL_PIN, PULL_NONE), - PAD_GPI(I2C3_SCL_PIN, PULL_NONE), - /* I2C4 is a slave device only */ +/* This table is for the initial conversion of all SCL pins to input with no pull. */ +static const struct soc_amd_i2c_scl_pin i2c_scl_pins[] = { + { PAD_GPI(I2C2_SCL_PIN, PULL_NONE), GPIO_I2C2_SCL }, + { PAD_GPI(I2C3_SCL_PIN, PULL_NONE), GPIO_I2C3_SCL }, + /* I2C4 is a peripheral device only */ }; -#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) +void soc_amd_get_i2c_peripheral_reset_info( + struct soc_amd_i2c_peripheral_reset_info *reset_info) { - save_table->mux_value = iomux_read8(gpio); - save_table->control_value = gpio_read32(gpio); -} + const struct soc_amd_picasso_config *cfg = config_of_soc();
-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_picasso_config *cfg; - struct soc_amd_i2c_save save_table[saved_pins_count]; - uint8_t i, j, control; - - cfg = config_of_soc(); - 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_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_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]); + reset_info->i2c_scl_reset = cfg->i2c_scl_reset; + reset_info->i2c_scl_reset_mask = GPIO_I2C_MASK; + reset_info->i2c_scl = i2c_scl_pins; + reset_info->num_pins = ARRAY_SIZE(i2c_scl_pins); } diff --git a/src/soc/amd/picasso/include/soc/i2c.h b/src/soc/amd/picasso/include/soc/i2c.h index 41b930b..f62cb11 100644 --- a/src/soc/amd/picasso/include/soc/i2c.h +++ b/src/soc/amd/picasso/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_I2C2_SCL BIT(2) #define GPIO_I2C3_SCL BIT(3) #define GPIO_I2C_MASK (BIT(2) | BIT(3)) @@ -28,8 +23,6 @@ #define USB_PD_RFMUX_OVERRIDE BIT(8) #define USB_PD_DP_OVERRIDE BIT(9)
-void sb_reset_i2c_slaves(void); - /* Sets the base address for the specific I2C bus. */ void i2c_set_bar(unsigned int bus, uintptr_t bar);
diff --git a/src/soc/amd/stoneyridge/Kconfig b/src/soc/amd/stoneyridge/Kconfig index dcd6b8f..5f1b65a 100644 --- a/src/soc/amd/stoneyridge/Kconfig +++ b/src/soc/amd/stoneyridge/Kconfig @@ -31,6 +31,7 @@ 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 248ee77..ac9e69c 100644 --- a/src/soc/amd/stoneyridge/bootblock.c +++ b/src/soc/amd/stoneyridge/bootblock.c @@ -12,6 +12,7 @@ #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> @@ -75,14 +76,14 @@ 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(); + sb_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 07103a5..82c5437 100644 --- a/src/soc/amd/stoneyridge/chip.h +++ b/src/soc/amd/stoneyridge/chip.h @@ -43,9 +43,9 @@ 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 295a833..c90ede9 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> @@ -116,92 +117,21 @@ .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), +/* This table is for the initial conversion of all SCL pins to input with no pull. */ +static const struct soc_amd_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 }, }; -#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) +void soc_amd_get_i2c_peripheral_reset_info( + struct soc_amd_i2c_peripheral_reset_info *reset_info) { - save_table->mux_value = iomux_read8(gpio); - save_table->control_value = gpio_read32(gpio); -} + const struct soc_amd_stoneyridge_config *cfg = config_of_soc();
-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]); + reset_info->i2c_scl_reset = cfg->i2c_scl_reset; + reset_info->i2c_scl_reset_mask = GPIO_I2C_MASK; + reset_info->i2c_scl = i2c_scl_pins; + reset_info->num_pins = ARRAY_SIZE(i2c_scl_pins); } diff --git a/src/soc/amd/stoneyridge/include/soc/i2c.h b/src/soc/amd/stoneyridge/include/soc/i2c.h index 0b61329..b16084b 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 @@ #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 */