Duncan Laurie has uploaded this change for review.

View Change

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
---
M src/soc/intel/common/block/include/intelblocks/pmc.h
M src/soc/intel/common/block/pmc/Kconfig
M src/soc/intel/common/block/pmc/pmc.c
3 files changed, 175 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/59/46259/1
diff --git a/src/soc/intel/common/block/include/intelblocks/pmc.h b/src/soc/intel/common/block/include/intelblocks/pmc.h
index 75e2127..b70d4dd 100644
--- a/src/soc/intel/common/block/include/intelblocks/pmc.h
+++ b/src/soc/intel/common/block/include/intelblocks/pmc.h
@@ -60,4 +60,27 @@
*/
const struct device *soc_get_pmc_mux_device(int port_number);

+/*
+ * Provides an ACPI method in the SSDT to write to the IPC mailbox which is
+ * defined in the PMC device MMIO address space.
+ *
+ * One possible use of this method is to to enable/disable the clock for a
+ * particular PCIe root port at runtime when the device is in D3 state.
+ *
+ * The ACPI method takes 7 arguments:
+ * IPCW (COMMAND, SUB_ID, SIZE, DATA0, DATA1, DATA2, DATA3)
+ *
+ * And can return:
+ * 0 = success
+ * 1 = error
+ * 2 = timeout
+ */
+void pmc_acpi_fill_ssdt_ipc_write_method(void);
+
+/*
+ * Call the ACPI method to write to the IPC mailbox and enable/disable the
+ * specified clock pin connected to the specified PCIe root port.
+ */
+void pmc_acpi_set_pci_clock(unsigned int pcie_rp, unsigned int clock_pin, bool enable);
+
#endif /* SOC_INTEL_COMMON_BLOCK_PMC_H */
diff --git a/src/soc/intel/common/block/pmc/Kconfig b/src/soc/intel/common/block/pmc/Kconfig
index ce41b23..cb6d888 100644
--- a/src/soc/intel/common/block/pmc/Kconfig
+++ b/src/soc/intel/common/block/pmc/Kconfig
@@ -13,6 +13,14 @@
config POWER_STATE_DEFAULT_ON_AFTER_FAILURE
default y

+config PMC_IPC_MAILBOX_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.
+
endif # SOC_INTEL_COMMON_BLOCK_PMC

config PMC_INVALID_READ_AFTER_WRITE
diff --git a/src/soc/intel/common/block/pmc/pmc.c b/src/soc/intel/common/block/pmc/pmc.c
index 24f28e3..76eaddb 100644
--- a/src/soc/intel/common/block/pmc/pmc.c
+++ b/src/soc/intel/common/block/pmc/pmc.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi.h>
+#include <acpi/acpigen.h>
#include <arch/io.h>
#include <device/pci_ops.h>
#include <console/console.h>
@@ -9,6 +10,16 @@
#include <device/pci_ids.h>
#include <intelblocks/pmc.h>
#include <soc/pci_devs.h>
+#include <soc/iomap.h>
+
+/* PMC IPC Mailbox Interface. */
+#define PMC_IPC_CMD 0x00 /* IPC command */
+#define PMC_IPC_CMD_OFFSET_COMMAND 0 /* IPC command.COMMAND */
+#define PMC_IPC_CMD_OFFSET_SUB_ID 12 /* IPC command.SUB_ID */
+#define PMC_IPC_CMD_OFFSET_SIZE 16 /* IPC command.SIZE */
+#define PMC_IPC_CMD_SET_PCIE_CLOCK 0xAC /* IPC command: Set PCIe Clock */
+#define PMC_IPC_STS 0x04 /* IPC Status */
+#define PMC_IPC_WBUF 0x80 /* IPC Write Buffer */

/* SoC overrides */

@@ -98,6 +109,138 @@
}
}

+void pmc_acpi_fill_ssdt_ipc_write_method(void)
+{
+ const struct fieldlist ipcw_fields[] = {
+ FIELDLIST_OFFSET(PMC_IPC_CMD), /* Command */
+ FIELDLIST_NAMESTR("ICMD", 32), /* Command Register */
+ FIELDLIST_OFFSET(PMC_IPC_STS), /* Status */
+ FIELDLIST_NAMESTR("IBSY", 1), /* Status Busy */
+ FIELDLIST_NAMESTR("IERR", 1), /* Status Error */
+ FIELDLIST_OFFSET(PMC_IPC_WBUF), /* 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 */
+ };
+ const struct opregion ipcw_opregion = OPREGION("IPCS", SYSTEMMEMORY,
+ PCH_PWRM_BASE_ADDRESS, 0xff);
+
+ if (!CONFIG(CONFIG_PMC_IPC_MAILBOX_ACPI_INTERFACE))
+ return;
+
+ acpigen_write_method_serialized("IPCW", 7);
+
+ acpigen_write_opregion(&ipcw_opregion);
+ acpigen_write_field("IPCS", ipcw_fields, ARRAY_SIZE(ipcw_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_OFFSET_COMMAND) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG0_OP, PMC_IPC_CMD_OFFSET_COMMAND);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Local0 += (Arg1 << PMC_IPC_CMD_OFFSET_SUB_ID) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG1_OP, PMC_IPC_CMD_OFFSET_SUB_ID);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Local0 += (Arg1 << PMC_IPC_CMD_OFFSET_SIZE) */
+ acpigen_emit_byte(ADD_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_write_shiftleft_op_int(ARG2_OP, PMC_IPC_CMD_OFFSET_SIZE);
+ 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 for up to 10ms. */
+ acpigen_write_store_int_to_op(10, LOCAL1_OP);
+
+ /* While (Local0 > 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_write_return_integer(0);
+ acpigen_pop_len();
+ /* If (IERR == 1) { Return (ERROR) } */
+ acpigen_write_if_lequal_namestr_int("IERR", 1);
+ acpigen_write_return_integer(1);
+ acpigen_pop_len();
+ /* Sleep (1) */
+ acpigen_write_sleep(1);
+ /* Decrement (Local0) */
+ acpigen_emit_byte(DECREMENT_OP);
+ acpigen_emit_byte(LOCAL1_OP);
+ acpigen_pop_len(); /* While */
+ /* Return (TIMEOUT) */
+ acpigen_write_return_integer(2);
+
+ acpigen_pop_len(); /* Method */
+}
+
+void pmc_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 ? 0 : 1) << clock_pin, /* Clock pin to set */
+ 1 << pcie_rp, /* PCIe root port to be modified */
+ (enable ? 0 : 1) << pcie_rp, /* PCIe root port to set */
+ };
+ const char *method = acpi_device_path_join(pcidev_path_on_root(PCH_DEVFN_PMC), "IPCW");
+
+ if (!CONFIG(CONFIG_PMC_IPC_MAILBOX_ACPI_INTERFACE))
+ return;
+
+ if (!method) {
+ printk(BIOS_ERR, "%s: Unable to find PMC device IPCW method\n", __func__);
+ return;
+ }
+
+ /*
+ * The PMC IPC mailbox write method takes 7 arguments:
+ * IPCW (COMMAND, SUB_ID, SIZE, DATA0, DATA1, DATA2, DATA3)
+ */
+ acpigen_emit_namestring(method);
+ acpigen_write_integer(PMC_IPC_CMD_SET_PCIE_CLOCK);
+ acpigen_write_integer(0);
+ acpigen_write_integer(sizeof(data));
+ acpigen_write_integer(data[0]);
+ acpigen_write_integer(data[1]);
+ acpigen_write_integer(data[2]);
+ acpigen_write_integer(data[3]);
+}
+
+static void pmc_fill_ssdt(const struct device *dev)
+{
+ const char *scope = acpi_device_scope(dev);
+
+ if (!scope)
+ return;
+
+ acpigen_write_scope(scope);
+
+ /* Define IPC Write Method */
+ pmc_acpi_fill_ssdt_ipc_write_method();
+
+ acpigen_pop_len(); /* Scope */
+}
+
static struct device_operations device_ops = {
.read_resources = pch_pmc_read_resources,
.set_resources = pci_dev_set_resources,
@@ -105,6 +248,7 @@
.init = pmc_soc_init,
.ops_pci = &pci_dev_ops_pci,
.scan_bus = scan_static_bus,
+ .acpi_fill_ssdt = pmc_fill_ssdt,
};

static const unsigned short pci_device_ids[] = {

To view, visit change 46259. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I95f5a1ba2bc6905e0f8ce0e8b2342ad1287a23a0
Gerrit-Change-Number: 46259
Gerrit-PatchSet: 1
Gerrit-Owner: Duncan Laurie <dlaurie@chromium.org>
Gerrit-Reviewer: Patrick Rudolph <siro@das-labor.org>
Gerrit-MessageType: newchange