diff options
Diffstat (limited to 'drivers/platform/x86/amd/pmf')
-rw-r--r-- | drivers/platform/x86/amd/pmf/Kconfig | 3 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/Makefile | 8 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/acpi.c | 103 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/auto-mode.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/cnqf.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/core.c | 71 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/pmf-quirks.c | 51 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/pmf.h | 126 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/spc.c | 159 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/sps.c | 72 | ||||
-rw-r--r-- | drivers/platform/x86/amd/pmf/tee-if.c | 230 |
11 files changed, 597 insertions, 238 deletions
diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index f4fa8bd8bda8..25b8f7ae3abd 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -7,10 +7,11 @@ config AMD_PMF tristate "AMD Platform Management Framework" depends on ACPI && PCI depends on POWER_SUPPLY - depends on AMD_NB + depends on AMD_NODE select ACPI_PLATFORM_PROFILE depends on TEE && AMDTEE depends on AMD_SFH_HID + depends on HAS_IOMEM help This driver provides support for the AMD Platform Management Framework. The goal is to enhance end user experience by making AMD PCs smarter, diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index 7d6079b02589..5978464e0eb7 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -4,7 +4,7 @@ # AMD Platform Management Framework # -obj-$(CONFIG_AMD_PMF) += amd-pmf.o -amd-pmf-objs := core.o acpi.o sps.o \ - auto-mode.o cnqf.o \ - tee-if.o spc.o pmf-quirks.o +obj-$(CONFIG_AMD_PMF) += amd-pmf.o +amd-pmf-y := core.o acpi.o sps.o \ + auto-mode.o cnqf.o \ + tee-if.o spc.o diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1157ec148880..f75f7ecd8cd9 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -220,7 +220,7 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work) if (!info) return; - schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000)); + schedule_delayed_work(&dev->heart_beat, secs_to_jiffies(dev->hb_interval)); kfree(info); } @@ -282,6 +282,29 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) return 0; } +static int apmf_notify_smart_pc_update(struct amd_pmf_dev *pdev, u32 val, u32 preq, u32 index) +{ + struct amd_pmf_notify_smart_pc_update args; + struct acpi_buffer params; + union acpi_object *info; + + args.size = sizeof(args); + args.pending_req = preq; + args.custom_bios[index] = val; + + params.length = sizeof(args); + params.pointer = &args; + + info = apmf_if_call(pdev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES, ¶ms); + if (!info) + return -EIO; + + kfree(info); + dev_dbg(pdev->dev, "Notify smart pc update, val: %u\n", val); + + return 0; +} + int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data) { return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data)); @@ -298,17 +321,29 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req req, sizeof(*req)); } +static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data) +{ + struct amd_pmf_dev *pmf_dev = data; + int ret; + + guard(mutex)(&pmf_dev->cb_mutex); + + ret = apmf_get_sbios_requests_v2(pmf_dev, &pmf_dev->req); + if (ret) + dev_err(pmf_dev->dev, "Failed to get v2 SBIOS requests: %d\n", ret); +} + static void apmf_event_handler(acpi_handle handle, u32 event, void *data) { struct amd_pmf_dev *pmf_dev = data; struct apmf_sbios_req req; int ret; - mutex_lock(&pmf_dev->update_mutex); + guard(mutex)(&pmf_dev->update_mutex); ret = apmf_get_sbios_requests(pmf_dev, &req); if (ret) { dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret); - goto out; + return; } if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) { @@ -330,8 +365,6 @@ static void apmf_event_handler(acpi_handle handle, u32 event, void *data) if (pmf_dev->amt_enabled) amd_pmf_update_2_cql(pmf_dev, req.cql_event); } -out: - mutex_unlock(&pmf_dev->update_mutex); } static int apmf_if_verify_interface(struct amd_pmf_dev *pdev) @@ -407,44 +440,53 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev) apmf_event_handler(ahandle, 0, pmf_dev); } + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) { + status = acpi_install_notify_handler(ahandle, ACPI_ALL_NOTIFY, + apmf_event_handler_v2, pmf_dev); + if (ACPI_FAILURE(status)) { + dev_err(pmf_dev->dev, "failed to install notify handler for custom BIOS inputs\n"); + return -ENODEV; + } + } + return 0; } -static acpi_status apmf_walk_resources(struct acpi_resource *res, void *data) +int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) { - struct amd_pmf_dev *dev = data; + struct platform_device *pdev = to_platform_device(pmf_dev->dev); - switch (res->type) { - case ACPI_RESOURCE_TYPE_ADDRESS64: - dev->policy_addr = res->data.address64.address.minimum; - dev->policy_sz = res->data.address64.address.address_length; - break; - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - dev->policy_addr = res->data.fixed_memory32.address; - dev->policy_sz = res->data.fixed_memory32.address_length; - break; + pmf_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pmf_dev->res) { + dev_dbg(pmf_dev->dev, "Failed to get I/O memory resource\n"); + return -EINVAL; } - if (!dev->policy_addr || dev->policy_sz > POLICY_BUF_MAX_SZ || dev->policy_sz == 0) { - pr_err("Incorrect Policy params, possibly a SBIOS bug\n"); - return AE_ERROR; + pmf_dev->policy_addr = pmf_dev->res->start; + /* + * We cannot use resource_size() here because it adds an extra byte to round off the size. + * In the case of PMF ResourceTemplate(), this rounding is already handled within the _CRS. + * Using resource_size() would increase the resource size by 1, causing a mismatch with the + * length field and leading to issues. Therefore, simply use end-start of the ACPI resource + * to obtain the actual length. + */ + pmf_dev->policy_sz = pmf_dev->res->end - pmf_dev->res->start; + + if (!pmf_dev->policy_addr || pmf_dev->policy_sz > POLICY_BUF_MAX_SZ || + pmf_dev->policy_sz == 0) { + dev_err(pmf_dev->dev, "Incorrect policy params, possibly a SBIOS bug\n"); + return -EINVAL; } - return AE_OK; + return 0; } -int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) +int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx) { - acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); - acpi_status status; - - status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, apmf_walk_resources, pmf_dev); - if (ACPI_FAILURE(status)) { - dev_dbg(pmf_dev->dev, "acpi_walk_resources failed :%d\n", status); + if (!is_apmf_func_supported(dev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES)) return -EINVAL; - } - return 0; + return apmf_notify_smart_pc_update(dev, val, preq, idx); } void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) @@ -457,6 +499,9 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) && is_apmf_func_supported(pmf_dev, APMF_FUNC_SBIOS_REQUESTS)) acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler); + + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) + acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler_v2); } int apmf_acpi_init(struct amd_pmf_dev *pmf_dev) diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c index 02ff68be10d0..a184922bba8d 100644 --- a/drivers/platform/x86/amd/pmf/auto-mode.c +++ b/drivers/platform/x86/amd/pmf/auto-mode.c @@ -120,9 +120,9 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx, amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL); amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL); + fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL); + fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL); if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual, diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c index bc8899e15c91..207a0b33d8d3 100644 --- a/drivers/platform/x86/amd/pmf/cnqf.c +++ b/drivers/platform/x86/amd/pmf/cnqf.c @@ -81,10 +81,10 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx, amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL); amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL); amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL); - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU], - NULL); - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2], - NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL); if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX)) apmf_update_fan_idx(dev, diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 64e6e34a2a9a..76910601cac8 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -8,13 +8,13 @@ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> */ -#include <asm/amd_nb.h> #include <linux/debugfs.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <asm/amd/node.h> #include "pmf.h" /* PMF-SMU communication registers */ @@ -37,11 +37,6 @@ #define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE #define AMD_PMF_RESULT_FAILED 0xFF -/* List of supported CPU ids */ -#define AMD_CPU_ID_RMB 0x14b5 -#define AMD_CPU_ID_PS 0x14e8 -#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 - #define PMF_MSG_DELAY_MIN_US 50 #define RESPONSE_REGISTER_LOOP_MAX 20000 @@ -132,7 +127,8 @@ static void amd_pmf_get_metrics(struct work_struct *work) ktime_t time_elapsed_ms; int socket_power; - mutex_lock(&dev->update_mutex); + guard(mutex)(&dev->update_mutex); + /* Transfer table contents */ memset(dev->buf, 0, sizeof(dev->m_table)); amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); @@ -154,7 +150,6 @@ static void amd_pmf_get_metrics(struct work_struct *work) dev->start_time = ktime_to_ms(ktime_get()); schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms)); - mutex_unlock(&dev->update_mutex); } static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset) @@ -181,12 +176,26 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev) dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value); } +/** + * fixp_q88_fromint: Convert integer to Q8.8 + * @val: input value + * + * Converts an integer into binary fixed point format where 8 bits + * are used for integer and 8 bits are used for the decimal. + * + * Return: unsigned integer converted to Q8.8 format + */ +u32 fixp_q88_fromint(u32 val) +{ + return val << 8; +} + int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data) { int rc; u32 val; - mutex_lock(&dev->lock); + guard(mutex)(&dev->lock); /* Wait until we get a valid response */ rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE, @@ -194,7 +203,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "failed to talk to SMU\n"); - goto out_unlock; + return rc; } /* Write zero to response register */ @@ -212,7 +221,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); if (rc) { dev_err(dev->dev, "SMU response timed out\n"); - goto out_unlock; + return rc; } switch (val) { @@ -226,21 +235,19 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 case AMD_PMF_RESULT_CMD_REJECT_BUSY: dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val); rc = -EBUSY; - goto out_unlock; + break; case AMD_PMF_RESULT_CMD_UNKNOWN: dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val); rc = -EINVAL; - goto out_unlock; + break; case AMD_PMF_RESULT_CMD_REJECT_PREREQ: case AMD_PMF_RESULT_FAILED: default: dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val); rc = -EIO; - goto out_unlock; + break; } -out_unlock: - mutex_unlock(&dev->lock); amd_pmf_dump_registers(dev); return rc; } @@ -249,6 +256,7 @@ static const struct pci_device_id pmf_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) }, { } }; @@ -259,7 +267,20 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer) /* Get Metrics Table Address */ if (alloc_buffer) { - dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL); + switch (dev->cpu_id) { + case AMD_CPU_ID_PS: + case AMD_CPU_ID_RMB: + dev->mtable_size = sizeof(dev->m_table); + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + dev->mtable_size = sizeof(dev->m_table_v2); + break; + default: + dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id); + } + + dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL); if (!dev->buf) return -ENOMEM; } @@ -364,7 +385,6 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) || is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) { power_supply_unreg_notifier(&dev->pwr_src_notifier); - amd_pmf_deinit_sps(dev); } if (dev->smart_pc_enabled) { @@ -381,6 +401,8 @@ static const struct acpi_device_id amd_pmf_acpi_ids[] = { {"AMDI0100", 0x100}, {"AMDI0102", 0}, {"AMDI0103", 0}, + {"AMDI0105", 0}, + {"AMDI0107", 0}, { } }; MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids); @@ -419,18 +441,18 @@ static int amd_pmf_probe(struct platform_device *pdev) err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val); if (err) { - dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); pci_dev_put(rdev); - return pcibios_err_to_errno(err); + return dev_err_probe(dev->dev, pcibios_err_to_errno(err), + "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); } base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val); if (err) { - dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); pci_dev_put(rdev); - return pcibios_err_to_errno(err); + return dev_err_probe(dev->dev, pcibios_err_to_errno(err), + "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); } base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK; @@ -444,8 +466,8 @@ static int amd_pmf_probe(struct platform_device *pdev) mutex_init(&dev->lock); mutex_init(&dev->update_mutex); + mutex_init(&dev->cb_mutex); - amd_pmf_quirks_init(dev); apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); amd_pmf_dbgfs_register(dev); @@ -470,6 +492,7 @@ static void amd_pmf_remove(struct platform_device *pdev) amd_pmf_dbgfs_unregister(dev); mutex_destroy(&dev->lock); mutex_destroy(&dev->update_mutex); + mutex_destroy(&dev->cb_mutex); kfree(dev->buf); } @@ -486,7 +509,7 @@ static struct platform_driver amd_pmf_driver = { .pm = pm_sleep_ptr(&amd_pmf_pm), }, .probe = amd_pmf_probe, - .remove_new = amd_pmf_remove, + .remove = amd_pmf_remove, }; module_platform_driver(amd_pmf_driver); diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c deleted file mode 100644 index 0b2eb0ae85fe..000000000000 --- a/drivers/platform/x86/amd/pmf/pmf-quirks.c +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AMD Platform Management Framework Driver Quirks - * - * Copyright (c) 2024, Advanced Micro Devices, Inc. - * All Rights Reserved. - * - * Author: Mario Limonciello <mario.limonciello@amd.com> - */ - -#include <linux/dmi.h> - -#include "pmf.h" - -struct quirk_entry { - u32 supported_func; -}; - -static struct quirk_entry quirk_no_sps_bug = { - .supported_func = 0x4003, -}; - -static const struct dmi_system_id fwbug_list[] = { - { - .ident = "ROG Zephyrus G14", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "GA403UV"), - }, - .driver_data = &quirk_no_sps_bug, - }, - {} -}; - -void amd_pmf_quirks_init(struct amd_pmf_dev *dev) -{ - const struct dmi_system_id *dmi_id; - struct quirk_entry *quirks; - - dmi_id = dmi_first_match(fwbug_list); - if (!dmi_id) - return; - - quirks = dmi_id->driver_data; - if (quirks->supported_func) { - dev->supported_func = quirks->supported_func; - pr_info("Using supported funcs quirk to avoid %s platform firmware bug\n", - dmi_id->ident); - } -} - diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index eeedd0c0395a..45b60238d527 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -12,12 +12,20 @@ #define PMF_H #include <linux/acpi.h> +#include <linux/input.h> +#include <linux/platform_device.h> #include <linux/platform_profile.h> #define POLICY_BUF_MAX_SZ 0x4b000 #define POLICY_SIGN_COOKIE 0x31535024 #define POLICY_COOKIE_OFFSET 0x10 +/* List of supported CPU ids */ +#define AMD_CPU_ID_RMB 0x14b5 +#define AMD_CPU_ID_PS 0x14e8 +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 +#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 + struct cookie_header { u32 sign; u32 length; @@ -34,6 +42,7 @@ struct cookie_header { #define APMF_FUNC_STATIC_SLIDER_GRANULAR 9 #define APMF_FUNC_DYN_SLIDER_AC 11 #define APMF_FUNC_DYN_SLIDER_DC 12 +#define APMF_FUNC_NOTIFY_SMART_PC_UPDATES 14 #define APMF_FUNC_SBIOS_HEARTBEAT_V2 16 /* Message Definitions */ @@ -81,15 +90,28 @@ struct cookie_header { #define PMF_POLICY_STT_SKINTEMP_APU 7 #define PMF_POLICY_STT_SKINTEMP_HS2 8 #define PMF_POLICY_SYSTEM_STATE 9 +#define PMF_POLICY_BIOS_OUTPUT_1 10 +#define PMF_POLICY_BIOS_OUTPUT_2 11 #define PMF_POLICY_P3T 38 +#define PMF_POLICY_BIOS_OUTPUT_3 57 +#define PMF_POLICY_BIOS_OUTPUT_4 58 +#define PMF_POLICY_BIOS_OUTPUT_5 59 +#define PMF_POLICY_BIOS_OUTPUT_6 60 +#define PMF_POLICY_BIOS_OUTPUT_7 61 +#define PMF_POLICY_BIOS_OUTPUT_8 62 +#define PMF_POLICY_BIOS_OUTPUT_9 63 +#define PMF_POLICY_BIOS_OUTPUT_10 64 /* TA macros */ #define PMF_TA_IF_VERSION_MAJOR 1 #define TA_PMF_ACTION_MAX 32 #define TA_PMF_UNDO_MAX 8 -#define TA_OUTPUT_RESERVED_MEM 906 +#define TA_OUTPUT_RESERVED_MEM 922 #define MAX_OPERATION_PARAMS 4 +#define TA_ERROR_CRYPTO_INVALID_PARAM 0x20002 +#define TA_ERROR_CRYPTO_BIN_TOO_LARGE 0x2000d + #define PMF_IF_V1 1 #define PMF_IF_V2 2 @@ -180,6 +202,53 @@ struct apmf_fan_idx { u32 fan_ctl_idx; } __packed; +struct smu_pmf_metrics_v2 { + u16 core_frequency[16]; /* MHz */ + u16 core_power[16]; /* mW */ + u16 core_temp[16]; /* centi-C */ + u16 gfx_temp; /* centi-C */ + u16 soc_temp; /* centi-C */ + u16 stapm_opn_limit; /* mW */ + u16 stapm_cur_limit; /* mW */ + u16 infra_cpu_maxfreq; /* MHz */ + u16 infra_gfx_maxfreq; /* MHz */ + u16 skin_temp; /* centi-C */ + u16 gfxclk_freq; /* MHz */ + u16 fclk_freq; /* MHz */ + u16 gfx_activity; /* GFX busy % [0-100] */ + u16 socclk_freq; /* MHz */ + u16 vclk_freq; /* MHz */ + u16 vcn_activity; /* VCN busy % [0-100] */ + u16 vpeclk_freq; /* MHz */ + u16 ipuclk_freq; /* MHz */ + u16 ipu_busy[8]; /* NPU busy % [0-100] */ + u16 dram_reads; /* MB/sec */ + u16 dram_writes; /* MB/sec */ + u16 core_c0residency[16]; /* C0 residency % [0-100] */ + u16 ipu_power; /* mW */ + u32 apu_power; /* mW */ + u32 gfx_power; /* mW */ + u32 dgpu_power; /* mW */ + u32 socket_power; /* mW */ + u32 all_core_power; /* mW */ + u32 filter_alpha_value; /* time constant [us] */ + u32 metrics_counter; + u16 memclk_freq; /* MHz */ + u16 mpipuclk_freq; /* MHz */ + u16 ipu_reads; /* MB/sec */ + u16 ipu_writes; /* MB/sec */ + u32 throttle_residency_prochot; + u32 throttle_residency_spl; + u32 throttle_residency_fppt; + u32 throttle_residency_sppt; + u32 throttle_residency_thm_core; + u32 throttle_residency_thm_gfx; + u32 throttle_residency_thm_soc; + u16 psys; + u16 spare1; + u32 spare[6]; +} __packed; + struct smu_pmf_metrics { u16 gfxclk_freq; /* in MHz */ u16 socclk_freq; /* in MHz */ @@ -272,11 +341,12 @@ struct amd_pmf_dev { struct mutex lock; /* protects the PMF interface */ u32 supported_func; enum platform_profile_option current_profile; - struct platform_profile_handler pprof; + struct device *ppdev; /* platform profile class device */ struct dentry *dbgfs_dir; int hb_interval; /* SBIOS heartbeat interval */ struct delayed_work heart_beat; struct smu_pmf_metrics m_table; + struct smu_pmf_metrics_v2 m_table_v2; struct delayed_work work_buffer; ktime_t start_time; int socket_power_history[AVG_SAMPLE_SIZE]; @@ -289,17 +359,22 @@ struct amd_pmf_dev { /* Smart PC solution builder */ struct dentry *esbin; unsigned char *policy_buf; - u32 policy_sz; + resource_size_t policy_sz; struct tee_context *tee_ctx; struct tee_shm *fw_shm_pool; u32 session_id; void *shbuf; struct delayed_work pb_work; struct pmf_action_table *prev_data; - u64 policy_addr; + resource_size_t policy_addr; void __iomem *policy_base; bool smart_pc_enabled; u16 pmf_if_version; + struct input_dev *pmf_idev; + size_t mtable_size; + struct resource *res; + struct apmf_sbios_req_v2 req; /* To get custom bios pending request */ + struct mutex cb_mutex; }; struct apmf_sps_prop_granular_v2 { @@ -342,6 +417,12 @@ struct os_power_slider { u8 slider_event; } __packed; +struct amd_pmf_notify_smart_pc_update { + u16 size; + u32 pending_req; + u32 custom_bios[10]; +} __packed; + struct fan_table_control { bool manual; unsigned long fan_id; @@ -540,6 +621,30 @@ enum ta_slider { TA_MAX, }; +enum apmf_smartpc_custom_bios_inputs { + APMF_SMARTPC_CUSTOM_BIOS_INPUT1, + APMF_SMARTPC_CUSTOM_BIOS_INPUT2, +}; + +enum apmf_preq_smartpc { + NOTIFY_CUSTOM_BIOS_INPUT1 = 5, + NOTIFY_CUSTOM_BIOS_INPUT2, +}; + +enum platform_type { + PTYPE_UNKNOWN = 0, + LID_CLOSE, + CLAMSHELL, + FLAT, + TENT, + STAND, + TABLET, + BOOK, + PRESENTATION, + PULL_FWD, + PTYPE_INVALID = 0xf, +}; + /* Command ids for TA communication */ enum ta_pmf_command { TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, @@ -581,7 +686,8 @@ struct ta_pmf_condition_info { u32 power_slider; u32 lid_state; bool user_present; - u32 rsvd1[2]; + u32 bios_input1; + u32 bios_input2; u32 monitor_count; u32 rsvd2[2]; u32 bat_design; @@ -591,7 +697,9 @@ struct ta_pmf_condition_info { u32 device_state; u32 socket_power; u32 skin_temperature; - u32 rsvd3[5]; + u32 rsvd3[2]; + u32 platform_type; + u32 rsvd3_1[2]; u32 ambient_light; u32 length; u32 avg_c0residency; @@ -669,13 +777,13 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev); int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag); int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer); int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag); +u32 fixp_q88_fromint(u32 val); /* SPS Layer */ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf); void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, struct amd_pmf_static_slider_granular *table); int amd_pmf_init_sps(struct amd_pmf_dev *dev); -void amd_pmf_deinit_sps(struct amd_pmf_dev *dev); int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev, struct apmf_static_slider_granular_output *output); bool is_pprof_balanced(struct amd_pmf_dev *pmf); @@ -715,12 +823,10 @@ extern const struct attribute_group cnqf_feature_attribute_group; int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev); void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev); int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev); +int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx); /* Smart PC - TA interfaces */ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); -/* Quirk infrastructure */ -void amd_pmf_quirks_init(struct amd_pmf_dev *dev); - #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index a3dec14c3004..1d90f9382024 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -16,6 +16,46 @@ #include "pmf.h" #ifdef CONFIG_AMD_PMF_DEBUG +static const char *platform_type_as_str(u16 platform_type) +{ + switch (platform_type) { + case CLAMSHELL: + return "CLAMSHELL"; + case FLAT: + return "FLAT"; + case TENT: + return "TENT"; + case STAND: + return "STAND"; + case TABLET: + return "TABLET"; + case BOOK: + return "BOOK"; + case PRESENTATION: + return "PRESENTATION"; + case PULL_FWD: + return "PULL_FWD"; + default: + return "UNKNOWN"; + } +} + +static const char *laptop_placement_as_str(u16 device_state) +{ + switch (device_state) { + case ON_TABLE: + return "ON_TABLE"; + case ON_LAP_MOTION: + return "ON_LAP_MOTION"; + case IN_BAG: + return "IN_BAG"; + case OUT_OF_BAG: + return "OUT_OF_BAG"; + default: + return "UNKNOWN"; + } +} + static const char *ta_slider_as_str(unsigned int state) { switch (state) { @@ -47,36 +87,82 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away"); dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light); + dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type)); + dev_dbg(dev->dev, "Laptop placement: %s\n", + laptop_placement_as_str(in->ev_info.device_state)); + dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1); + dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2); dev_dbg(dev->dev, "==== TA inputs END ====\n"); } #else void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} #endif -static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, + struct ta_pmf_enact_table *in) { - u16 max, avg = 0; - int i; + if (!pdev->req.pending_req) + return; - memset(dev->buf, 0, sizeof(dev->m_table)); - amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); - memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table)); + switch (pdev->req.pending_req) { + case BIT(NOTIFY_CUSTOM_BIOS_INPUT1): + in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1]; + break; + case BIT(NOTIFY_CUSTOM_BIOS_INPUT2): + in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2]; + break; + default: + dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req); + } - in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; - in->ev_info.skin_temperature = dev->m_table.skin_temp; + /* Clear pending requests after handling */ + memset(&pdev->req, 0, sizeof(pdev->req)); +} + +static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) +{ + u16 max, avg = 0; + int i; /* Get the avg and max C0 residency of all the cores */ - max = dev->m_table.avg_core_c0residency[0]; - for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) { - avg += dev->m_table.avg_core_c0residency[i]; - if (dev->m_table.avg_core_c0residency[i] > max) - max = dev->m_table.avg_core_c0residency[i]; + max = *core_res; + for (i = 0; i < size; i++) { + avg += core_res[i]; + if (core_res[i] > max) + max = core_res[i]; } - - avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency)); + avg = DIV_ROUND_CLOSEST(avg, size); in->ev_info.avg_c0residency = avg; in->ev_info.max_c0residency = max; - in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; +} + +static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + /* Get the updated metrics table data */ + memset(dev->buf, 0, dev->mtable_size); + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); + + switch (dev->cpu_id) { + case AMD_CPU_ID_PS: + memcpy(&dev->m_table, dev->buf, dev->mtable_size); + in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; + in->ev_info.skin_temperature = dev->m_table.skin_temp; + in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; + amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency, + ARRAY_SIZE(dev->m_table.avg_core_c0residency), in); + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size); + in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power; + in->ev_info.skin_temperature = dev->m_table_v2.skin_temp; + in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity; + amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency, + ARRAY_SIZE(dev->m_table_v2.core_c0residency), in); + break; + default: + dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id); + } } static const char * const pmf_battery_supply_name[] = { @@ -133,12 +219,14 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_ switch (dev->current_profile) { case PLATFORM_PROFILE_PERFORMANCE: + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: val = TA_BEST_PERFORMANCE; break; case PLATFORM_PROFILE_BALANCED: val = TA_BETTER_PERFORMANCE; break; case PLATFORM_PROFILE_LOW_POWER: + case PLATFORM_PROFILE_QUIET: val = TA_BEST_BATTERY; break; default: @@ -150,36 +238,34 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_ return 0; } -static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) { struct amd_sfh_info sfh_info; - int ret; + + /* Get the latest information from SFH */ + in->ev_info.user_present = false; /* Get ALS data */ - ret = amd_get_sfh_info(&sfh_info, MT_ALS); - if (!ret) + if (!amd_get_sfh_info(&sfh_info, MT_ALS)) in->ev_info.ambient_light = sfh_info.ambient_light; else - return ret; + dev_dbg(dev->dev, "ALS is not enabled/detected\n"); /* get HPD data */ - ret = amd_get_sfh_info(&sfh_info, MT_HPD); - if (ret) - return ret; - - switch (sfh_info.user_present) { - case SFH_NOT_DETECTED: - in->ev_info.user_present = 0xff; /* assume no sensors connected */ - break; - case SFH_USER_PRESENT: - in->ev_info.user_present = 1; - break; - case SFH_USER_AWAY: - in->ev_info.user_present = 0; - break; + if (!amd_get_sfh_info(&sfh_info, MT_HPD)) { + if (sfh_info.user_present == SFH_USER_PRESENT) + in->ev_info.user_present = true; + } else { + dev_dbg(dev->dev, "HPD is not enabled/detected\n"); } - return 0; + /* Get SRA (Secondary Accelerometer) data */ + if (!amd_get_sfh_info(&sfh_info, MT_SRA)) { + in->ev_info.platform_type = sfh_info.platform_type; + in->ev_info.device_state = sfh_info.laptop_placement; + } else { + dev_dbg(dev->dev, "SRA is not enabled/detected\n"); + } } void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) @@ -191,4 +277,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab amd_pmf_get_battery_info(dev, in); amd_pmf_get_slider_info(dev, in); amd_pmf_get_sensor_info(dev, in); + amd_pmf_get_custom_bios_inputs(dev, in); } diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 92f7fb22277d..49e14ca94a9e 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -198,9 +198,11 @@ static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx) amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, apts_config_store.val[idx].stt_min_limit, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL); + fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu), + NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL); + fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2), + NULL); } void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, @@ -217,9 +219,11 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx, amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, config_store.prop[src][idx].stt_min, NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, - config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL); + fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]), + NULL); amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, - config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL); + fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]), + NULL); } else if (op == SLIDER_OP_GET) { amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl); amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt); @@ -282,10 +286,10 @@ bool is_pprof_balanced(struct amd_pmf_dev *pmf) return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false; } -static int amd_pmf_profile_get(struct platform_profile_handler *pprof, +static int amd_pmf_profile_get(struct device *dev, enum platform_profile_option *profile) { - struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof); + struct amd_pmf_dev *pmf = dev_get_drvdata(dev); *profile = pmf->current_profile; return 0; @@ -297,12 +301,14 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf) switch (pmf->current_profile) { case PLATFORM_PROFILE_PERFORMANCE: + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: mode = POWER_MODE_PERFORMANCE; break; case PLATFORM_PROFILE_BALANCED: mode = POWER_MODE_BALANCED_POWER; break; case PLATFORM_PROFILE_LOW_POWER: + case PLATFORM_PROFILE_QUIET: mode = POWER_MODE_POWER_SAVER; break; default: @@ -363,10 +369,10 @@ int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev) return 0; } -static int amd_pmf_profile_set(struct platform_profile_handler *pprof, +static int amd_pmf_profile_set(struct device *dev, enum platform_profile_option profile) { - struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof); + struct amd_pmf_dev *pmf = dev_get_drvdata(dev); int ret = 0; pmf->current_profile = profile; @@ -387,10 +393,32 @@ static int amd_pmf_profile_set(struct platform_profile_handler *pprof, return 0; } -int amd_pmf_init_sps(struct amd_pmf_dev *dev) +static int amd_pmf_hidden_choices(void *drvdata, unsigned long *choices) { - int err; + set_bit(PLATFORM_PROFILE_QUIET, choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + + return 0; +} + +static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} +static const struct platform_profile_ops amd_pmf_profile_ops = { + .probe = amd_pmf_profile_probe, + .hidden_choices = amd_pmf_hidden_choices, + .profile_get = amd_pmf_profile_get, + .profile_set = amd_pmf_profile_set, +}; + +int amd_pmf_init_sps(struct amd_pmf_dev *dev) +{ dev->current_profile = PLATFORM_PROFILE_BALANCED; if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { @@ -405,24 +433,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) amd_pmf_set_sps_power_limits(dev); } - dev->pprof.profile_get = amd_pmf_profile_get; - dev->pprof.profile_set = amd_pmf_profile_set; - - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices); - set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices); - /* Create platform_profile structure and register */ - err = platform_profile_register(&dev->pprof); - if (err) - dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n", - err); - - return err; -} + dev->ppdev = devm_platform_profile_register(dev->dev, "amd-pmf", dev, + &amd_pmf_profile_ops); + if (IS_ERR(dev->ppdev)) + dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %ld\n", + PTR_ERR(dev->ppdev)); -void amd_pmf_deinit_sps(struct amd_pmf_dev *dev) -{ - platform_profile_remove(); + return PTR_ERR_OR_ZERO(dev->ppdev); } diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index b438de4d6bfc..d3bd12ad036a 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -27,8 +27,11 @@ module_param(pb_side_load, bool, 0444); MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures"); #endif -static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, - 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43); +static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a, + 0xcc, 0x2b, 0x2b, 0x60, 0xd6), + UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5, + 0x29, 0xb1, 0x3d, 0x85, 0x43), + }; static const char *amd_pmf_uevent_as_str(unsigned int state) { @@ -62,18 +65,12 @@ static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd, param[0].u.memref.shm_offs = 0; } -static int amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event) +static void amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event) { - char *envp[2] = {}; - - envp[0] = kasprintf(GFP_KERNEL, "EVENT_ID=%d", event); - if (!envp[0]) - return -EINVAL; - - kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, envp); - - kfree(envp[0]); - return 0; + input_report_key(dev->pmf_idev, event, 1); /* key press */ + input_sync(dev->pmf_idev); + input_report_key(dev->pmf_idev, event, 0); /* key release */ + input_sync(dev->pmf_idev); } static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out) @@ -126,7 +123,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ case PMF_POLICY_STT_SKINTEMP_APU: if (dev->prev_data->stt_skintemp_apu != val) { - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, + fixp_q88_fromint(val), NULL); dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val); dev->prev_data->stt_skintemp_apu = val; } @@ -134,7 +132,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ case PMF_POLICY_STT_SKINTEMP_HS2: if (dev->prev_data->stt_skintemp_hs2 != val) { - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL); + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, + fixp_q88_fromint(val), NULL); dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val); dev->prev_data->stt_skintemp_hs2 = val; } @@ -149,10 +148,63 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ break; case PMF_POLICY_SYSTEM_STATE: - amd_pmf_update_uevents(dev, val); + switch (val) { + case 0: + amd_pmf_update_uevents(dev, KEY_SLEEP); + break; + case 1: + amd_pmf_update_uevents(dev, KEY_SUSPEND); + break; + case 2: + amd_pmf_update_uevents(dev, KEY_SCREENLOCK); + break; + default: + dev_err(dev->dev, "Invalid PMF policy system state: %d\n", val); + } + dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n", amd_pmf_uevent_as_str(val)); break; + + case PMF_POLICY_BIOS_OUTPUT_1: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(0), 0); + break; + + case PMF_POLICY_BIOS_OUTPUT_2: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(1), 1); + break; + + case PMF_POLICY_BIOS_OUTPUT_3: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(2), 2); + break; + + case PMF_POLICY_BIOS_OUTPUT_4: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(3), 3); + break; + + case PMF_POLICY_BIOS_OUTPUT_5: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(4), 4); + break; + + case PMF_POLICY_BIOS_OUTPUT_6: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(5), 5); + break; + + case PMF_POLICY_BIOS_OUTPUT_7: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(6), 6); + break; + + case PMF_POLICY_BIOS_OUTPUT_8: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(7), 7); + break; + + case PMF_POLICY_BIOS_OUTPUT_9: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(8), 8); + break; + + case PMF_POLICY_BIOS_OUTPUT_10: + amd_pmf_smartpc_apply_bios_output(dev, val, BIT(9), 9); + break; } } } @@ -210,7 +262,7 @@ static int amd_pmf_invoke_cmd_init(struct amd_pmf_dev *dev) return -ENODEV; } - dev_dbg(dev->dev, "Policy Binary size: %u bytes\n", dev->policy_sz); + dev_dbg(dev->dev, "Policy Binary size: %llu bytes\n", (unsigned long long)dev->policy_sz); memset(dev->shbuf, 0, dev->policy_sz); ta_sm = dev->shbuf; in = &ta_sm->pmf_input.init_table; @@ -274,14 +326,19 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) */ schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3)); } else { - dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res); + dev_dbg(dev->dev, "ta invoke cmd init failed err: %x\n", res); dev->smart_pc_enabled = false; - return -EIO; + return res; } return 0; } +static inline bool amd_pmf_pb_valid(struct amd_pmf_dev *dev) +{ + return memchr_inv(dev->policy_buf, 0xff, dev->policy_sz); +} + #ifdef CONFIG_AMD_PMF_DEBUG static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev) { @@ -301,25 +358,30 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, return -EINVAL; /* re-alloc to the new buffer length of the policy binary */ - new_policy_buf = kzalloc(length, GFP_KERNEL); - if (!new_policy_buf) - return -ENOMEM; - - if (copy_from_user(new_policy_buf, buf, length)) { - kfree(new_policy_buf); - return -EFAULT; - } + new_policy_buf = memdup_user(buf, length); + if (IS_ERR(new_policy_buf)) + return PTR_ERR(new_policy_buf); kfree(dev->policy_buf); dev->policy_buf = new_policy_buf; dev->policy_sz = length; + if (!amd_pmf_pb_valid(dev)) { + ret = -EINVAL; + goto cleanup; + } + amd_pmf_hex_dump_pb(dev); ret = amd_pmf_start_policy_engine(dev); if (ret < 0) - return ret; + goto cleanup; return length; + +cleanup: + kfree(dev->policy_buf); + dev->policy_buf = NULL; + return ret; } static const struct file_operations pb_fops = { @@ -348,12 +410,12 @@ static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const voi return ver->impl_id == TEE_IMPL_ID_AMDTEE; } -static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id) +static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id, const uuid_t *uuid) { struct tee_ioctl_open_session_arg sess_arg = {}; int rc; - export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid); + export_uuid(sess_arg.uuid, uuid); sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; sess_arg.num_params = 0; @@ -368,7 +430,31 @@ static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id) return rc; } -static int amd_pmf_tee_init(struct amd_pmf_dev *dev) +static int amd_pmf_register_input_device(struct amd_pmf_dev *dev) +{ + int err; + + dev->pmf_idev = devm_input_allocate_device(dev->dev); + if (!dev->pmf_idev) + return -ENOMEM; + + dev->pmf_idev->name = "PMF-TA output events"; + dev->pmf_idev->phys = "amd-pmf/input0"; + + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SLEEP); + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SCREENLOCK); + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SUSPEND); + + err = input_register_device(dev->pmf_idev); + if (err) { + dev_err(dev->dev, "Failed to register input device: %d\n", err); + return err; + } + + return 0; +} + +static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid) { u32 size; int ret; @@ -379,7 +465,7 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev) return PTR_ERR(dev->tee_ctx); } - ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id); + ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id, uuid); if (ret) { dev_err(dev->dev, "Failed to open TA session (%d)\n", ret); ret = -EINVAL; @@ -423,7 +509,8 @@ static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev) int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) { - int ret; + bool status; + int ret, i; ret = apmf_check_smart_pc(dev); if (ret) { @@ -436,55 +523,100 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) return -ENODEV; } - ret = amd_pmf_tee_init(dev); - if (ret) - return ret; - INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd); ret = amd_pmf_set_dram_addr(dev, true); if (ret) - goto error; + goto err_cancel_work; - dev->policy_base = devm_ioremap(dev->dev, dev->policy_addr, dev->policy_sz); - if (!dev->policy_base) { - ret = -ENOMEM; - goto error; + dev->policy_base = devm_ioremap_resource(dev->dev, dev->res); + if (IS_ERR(dev->policy_base)) { + ret = PTR_ERR(dev->policy_base); + goto err_free_dram_buf; } dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL); if (!dev->policy_buf) { ret = -ENOMEM; - goto error; + goto err_free_dram_buf; } memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz); + if (!amd_pmf_pb_valid(dev)) { + dev_info(dev->dev, "No Smart PC policy present\n"); + ret = -EINVAL; + goto err_free_policy; + } + amd_pmf_hex_dump_pb(dev); dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL); if (!dev->prev_data) { ret = -ENOMEM; - goto error; + goto err_free_policy; } - ret = amd_pmf_start_policy_engine(dev); - if (ret) - goto error; + for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) { + ret = amd_pmf_tee_init(dev, &amd_pmf_ta_uuid[i]); + if (ret) + goto err_free_prev_data; + + ret = amd_pmf_start_policy_engine(dev); + switch (ret) { + case TA_PMF_TYPE_SUCCESS: + status = true; + break; + case TA_ERROR_CRYPTO_INVALID_PARAM: + case TA_ERROR_CRYPTO_BIN_TOO_LARGE: + amd_pmf_tee_deinit(dev); + status = false; + break; + default: + ret = -EINVAL; + amd_pmf_tee_deinit(dev); + goto err_free_prev_data; + } + + if (status) + break; + } + + if (!status && !pb_side_load) { + ret = -EINVAL; + goto err_free_prev_data; + } if (pb_side_load) amd_pmf_open_pb(dev, dev->dbgfs_dir); + ret = amd_pmf_register_input_device(dev); + if (ret) + goto err_pmf_remove_pb; + return 0; -error: - amd_pmf_deinit_smart_pc(dev); +err_pmf_remove_pb: + if (pb_side_load && dev->esbin) + amd_pmf_remove_pb(dev); + amd_pmf_tee_deinit(dev); +err_free_prev_data: + kfree(dev->prev_data); +err_free_policy: + kfree(dev->policy_buf); +err_free_dram_buf: + kfree(dev->buf); +err_cancel_work: + cancel_delayed_work_sync(&dev->pb_work); return ret; } void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) { + if (dev->pmf_idev) + input_unregister_device(dev->pmf_idev); + if (pb_side_load && dev->esbin) amd_pmf_remove_pb(dev); |