Path: blob/master/drivers/firmware/arm_scmi/transports/smc.c
26483 views
// SPDX-License-Identifier: GPL-2.01/*2* System Control and Management Interface (SCMI) Message SMC/HVC3* Transport driver4*5* Copyright 2020 NXP6*/78#include <linux/arm-smccc.h>9#include <linux/atomic.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/interrupt.h>13#include <linux/mutex.h>14#include <linux/of.h>15#include <linux/of_address.h>16#include <linux/of_irq.h>17#include <linux/limits.h>18#include <linux/platform_device.h>19#include <linux/processor.h>20#include <linux/slab.h>2122#include "../common.h"2324/*25* The shmem address is split into 4K page and offset.26* This is to make sure the parameters fit in 32bit arguments of the27* smc/hvc call to keep it uniform across smc32/smc64 conventions.28* This however limits the shmem address to 44 bit.29*30* These optional parameters can be used to distinguish among multiple31* scmi instances that are using the same smc-id.32* The page parameter is passed in r1/x1/w1 register and the offset parameter33* is passed in r2/x2/w2 register.34*/3536#define SHMEM_SIZE (SZ_4K)37#define SHMEM_SHIFT 1238#define SHMEM_PAGE(x) (_UL((x) >> SHMEM_SHIFT))39#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1))4041/**42* struct scmi_smc - Structure representing a SCMI smc transport43*44* @irq: An optional IRQ for completion45* @cinfo: SCMI channel info46* @shmem: Transmit/Receive shared memory area47* @io_ops: Transport specific I/O operations48* @shmem_lock: Lock to protect access to Tx/Rx shared memory area.49* Used when NOT operating in atomic mode.50* @inflight: Atomic flag to protect access to Tx/Rx shared memory area.51* Used when operating in atomic mode.52* @func_id: smc/hvc call function id53* @param_page: 4K page number of the shmem channel54* @param_offset: Offset within the 4K page of the shmem channel55* @cap_id: smc/hvc doorbell's capability id to be used on Qualcomm virtual56* platforms57*/5859struct scmi_smc {60int irq;61struct scmi_chan_info *cinfo;62struct scmi_shared_mem __iomem *shmem;63struct scmi_shmem_io_ops *io_ops;64/* Protect access to shmem area */65struct mutex shmem_lock;66#define INFLIGHT_NONE MSG_TOKEN_MAX67atomic_t inflight;68unsigned long func_id;69unsigned long param_page;70unsigned long param_offset;71unsigned long cap_id;72};7374static struct scmi_transport_core_operations *core;7576static irqreturn_t smc_msg_done_isr(int irq, void *data)77{78struct scmi_smc *scmi_info = data;7980core->rx_callback(scmi_info->cinfo,81core->shmem->read_header(scmi_info->shmem), NULL);8283return IRQ_HANDLED;84}8586static bool smc_chan_available(struct device_node *of_node, int idx)87{88struct device_node *np __free(device_node) =89of_parse_phandle(of_node, "shmem", 0);90if (!np)91return false;9293return true;94}9596static inline void smc_channel_lock_init(struct scmi_smc *scmi_info)97{98if (IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE))99atomic_set(&scmi_info->inflight, INFLIGHT_NONE);100else101mutex_init(&scmi_info->shmem_lock);102}103104static bool smc_xfer_inflight(struct scmi_xfer *xfer, atomic_t *inflight)105{106int ret;107108ret = atomic_cmpxchg(inflight, INFLIGHT_NONE, xfer->hdr.seq);109110return ret == INFLIGHT_NONE;111}112113static inline void114smc_channel_lock_acquire(struct scmi_smc *scmi_info,115struct scmi_xfer *xfer __maybe_unused)116{117if (IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE))118spin_until_cond(smc_xfer_inflight(xfer, &scmi_info->inflight));119else120mutex_lock(&scmi_info->shmem_lock);121}122123static inline void smc_channel_lock_release(struct scmi_smc *scmi_info)124{125if (IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE))126atomic_set(&scmi_info->inflight, INFLIGHT_NONE);127else128mutex_unlock(&scmi_info->shmem_lock);129}130131static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,132bool tx)133{134struct device *cdev = cinfo->dev;135unsigned long cap_id = ULONG_MAX;136struct scmi_smc *scmi_info;137struct resource res = {};138u32 func_id;139int ret;140141if (!tx)142return -ENODEV;143144scmi_info = devm_kzalloc(dev, sizeof(*scmi_info), GFP_KERNEL);145if (!scmi_info)146return -ENOMEM;147148scmi_info->shmem = core->shmem->setup_iomap(cinfo, dev, tx, &res,149&scmi_info->io_ops);150if (IS_ERR(scmi_info->shmem))151return PTR_ERR(scmi_info->shmem);152153ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id);154if (ret < 0)155return ret;156157if (of_device_is_compatible(dev->of_node, "qcom,scmi-smc")) {158resource_size_t size = resource_size(&res);159void __iomem *ptr = (void __iomem *)scmi_info->shmem + size - 8;160/* The capability-id is kept in last 8 bytes of shmem.161* +-------+ <-- 0162* | shmem |163* +-------+ <-- size - 8164* | capId |165* +-------+ <-- size166*/167memcpy_fromio(&cap_id, ptr, sizeof(cap_id));168}169170if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) {171scmi_info->param_page = SHMEM_PAGE(res.start);172scmi_info->param_offset = SHMEM_OFFSET(res.start);173}174/*175* If there is an interrupt named "a2p", then the service and176* completion of a message is signaled by an interrupt rather than by177* the return of the SMC call.178*/179scmi_info->irq = of_irq_get_byname(cdev->of_node, "a2p");180if (scmi_info->irq > 0) {181ret = request_irq(scmi_info->irq, smc_msg_done_isr,182IRQF_NO_SUSPEND, dev_name(dev), scmi_info);183if (ret) {184dev_err(dev, "failed to setup SCMI smc irq\n");185return ret;186}187} else {188cinfo->no_completion_irq = true;189}190191scmi_info->func_id = func_id;192scmi_info->cap_id = cap_id;193scmi_info->cinfo = cinfo;194smc_channel_lock_init(scmi_info);195cinfo->transport_info = scmi_info;196197return 0;198}199200static int smc_chan_free(int id, void *p, void *data)201{202struct scmi_chan_info *cinfo = p;203struct scmi_smc *scmi_info = cinfo->transport_info;204205/*206* Different protocols might share the same chan info, so a previous207* smc_chan_free call might have already freed the structure.208*/209if (!scmi_info)210return 0;211212/* Ignore any possible further reception on the IRQ path */213if (scmi_info->irq > 0)214free_irq(scmi_info->irq, scmi_info);215216cinfo->transport_info = NULL;217scmi_info->cinfo = NULL;218219return 0;220}221222static int smc_send_message(struct scmi_chan_info *cinfo,223struct scmi_xfer *xfer)224{225struct scmi_smc *scmi_info = cinfo->transport_info;226struct arm_smccc_res res;227228/*229* Channel will be released only once response has been230* surely fully retrieved, so after .mark_txdone()231*/232smc_channel_lock_acquire(scmi_info, xfer);233234core->shmem->tx_prepare(scmi_info->shmem, xfer, cinfo,235scmi_info->io_ops->toio);236237if (scmi_info->cap_id != ULONG_MAX)238arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->cap_id, 0,2390, 0, 0, 0, 0, &res);240else241arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->param_page,242scmi_info->param_offset, 0, 0, 0, 0, 0,243&res);244245/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */246if (res.a0) {247smc_channel_lock_release(scmi_info);248return -EOPNOTSUPP;249}250251return 0;252}253254static void smc_fetch_response(struct scmi_chan_info *cinfo,255struct scmi_xfer *xfer)256{257struct scmi_smc *scmi_info = cinfo->transport_info;258259core->shmem->fetch_response(scmi_info->shmem, xfer,260scmi_info->io_ops->fromio);261}262263static void smc_mark_txdone(struct scmi_chan_info *cinfo, int ret,264struct scmi_xfer *__unused)265{266struct scmi_smc *scmi_info = cinfo->transport_info;267268smc_channel_lock_release(scmi_info);269}270271static const struct scmi_transport_ops scmi_smc_ops = {272.chan_available = smc_chan_available,273.chan_setup = smc_chan_setup,274.chan_free = smc_chan_free,275.send_message = smc_send_message,276.mark_txdone = smc_mark_txdone,277.fetch_response = smc_fetch_response,278};279280static struct scmi_desc scmi_smc_desc = {281.ops = &scmi_smc_ops,282.max_rx_timeout_ms = 30,283.max_msg = 20,284.max_msg_size = SCMI_SHMEM_MAX_PAYLOAD_SIZE,285/*286* Setting .sync_cmds_atomic_replies to true for SMC assumes that,287* once the SMC instruction has completed successfully, the issued288* SCMI command would have been already fully processed by the SCMI289* platform firmware and so any possible response value expected290* for the issued command will be immmediately ready to be fetched291* from the shared memory area.292*/293.sync_cmds_completed_on_ret = true,294.atomic_enabled = IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE),295};296297static const struct of_device_id scmi_of_match[] = {298{ .compatible = "arm,scmi-smc" },299{ .compatible = "arm,scmi-smc-param" },300{ .compatible = "qcom,scmi-smc" },301{ /* Sentinel */ },302};303MODULE_DEVICE_TABLE(of, scmi_of_match);304305DEFINE_SCMI_TRANSPORT_DRIVER(scmi_smc, scmi_smc_driver, scmi_smc_desc,306scmi_of_match, core);307module_platform_driver(scmi_smc_driver);308309MODULE_AUTHOR("Peng Fan <[email protected]>");310MODULE_AUTHOR("Nikunj Kela <[email protected]>");311MODULE_DESCRIPTION("SCMI SMC Transport driver");312MODULE_LICENSE("GPL");313314315