diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 5f298f00a82e37..e8939356c14e23 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o +obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o scmi-bus-y = bus.o scmi-driver-y = driver.o +scmi-transport-y = smt.o mailbox.o smccc_func_id.o +scmi-transport-$(CONFIG_OPTEE) = optee_service.o scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 5237c2ff79fea1..7f9b4e05e15a22 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -6,6 +6,8 @@ * * Copyright (C) 2018 ARM Ltd. */ +#ifndef ARM_SCMI_COMMON_H +#define ARM_SCMI_COMMON_H #include #include @@ -13,6 +15,7 @@ #include #include #include +#include #include #include @@ -33,8 +36,8 @@ enum scmi_common_cmd { /** * struct scmi_msg_resp_prot_version - Response for a message * - * @major_version: Major version of the ABI that firmware supports * @minor_version: Minor version of the ABI that firmware supports + * @major_version: Major version of the ABI that firmware supports * * In general, ABI version changes follow the rule that minor version increments * are backward compatible. Major revision changes in ABI may not be @@ -47,6 +50,20 @@ struct scmi_msg_resp_prot_version { __le16 major_version; }; +// This is SMT specific, not SCMI. => move to smt.h +#define MSG_ID_MASK GENMASK(7, 0) +#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) +#define MSG_TYPE_MASK GENMASK(9, 8) +#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) +#define MSG_TYPE_COMMAND 0 +#define MSG_TYPE_DELAYED_RESP 2 +#define MSG_TYPE_NOTIFICATION 3 +#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) +#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) +#define MSG_TOKEN_ID_MASK GENMASK(27, 18) +#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) +#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) + /** * struct scmi_msg_hdr - Message(Tx/Rx) header * @@ -67,6 +84,33 @@ struct scmi_msg_hdr { bool poll_completion; }; +/** + * pack_scmi_header() - packs and returns 32-bit header + * + * @hdr: pointer to header containing all the information on message id, + * protocol id and sequence id. + * + * Return: 32-bit packed message header to be sent to the platform. + */ +static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) +{ + return FIELD_PREP(MSG_ID_MASK, hdr->id) | + FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) | + FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id); +} + +/** + * unpack_scmi_header() - unpacks and records message and protocol id + * + * @msg_hdr: 32-bit packed message header sent from the platform + * @hdr: pointer to header to fetch message and protocol id. + */ +static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) +{ + hdr->id = MSG_XTRACT_ID(msg_hdr); + hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); +} + /** * struct scmi_msg - Message(Tx/Rx) structure * @@ -87,7 +131,7 @@ struct scmi_msg { * message. If request-ACK protocol is used, we can reuse the same * buffer for the rx path as we use for the tx path. * @done: command message transmit completion event - * @async: pointer to delayed response message received event completion + * @async_done: pointer to delayed response message received event completion */ struct scmi_xfer { struct scmi_msg_hdr hdr; @@ -111,3 +155,64 @@ void scmi_setup_protocol_implemented(const struct scmi_handle *handle, u8 *prot_imp); int scmi_base_protocol_init(struct scmi_handle *h); + +/* SCMI Transport */ +/** + * struct scmi_chan_info - Structure representing a SCMI channel information + * + * @dev: Reference to device in the SCMI hierarchy corresponding to this + * channel + * @handle: Pointer to SCMI entity handle + * @transport_info: Transport layer related information + */ +struct scmi_chan_info { + struct device *dev; + struct scmi_handle *handle; + void *transport_info; +}; + +/** + * struct scmi_transport_ops - Structure representing a SCMI transport ops + * + * @chan_available: Callback to check if channel is available or not + * @chan_setup: Callback to allocate and setup a channel + * @chan_free: Callback to free a channel + * @send_message: Callback to send a message + * @mark_txdone: Callback to mark tx as done + * @fetch_response: Callback to fetch response + * @poll_done: Callback to poll transfer status + */ +struct scmi_transport_ops { + bool (*chan_available)(struct device *dev, int idx); + int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev, bool tx); + int (*chan_free)(int id, void *p, void *data); + int (*send_message)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); + void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); + void (*fetch_response)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); + bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); +}; + +/** + * struct scmi_desc - Description of SoC integration + * + * @ops: Pointer to the transport specific ops structure + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) + * @max_msg: Maximum number of messages that can be pending + * simultaneously in the system + * @max_msg_size: Maximum size of data per message that can be handled. + */ +struct scmi_desc { + struct scmi_transport_ops *ops; + int max_rx_timeout_ms; + int max_msg; + int max_msg_size; +}; + +extern const struct scmi_desc scmi_mailbox_desc; +extern const struct scmi_desc scmi_smc_desc; +extern const struct scmi_desc scmi_optee_desc; + +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); +void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); + +#endif /* ARM_SCMI_COMMON_H */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3eb0382491cebb..646646720be247 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -19,29 +19,14 @@ #include #include #include -#include #include #include #include #include -#include #include #include "common.h" -#define MSG_ID_MASK GENMASK(7, 0) -#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) -#define MSG_TYPE_MASK GENMASK(9, 8) -#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) -#define MSG_TYPE_COMMAND 0 -#define MSG_TYPE_DELAYED_RESP 2 -#define MSG_TYPE_NOTIFICATION 3 -#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) -#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) -#define MSG_TOKEN_ID_MASK GENMASK(27, 18) -#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) -#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) - enum scmi_error_codes { SCMI_SUCCESS = 0, /* Success */ SCMI_ERR_SUPPORT = -1, /* Not supported */ @@ -77,46 +62,14 @@ struct scmi_xfers_info { spinlock_t xfer_lock; }; -/** - * struct scmi_desc - Description of SoC integration - * - * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) - * @max_msg: Maximum number of messages that can be pending - * simultaneously in the system - * @max_msg_size: Maximum size of data per message that can be handled. - */ -struct scmi_desc { - int max_rx_timeout_ms; - int max_msg; - int max_msg_size; -}; - -/** - * struct scmi_chan_info - Structure representing a SCMI channel information - * - * @cl: Mailbox Client - * @chan: Transmit/Receive mailbox channel - * @payload: Transmit/Receive mailbox channel payload area - * @dev: Reference to device in the SCMI hierarchy corresponding to this - * channel - * @handle: Pointer to SCMI entity handle - */ -struct scmi_chan_info { - struct mbox_client cl; - struct mbox_chan *chan; - void __iomem *payload; - struct device *dev; - struct scmi_handle *handle; -}; - /** * struct scmi_info - Structure representing a SCMI instance * * @dev: Device pointer * @desc: SoC description for this instance - * @handle: Instance of SCMI handle to send to clients * @version: SCMI revision information containing protocol version, * implementation version and (sub-)vendor identification. + * @handle: Instance of SCMI handle to send to clients * @tx_minfo: Universal Transmit Message management info * @tx_idr: IDR object to map protocol id to Tx channel info pointer * @rx_idr: IDR object to map protocol id to Rx channel info pointer @@ -138,27 +91,8 @@ struct scmi_info { int users; }; -#define client_to_scmi_chan_info(c) container_of(c, struct scmi_chan_info, cl) #define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle) -/* - * SCMI specification requires all parameters, message headers, return - * arguments or any protocol data to be expressed in little endian - * format only. - */ -struct scmi_shared_mem { - __le32 reserved; - __le32 channel_status; -#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1) -#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0) - __le32 reserved1[2]; - __le32 flags; -#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) - __le32 length; - __le32 msg_header; - u8 msg_payload[0]; -}; - static const int scmi_linux_errmap[] = { /* better than switch case as long as return value is continuous */ 0, /* SCMI_SUCCESS */ @@ -194,77 +128,6 @@ static inline void scmi_dump_header_dbg(struct device *dev, hdr->id, hdr->seq, hdr->protocol_id); } -static void scmi_fetch_response(struct scmi_xfer *xfer, - struct scmi_shared_mem __iomem *mem) -{ - xfer->hdr.status = ioread32(mem->msg_payload); - /* Skip the length of header and status in payload area i.e 8 bytes */ - xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8); - - /* Take a copy to the rx buffer.. */ - memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len); -} - -/** - * pack_scmi_header() - packs and returns 32-bit header - * - * @hdr: pointer to header containing all the information on message id, - * protocol id and sequence id. - * - * Return: 32-bit packed message header to be sent to the platform. - */ -static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) -{ - return FIELD_PREP(MSG_ID_MASK, hdr->id) | - FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) | - FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id); -} - -/** - * unpack_scmi_header() - unpacks and records message and protocol id - * - * @msg_hdr: 32-bit packed message header sent from the platform - * @hdr: pointer to header to fetch message and protocol id. - */ -static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) -{ - hdr->id = MSG_XTRACT_ID(msg_hdr); - hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); -} - -/** - * scmi_tx_prepare() - mailbox client callback to prepare for the transfer - * - * @cl: client pointer - * @m: mailbox message - * - * This function prepares the shared memory which contains the header and the - * payload. - */ -static void scmi_tx_prepare(struct mbox_client *cl, void *m) -{ - struct scmi_xfer *t = m; - struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); - struct scmi_shared_mem __iomem *mem = cinfo->payload; - - /* - * Ideally channel must be free by now unless OS timeout last - * request and platform continued to process the same, wait - * until it releases the shared memory, otherwise we may endup - * overwriting its response with new message payload or vice-versa - */ - spin_until_cond(ioread32(&mem->channel_status) & - SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); - /* Mark channel busy + clear error */ - iowrite32(0x0, &mem->channel_status); - iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, - &mem->flags); - iowrite32(sizeof(mem->msg_header) + t->tx.len, &mem->length); - iowrite32(pack_scmi_header(&t->hdr), &mem->msg_header); - if (t->tx.buf) - memcpy_toio(mem->msg_payload, t->tx.buf, t->tx.len); -} - /** * scmi_xfer_get() - Allocate one message * @@ -332,10 +195,10 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) } /** - * scmi_rx_callback() - mailbox client callback for receive messages + * scmi_rx_callback() - callback for receiving messages * - * @cl: client pointer - * @m: mailbox message + * @cinfo: SCMI channel info + * @msg_hdr: Message header * * Processes one received message to appropriate transfer information and * signals completion of the transfer. @@ -343,21 +206,14 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) * NOTE: This function will be invoked in IRQ context, hence should be * as optimal as possible. */ -static void scmi_rx_callback(struct mbox_client *cl, void *m) +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) { - u8 msg_type; - u32 msg_hdr; - u16 xfer_id; - struct scmi_xfer *xfer; - struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); - struct device *dev = cinfo->dev; struct scmi_info *info = handle_to_scmi_info(cinfo->handle); struct scmi_xfers_info *minfo = &info->tx_minfo; - struct scmi_shared_mem __iomem *mem = cinfo->payload; - - msg_hdr = ioread32(&mem->msg_header); - msg_type = MSG_XTRACT_TYPE(msg_hdr); - xfer_id = MSG_XTRACT_TOKEN(msg_hdr); + u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr); + u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); + struct device *dev = cinfo->dev; + struct scmi_xfer *xfer; if (msg_type == MSG_TYPE_NOTIFICATION) return; /* Notifications not yet supported */ @@ -372,7 +228,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) scmi_dump_header_dbg(dev, &xfer->hdr); - scmi_fetch_response(xfer, mem); + info->desc->ops->fetch_response(cinfo, xfer); if (msg_type == MSG_TYPE_DELAYED_RESP) complete(xfer->async_done); @@ -393,28 +249,15 @@ void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) __scmi_xfer_put(&info->tx_minfo, xfer); } -static bool -scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) -{ - struct scmi_shared_mem __iomem *mem = cinfo->payload; - u16 xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); - - if (xfer->hdr.seq != xfer_id) - return false; - - return ioread32(&mem->channel_status) & - (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | - SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); -} - #define SCMI_MAX_POLL_TO_NS (100 * NSEC_PER_USEC) -static bool scmi_xfer_done_no_timeout(const struct scmi_chan_info *cinfo, +static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer, ktime_t stop) { - ktime_t __cur = ktime_get(); + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); - return scmi_xfer_poll_done(cinfo, xfer) || ktime_after(__cur, stop); + return info->desc->ops->poll_done(cinfo, xfer) || + ktime_after(ktime_get(), stop); } /** @@ -439,29 +282,26 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) if (unlikely(!cinfo)) return -EINVAL; - ret = mbox_send_message(cinfo->chan, xfer); - if (ret < 0) { - dev_dbg(dev, "mbox send fail %d\n", ret); + ret = info->desc->ops->send_message(cinfo, xfer); + if (ret) { + dev_dbg(dev, "Failed to send message %d\n", ret); return ret; } - /* mbox_send_message returns non-negative value on success, so reset */ - ret = 0; - if (xfer->hdr.poll_completion) { ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS); spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop)); - if (ktime_before(ktime_get(), stop)) - scmi_fetch_response(xfer, cinfo->payload); + if (info->desc->ops->poll_done(cinfo, xfer)) + info->desc->ops->fetch_response(cinfo, xfer); else ret = -ETIMEDOUT; } else { /* And we wait for the response. */ timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms); if (!wait_for_completion_timeout(&xfer->done, timeout)) { - dev_err(dev, "mbox timed out in resp(caller: %pS)\n", + dev_err(dev, "timed out in resp(caller: %pS)\n", (void *)_RET_IP_); ret = -ETIMEDOUT; } @@ -470,13 +310,8 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) if (!ret && xfer->hdr.status) ret = scmi_to_linux_errno(xfer->hdr.status); - /* - * NOTE: we might prefer not to need the mailbox ticker to manage the - * transfer queueing since the protocol layer queues things by itself. - * Unfortunately, we have to kick the mailbox framework after we have - * received our message. - */ - mbox_client_txdone(cinfo->chan, ret); + if (info->desc->ops->mark_txdone) + info->desc->ops->mark_txdone(cinfo, ret); return ret; } @@ -713,29 +548,18 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) return 0; } -static int scmi_mailbox_check(struct device_node *np, int idx) -{ - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", - idx, NULL); -} - -static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, - int prot_id, bool tx) +static int scmi_chan_setup(struct scmi_info *info, struct device *dev, + int prot_id, bool tx) { int ret, idx; - struct resource res; - resource_size_t size; - struct device_node *shmem, *np = dev->of_node; struct scmi_chan_info *cinfo; - struct mbox_client *cl; struct idr *idr; - const char *desc = tx ? "Tx" : "Rx"; /* Transmit channel is first entry i.e. index 0 */ idx = tx ? 0 : 1; idr = tx ? &info->tx_idr : &info->rx_idr; - if (scmi_mailbox_check(np, idx)) { + if (!info->desc->ops->chan_available(dev, idx)) { cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ return -EINVAL; @@ -748,36 +572,9 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, cinfo->dev = dev; - cl = &cinfo->cl; - cl->dev = dev; - cl->rx_callback = scmi_rx_callback; - cl->tx_prepare = tx ? scmi_tx_prepare : NULL; - cl->tx_block = false; - cl->knows_txdone = tx; - - shmem = of_parse_phandle(np, "shmem", idx); - ret = of_address_to_resource(shmem, 0, &res); - of_node_put(shmem); - if (ret) { - dev_err(dev, "failed to get SCMI %s payload memory\n", desc); - return ret; - } - - size = resource_size(&res); - cinfo->payload = devm_ioremap(info->dev, res.start, size); - if (!cinfo->payload) { - dev_err(dev, "failed to ioremap SCMI %s payload\n", desc); - return -EADDRNOTAVAIL; - } - - cinfo->chan = mbox_request_channel(cl, idx); - if (IS_ERR(cinfo->chan)) { - ret = PTR_ERR(cinfo->chan); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request SCMI %s mailbox\n", - desc); + ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); + if (ret) return ret; - } idr_alloc: ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); @@ -791,12 +588,12 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, } static inline int -scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) +scmi_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) { - int ret = scmi_mbox_chan_setup(info, dev, prot_id, true); + int ret = scmi_chan_setup(info, dev, prot_id, true); if (!ret) /* Rx is optional, hence no error check */ - scmi_mbox_chan_setup(info, dev, prot_id, false); + scmi_chan_setup(info, dev, prot_id, false); return ret; } @@ -814,7 +611,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, return; } - if (scmi_mbox_txrx_setup(info, &sdev->dev, prot_id)) { + if (scmi_txrx_setup(info, &sdev->dev, prot_id)) { dev_err(&sdev->dev, "failed to setup transport\n"); scmi_device_destroy(sdev); return; @@ -833,12 +630,6 @@ static int scmi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *child, *np = dev->of_node; - /* Only mailbox method supported, check for the presence of one */ - if (scmi_mailbox_check(np, 0)) { - dev_err(dev, "no mailbox found in %pOF\n", np); - return -EINVAL; - } - desc = of_device_get_match_data(dev); if (!desc) return -EINVAL; @@ -863,7 +654,7 @@ static int scmi_probe(struct platform_device *pdev) handle->dev = info->dev; handle->version = &info->version; - ret = scmi_mbox_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); + ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); if (ret) return ret; @@ -898,19 +689,9 @@ static int scmi_probe(struct platform_device *pdev) return 0; } -static int scmi_mbox_free_channel(int id, void *p, void *data) +void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id) { - struct scmi_chan_info *cinfo = p; - struct idr *idr = data; - - if (!IS_ERR_OR_NULL(cinfo->chan)) { - mbox_free_channel(cinfo->chan); - cinfo->chan = NULL; - } - idr_remove(idr, id); - - return 0; } static int scmi_remove(struct platform_device *pdev) @@ -930,25 +711,22 @@ static int scmi_remove(struct platform_device *pdev) return ret; /* Safe to free channels since no more users */ - ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + ret = idr_for_each(idr, info->desc->ops->chan_free, idr); idr_destroy(&info->tx_idr); idr = &info->rx_idr; - ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + ret = idr_for_each(idr, info->desc->ops->chan_free, idr); idr_destroy(&info->rx_idr); return ret; } -static const struct scmi_desc scmi_generic_desc = { - .max_rx_timeout_ms = 30, /* We may increase this if required */ - .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ - .max_msg_size = 128, -}; - /* Each compatible listed below must have descriptor associated with it */ static const struct of_device_id scmi_of_match[] = { - { .compatible = "arm,scmi", .data = &scmi_generic_desc }, + { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, + { .compatible = "arm,scmi-mbox", .data = &scmi_mailbox_desc }, + { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc }, + { .compatible = "arm,scmi-optee", .data = &scmi_optee_desc }, { /* Sentinel */ }, }; diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c new file mode 100644 index 00000000000000..77df576b23b2c7 --- /dev/null +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Message Mailbox Transport + * driver. + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "smt.h" + +/** + * struct scmi_mailbox - Structure representing a SCMI mailbox transport + * + * @cl: Mailbox Client + * @chan: Transmit/Receive mailbox channel + * @cinfo: SCMI channel info + * @shmem: Transmit/Receive shared memory area + */ +struct scmi_mailbox { + struct mbox_client cl; + struct mbox_chan *chan; + struct scmi_chan_info *cinfo; + struct scmi_shared_mem __iomem *shmem; +}; + +#define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl) + +static void tx_prepare(struct mbox_client *cl, void *m) +{ + struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); + struct scmi_xfer *xfer = m; + + scmi_smt_tx_prepare(smbox->shmem, xfer); +} + +static void rx_callback(struct mbox_client *cl, void *m) +{ + struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); + + scmi_rx_callback(smbox->cinfo, scmi_smt_read_msg_header(smbox->shmem)); +} + +static bool mailbox_chan_available(struct device *dev, int idx) +{ + return !of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", idx, NULL); +} + +static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, + bool tx) +{ + const char *desc = tx ? "Tx" : "Rx"; + struct device *cdev = cinfo->dev; + struct scmi_mailbox *smbox; + struct device_node *shmem; + int ret, idx = tx ? 0 : 1; + struct mbox_client *cl; + resource_size_t size; + struct resource res; + + smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL); + if (!smbox) + return -ENOMEM; + + shmem = of_parse_phandle(cdev->of_node, "shmem", idx); + ret = of_address_to_resource(shmem, 0, &res); + of_node_put(shmem); + if (ret) { + dev_err(cdev, "failed to get SCMI %s shared memory\n", desc); + return ret; + } + + size = resource_size(&res); + smbox->shmem = devm_ioremap(dev, res.start, size); + if (!smbox->shmem) { + dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc); + return -EADDRNOTAVAIL; + } + + cl = &smbox->cl; + cl->dev = cdev; + cl->tx_prepare = tx ? tx_prepare : NULL; + cl->rx_callback = rx_callback; + cl->tx_block = false; + cl->knows_txdone = tx; + + smbox->chan = mbox_request_channel(cl, tx ? 0 : 1); + if (IS_ERR(smbox->chan)) { + ret = PTR_ERR(smbox->chan); + if (ret != -EPROBE_DEFER) + dev_err(cdev, "failed to request SCMI %s mailbox\n", + tx ? "Tx" : "Rx"); + return ret; + } + + cinfo->transport_info = smbox; + smbox->cinfo = cinfo; + + return 0; +} + +static int mailbox_chan_free(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct scmi_mailbox *smbox = cinfo->transport_info; + + if (!IS_ERR(smbox->chan)) { + mbox_free_channel(smbox->chan); + cinfo->transport_info = NULL; + smbox->chan = NULL; + smbox->cinfo = NULL; + } + + scmi_free_channel(cinfo, data, id); + + return 0; +} + +static int mailbox_send_message(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + int ret; + + ret = mbox_send_message(smbox->chan, xfer); + + /* mbox_send_message returns non-negative value on success, so reset */ + if (ret > 0) + ret = 0; + + return ret; +} + +static void mailbox_mark_txdone(struct scmi_chan_info *cinfo, int ret) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + + /* + * NOTE: we might prefer not to need the mailbox ticker to manage the + * transfer queueing since the protocol layer queues things by itself. + * Unfortunately, we have to kick the mailbox framework after we have + * received our message. + */ + mbox_client_txdone(smbox->chan, ret); +} + +static void mailbox_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + + scmi_smt_fetch_response(smbox->shmem, xfer); +} + +static bool +mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + + return scmi_smt_poll_done(smbox->shmem, xfer); +} + +static struct scmi_transport_ops scmi_mailbox_ops = { + .chan_available = mailbox_chan_available, + .chan_setup = mailbox_chan_setup, + .chan_free = mailbox_chan_free, + .send_message = mailbox_send_message, + .mark_txdone = mailbox_mark_txdone, + .fetch_response = mailbox_fetch_response, + .poll_done = mailbox_poll_done, +}; + +const struct scmi_desc scmi_mailbox_desc = { + .ops = &scmi_mailbox_ops, + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ + .max_msg_size = 128, +}; diff --git a/drivers/firmware/arm_scmi/optee_service.c b/drivers/firmware/arm_scmi/optee_service.c new file mode 100644 index 00000000000000..328f88aeb9ce07 --- /dev/null +++ b/drivers/firmware/arm_scmi/optee_service.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "smt.h" + +#define DRIVER_NAME "optee-scmi-agent" + +/* + * TA_CMD_CHANNEL_COUNT - Get number of channels supported + * + * param[0] (out value) - value.a: Number of communication channels + * param[1] unused + * param[2] unused + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + */ +#define TA_CMD_CHANNEL_COUNT 0x0 + +/* + * TA_CMD_GET_CHANNEL - Get channel identifer for a buffer pool + * + * param[0] (in value) - Message buffer physical start address + * param[1] (in value) - Message buffer byte size + * param[2] (out value) - value.a: Output channel identifier + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + */ +#define TA_CMD_GET_CHANNEL 0x1 + + +/* + * TA_CMD_PROCESS_CHANNEL - Process message in SCMI channel + * + * param[0] (in value) - value.a: SCMI channel identifier + * param[1] unused + * param[2] unused + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + */ +#define TA_CMD_PROCESS_CHANNEL 0x2 + +/** + * struct optee_scmi_agent - OP-TEE Random Number Generator private data + * @dev: OP-TEE based SCMI server device. + * @ctx: OP-TEE context handler. + * @session_id: SCMI server TA session identifier. + * @agent_count: Count of agent channels supported by the server + */ +struct optee_scmi_agent { + struct device *dev; + struct tee_context *ctx; + u32 session_id; + unsigned int agent_count; +}; + +static struct optee_scmi_agent agent_private; + +static int get_channel_count(void) +{ + int ret = 0; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + + dev_info(agent_private.dev, "count channels\n"); + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + /* Invoke TA_CMD_CHANNEL_COUNT function of Trusted App */ + inv_arg.func = TA_CMD_CHANNEL_COUNT; + inv_arg.session = agent_private.session_id; + inv_arg.num_params = 4; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(agent_private.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(agent_private.dev, "Failed to get agent count: 0x%x\n", + inv_arg.ret); + return -ENOTSUPP; + } + + dev_info(agent_private.dev, "count channels: back: %u\n", + (unsigned)param[0].u.value.a); + + agent_private.agent_count = param[0].u.value.a; + + return 0; +} + +static int get_channel(unsigned long pa, size_t length, int *channel_id) +{ + int ret = 0; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + uint64_t addr = pa; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + /* Invoke TA_CMD_GET_CHANNEL function of Trusted App */ + inv_arg.func = TA_CMD_GET_CHANNEL; + inv_arg.session = agent_private.session_id; + inv_arg.num_params = 4; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = addr >> 32; + param[0].u.value.b = addr & 0xffffffff; + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[1].u.value.a = length; + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(agent_private.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(agent_private.dev, "Failed to get channel: 0x%x\n", + inv_arg.ret); + return -ENOTSUPP; + } + + *channel_id = param[2].u.value.a; + + return 0; +} + +static int process_event(unsigned int channel_id) +{ + int ret = 0; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + /* Invoke TA_CMD_PROCESS_CHANNEL function of Trusted App */ + inv_arg.func = TA_CMD_PROCESS_CHANNEL; + inv_arg.session = agent_private.session_id; + inv_arg.num_params = 4; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = channel_id; + + ret = tee_client_invoke_func(agent_private.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(agent_private.dev, "Failed on channel %u: 0x%x\n", + channel_id, inv_arg.ret); + return -EIO; + } + + return 0; +} + +/** + * struct optee_scmi_channel - OP-TEE server assigns channel ID per shmem + * @channle_id: Id provided by OP-TEE for the channel + */ +struct optee_scmi_channel { + uint32_t channel_id; + struct scmi_chan_info *cinfo; + struct scmi_shared_mem __iomem *shmem; +}; + +static int optee_scmi_get_channel(struct device *dev, struct resource *res, + struct optee_scmi_channel *channel) +{ + int ret; + unsigned int id = 0; + + if (!agent_private.ctx) + return -EPROBE_DEFER; + + if (!agent_private.agent_count) + return -ENOENT; + + ret = get_channel(res->start, resource_size(res), &id); + if (ret) + return ret; + + channel->channel_id = id; + + return 0; +} + +static int optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, + bool tx) +{ + const char *desc = tx ? "Tx" : "Rx"; + struct device *cdev = cinfo->dev; + struct optee_scmi_channel *channel; + struct device_node *shmem; + resource_size_t size; + struct resource res; + int ret; + + // We don't expect Rx channel. Likely a std mailbox (interrupt+shm) + pr_err(" ---------- SCMI OP-TEE channel setup ------------------\n"); + + channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + shmem = of_parse_phandle(cdev->of_node, "shmem", (int)tx); + ret = of_address_to_resource(shmem, 0, &res); + of_node_put(shmem); + if (ret) { + dev_err(cdev, "failed to get SCMI %s shared memory\n", desc); + return ret; + } + + /* Get channel IDs from shm location */ + ret = optee_scmi_get_channel(dev, &res, channel); + if (ret) { + dev_err(dev, "failed to get OP-TEE channel %d\n", ret); + return ret; + } + + size = resource_size(&res); + channel->shmem = devm_ioremap(dev, res.start, size); + if (!channel->shmem) { + dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc); + return -EADDRNOTAVAIL; + } + + cinfo->transport_info = channel; + channel->cinfo = cinfo; + + return 0; +} + +static int optee_chan_free(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct optee_scmi_channel *channel = cinfo->transport_info; + + cinfo->transport_info = NULL; + channel->cinfo = NULL; + + scmi_free_channel(cinfo, data, id); + + return 0; +} + +static int optee_send_message(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct optee_scmi_channel *channel = cinfo->transport_info; + int ret; + + pr_err(" ---------- SCMI OP-TEE send message ------------------\n"); + + if (!channel && !agent_private.ctx) + return -EINVAL; + + scmi_smt_tx_prepare(channel->shmem, xfer); + + ret = process_event(channel->channel_id); + if (ret) + return ret; + + scmi_rx_callback(cinfo, scmi_smt_read_msg_header(channel->shmem)); + + return 0; +} + +static void optee_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct optee_scmi_channel *channel = cinfo->transport_info; + + pr_err(" ---------- SCMI OP-TEE fetch response -----------------\n"); + scmi_smt_fetch_response(channel->shmem, xfer); +} + +static bool optee_poll_done(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct optee_scmi_channel *channel = cinfo->transport_info; + + pr_err(" ---------- SCMI OP-TEE poll done ------------------\n"); + return scmi_smt_poll_done(channel->shmem, xfer); +} + +static bool optee_chan_available(struct device *dev, int idx) +{ + return true; +} + +static struct scmi_transport_ops scmi_optee_ops = { + .chan_available = optee_chan_available, + .chan_setup = optee_chan_setup, + .chan_free = optee_chan_free, + .send_message = optee_send_message, + .fetch_response = optee_fetch_response, + .poll_done = optee_poll_done, +}; + +const struct scmi_desc scmi_optee_desc = { + .ops = &scmi_optee_ops, + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 1, + .max_msg_size = 128, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, + const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE; +} + +static int optee_scmi_probe(struct device *dev) +{ + struct tee_client_device *scmi_device = to_tee_client_device(dev); + int ret = 0, err = -ENODEV; + struct tee_ioctl_open_session_arg sess_arg; + + memset(&sess_arg, 0, sizeof(sess_arg)); + + /* Open context with TEE driver */ + agent_private.ctx = tee_client_open_context(NULL, optee_ctx_match, + NULL, NULL); + if (IS_ERR(agent_private.ctx)) + return -ENODEV; + + /* Open session with SCMI server TA */ + memcpy(sess_arg.uuid, scmi_device->id.uuid.b, TEE_IOCTL_UUID_LEN); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + ret = tee_client_open_session(agent_private.ctx, &sess_arg, NULL); + if ((ret < 0) || (sess_arg.ret != 0)) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", + sess_arg.ret); + err = -EINVAL; + goto out_ctx; + } + agent_private.session_id = sess_arg.session; + + err = get_channel_count(); + if (err) + goto out_sess; + + agent_private.dev = dev; + + dev_info(dev, "OP-TEE SCMI channel probed\n"); + + return 0; + +out_sess: + tee_client_close_session(agent_private.ctx, agent_private.session_id); +out_ctx: + tee_client_close_context(agent_private.ctx); + agent_private.ctx = NULL; + + return err; +} + +static int optee_scmi_remove(struct device *dev) +{ + tee_client_close_session(agent_private.ctx, agent_private.session_id); + tee_client_close_context(agent_private.ctx); + agent_private.ctx = NULL; + + return 0; +} + +static const struct tee_client_device_id optee_scmi_id_table[] = { + { + UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e, + 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99) + }, + { } +}; + +MODULE_DEVICE_TABLE(tee, optee_scmi_id_table); + +static struct tee_client_driver optee_scmi_driver = { + .id_table = optee_scmi_id_table, + .driver = { + .name = DRIVER_NAME, + .bus = &tee_bus_type, + .probe = optee_scmi_probe, + .remove = optee_scmi_remove, + }, +}; + +static int __init optee_scmi_init(void) +{ + return driver_register(&optee_scmi_driver.driver); +} + +static void __exit optee_scmi_exit(void) +{ + driver_unregister(&optee_scmi_driver.driver); +} + +module_init(optee_scmi_init); +module_exit(optee_scmi_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Etienne Carriere "); +MODULE_DESCRIPTION("OP-TEE SCMI agent driver"); diff --git a/drivers/firmware/arm_scmi/smccc_func_id.c b/drivers/firmware/arm_scmi/smccc_func_id.c new file mode 100644 index 00000000000000..8af111701be317 --- /dev/null +++ b/drivers/firmware/arm_scmi/smccc_func_id.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Message Mailbox Transport + * driver. + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "smt.h" + +/** + * struct scmi_smc - Structure representing a SCMI mailbox transport + * + * @func_id: SMCCC function ID to be used to invoke the SCMI server + * @cinfo: SCMI channel info + * @shmem: Transmit/Receive shared memory area + */ +struct scmi_smc { + unsigned long func_id; + struct scmi_chan_info *cinfo; + struct scmi_shared_mem __iomem *shmem; +}; + +static int smc_smt_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, + bool tx) +{ + const char *desc = tx ? "Tx" : "Rx"; + struct device *cdev = cinfo->dev; + struct scmi_smc *smc_smt; + struct device_node *shmem; + resource_size_t size; + struct resource res; + int idx = tx ? 0 : 1; + int ret; + unsigned int func_id; + + if (of_property_read_u32(dev->of_node, "arm,func-id", &func_id)) + return -EINVAL; + + smc_smt = devm_kzalloc(dev, sizeof(*smc_smt), GFP_KERNEL); + if (!smc_smt) + return -ENOMEM; + + smc_smt->func_id = func_id; + + shmem = of_parse_phandle(cdev->of_node, "shmem", idx); + ret = of_address_to_resource(shmem, 0, &res); + of_node_put(shmem); + if (ret) { + dev_err(cdev, "failed to get SCMI %s shared memory\n", desc); + return ret; + } + + size = resource_size(&res); + smc_smt->shmem = devm_ioremap(dev, res.start, size); + if (!smc_smt->shmem) { + dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc); + return -EADDRNOTAVAIL; + } + + cinfo->transport_info = smc_smt; + smc_smt->cinfo = cinfo; + + return 0; +} + +static int smc_smt_chan_free(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct scmi_smc *smc_smt = cinfo->transport_info; + + cinfo->transport_info = NULL; + smc_smt->cinfo = NULL; + + scmi_free_channel(cinfo, data, id); + + return 0; +} + +static int smc_smt_send_message(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_smc *smc_smt = cinfo->transport_info; + struct arm_smccc_res res; + int ret = 0; + + scmi_smt_tx_prepare(smc_smt->shmem, xfer); + + arm_smccc_smc(smc_smt->func_id, 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0) + ret = -EIO; + + scmi_rx_callback(cinfo, scmi_smt_read_msg_header(smc_smt->shmem)); + + return ret; +} + +static void smt_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_smc *smc_smt = cinfo->transport_info; + + scmi_smt_fetch_response(smc_smt->shmem, xfer); +} + +static bool smt_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) +{ + struct scmi_smc *smc_smt = cinfo->transport_info; + + return scmi_smt_poll_done(smc_smt->shmem, xfer); +} + +static bool smc_smt_chan_available(struct device *dev, int idx) +{ + unsigned int func_id; + + return !of_property_read_u32(dev->of_node, "arm,func-id", &func_id); +} + +static struct scmi_transport_ops scmi_smc_ops = { + .chan_available = smc_smt_chan_available, + .chan_setup = smc_smt_chan_setup, + .chan_free = smc_smt_chan_free, + .send_message = smc_smt_send_message, + .fetch_response = smt_fetch_response, + .poll_done = smt_poll_done, +}; + +const struct scmi_desc scmi_smc_desc = { + .ops = &scmi_smc_ops, + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 1, + .max_msg_size = 128, +}; diff --git a/drivers/firmware/arm_scmi/smt.c b/drivers/firmware/arm_scmi/smt.c new file mode 100644 index 00000000000000..5eceb2cd44dd5f --- /dev/null +++ b/drivers/firmware/arm_scmi/smt.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + */ + +#include +#include +#include +#include +#include + +#include "common.h" + + +/* + * SCMI specification requires all parameters, message headers, return + * arguments or any protocol data to be expressed in little endian + * format only. + */ +struct scmi_shared_mem { + __le32 reserved; + __le32 channel_status; +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1) +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0) + __le32 reserved1[2]; + __le32 flags; +#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) + __le32 length; + __le32 msg_header; + u8 msg_payload[0]; +}; + +void scmi_smt_tx_prepare(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer) +{ + /* + * Ideally channel must be free by now unless OS timeout last + * request and platform continued to process the same, wait + * until it releases the shared memory, otherwise we may endup + * overwriting its response with new message payload or vice-versa + */ + spin_until_cond(ioread32(&shmem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); + + /* Mark channel busy + clear error */ + iowrite32(0x0, &shmem->channel_status); + iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, + &shmem->flags); + iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length); + iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header); + + if (xfer->tx.buf) + memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len); +} + +__le32 scmi_smt_read_msg_header(struct scmi_shared_mem __iomem *shmem) +{ + return ioread32(&shmem->msg_header); +} + +void scmi_smt_fetch_response(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer) +{ + xfer->hdr.status = ioread32(shmem->msg_payload); + + /* Skip the length of header and status in shmem area i.e 8 bytes */ + xfer->rx.len = min_t(size_t, xfer->rx.len, + ioread32(&shmem->length) - 8); + + memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); +} + +bool scmi_smt_poll_done(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer) +{ + u16 xfer_id; + + xfer_id = MSG_XTRACT_TOKEN(ioread32(&shmem->msg_header)); + + if (xfer->hdr.seq != xfer_id) + return false; + + return ioread32(&shmem->channel_status) & + (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); +} diff --git a/drivers/firmware/arm_scmi/smt.h b/drivers/firmware/arm_scmi/smt.h new file mode 100644 index 00000000000000..ba13cfc6487c59 --- /dev/null +++ b/drivers/firmware/arm_scmi/smt.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Linaro Ltd. + * Copyright (C) 2019 ARM Ltd. + */ +#ifndef ARM_SCMI_SMT_H +#define ARM_SCMI_SMT_H + +#include "common.h" + +struct scmi_shared_mem; + +/* Wait SMT buffer is free and Write write SCMI message into */ +void scmi_smt_tx_prepare(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer); + +/* Read the msg_header from the SMT buffer */ +__le32 scmi_smt_read_msg_header(struct scmi_shared_mem __iomem *shmem); + +/* Copy response message from STM buffer to target location */ +void scmi_smt_fetch_response(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer); + +/* Return whether SMT buffer contains data from the SCMI server */ +bool scmi_smt_poll_done(struct scmi_shared_mem __iomem *shmem, + struct scmi_xfer *xfer); + +#endif /* ARM_SCMI_SMT_H */