Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Author: Sudeep Holla <sudeep.holla@arm.com>
4 * Copyright 2021 Arm Limited
5 *
6 * The PCC Address Space also referred as PCC Operation Region pertains to the
7 * region of PCC subspace that succeeds the PCC signature. The PCC Operation
8 * Region works in conjunction with the PCC Table(Platform Communications
9 * Channel Table). PCC subspaces that are marked for use as PCC Operation
10 * Regions must not be used as PCC subspaces for the standard ACPI features
11 * such as CPPC, RASF, PDTT and MPST. These standard features must always use
12 * the PCC Table instead.
13 *
14 * This driver sets up the PCC Address Space and installs an handler to enable
15 * handling of PCC OpRegion in the firmware.
16 *
17 */
18#include <linux/kernel.h>
19#include <linux/acpi.h>
20#include <linux/completion.h>
21#include <linux/idr.h>
22#include <linux/io.h>
23
24#include <acpi/pcc.h>
25
26struct pcc_data {
27 struct pcc_mbox_chan *pcc_chan;
28 void __iomem *pcc_comm_addr;
29 struct completion done;
30 struct mbox_client cl;
31 struct acpi_pcc_info ctx;
32};
33
34static struct acpi_pcc_info pcc_ctx;
35
36static void pcc_rx_callback(struct mbox_client *cl, void *m)
37{
38 struct pcc_data *data = container_of(cl, struct pcc_data, cl);
39
40 complete(&data->done);
41}
42
43static acpi_status
44acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
45 void *handler_context, void **region_context)
46{
47 struct pcc_data *data;
48 struct acpi_pcc_info *ctx = handler_context;
49 struct pcc_mbox_chan *pcc_chan;
50
51 data = kzalloc(sizeof(*data), GFP_KERNEL);
52 if (!data)
53 return AE_NO_MEMORY;
54
55 data->cl.rx_callback = pcc_rx_callback;
56 data->cl.knows_txdone = true;
57 data->ctx.length = ctx->length;
58 data->ctx.subspace_id = ctx->subspace_id;
59 data->ctx.internal_buffer = ctx->internal_buffer;
60
61 init_completion(&data->done);
62 data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id);
63 if (IS_ERR(data->pcc_chan)) {
64 pr_err("Failed to find PCC channel for subspace %d\n",
65 ctx->subspace_id);
66 return AE_NOT_FOUND;
67 }
68
69 pcc_chan = data->pcc_chan;
70 data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr,
71 pcc_chan->shmem_size);
72 if (!data->pcc_comm_addr) {
73 pr_err("Failed to ioremap PCC comm region mem for %d\n",
74 ctx->subspace_id);
75 return AE_NO_MEMORY;
76 }
77
78 *region_context = data;
79 return AE_OK;
80}
81
82static acpi_status
83acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
84 u32 bits, acpi_integer *value,
85 void *handler_context, void *region_context)
86{
87 int ret;
88 struct pcc_data *data = region_context;
89
90 reinit_completion(&data->done);
91
92 /* Write to Shared Memory */
93 memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length);
94
95 ret = mbox_send_message(data->pcc_chan->mchan, NULL);
96 if (ret < 0)
97 return AE_ERROR;
98
99 if (data->pcc_chan->mchan->mbox->txdone_irq)
100 wait_for_completion(&data->done);
101
102 mbox_client_txdone(data->pcc_chan->mchan, ret);
103
104 memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length);
105
106 return AE_OK;
107}
108
109void __init acpi_init_pcc(void)
110{
111 acpi_status status;
112
113 status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
114 ACPI_ADR_SPACE_PLATFORM_COMM,
115 &acpi_pcc_address_space_handler,
116 &acpi_pcc_address_space_setup,
117 &pcc_ctx);
118 if (ACPI_FAILURE(status))
119 pr_alert("OperationRegion handler could not be installed\n");
120}