Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | // 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; 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"); } |