aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/pmc
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@google.com>2020-10-10 00:05:36 +0000
committerPatrick Georgi <pgeorgi@google.com>2020-11-09 07:32:13 +0000
commit44caa1963fb9e3aa0a392223dab3b9c33acdc441 (patch)
tree55972f116bd121b5b4d19e39a10d632b63e83b55 /src/soc/intel/common/block/pmc
parent74c16d0a8ba76f07b17d0db58071933b7d7f12b6 (diff)
intel/common/pmc: Add functions for IPC mailbox in ACPI
This change adds two functions that provide an IPC mailbox method via ACPI for runtime clock configuration. pmc_acpi_fill_ssdt_ipc_write_method() will provide a method in the SSDT that can be called by other ACPI devices to send an IPC mailbox command. This function is exported because some SOCs override the default PMC device and need to call this function to write the method into the SSDT. pmc_acpi_set_pci_clock() will call the method defined by the previous function to enable or disable the PCIe SRCCLK for a specified root port and clock pin. It can be called by the PCIe root port after turning off power to the attached device. BUG=b:160996445 TEST=boot on volteer device and disassemble the SSDT to ensure that this method exists. Signed-off-by: Duncan Laurie <dlaurie@google.com> Change-Id: I95f5a1ba2bc6905e0f8ce0e8b2342ad1287a23a0 Reviewed-on: https://review.coreboot.org/c/coreboot/+/46259 Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/soc/intel/common/block/pmc')
-rw-r--r--src/soc/intel/common/block/pmc/Kconfig8
-rw-r--r--src/soc/intel/common/block/pmc/Makefile.inc1
-rw-r--r--src/soc/intel/common/block/pmc/pmc_ipc.c156
3 files changed, 162 insertions, 3 deletions
diff --git a/src/soc/intel/common/block/pmc/Kconfig b/src/soc/intel/common/block/pmc/Kconfig
index ce41b23620..5cb2bea1d9 100644
--- a/src/soc/intel/common/block/pmc/Kconfig
+++ b/src/soc/intel/common/block/pmc/Kconfig
@@ -22,6 +22,14 @@ config PMC_INVALID_READ_AFTER_WRITE
Enable this for PMC devices where a read back of ACPI BAR and
IO access bit does not return the previously written value.
+config PMC_IPC_ACPI_INTERFACE
+ bool
+ default n
+ depends on HAVE_ACPI_TABLES
+ help
+ Enable this to have the PMC IPC mailbox ACPI interface added
+ to the SSDT for use by other drivers.
+
config PMC_GLOBAL_RESET_ENABLE_LOCK
bool
help
diff --git a/src/soc/intel/common/block/pmc/Makefile.inc b/src/soc/intel/common/block/pmc/Makefile.inc
index 796a039aed..49a0902847 100644
--- a/src/soc/intel/common/block/pmc/Makefile.inc
+++ b/src/soc/intel/common/block/pmc/Makefile.inc
@@ -6,4 +6,5 @@ ramstage-y += pmclib.c pmc_ipc.c
smm-y += pmclib.c
verstage-y += pmclib.c
postcar-y += pmclib.c
+ramstage-$(CONFIG_PMC_IPC_ACPI_INTERFACE) += pmc_ipc.c
endif
diff --git a/src/soc/intel/common/block/pmc/pmc_ipc.c b/src/soc/intel/common/block/pmc/pmc_ipc.c
index 7decf790a9..7c811aed44 100644
--- a/src/soc/intel/common/block/pmc/pmc_ipc.c
+++ b/src/soc/intel/common/block/pmc/pmc_ipc.c
@@ -1,10 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+#include <acpi/acpigen.h>
#include <device/mmio.h>
#include <console/console.h>
#include <delay.h>
#include <intelblocks/pmclib.h>
#include <intelblocks/pmc_ipc.h>
+#include <soc/pci_devs.h>
#include <stdint.h>
#include <timer.h>
@@ -64,16 +66,16 @@ static int check_ipc_sts(void)
if (IPC_STS_HAS_ERROR(ipc_sts)) {
printk(BIOS_ERR, "IPC_STS.error_code 0x%x\n",
IPC_STS_ERROR_CODE(ipc_sts));
- return -1;
+ return PMC_IPC_ERROR;
}
- return 0;
+ return PMC_IPC_SUCCESS;
}
udelay(50);
} while (!stopwatch_expired(&sw));
printk(BIOS_ERR, "PMC IPC timeout after %u ms\n", PMC_IPC_XFER_TIMEOUT_MS);
- return -1;
+ return PMC_IPC_TIMEOUT;
}
enum cb_err pmc_send_ipc_cmd(uint32_t cmd, const struct pmc_ipc_buffer *wbuf,
@@ -94,3 +96,151 @@ enum cb_err pmc_send_ipc_cmd(uint32_t cmd, const struct pmc_ipc_buffer *wbuf,
return CB_SUCCESS;
}
+
+void pmc_ipc_acpi_fill_ssdt(void)
+{
+ const struct fieldlist ipcs_fields[] = {
+ FIELDLIST_OFFSET(PMC_IPC_CMD_OFFSET), /* Command */
+ FIELDLIST_NAMESTR("ICMD", 32), /* Command Register */
+ FIELDLIST_OFFSET(PMC_IPC_STS_OFFSET), /* Status */
+ FIELDLIST_NAMESTR("IBSY", 1), /* Status Busy */
+ FIELDLIST_NAMESTR("IERR", 1), /* Status Error */
+ FIELDLIST_RESERVED(14),
+ FIELDLIST_NAMESTR("IERC", 8), /* Status Error Code */
+ FIELDLIST_OFFSET(IPC_WBUF0), /* Write Buffer */
+ FIELDLIST_NAMESTR("IWB0", 32), /* Write Buffer 0 */
+ FIELDLIST_NAMESTR("IWB1", 32), /* Write Buffer 1 */
+ FIELDLIST_NAMESTR("IWB2", 32), /* Write Buffer 2 */
+ FIELDLIST_NAMESTR("IWB3", 32), /* Write Buffer 3 */
+ FIELDLIST_OFFSET(IPC_RBUF0), /* Read Buffer */
+ FIELDLIST_NAMESTR("IRB0", 32), /* Read Buffer 0 */
+ FIELDLIST_NAMESTR("IRB1", 32), /* Read Buffer 1 */
+ FIELDLIST_NAMESTR("IRB2", 32), /* Read Buffer 2 */
+ FIELDLIST_NAMESTR("IRB3", 32), /* Read Buffer 3 */
+ };
+ const struct opregion ipcs_opregion = OPREGION("IPCM", SYSTEMMEMORY,
+ soc_read_pmc_base(), 0xff);
+ int i;
+
+ /* Package with return value and read buffer. */
+ acpigen_write_name("RVAL");
+ acpigen_write_package(5);
+ for (i = 0; i < 5; ++i)
+ acpigen_write_integer(0);
+ acpigen_pop_len();
+
+ acpigen_write_method_serialized("IPCS", 7);
+
+ acpigen_write_opregion(&ipcs_opregion);
+ acpigen_write_field("IPCM", ipcs_fields, ARRAY_SIZE(ipcs_fields),
+ FIELD_DWORDACC | FIELD_NOLOCK | FIELD_PRESERVE);
+
+ /* Fill write buffer data registers. */
+ acpigen_write_store_op_to_namestr(ARG3_OP, "IWB0");
+ acpigen_write_store_op_to_namestr(ARG4_OP, "IWB1");
+ acpigen_write_store_op_to_namestr(ARG5_OP, "IWB2");
+ acpigen_write_store_op_to_namestr(ARG6_OP, "IWB3");
+
+ /* Program the command register with command and size of write data. */
+ acpigen_write_store_int_to_op(0, LOCAL0_OP);
+
+ /* Local0 += (Arg0 << PMC_IPC_CMD_COMMAND_SHIFT) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG0_OP, PMC_IPC_CMD_COMMAND_SHIFT);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Local0 += (Arg1 << PMC_IPC_CMD_SUB_COMMAND_SHIFT) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG1_OP, PMC_IPC_CMD_SUB_COMMAND_SHIFT);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Local1 = PMC_IPC_CMD_NO_MSI */
+ acpigen_write_store_int_to_op(PMC_IPC_CMD_NO_MSI, LOCAL1_OP);
+ /* Local0 += (Local1 << PMC_IPC_CMD_MSI_SHIFT) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(LOCAL1_OP, PMC_IPC_CMD_MSI_SHIFT);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Local0 += (Arg1 << PMC_IPC_CMD_SIZE_SHIFT) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG2_OP, PMC_IPC_CMD_SIZE_SHIFT);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Start mailbox command with one 32bit write. */
+ acpigen_write_store_op_to_namestr(LOCAL0_OP, "ICMD");
+
+ /* Read status register to get busy/error status. */
+ acpigen_write_store_int_to_op(PMC_IPC_XFER_TIMEOUT_MS, LOCAL1_OP);
+
+ /* While (Local1 > 0) */
+ acpigen_emit_byte(WHILE_OP);
+ acpigen_write_len_f();
+ acpigen_emit_byte(LGREATER_OP);
+ acpigen_emit_byte(LOCAL1_OP);
+ acpigen_emit_byte(ZERO_OP);
+
+ /* If (IBSY == 0) { Return (SUCCESS) } */
+ acpigen_write_if_lequal_namestr_int("IBSY", 0);
+ acpigen_set_package_element_int("RVAL", 0, PMC_IPC_SUCCESS);
+ acpigen_set_package_element_namestr("RVAL", 1, "IRB0");
+ acpigen_set_package_element_namestr("RVAL", 2, "IRB1");
+ acpigen_set_package_element_namestr("RVAL", 3, "IRB2");
+ acpigen_set_package_element_namestr("RVAL", 4, "IRB3");
+ acpigen_write_return_namestr("RVAL");
+ acpigen_pop_len();
+
+ /* If (IERR == 1) { Return (ERROR) } */
+ acpigen_write_if_lequal_namestr_int("IERR", 1);
+ acpigen_write_debug_string("IPCS ERROR");
+ acpigen_write_debug_namestr("IERC");
+ acpigen_set_package_element_int("RVAL", 0, PMC_IPC_ERROR);
+ acpigen_write_return_namestr("RVAL");
+ acpigen_pop_len();
+
+ /* Sleep (1) */
+ acpigen_write_sleep(1);
+ /* Decrement (Local1) */
+ acpigen_emit_byte(DECREMENT_OP);
+ acpigen_emit_byte(LOCAL1_OP);
+ acpigen_pop_len(); /* While */
+
+ /* Return (TIMEOUT) */
+ acpigen_write_debug_string("IPCS TIMEOUT");
+ acpigen_set_package_element_int("RVAL", 0, PMC_IPC_TIMEOUT);
+ acpigen_write_return_namestr("RVAL");
+
+ acpigen_pop_len(); /* Method */
+}
+
+void pmc_ipc_acpi_set_pci_clock(unsigned int pcie_rp, unsigned int clock_pin, bool enable)
+{
+ const uint32_t data[] = {
+ 1 << clock_pin, /* Clock pin to be modified */
+ (enable ? 1 : 0) << clock_pin, /* Clock pin to set */
+ 1 << pcie_rp, /* PCIe root port to be modified */
+ (enable ? 1 : 0) << pcie_rp, /* PCIe root port to set */
+ };
+ const char *method = acpi_device_path_join(pcidev_path_on_root(PCH_DEVFN_PMC), "IPCS");
+
+ if (!method) {
+ printk(BIOS_ERR, "%s: Unable to find PMC device IPCS method\n", __func__);
+ return;
+ }
+
+ /*
+ * The PMC IPC mailbox method takes 7 arguments:
+ * IPCS (COMMAND, SUB_ID, SIZE, DATA0, DATA1, DATA2, DATA3)
+ */
+ acpigen_emit_namestring(method);
+ acpigen_write_integer(PMC_IPC_CMD_ID_SET_PCIE_CLOCK);
+ acpigen_write_integer(0);
+ acpigen_write_integer(sizeof(data));
+ acpigen_write_dword(data[0]);
+ acpigen_write_dword(data[1]);
+ acpigen_write_dword(data[2]);
+ acpigen_write_dword(data[3]);
+}