Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/acpi_pcc.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Author: Sudeep Holla <[email protected]>
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
32
struct 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
39
static struct acpi_pcc_info pcc_ctx;
40
41
static 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
48
static acpi_status
49
acpi_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(&data->done);
68
data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id);
69
if (IS_ERR(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
87
err_free_channel:
88
pcc_mbox_free_channel(data->pcc_chan);
89
err_free_data:
90
kfree(data);
91
92
return ret;
93
}
94
95
static acpi_status
96
acpi_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(&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(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(&data->done,
120
usecs_to_jiffies(usecs_lat));
121
if (ret == 0) {
122
pr_err("PCC command executed timeout!\n");
123
return AE_TIME;
124
}
125
126
mbox_chan_txdone(data->pcc_chan->mchan, ret);
127
128
memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length);
129
130
return AE_OK;
131
}
132
133
void __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
&acpi_pcc_address_space_handler,
140
&acpi_pcc_address_space_setup,
141
&pcc_ctx);
142
if (ACPI_FAILURE(status))
143
pr_alert("OperationRegion handler could not be installed\n");
144
}
145
146