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 */ 3091cefefbSHuisong Li #define PCC_CMD_WAIT_RETRIES_NUM 500 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 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 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; 5677e2a047SSudeep Holla 5777e2a047SSudeep Holla data = kzalloc(sizeof(*data), GFP_KERNEL); 5877e2a047SSudeep Holla if (!data) 5977e2a047SSudeep Holla return AE_NO_MEMORY; 6077e2a047SSudeep Holla 6177e2a047SSudeep Holla data->cl.rx_callback = pcc_rx_callback; 6277e2a047SSudeep Holla data->cl.knows_txdone = true; 6377e2a047SSudeep Holla data->ctx.length = ctx->length; 6477e2a047SSudeep Holla data->ctx.subspace_id = ctx->subspace_id; 6577e2a047SSudeep Holla data->ctx.internal_buffer = ctx->internal_buffer; 6677e2a047SSudeep Holla 6777e2a047SSudeep Holla init_completion(&data->done); 6877e2a047SSudeep Holla data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id); 6977e2a047SSudeep Holla if (IS_ERR(data->pcc_chan)) { 7077e2a047SSudeep Holla pr_err("Failed to find PCC channel for subspace %d\n", 7177e2a047SSudeep Holla ctx->subspace_id); 72f890157eSRafael Mendonca kfree(data); 7377e2a047SSudeep Holla return AE_NOT_FOUND; 7477e2a047SSudeep Holla } 7577e2a047SSudeep Holla 7677e2a047SSudeep Holla pcc_chan = data->pcc_chan; 7777e2a047SSudeep Holla data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr, 7877e2a047SSudeep Holla pcc_chan->shmem_size); 7977e2a047SSudeep Holla if (!data->pcc_comm_addr) { 8077e2a047SSudeep Holla pr_err("Failed to ioremap PCC comm region mem for %d\n", 8177e2a047SSudeep Holla ctx->subspace_id); 82f890157eSRafael Mendonca pcc_mbox_free_channel(data->pcc_chan); 83f890157eSRafael Mendonca kfree(data); 8477e2a047SSudeep Holla return AE_NO_MEMORY; 8577e2a047SSudeep Holla } 8677e2a047SSudeep Holla 8777e2a047SSudeep Holla *region_context = data; 8877e2a047SSudeep Holla return AE_OK; 8977e2a047SSudeep Holla } 9077e2a047SSudeep Holla 9177e2a047SSudeep Holla static acpi_status 9277e2a047SSudeep Holla acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr, 9377e2a047SSudeep Holla u32 bits, acpi_integer *value, 9477e2a047SSudeep Holla void *handler_context, void *region_context) 9577e2a047SSudeep Holla { 9677e2a047SSudeep Holla int ret; 9777e2a047SSudeep Holla struct pcc_data *data = region_context; 9891cefefbSHuisong Li u64 usecs_lat; 9977e2a047SSudeep Holla 10077e2a047SSudeep Holla reinit_completion(&data->done); 10177e2a047SSudeep Holla 10277e2a047SSudeep Holla /* Write to Shared Memory */ 10377e2a047SSudeep Holla memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length); 10477e2a047SSudeep Holla 10577e2a047SSudeep Holla ret = mbox_send_message(data->pcc_chan->mchan, NULL); 10677e2a047SSudeep Holla if (ret < 0) 10777e2a047SSudeep Holla return AE_ERROR; 10877e2a047SSudeep Holla 10991cefefbSHuisong Li if (data->pcc_chan->mchan->mbox->txdone_irq) { 11091cefefbSHuisong Li /* 11191cefefbSHuisong Li * pcc_chan->latency is just a Nominal value. In reality the remote 11291cefefbSHuisong Li * processor could be much slower to reply. So add an arbitrary 11391cefefbSHuisong Li * amount of wait on top of Nominal. 11491cefefbSHuisong Li */ 11591cefefbSHuisong Li usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency; 11691cefefbSHuisong Li ret = wait_for_completion_timeout(&data->done, 11791cefefbSHuisong Li usecs_to_jiffies(usecs_lat)); 11891cefefbSHuisong Li if (ret == 0) { 11991cefefbSHuisong Li pr_err("PCC command executed timeout!\n"); 12091cefefbSHuisong Li return AE_TIME; 12191cefefbSHuisong Li } 12291cefefbSHuisong Li } 12377e2a047SSudeep Holla 124*18729106SHuisong Li mbox_chan_txdone(data->pcc_chan->mchan, ret); 12577e2a047SSudeep Holla 12677e2a047SSudeep Holla memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length); 12777e2a047SSudeep Holla 12877e2a047SSudeep Holla return AE_OK; 12977e2a047SSudeep Holla } 13077e2a047SSudeep Holla 13177e2a047SSudeep Holla void __init acpi_init_pcc(void) 13277e2a047SSudeep Holla { 13377e2a047SSudeep Holla acpi_status status; 13477e2a047SSudeep Holla 13577e2a047SSudeep Holla status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, 13677e2a047SSudeep Holla ACPI_ADR_SPACE_PLATFORM_COMM, 13777e2a047SSudeep Holla &acpi_pcc_address_space_handler, 13877e2a047SSudeep Holla &acpi_pcc_address_space_setup, 13977e2a047SSudeep Holla &pcc_ctx); 14077e2a047SSudeep Holla if (ACPI_FAILURE(status)) 14177e2a047SSudeep Holla pr_alert("OperationRegion handler could not be installed\n"); 14277e2a047SSudeep Holla } 143