| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Author: Sudeep Holla <sudeep.holla@arm.com> | 
 |  * Copyright 2021 Arm Limited | 
 |  * | 
 |  * The PCC Address Space also referred as PCC Operation Region pertains to the | 
 |  * region of PCC subspace that succeeds the PCC signature. The PCC Operation | 
 |  * Region works in conjunction with the PCC Table(Platform Communications | 
 |  * Channel Table). PCC subspaces that are marked for use as PCC Operation | 
 |  * Regions must not be used as PCC subspaces for the standard ACPI features | 
 |  * such as CPPC, RASF, PDTT and MPST. These standard features must always use | 
 |  * the PCC Table instead. | 
 |  * | 
 |  * This driver sets up the PCC Address Space and installs an handler to enable | 
 |  * handling of PCC OpRegion in the firmware. | 
 |  * | 
 |  */ | 
 | #include <linux/kernel.h> | 
 | #include <linux/acpi.h> | 
 | #include <linux/completion.h> | 
 | #include <linux/idr.h> | 
 | #include <linux/io.h> | 
 |  | 
 | #include <acpi/pcc.h> | 
 |  | 
 | /* | 
 |  * Arbitrary retries in case the remote processor is slow to respond | 
 |  * to PCC commands | 
 |  */ | 
 | #define PCC_CMD_WAIT_RETRIES_NUM	500ULL | 
 |  | 
 | struct pcc_data { | 
 | 	struct pcc_mbox_chan *pcc_chan; | 
 | 	void __iomem *pcc_comm_addr; | 
 | 	struct completion done; | 
 | 	struct mbox_client cl; | 
 | 	struct acpi_pcc_info ctx; | 
 | }; | 
 |  | 
 | static struct acpi_pcc_info pcc_ctx; | 
 |  | 
 | static void pcc_rx_callback(struct mbox_client *cl, void *m) | 
 | { | 
 | 	struct pcc_data *data = container_of(cl, struct pcc_data, cl); | 
 |  | 
 | 	complete(&data->done); | 
 | } | 
 |  | 
 | static acpi_status | 
 | acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function, | 
 | 			     void *handler_context,  void **region_context) | 
 | { | 
 | 	struct pcc_data *data; | 
 | 	struct acpi_pcc_info *ctx = handler_context; | 
 | 	struct pcc_mbox_chan *pcc_chan; | 
 | 	static acpi_status ret; | 
 |  | 
 | 	data = kzalloc(sizeof(*data), GFP_KERNEL); | 
 | 	if (!data) | 
 | 		return AE_NO_MEMORY; | 
 |  | 
 | 	data->cl.rx_callback = pcc_rx_callback; | 
 | 	data->cl.knows_txdone = true; | 
 | 	data->ctx.length = ctx->length; | 
 | 	data->ctx.subspace_id = ctx->subspace_id; | 
 | 	data->ctx.internal_buffer = ctx->internal_buffer; | 
 |  | 
 | 	init_completion(&data->done); | 
 | 	data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id); | 
 | 	if (IS_ERR(data->pcc_chan)) { | 
 | 		pr_err("Failed to find PCC channel for subspace %d\n", | 
 | 		       ctx->subspace_id); | 
 | 		ret = AE_NOT_FOUND; | 
 | 		goto err_free_data; | 
 | 	} | 
 |  | 
 | 	pcc_chan = data->pcc_chan; | 
 | 	if (!pcc_chan->mchan->mbox->txdone_irq) { | 
 | 		pr_err("This channel-%d does not support interrupt.\n", | 
 | 		       ctx->subspace_id); | 
 | 		ret = AE_SUPPORT; | 
 | 		goto err_free_channel; | 
 | 	} | 
 | 	data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr, | 
 | 					      pcc_chan->shmem_size); | 
 | 	if (!data->pcc_comm_addr) { | 
 | 		pr_err("Failed to ioremap PCC comm region mem for %d\n", | 
 | 		       ctx->subspace_id); | 
 | 		ret = AE_NO_MEMORY; | 
 | 		goto err_free_channel; | 
 | 	} | 
 |  | 
 | 	*region_context = data; | 
 | 	return AE_OK; | 
 |  | 
 | err_free_channel: | 
 | 	pcc_mbox_free_channel(data->pcc_chan); | 
 | err_free_data: | 
 | 	kfree(data); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 | 	int ret; | 
 | 	struct pcc_data *data = region_context; | 
 | 	u64 usecs_lat; | 
 |  | 
 | 	reinit_completion(&data->done); | 
 |  | 
 | 	/* Write to Shared Memory */ | 
 | 	memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length); | 
 |  | 
 | 	ret = mbox_send_message(data->pcc_chan->mchan, NULL); | 
 | 	if (ret < 0) | 
 | 		return AE_ERROR; | 
 |  | 
 | 	/* | 
 | 	 * pcc_chan->latency is just a Nominal value. In reality the remote | 
 | 	 * processor could be much slower to reply. So add an arbitrary | 
 | 	 * amount of wait on top of Nominal. | 
 | 	 */ | 
 | 	usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency; | 
 | 	ret = wait_for_completion_timeout(&data->done, | 
 | 						usecs_to_jiffies(usecs_lat)); | 
 | 	if (ret == 0) { | 
 | 		pr_err("PCC command executed timeout!\n"); | 
 | 		return AE_TIME; | 
 | 	} | 
 |  | 
 | 	mbox_chan_txdone(data->pcc_chan->mchan, ret); | 
 |  | 
 | 	memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length); | 
 |  | 
 | 	return AE_OK; | 
 | } | 
 |  | 
 | void __init acpi_init_pcc(void) | 
 | { | 
 | 	acpi_status status; | 
 |  | 
 | 	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, | 
 | 						    ACPI_ADR_SPACE_PLATFORM_COMM, | 
 | 						    &acpi_pcc_address_space_handler, | 
 | 						    &acpi_pcc_address_space_setup, | 
 | 						    &pcc_ctx); | 
 | 	if (ACPI_FAILURE(status)) | 
 | 		pr_alert("OperationRegion handler could not be installed\n"); | 
 | } |