1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Author: Sudeep Holla <sudeep.holla@arm.com> 4 * Copyright 2021 Arm Limited 5 * 6 * The PCC Address Space also referred as PCC Operation Region pertains to the 7 * region of PCC subspace that succeeds the PCC signature. The PCC Operation 8 * Region works in conjunction with the PCC Table(Platform Communications 9 * Channel Table). PCC subspaces that are marked for use as PCC Operation 10 * Regions must not be used as PCC subspaces for the standard ACPI features 11 * such as CPPC, RASF, PDTT and MPST. These standard features must always use 12 * the PCC Table instead. 13 * 14 * This driver sets up the PCC Address Space and installs an handler to enable 15 * handling of PCC OpRegion in the firmware. 16 * 17 */ 18 #include <linux/kernel.h> 19 #include <linux/acpi.h> 20 #include <linux/completion.h> 21 #include <linux/idr.h> 22 #include <linux/io.h> 23 24 #include <acpi/pcc.h> 25 26 /* 27 * Arbitrary retries in case the remote processor is slow to respond 28 * to PCC commands 29 */ 30 #define PCC_CMD_WAIT_RETRIES_NUM 500ULL 31 32 struct pcc_data { 33 struct pcc_mbox_chan *pcc_chan; 34 struct completion done; 35 struct mbox_client cl; 36 struct acpi_pcc_info ctx; 37 }; 38 39 static struct acpi_pcc_info pcc_ctx; 40 41 static void pcc_rx_callback(struct mbox_client *cl, void *m) 42 { 43 struct pcc_data *data = container_of(cl, struct pcc_data, cl); 44 45 complete(&data->done); 46 } 47 48 static acpi_status 49 acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function, 50 void *handler_context, void **region_context) 51 { 52 struct pcc_data *data; 53 struct acpi_pcc_info *ctx = handler_context; 54 struct pcc_mbox_chan *pcc_chan; 55 static acpi_status ret; 56 57 data = kzalloc(sizeof(*data), GFP_KERNEL); 58 if (!data) 59 return AE_NO_MEMORY; 60 61 data->cl.rx_callback = pcc_rx_callback; 62 data->cl.knows_txdone = true; 63 data->ctx.length = ctx->length; 64 data->ctx.subspace_id = ctx->subspace_id; 65 data->ctx.internal_buffer = ctx->internal_buffer; 66 67 init_completion(&data->done); 68 data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id); 69 if (IS_ERR(data->pcc_chan)) { 70 pr_err("Failed to find PCC channel for subspace %d\n", 71 ctx->subspace_id); 72 ret = AE_NOT_FOUND; 73 goto err_free_data; 74 } 75 76 pcc_chan = data->pcc_chan; 77 if (!pcc_chan->mchan->mbox->txdone_irq) { 78 pr_err("This channel-%d does not support interrupt.\n", 79 ctx->subspace_id); 80 ret = AE_SUPPORT; 81 goto err_free_channel; 82 } 83 84 *region_context = data; 85 return AE_OK; 86 87 err_free_channel: 88 pcc_mbox_free_channel(data->pcc_chan); 89 err_free_data: 90 kfree(data); 91 92 return ret; 93 } 94 95 static acpi_status 96 acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr, 97 u32 bits, acpi_integer *value, 98 void *handler_context, void *region_context) 99 { 100 int ret; 101 struct pcc_data *data = region_context; 102 u64 usecs_lat; 103 104 reinit_completion(&data->done); 105 106 /* Write to Shared Memory */ 107 memcpy_toio(data->pcc_chan->shmem, (void *)value, data->ctx.length); 108 109 ret = mbox_send_message(data->pcc_chan->mchan, NULL); 110 if (ret < 0) 111 return AE_ERROR; 112 113 /* 114 * pcc_chan->latency is just a Nominal value. In reality the remote 115 * processor could be much slower to reply. So add an arbitrary 116 * amount of wait on top of Nominal. 117 */ 118 usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency; 119 ret = wait_for_completion_timeout(&data->done, 120 usecs_to_jiffies(usecs_lat)); 121 if (ret == 0) { 122 pr_err("PCC command executed timeout!\n"); 123 return AE_TIME; 124 } 125 126 mbox_chan_txdone(data->pcc_chan->mchan, ret); 127 128 memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length); 129 130 return AE_OK; 131 } 132 133 void __init acpi_init_pcc(void) 134 { 135 acpi_status status; 136 137 status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, 138 ACPI_ADR_SPACE_PLATFORM_COMM, 139 &acpi_pcc_address_space_handler, 140 &acpi_pcc_address_space_setup, 141 &pcc_ctx); 142 if (ACPI_FAILURE(status)) 143 pr_alert("OperationRegion handler could not be installed\n"); 144 } 145