From fa34bcaf9e7b5bd86a417a14ea86bb88d9ea524e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 27 Aug 2024 11:24:50 -0700 Subject: [PATCH] BACKPORT: firmware: arm_scmi: Support 'reg-io-width' property for shared memory Some shared memory areas might only support a certain access width, such as 32-bit, which memcpy_{from,to}_io() does not adhere to at least on ARM64 by making both 8-bit and 64-bit accesses to such memory. Update the shmem layer to support reading from and writing to such shared memory area using the specified I/O width in the Device Tree. The various transport layers making use of the shmem.c code are updated accordingly to pass the I/O accessors that they store. Bug: 369085303 Change-Id: I97d80dd4027fe8290781ad7fc3859c2bdaf34522 Signed-off-by: Florian Fainelli Message-Id: <20240827182450.3608307-3-florian.fainelli@broadcom.com> Signed-off-by: Sudeep Holla (cherry picked from commit 2cd7f3db25feeb7c204e36df9f1bb13bea3a3a20) Signed-off-by: Danesh Petigara Signed-off-by: Pierre Couillaud --- drivers/firmware/arm_scmi/common.h | 30 +++++++++-- drivers/firmware/arm_scmi/mailbox.c | 11 ++-- drivers/firmware/arm_scmi/optee.c | 10 +++- drivers/firmware/arm_scmi/shmem.c | 78 ++++++++++++++++++++++++++--- drivers/firmware/arm_scmi/smc.c | 10 +++- 5 files changed, 123 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 4ac72754c255..95b5e0738105 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -231,20 +231,44 @@ extern const struct scmi_desc scmi_optee_desc; void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv); void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); +/* Used for compactness and signature validation of the function pointers being + * passed. + */ +typedef void (*shmem_copy_toio_t)(void __iomem *to, const void *from, + size_t count); +typedef void (*shmem_copy_fromio_t)(void *to, const void __iomem *from, + size_t count); + +/** + * struct scmi_shmem_io_ops - I/O operations to read from/write to + * Shared Memory + * + * @toio: Copy data to the shared memory area + * @fromio: Copy data from the shared memory area + */ +struct scmi_shmem_io_ops { + shmem_copy_fromio_t fromio; + shmem_copy_toio_t toio; +}; + /* shmem related declarations */ struct scmi_shared_mem; void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer, struct scmi_chan_info *cinfo); + struct scmi_xfer *xfer, struct scmi_chan_info *cinfo, + shmem_copy_toio_t toio); u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer); + struct scmi_xfer *xfer, + shmem_copy_fromio_t fromio); void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, - size_t max_len, struct scmi_xfer *xfer); + size_t max_len, struct scmi_xfer *xfer, + shmem_copy_fromio_t fromio); void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem); bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem); +struct scmi_shmem_io_ops *shmem_get_io_ops(struct device_node *shmem); /* declarations for message passing transports */ struct scmi_msg_payld; diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 01b4e532b26c..6c664fc1bead 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -23,6 +23,7 @@ * @cinfo: SCMI channel info * @shmem: Transmit/Receive shared memory area * @chan_lock: Lock that prevents multiple xfers from being queued + * @io_ops: Transport specific I/O operations */ struct scmi_mailbox { struct mbox_client cl; @@ -30,6 +31,7 @@ struct scmi_mailbox { struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; struct mutex chan_lock; + struct scmi_shmem_io_ops *io_ops; }; #define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl) @@ -38,7 +40,8 @@ static void tx_prepare(struct mbox_client *cl, void *m) { struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); - shmem_tx_prepare(smbox->shmem, m, smbox->cinfo); + shmem_tx_prepare(smbox->shmem, m, smbox->cinfo, + smbox->io_ops->toio); } static void rx_callback(struct mbox_client *cl, void *m) @@ -147,6 +150,7 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, cl->rx_callback = rx_callback; cl->tx_block = false; cl->knows_txdone = tx; + smbox->io_ops = shmem_get_io_ops(shmem); smbox->chan = mbox_request_channel(cl, tx ? 0 : 1); if (IS_ERR(smbox->chan)) { @@ -222,7 +226,7 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo, { struct scmi_mailbox *smbox = cinfo->transport_info; - shmem_fetch_response(smbox->shmem, xfer); + shmem_fetch_response(smbox->shmem, xfer, smbox->io_ops->fromio); } static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, @@ -230,7 +234,8 @@ static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, { struct scmi_mailbox *smbox = cinfo->transport_info; - shmem_fetch_notification(smbox->shmem, max_len, xfer); + shmem_fetch_notification(smbox->shmem, max_len, xfer, + smbox->io_ops->fromio); } static void mailbox_clear_channel(struct scmi_chan_info *cinfo) diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c index f5f6ec83d3e1..8ddd60536a5f 100644 --- a/drivers/firmware/arm_scmi/optee.c +++ b/drivers/firmware/arm_scmi/optee.c @@ -111,6 +111,7 @@ enum scmi_optee_pta_cmd { * @cinfo: SCMI channel information * @shmem: Virtual base address of the shared memory * @req: Shared memory protocol handle for SCMI request and synchronous response + * @io_ops: Transport specific I/O operations * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem * @link: Reference in agent's channel list */ @@ -125,6 +126,7 @@ struct scmi_optee_channel { struct scmi_shared_mem __iomem *shmem; struct scmi_msg_payld *msg; } req; + struct scmi_shmem_io_ops *io_ops; struct tee_shm *tee_shm; struct list_head link; }; @@ -392,6 +394,8 @@ static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, goto out; } + channel->io_ops = shmem_get_io_ops(np); + ret = 0; out: @@ -505,7 +509,8 @@ static int scmi_optee_send_message(struct scmi_chan_info *cinfo, msg_tx_prepare(channel->req.msg, xfer); ret = invoke_process_msg_channel(channel, msg_command_size(xfer)); } else { - shmem_tx_prepare(channel->req.shmem, xfer, cinfo); + shmem_tx_prepare(channel->req.shmem, xfer, cinfo, + channel->io_ops->toio); ret = invoke_process_smt_channel(channel); } @@ -523,7 +528,8 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, if (channel->tee_shm) msg_fetch_response(channel->req.msg, channel->rx_len, xfer); else - shmem_fetch_response(channel->req.shmem, xfer); + shmem_fetch_response(channel->req.shmem, xfer, + channel->io_ops->fromio); } static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index 517d52fb3bcb..cffbaa66c8a4 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -32,8 +33,58 @@ struct scmi_shared_mem { u8 msg_payload[]; }; +static inline void shmem_memcpy_fromio32(void *to, + const void __iomem *from, + size_t count) +{ + WARN_ON(!IS_ALIGNED((unsigned long)from, 4) || + !IS_ALIGNED((unsigned long)to, 4) || + count % 4); + + __ioread32_copy(to, from, count / 4); +} + +static inline void shmem_memcpy_toio32(void __iomem *to, + const void *from, + size_t count) +{ + WARN_ON(!IS_ALIGNED((unsigned long)from, 4) || + !IS_ALIGNED((unsigned long)to, 4) || + count % 4); + + __iowrite32_copy(to, from, count / 4); +} + +static struct scmi_shmem_io_ops shmem_io_ops32 = { + .fromio = shmem_memcpy_fromio32, + .toio = shmem_memcpy_toio32, +}; + +/* Wrappers are needed for proper memcpy_{from,to}_io expansion by the + * pre-processor. + */ +static inline void shmem_memcpy_fromio(void *to, + const void __iomem *from, + size_t count) +{ + memcpy_fromio(to, from, count); +} + +static inline void shmem_memcpy_toio(void __iomem *to, + const void *from, + size_t count) +{ + memcpy_toio(to, from, count); +} + +static struct scmi_shmem_io_ops shmem_io_ops_default = { + .fromio = shmem_memcpy_fromio, + .toio = shmem_memcpy_toio, +}; + void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer, struct scmi_chan_info *cinfo) + struct scmi_xfer *xfer, struct scmi_chan_info *cinfo, + shmem_copy_toio_t copy_toio) { ktime_t stop; @@ -70,7 +121,7 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, 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); + copy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len); } u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem) @@ -79,7 +130,8 @@ u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem) } void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, - struct scmi_xfer *xfer) + struct scmi_xfer *xfer, + shmem_copy_fromio_t copy_fromio) { size_t len = ioread32(&shmem->length); @@ -88,11 +140,12 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, xfer->rx.len = min_t(size_t, xfer->rx.len, len > 8 ? len - 8 : 0); /* Take a copy to the rx buffer.. */ - memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); + copy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); } void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, - size_t max_len, struct scmi_xfer *xfer) + size_t max_len, struct scmi_xfer *xfer, + shmem_copy_fromio_t copy_fromio) { size_t len = ioread32(&shmem->length); @@ -100,7 +153,7 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, xfer->rx.len = min_t(size_t, max_len, len > 4 ? len - 4 : 0); /* Take a copy to the rx buffer.. */ - memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); + copy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); } void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem) @@ -128,3 +181,16 @@ bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem) return (ioread32(&shmem->channel_status) & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); } + +struct scmi_shmem_io_ops *shmem_get_io_ops(struct device_node *shmem) +{ + u32 reg_io_width; + + of_property_read_u32(shmem, "reg-io-width", ®_io_width); + switch (reg_io_width) { + case 4: + return &shmem_io_ops32; + } + + return &shmem_io_ops_default; +} diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 42ea308a2c1d..3953ca75d09f 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -26,6 +26,7 @@ * @irq: An optional IRQ for completion * @cinfo: SCMI channel info * @shmem: Transmit/Receive shared memory area + * @io_ops: Transport specific I/O operations * @shmem_lock: Lock to protect access to Tx/Rx shared memory area. * Used when NOT operating in atomic mode. * @inflight: Atomic flag to protect access to Tx/Rx shared memory area. @@ -37,6 +38,7 @@ struct scmi_smc { int irq; struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; + struct scmi_shmem_io_ops *io_ops; /* Protect access to shmem area */ struct mutex shmem_lock; #define INFLIGHT_NONE MSG_TOKEN_MAX @@ -158,6 +160,8 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, cinfo->no_completion_irq = true; } + scmi_info->io_ops = shmem_get_io_ops(np); + scmi_info->func_id = func_id; scmi_info->cinfo = cinfo; smc_channel_lock_init(scmi_info); @@ -202,7 +206,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo, */ smc_channel_lock_acquire(scmi_info, xfer); - shmem_tx_prepare(scmi_info->shmem, xfer, cinfo); + shmem_tx_prepare(scmi_info->shmem, xfer, cinfo, + scmi_info->io_ops->toio); arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); @@ -220,7 +225,8 @@ static void smc_fetch_response(struct scmi_chan_info *cinfo, { struct scmi_smc *scmi_info = cinfo->transport_info; - shmem_fetch_response(scmi_info->shmem, xfer); + shmem_fetch_response(scmi_info->shmem, xfer, + scmi_info->io_ops->fromio); } static void smc_mark_txdone(struct scmi_chan_info *cinfo, int ret,