diff options
Diffstat (limited to 'drivers/firmware/arm_scmi/smc.c')
-rw-r--r-- | drivers/firmware/arm_scmi/smc.c | 51 |
1 files changed, 43 insertions, 8 deletions
diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 93272e4bbd12..c193516a254d 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -20,9 +20,27 @@ #include "common.h" +/* + * The shmem address is split into 4K page and offset. + * This is to make sure the parameters fit in 32bit arguments of the + * smc/hvc call to keep it uniform across smc32/smc64 conventions. + * This however limits the shmem address to 44 bit. + * + * These optional parameters can be used to distinguish among multiple + * scmi instances that are using the same smc-id. + * The page parameter is passed in r1/x1/w1 register and the offset parameter + * is passed in r2/x2/w2 register. + */ + +#define SHMEM_SIZE (SZ_4K) +#define SHMEM_SHIFT 12 +#define SHMEM_PAGE(x) (_UL((x) >> SHMEM_SHIFT)) +#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1)) + /** * struct scmi_smc - Structure representing a SCMI smc transport * + * @irq: An optional IRQ for completion * @cinfo: SCMI channel info * @shmem: Transmit/Receive shared memory area * @shmem_lock: Lock to protect access to Tx/Rx shared memory area. @@ -30,9 +48,12 @@ * @inflight: Atomic flag to protect access to Tx/Rx shared memory area. * Used when operating in atomic mode. * @func_id: smc/hvc call function id + * @param_page: 4K page number of the shmem channel + * @param_offset: Offset within the 4K page of the shmem channel */ struct scmi_smc { + int irq; struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; /* Protect access to shmem area */ @@ -40,6 +61,8 @@ struct scmi_smc { #define INFLIGHT_NONE MSG_TOKEN_MAX atomic_t inflight; u32 func_id; + u32 param_page; + u32 param_offset; }; static irqreturn_t smc_msg_done_isr(int irq, void *data) @@ -106,7 +129,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, struct resource res; struct device_node *np; u32 func_id; - int ret, irq; + int ret; if (!tx) return -ENODEV; @@ -116,8 +139,10 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, return -ENOMEM; np = of_parse_phandle(cdev->of_node, "shmem", 0); - if (!of_device_is_compatible(np, "arm,scmi-shmem")) + if (!of_device_is_compatible(np, "arm,scmi-shmem")) { + of_node_put(np); return -ENXIO; + } ret = of_address_to_resource(np, 0, &res); of_node_put(np); @@ -137,16 +162,19 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, if (ret < 0) return ret; + if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) { + scmi_info->param_page = SHMEM_PAGE(res.start); + scmi_info->param_offset = SHMEM_OFFSET(res.start); + } /* * If there is an interrupt named "a2p", then the service and * completion of a message is signaled by an interrupt rather than by * the return of the SMC call. */ - irq = of_irq_get_byname(cdev->of_node, "a2p"); - if (irq > 0) { - ret = devm_request_irq(dev, irq, smc_msg_done_isr, - IRQF_NO_SUSPEND, - dev_name(dev), scmi_info); + scmi_info->irq = of_irq_get_byname(cdev->of_node, "a2p"); + if (scmi_info->irq > 0) { + ret = request_irq(scmi_info->irq, smc_msg_done_isr, + IRQF_NO_SUSPEND, dev_name(dev), scmi_info); if (ret) { dev_err(dev, "failed to setup SCMI smc irq\n"); return ret; @@ -168,6 +196,10 @@ static int smc_chan_free(int id, void *p, void *data) struct scmi_chan_info *cinfo = p; struct scmi_smc *scmi_info = cinfo->transport_info; + /* Ignore any possible further reception on the IRQ path */ + if (scmi_info->irq > 0) + free_irq(scmi_info->irq, scmi_info); + cinfo->transport_info = NULL; scmi_info->cinfo = NULL; @@ -179,6 +211,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo, { struct scmi_smc *scmi_info = cinfo->transport_info; struct arm_smccc_res res; + unsigned long page = scmi_info->param_page; + unsigned long offset = scmi_info->param_offset; /* * Channel will be released only once response has been @@ -188,7 +222,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo, shmem_tx_prepare(scmi_info->shmem, xfer, cinfo); - arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); + arm_smccc_1_1_invoke(scmi_info->func_id, page, offset, 0, 0, 0, 0, 0, + &res); /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */ if (res.a0) { |