xref: /openbmc/linux/drivers/acpi/acpi_pcc.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
177e2a047SSudeep Holla // SPDX-License-Identifier: GPL-2.0-only
277e2a047SSudeep Holla /*
377e2a047SSudeep Holla  * Author: Sudeep Holla <sudeep.holla@arm.com>
477e2a047SSudeep Holla  * Copyright 2021 Arm Limited
577e2a047SSudeep Holla  *
677e2a047SSudeep Holla  * The PCC Address Space also referred as PCC Operation Region pertains to the
777e2a047SSudeep Holla  * region of PCC subspace that succeeds the PCC signature. The PCC Operation
877e2a047SSudeep Holla  * Region works in conjunction with the PCC Table(Platform Communications
977e2a047SSudeep Holla  * Channel Table). PCC subspaces that are marked for use as PCC Operation
1077e2a047SSudeep Holla  * Regions must not be used as PCC subspaces for the standard ACPI features
1177e2a047SSudeep Holla  * such as CPPC, RASF, PDTT and MPST. These standard features must always use
1277e2a047SSudeep Holla  * the PCC Table instead.
1377e2a047SSudeep Holla  *
1477e2a047SSudeep Holla  * This driver sets up the PCC Address Space and installs an handler to enable
1577e2a047SSudeep Holla  * handling of PCC OpRegion in the firmware.
1677e2a047SSudeep Holla  *
1777e2a047SSudeep Holla  */
1877e2a047SSudeep Holla #include <linux/kernel.h>
1977e2a047SSudeep Holla #include <linux/acpi.h>
2077e2a047SSudeep Holla #include <linux/completion.h>
2177e2a047SSudeep Holla #include <linux/idr.h>
2277e2a047SSudeep Holla #include <linux/io.h>
2377e2a047SSudeep Holla 
2477e2a047SSudeep Holla #include <acpi/pcc.h>
2577e2a047SSudeep Holla 
2691cefefbSHuisong Li /*
2791cefefbSHuisong Li  * Arbitrary retries in case the remote processor is slow to respond
2891cefefbSHuisong Li  * to PCC commands
2991cefefbSHuisong Li  */
308338b74aSManank Patel #define PCC_CMD_WAIT_RETRIES_NUM	500ULL
3191cefefbSHuisong Li 
3277e2a047SSudeep Holla struct pcc_data {
3377e2a047SSudeep Holla 	struct pcc_mbox_chan *pcc_chan;
3477e2a047SSudeep Holla 	void __iomem *pcc_comm_addr;
3577e2a047SSudeep Holla 	struct completion done;
3677e2a047SSudeep Holla 	struct mbox_client cl;
3777e2a047SSudeep Holla 	struct acpi_pcc_info ctx;
3877e2a047SSudeep Holla };
3977e2a047SSudeep Holla 
40415b4b6cSkernel test robot static struct acpi_pcc_info pcc_ctx;
4177e2a047SSudeep Holla 
pcc_rx_callback(struct mbox_client * cl,void * m)4277e2a047SSudeep Holla static void pcc_rx_callback(struct mbox_client *cl, void *m)
4377e2a047SSudeep Holla {
4477e2a047SSudeep Holla 	struct pcc_data *data = container_of(cl, struct pcc_data, cl);
4577e2a047SSudeep Holla 
4677e2a047SSudeep Holla 	complete(&data->done);
4777e2a047SSudeep Holla }
4877e2a047SSudeep Holla 
4977e2a047SSudeep Holla static acpi_status
acpi_pcc_address_space_setup(acpi_handle region_handle,u32 function,void * handler_context,void ** region_context)5077e2a047SSudeep Holla acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
5177e2a047SSudeep Holla 			     void *handler_context,  void **region_context)
5277e2a047SSudeep Holla {
5377e2a047SSudeep Holla 	struct pcc_data *data;
5477e2a047SSudeep Holla 	struct acpi_pcc_info *ctx = handler_context;
5577e2a047SSudeep Holla 	struct pcc_mbox_chan *pcc_chan;
56*a10b1c99SHuisong Li 	static acpi_status ret;
5777e2a047SSudeep Holla 
5877e2a047SSudeep Holla 	data = kzalloc(sizeof(*data), GFP_KERNEL);
5977e2a047SSudeep Holla 	if (!data)
6077e2a047SSudeep Holla 		return AE_NO_MEMORY;
6177e2a047SSudeep Holla 
6277e2a047SSudeep Holla 	data->cl.rx_callback = pcc_rx_callback;
6377e2a047SSudeep Holla 	data->cl.knows_txdone = true;
6477e2a047SSudeep Holla 	data->ctx.length = ctx->length;
6577e2a047SSudeep Holla 	data->ctx.subspace_id = ctx->subspace_id;
6677e2a047SSudeep Holla 	data->ctx.internal_buffer = ctx->internal_buffer;
6777e2a047SSudeep Holla 
6877e2a047SSudeep Holla 	init_completion(&data->done);
6977e2a047SSudeep Holla 	data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id);
7077e2a047SSudeep Holla 	if (IS_ERR(data->pcc_chan)) {
7177e2a047SSudeep Holla 		pr_err("Failed to find PCC channel for subspace %d\n",
7277e2a047SSudeep Holla 		       ctx->subspace_id);
73*a10b1c99SHuisong Li 		ret = AE_NOT_FOUND;
74*a10b1c99SHuisong Li 		goto err_free_data;
7577e2a047SSudeep Holla 	}
7677e2a047SSudeep Holla 
7777e2a047SSudeep Holla 	pcc_chan = data->pcc_chan;
78*a10b1c99SHuisong Li 	if (!pcc_chan->mchan->mbox->txdone_irq) {
79*a10b1c99SHuisong Li 		pr_err("This channel-%d does not support interrupt.\n",
80*a10b1c99SHuisong Li 		       ctx->subspace_id);
81*a10b1c99SHuisong Li 		ret = AE_SUPPORT;
82*a10b1c99SHuisong Li 		goto err_free_channel;
83*a10b1c99SHuisong Li 	}
8477e2a047SSudeep Holla 	data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr,
8577e2a047SSudeep Holla 					      pcc_chan->shmem_size);
8677e2a047SSudeep Holla 	if (!data->pcc_comm_addr) {
8777e2a047SSudeep Holla 		pr_err("Failed to ioremap PCC comm region mem for %d\n",
8877e2a047SSudeep Holla 		       ctx->subspace_id);
89*a10b1c99SHuisong Li 		ret = AE_NO_MEMORY;
90*a10b1c99SHuisong Li 		goto err_free_channel;
9177e2a047SSudeep Holla 	}
9277e2a047SSudeep Holla 
9377e2a047SSudeep Holla 	*region_context = data;
9477e2a047SSudeep Holla 	return AE_OK;
95*a10b1c99SHuisong Li 
96*a10b1c99SHuisong Li err_free_channel:
97*a10b1c99SHuisong Li 	pcc_mbox_free_channel(data->pcc_chan);
98*a10b1c99SHuisong Li err_free_data:
99*a10b1c99SHuisong Li 	kfree(data);
100*a10b1c99SHuisong Li 
101*a10b1c99SHuisong Li 	return ret;
10277e2a047SSudeep Holla }
10377e2a047SSudeep Holla 
10477e2a047SSudeep Holla static acpi_status
acpi_pcc_address_space_handler(u32 function,acpi_physical_address addr,u32 bits,acpi_integer * value,void * handler_context,void * region_context)10577e2a047SSudeep Holla acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
10677e2a047SSudeep Holla 			       u32 bits, acpi_integer *value,
10777e2a047SSudeep Holla 			       void *handler_context, void *region_context)
10877e2a047SSudeep Holla {
10977e2a047SSudeep Holla 	int ret;
11077e2a047SSudeep Holla 	struct pcc_data *data = region_context;
11191cefefbSHuisong Li 	u64 usecs_lat;
11277e2a047SSudeep Holla 
11377e2a047SSudeep Holla 	reinit_completion(&data->done);
11477e2a047SSudeep Holla 
11577e2a047SSudeep Holla 	/* Write to Shared Memory */
11677e2a047SSudeep Holla 	memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length);
11777e2a047SSudeep Holla 
11877e2a047SSudeep Holla 	ret = mbox_send_message(data->pcc_chan->mchan, NULL);
11977e2a047SSudeep Holla 	if (ret < 0)
12077e2a047SSudeep Holla 		return AE_ERROR;
12177e2a047SSudeep Holla 
12291cefefbSHuisong Li 	/*
12391cefefbSHuisong Li 	 * pcc_chan->latency is just a Nominal value. In reality the remote
12491cefefbSHuisong Li 	 * processor could be much slower to reply. So add an arbitrary
12591cefefbSHuisong Li 	 * amount of wait on top of Nominal.
12691cefefbSHuisong Li 	 */
12791cefefbSHuisong Li 	usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency;
12891cefefbSHuisong Li 	ret = wait_for_completion_timeout(&data->done,
12991cefefbSHuisong Li 						usecs_to_jiffies(usecs_lat));
13091cefefbSHuisong Li 	if (ret == 0) {
13191cefefbSHuisong Li 		pr_err("PCC command executed timeout!\n");
13291cefefbSHuisong Li 		return AE_TIME;
13391cefefbSHuisong Li 	}
13477e2a047SSudeep Holla 
13518729106SHuisong Li 	mbox_chan_txdone(data->pcc_chan->mchan, ret);
13677e2a047SSudeep Holla 
13777e2a047SSudeep Holla 	memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length);
13877e2a047SSudeep Holla 
13977e2a047SSudeep Holla 	return AE_OK;
14077e2a047SSudeep Holla }
14177e2a047SSudeep Holla 
acpi_init_pcc(void)14277e2a047SSudeep Holla void __init acpi_init_pcc(void)
14377e2a047SSudeep Holla {
14477e2a047SSudeep Holla 	acpi_status status;
14577e2a047SSudeep Holla 
14677e2a047SSudeep Holla 	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
14777e2a047SSudeep Holla 						    ACPI_ADR_SPACE_PLATFORM_COMM,
14877e2a047SSudeep Holla 						    &acpi_pcc_address_space_handler,
14977e2a047SSudeep Holla 						    &acpi_pcc_address_space_setup,
15077e2a047SSudeep Holla 						    &pcc_ctx);
15177e2a047SSudeep Holla 	if (ACPI_FAILURE(status))
15277e2a047SSudeep Holla 		pr_alert("OperationRegion handler could not be installed\n");
15377e2a047SSudeep Holla }
154