Cliff Huang has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/61354 )
Change subject: drivers/wwan/fm: Add Fibocom 5G WWAN ACPI support ......................................................................
drivers/wwan/fm: Add Fibocom 5G WWAN ACPI support
Support PXSX._RST and PXSX.MRST._RST for warm and cold reset. PXSX._RST is invoked when driver removal.
Test: Add chip entry to the corresponding root port and check PXSX Device is generated in ssdt.
Signed-off-by: Cliff Huang cliff.huang@intel.com Change-Id: I1e0b9fd405f6cfb1e216ea27558bb9299a09e566 --- A src/drivers/wwan/fm/Kconfig A src/drivers/wwan/fm/Makefile.inc A src/drivers/wwan/fm/acpi_fm350gl.c A src/drivers/wwan/fm/chip.h 4 files changed, 223 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/54/61354/1
diff --git a/src/drivers/wwan/fm/Kconfig b/src/drivers/wwan/fm/Kconfig new file mode 100644 index 0000000..4ad94a5 --- /dev/null +++ b/src/drivers/wwan/fm/Kconfig @@ -0,0 +1,8 @@ +config DRIVERS_WWAN_FM350GL + bool + default n + depends on SOC_INTEL_COMMON_BLOCK_PCIE_RTD3 + help + This driver is for Fibocom FM350-GL PCIe 5G WWAN. + When enabled, this driver will add support for ACPI controlled + WWAN using GPIOs for power/reset control of the device. diff --git a/src/drivers/wwan/fm/Makefile.inc b/src/drivers/wwan/fm/Makefile.inc new file mode 100644 index 0000000..8074a08 --- /dev/null +++ b/src/drivers/wwan/fm/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_DRIVERS_WWAN_FM350GL) += acpi_fm350gl.c diff --git a/src/drivers/wwan/fm/acpi_fm350gl.c b/src/drivers/wwan/fm/acpi_fm350gl.c new file mode 100644 index 0000000..5438225 --- /dev/null +++ b/src/drivers/wwan/fm/acpi_fm350gl.c @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include "chip.h" + +/* FCPO# to RESET# delay time during WWAN ON */ +#define FM350GL_TN2B 20 +/* RESET# to PERST# delay time during WWAN ON */ +#define FM350GL_TB2R 80 +/* The delay between de-assertion of PERST# to change of PDS state from 0 to 1 during WWAN ON */ +#define FM350GL_TR2P 0 +/* RESET# to FCPO# delay time during WWAN OFF */ +#define FM350GL_TB2F 10 +/* Time to allow the WWAN module to fully discharge any residual voltages before FCPO# could be + de-asserted again. */ +#define FM350GL_TFDI 500 +/* The delay between assertion and de-assertion RESET# during FLDR */ +#define FM350GL_TBTG 10 +/* The delay between de-assertion of RESET# and change of PDS state from 0 to 1 after FLDR */ +#define FM350GL_TBTP 170 +/* PERST# to RESET# delay time during WWAN OFF */ +#define FM350GL_TR2B 10 +/* 20s HW initialization needed after de-assertion of PERST# + However, it is not required and is not proper place to ensure HW initializatioin in ACPI. The + delay here is to ensure the following reset or rtd3 _OFF method won't be called immediately. + */ +#define FM350GL_TIME_HW_INIT 100 + +static void wwan_fm350gl_acpi_method_fhrf(const struct device *parent_dev, + const struct drivers_wwan_fm_config *config) +{ + acpigen_write_method_serialized("FHRF", 1); + /* LOCAL0 = PERST# */ + acpigen_get_tx_gpio(&config->perst); + acpigen_write_if_lequal_op_int(LOCAL0_OP, 0); + acpigen_emit_namestring(acpi_device_path_join(parent_dev, "DL23")); + /* assert PERST# pin */ + acpigen_enable_tx_gpio(&config->perst); + acpigen_pop_len(); /* If */ + acpigen_write_sleep(FM350GL_TR2B); + /* assert RESET# pin */ + acpigen_enable_tx_gpio(&config->reset); + /* warm reset */ + acpigen_write_if_lequal_op_int(ARG0_OP, 0); + acpigen_write_sleep(FM350GL_TBTG); + /* cold reset */ + acpigen_write_else(); + acpigen_write_if_lequal_op_int(ARG0_OP, 1); + /* disable source clock */ + acpigen_emit_namestring(acpi_device_path_join(parent_dev, "SRCK")); + acpigen_emit_byte(ZERO_OP); + acpigen_write_sleep(FM350GL_TB2F); + /* assert FCPO# pin */ + acpigen_enable_tx_gpio(&config->fcpo); + acpigen_write_sleep(FM350GL_TFDI); + acpigen_pop_len(); /* If */ + acpigen_pop_len(); /* Else */ + acpigen_pop_len(); /* Method */ +} + +static void wwan_fm350gl_acpi_method_shrf(const struct device *parent_dev, + const struct drivers_wwan_fm_config *config) +{ + acpigen_write_method_serialized("SHRF", 0); + /* call rtd3 method to Disable ModPHY Power Gating. */ + acpigen_emit_namestring(acpi_device_path_join(parent_dev, "PSD0")); + /* call rtd3 method to Enable SRC Clock. */ + acpigen_emit_namestring(acpi_device_path_join(parent_dev, "SRCK")); + acpigen_emit_byte(ONE_OP); + /* De-assert FCPO# GPIO. */ + acpigen_disable_tx_gpio(&config->fcpo); + acpigen_write_sleep(FM350GL_TN2B); + /* De-assert RESET# GPIO. */ + acpigen_disable_tx_gpio(&config->reset); + acpigen_write_sleep(FM350GL_TB2R); + /* De-assert PERST# GPIO. */ + acpigen_disable_tx_gpio(&config->perst); + /* Call rtd3 method to trigger L2/L3 ready exit flow in root port */ + acpigen_emit_namestring(acpi_device_path_join(parent_dev, "L23D")); + acpigen_write_sleep(FM350GL_TIME_HW_INIT); + acpigen_pop_len(); /* Method */ +} + +static void wwan_fm350gl_acpi_method_rst(const struct device *parent_dev, + const struct drivers_wwan_fm_config *config) +{ + acpigen_write_method_serialized("_RST", 0); + /* Indicates that the following _Off will be skipped. */ + acpigen_write_store_int_to_namestr(1, acpi_device_path_join(parent_dev, "RTD3._OFS")); + /* Perform 1st Half of FLDR Flow for soft reset: FHRF(0) */ + acpigen_emit_namestring("FHRF"); + acpigen_emit_byte(ZERO_OP); + /* Perform 2nd Half of FLDR Flow: SHRF() */ + acpigen_emit_namestring("SHRF"); + /* Indicates that the following _Off will be skipped. */ + acpigen_write_store_int_to_namestr(1, acpi_device_path_join(parent_dev, "RTD3._OFS")); + acpigen_pop_len(); /* Method */ +} + +static void wwan_fm350gl_acpi_method_mrst_rst(const struct device *parent_dev, + const struct drivers_wwan_fm_config *config) +{ + acpigen_write_method_serialized("_RST", 0); + /* Indicates that the following _Off will be skipped once. */ + acpigen_write_store_int_to_namestr(1, acpi_device_path_join(parent_dev, "RTD3._OFS")); + /* Perform 1st Half of FLDR Flow for cold reset: FHRF (1) */ + acpigen_emit_namestring("FHRF"); + acpigen_emit_byte(ONE_OP); + /* Perform 2nd Half of FLDR Flow: SHRF () */ + acpigen_emit_namestring("SHRF"); + /* Indicate kernel ACPI PM to skip _off RTD3 after reset at the end of driver removal */ + acpigen_write_store_int_to_namestr(1, acpi_device_path_join(parent_dev, "RTD3._OFS")); + acpigen_pop_len(); /* Method */ +} + +static const char *wwan_fm350gl_acpi_name(const struct device *dev) +{ + /* Attached device name must be "PXSX" for the Linux Kernel to recognize it. */ + return "PXSX"; +} + +static void wwan_fm350gl_acpi_fill_ssdt(const struct device *dev) +{ + const struct drivers_wwan_fm_config *config = config_of(dev); + const struct device *parent = dev->bus->dev; + const char *scope = acpi_device_path(parent); + + if (!is_dev_enabled(parent)) { + printk(BIOS_ERR, "%s: root port not enabled\n", __func__); + return; + } + if (!scope) { + printk(BIOS_ERR, "%s: root port scope not found\n", __func__); + return; + } + if (!config->fcpo.pin_count && !config->reset.pin_count && + !config->perst.pin_count) { + printk(BIOS_ERR, "%s: FCPO, RESET, PERST GPIO required for %s.\n", + __func__, scope); + return; + } + printk(BIOS_INFO, "%s: Enable WWAN for %s (%s)\n", scope, dev_path(parent), + config->desc ?: dev->chip_ops->name); + acpigen_write_scope(scope); + acpigen_write_device(wwan_fm350gl_acpi_name(dev)); + acpigen_write_ADR(0); + if (config->name) + acpigen_write_name_string("_DDN", config->name); + if (config->desc) + acpigen_write_name_unicode("_STR", config->desc); + wwan_fm350gl_acpi_method_fhrf(parent, config); + wwan_fm350gl_acpi_method_shrf(parent, config); + wwan_fm350gl_acpi_method_rst(parent, config); + /* NOTE: the 5G driver is looking for MRST._RST for cold reset called during + * firmware update. + */ + acpigen_write_device("MRST"); + acpigen_write_ADR(0); + wwan_fm350gl_acpi_method_mrst_rst(parent, config); + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ +} + +static struct device_operations wwan_fm350gl_ops = { + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .acpi_fill_ssdt = wwan_fm350gl_acpi_fill_ssdt, + .acpi_name = wwan_fm350gl_acpi_name, +}; + +static void wwan_fm350gl_acpi_enable(struct device *dev) +{ + dev->ops = &wwan_fm350gl_ops; +} + +struct chip_operations drivers_wwan_fm_ops = { + CHIP_NAME("Fibocom FM-350-GL") + .enable_dev = wwan_fm350gl_acpi_enable +}; diff --git a/src/drivers/wwan/fm/chip.h b/src/drivers/wwan/fm/chip.h new file mode 100644 index 0000000..8f7f147 --- /dev/null +++ b/src/drivers/wwan/fm/chip.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_WWAN_FM_CHIP_H__ +#define __DRIVERS_WWAN_FM_CHIP_H__ + +struct drivers_wwan_fm_config { + const char *name; + const char *desc; + /* GPIO used for FULL_CARD_POWER_OFF# */ + struct acpi_gpio fcpo; + /* Delay to be inserted after fcpo is deasserted. */ + unsigned int fcpo_asserted_delay_ms; + /* Delay to be inserted after fcpo is asserted. */ + unsigned int fcpo_deasserted_delay_ms; + + /* GPIO used for RESET# */ + struct acpi_gpio reset; + /* Delay to be inserted after reset is deasserted. */ + unsigned int reset_asserted_delay_ms; + /* Delay to be inserted after reset is asserted. */ + unsigned int reset_deasserted_delay_ms; + + /* GPIO used for PERST# */ + struct acpi_gpio perst; + /* Delay to be inserted after perst is deasserted. */ + unsigned int perst_asserted_delay_ms; + /* Delay to be inserted after perst is asserted. */ + unsigned int perst_deasserted_delay_ms; + + struct acpi_gpio wake; +}; + +#endif /* __DRIVERS_WWAN_FM_CHIP_H__ */