xref: /linux/drivers/acpi/acpi_pcc.c (revision 0be3ff0c)
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 struct 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 
34 static struct acpi_pcc_info pcc_ctx;
35 
36 static 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 
43 static acpi_status
44 acpi_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 
82 static acpi_status
83 acpi_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 
109 void __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 }
121