123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Author: Sudeep Holla <[email protected]>
- * 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;
- 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);
- kfree(data);
- return AE_NOT_FOUND;
- }
- pcc_chan = data->pcc_chan;
- 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);
- pcc_mbox_free_channel(data->pcc_chan);
- kfree(data);
- return AE_NO_MEMORY;
- }
- *region_context = data;
- return AE_OK;
- }
- 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;
- if (data->pcc_chan->mchan->mbox->txdone_irq) {
- /*
- * 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");
- }
|