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
26/*
27 * Arbitrary retries in case the remote processor is slow to respond
28 * to PCC commands
29 */
30#define PCC_CMD_WAIT_RETRIES_NUM 500ULL
31
32struct pcc_data {
33 struct pcc_mbox_chan *pcc_chan;
34 struct completion done;
35 struct mbox_client cl;
36 struct acpi_pcc_info ctx;
37};
38
39static struct acpi_pcc_info pcc_ctx;
40
41static void pcc_rx_callback(struct mbox_client *cl, void *m)
42{
43 struct pcc_data *data = container_of(cl, struct pcc_data, cl);
44
45 complete(&data->done);
46}
47
48static acpi_status
49acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
50 void *handler_context, void **region_context)
51{
52 struct pcc_data *data;
53 struct acpi_pcc_info *ctx = handler_context;
54 struct pcc_mbox_chan *pcc_chan;
55 static acpi_status ret;
56
57 data = kzalloc(sizeof(*data), GFP_KERNEL);
58 if (!data)
59 return AE_NO_MEMORY;
60
61 data->cl.rx_callback = pcc_rx_callback;
62 data->cl.knows_txdone = true;
63 data->ctx.length = ctx->length;
64 data->ctx.subspace_id = ctx->subspace_id;
65 data->ctx.internal_buffer = ctx->internal_buffer;
66
67 init_completion(x: &data->done);
68 data->pcc_chan = pcc_mbox_request_channel(cl: &data->cl, subspace_id: ctx->subspace_id);
69 if (IS_ERR(ptr: data->pcc_chan)) {
70 pr_err("Failed to find PCC channel for subspace %d\n",
71 ctx->subspace_id);
72 ret = AE_NOT_FOUND;
73 goto err_free_data;
74 }
75
76 pcc_chan = data->pcc_chan;
77 if (!pcc_chan->mchan->mbox->txdone_irq) {
78 pr_err("This channel-%d does not support interrupt.\n",
79 ctx->subspace_id);
80 ret = AE_SUPPORT;
81 goto err_free_channel;
82 }
83
84 *region_context = data;
85 return AE_OK;
86
87err_free_channel:
88 pcc_mbox_free_channel(chan: data->pcc_chan);
89err_free_data:
90 kfree(objp: data);
91
92 return ret;
93}
94
95static acpi_status
96acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
97 u32 bits, acpi_integer *value,
98 void *handler_context, void *region_context)
99{
100 int ret;
101 struct pcc_data *data = region_context;
102 u64 usecs_lat;
103
104 reinit_completion(x: &data->done);
105
106 /* Write to Shared Memory */
107 memcpy_toio(data->pcc_chan->shmem, (void *)value, data->ctx.length);
108
109 ret = mbox_send_message(chan: data->pcc_chan->mchan, NULL);
110 if (ret < 0)
111 return AE_ERROR;
112
113 /*
114 * pcc_chan->latency is just a Nominal value. In reality the remote
115 * processor could be much slower to reply. So add an arbitrary
116 * amount of wait on top of Nominal.
117 */
118 usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency;
119 ret = wait_for_completion_timeout(x: &data->done,
120 timeout: usecs_to_jiffies(u: usecs_lat));
121 if (ret == 0) {
122 pr_err("PCC command executed timeout!\n");
123 return AE_TIME;
124 }
125
126 mbox_chan_txdone(chan: data->pcc_chan->mchan, r: ret);
127
128 memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length);
129
130 return AE_OK;
131}
132
133void __init acpi_init_pcc(void)
134{
135 acpi_status status;
136
137 status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
138 ACPI_ADR_SPACE_PLATFORM_COMM,
139 handler: &acpi_pcc_address_space_handler,
140 setup: &acpi_pcc_address_space_setup,
141 context: &pcc_ctx);
142 if (ACPI_FAILURE(status))
143 pr_alert("OperationRegion handler could not be installed\n");
144}
145