mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
platform/x86/amd/pmf: Use ring buffer to store custom BIOS input values
Custom BIOS input values can be updated by multiple sources, such as power mode changes and sensor events, each triggering a custom BIOS input event. When these events occur in rapid succession, new data may overwrite previous values before they are processed, resulting in lost updates. To address this, introduce a fixed-size, power-of-two ring buffer to capture every custom BIOS input event, storing both the pending request and its associated input values. Access to the ring buffer is synchronized using a mutex. The previous use of memset() to clear the pending request structure after each event is removed, as each BIOS input value is now copied into the buffer as a snapshot. Consumers now process entries directly from the ring buffer, making explicit clearing of the pending request structure unnecessary. Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> Tested-by: Yijun Shen <Yijun.Shen@Dell.com> Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com> Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://patch.msgid.link/20251202042219.245173-1-Shyam-sundar.S-k@amd.com Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
parent
dd0a2d47cf
commit
2a2c085de1
|
|
@ -9,6 +9,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include "pmf.h"
|
||||
|
||||
#define APMF_CQL_NOTIFICATION 2
|
||||
|
|
@ -331,6 +334,39 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req
|
|||
req, sizeof(*req));
|
||||
}
|
||||
|
||||
/* Store custom BIOS inputs data in ring buffer */
|
||||
static void amd_pmf_custom_bios_inputs_rb(struct amd_pmf_dev *pmf_dev)
|
||||
{
|
||||
struct pmf_cbi_ring_buffer *rb = &pmf_dev->cbi_buf;
|
||||
int i;
|
||||
|
||||
guard(mutex)(&pmf_dev->cbi_mutex);
|
||||
|
||||
switch (pmf_dev->cpu_id) {
|
||||
case AMD_CPU_ID_PS:
|
||||
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs_v1); i++)
|
||||
rb->data[rb->head].val[i] = pmf_dev->req1.custom_policy[i];
|
||||
rb->data[rb->head].preq = pmf_dev->req1.pending_req;
|
||||
break;
|
||||
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
|
||||
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
|
||||
rb->data[rb->head].val[i] = pmf_dev->req.custom_policy[i];
|
||||
rb->data[rb->head].preq = pmf_dev->req.pending_req;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (CIRC_SPACE(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0) {
|
||||
/* Rare case: ensures the newest BIOS input value is kept */
|
||||
dev_warn(pmf_dev->dev, "Overwriting BIOS input value, data may be lost\n");
|
||||
rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
|
||||
}
|
||||
|
||||
rb->head = (rb->head + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
|
||||
}
|
||||
|
||||
static void amd_pmf_handle_early_preq(struct amd_pmf_dev *pdev)
|
||||
{
|
||||
if (!pdev->cb_flag)
|
||||
|
|
@ -356,6 +392,8 @@ static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data)
|
|||
dev_dbg(pmf_dev->dev, "Pending request (preq): 0x%x\n", pmf_dev->req.pending_req);
|
||||
|
||||
amd_pmf_handle_early_preq(pmf_dev);
|
||||
|
||||
amd_pmf_custom_bios_inputs_rb(pmf_dev);
|
||||
}
|
||||
|
||||
static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data)
|
||||
|
|
@ -374,6 +412,8 @@ static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data)
|
|||
dev_dbg(pmf_dev->dev, "Pending request (preq1): 0x%x\n", pmf_dev->req1.pending_req);
|
||||
|
||||
amd_pmf_handle_early_preq(pmf_dev);
|
||||
|
||||
amd_pmf_custom_bios_inputs_rb(pmf_dev);
|
||||
}
|
||||
|
||||
static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
|
@ -477,6 +478,10 @@ static int amd_pmf_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_mutex_init(dev->dev, &dev->cbi_mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
apmf_acpi_init(dev);
|
||||
platform_set_drvdata(pdev, dev);
|
||||
amd_pmf_dbgfs_register(dev);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
#define PMF_H
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mutex_types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_profile.h>
|
||||
|
||||
|
|
@ -120,6 +122,7 @@ struct cookie_header {
|
|||
#define APTS_MAX_STATES 16
|
||||
#define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7)
|
||||
#define BIOS_INPUTS_MAX 10
|
||||
#define CUSTOM_BIOS_INPUT_RING_ENTRIES 64 /* Must be power of two for CIRC_* macros */
|
||||
|
||||
/* amd_pmf_send_cmd() set/get */
|
||||
#define SET_CMD false
|
||||
|
|
@ -365,6 +368,22 @@ struct pmf_bios_inputs_prev {
|
|||
u32 custom_bios_inputs[BIOS_INPUTS_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pmf_bios_input_entry - Snapshot of custom BIOS input event
|
||||
* @val: Array of custom BIOS input values
|
||||
* @preq: Pending request value associated with this event
|
||||
*/
|
||||
struct pmf_bios_input_entry {
|
||||
u32 val[BIOS_INPUTS_MAX];
|
||||
u32 preq;
|
||||
};
|
||||
|
||||
struct pmf_cbi_ring_buffer {
|
||||
struct pmf_bios_input_entry data[CUSTOM_BIOS_INPUT_RING_ENTRIES];
|
||||
int head;
|
||||
int tail;
|
||||
};
|
||||
|
||||
struct amd_pmf_dev {
|
||||
void __iomem *regbase;
|
||||
void __iomem *smu_virt_addr;
|
||||
|
|
@ -413,6 +432,8 @@ struct amd_pmf_dev {
|
|||
struct apmf_sbios_req_v1 req1;
|
||||
struct pmf_bios_inputs_prev cb_prev; /* To preserve custom BIOS inputs */
|
||||
bool cb_flag; /* To handle first custom BIOS input */
|
||||
struct pmf_cbi_ring_buffer cbi_buf;
|
||||
struct mutex cbi_mutex; /* Protects ring buffer access */
|
||||
};
|
||||
|
||||
struct apmf_sps_prop_granular_v2 {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <acpi/button.h>
|
||||
#include <linux/amd-pmf-io.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/units.h>
|
||||
#include "pmf.h"
|
||||
|
|
@ -132,32 +133,39 @@ static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int
|
|||
}
|
||||
}
|
||||
|
||||
static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, u32 pending_req,
|
||||
static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, struct pmf_bios_input_entry *data,
|
||||
const struct amd_pmf_pb_bitmap *inputs,
|
||||
const u32 *custom_policy, struct ta_pmf_enact_table *in)
|
||||
struct ta_pmf_enact_table *in)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {
|
||||
if (!(pending_req & inputs[i].bit_mask))
|
||||
if (!(data->preq & inputs[i].bit_mask))
|
||||
continue;
|
||||
amd_pmf_set_ta_custom_bios_input(in, i, custom_policy[i]);
|
||||
pdev->cb_prev.custom_bios_inputs[i] = custom_policy[i];
|
||||
dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, custom_policy[i]);
|
||||
amd_pmf_set_ta_custom_bios_input(in, i, data->val[i]);
|
||||
pdev->cb_prev.custom_bios_inputs[i] = data->val[i];
|
||||
dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, data->val[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
|
||||
struct ta_pmf_enact_table *in)
|
||||
{
|
||||
struct pmf_cbi_ring_buffer *rb = &pdev->cbi_buf;
|
||||
unsigned int i;
|
||||
|
||||
guard(mutex)(&pdev->cbi_mutex);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
|
||||
amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);
|
||||
|
||||
if (!(pdev->req.pending_req || pdev->req1.pending_req))
|
||||
if (CIRC_CNT(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0)
|
||||
return;
|
||||
|
||||
/* If no active custom BIOS input pending request, do not consume further work */
|
||||
if (!rb->data[rb->tail].preq)
|
||||
goto out_rbadvance;
|
||||
|
||||
if (!pdev->smart_pc_enabled)
|
||||
return;
|
||||
|
||||
|
|
@ -165,20 +173,17 @@ static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
|
|||
case PMF_IF_V1:
|
||||
if (!is_apmf_bios_input_notifications_supported(pdev))
|
||||
return;
|
||||
amd_pmf_update_bios_inputs(pdev, pdev->req1.pending_req, custom_bios_inputs_v1,
|
||||
pdev->req1.custom_policy, in);
|
||||
amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs_v1, in);
|
||||
break;
|
||||
case PMF_IF_V2:
|
||||
amd_pmf_update_bios_inputs(pdev, pdev->req.pending_req, custom_bios_inputs,
|
||||
pdev->req.custom_policy, in);
|
||||
amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs, in);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear pending requests after handling */
|
||||
memset(&pdev->req, 0, sizeof(pdev->req));
|
||||
memset(&pdev->req1, 0, sizeof(pdev->req1));
|
||||
out_rbadvance:
|
||||
rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
|
||||
}
|
||||
|
||||
static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
|
||||
|
|
|
|||
|
|
@ -591,6 +591,8 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
|
|||
status = ret == TA_PMF_TYPE_SUCCESS;
|
||||
if (status) {
|
||||
dev->cb_flag = true;
|
||||
dev->cbi_buf.head = 0;
|
||||
dev->cbi_buf.tail = 0;
|
||||
break;
|
||||
}
|
||||
amd_pmf_tee_deinit(dev);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user