From 1dc6558062dadfabd2fb3bd885fa6e92ec7196f2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Sun, 8 Mar 2020 21:24:39 +0800 Subject: firmware: arm_scmi: Add smc/hvc transport Use the value of "arm,smc-id" property from the device tree as the first argument for SMCCC call leaving all the other arguments as zero for now. There is no Rx, only Tx because of smc/hvc not support Rx. Link: https://lore.kernel.org/r/1583673879-20714-3-git-send-email-peng.fan@nxp.com Signed-off-by: Peng Fan [sudeep.holla: reworded commit log/subject and fixed !HAVE_ARM_SMCCC build] Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 4 +- drivers/firmware/arm_scmi/common.h | 3 + drivers/firmware/arm_scmi/driver.c | 3 + drivers/firmware/arm_scmi/smc.c | 152 +++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/smc.c (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 6694d0d908d6..11b238f81923 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -2,6 +2,8 @@ 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 = mailbox.o shmem.o +scmi-transport-y = shmem.o +scmi-transport-$(CONFIG_MAILBOX) += mailbox.o +scmi-transport-$(CONFIG_HAVE_ARM_SMCCC) += smc.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 5ac06469b01c..34bfadca14cc 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -210,6 +210,9 @@ struct scmi_desc { }; extern const struct scmi_desc scmi_mailbox_desc; +#ifdef CONFIG_HAVE_ARM_SMCCC +extern const struct scmi_desc scmi_smc_desc; +#endif 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); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index dbec767222e9..6ef61e52eef7 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -827,6 +827,9 @@ ATTRIBUTE_GROUPS(versions); /* Each compatible listed below must have descriptor associated with it */ static const struct of_device_id scmi_of_match[] = { { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, +#ifdef CONFIG_HAVE_ARM_SMCCC + { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, +#endif { /* Sentinel */ }, }; diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c new file mode 100644 index 000000000000..336168e40f49 --- /dev/null +++ b/drivers/firmware/arm_scmi/smc.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Message SMC/HVC + * Transport driver + * + * Copyright 2020 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/** + * struct scmi_smc - Structure representing a SCMI smc transport + * + * @cinfo: SCMI channel info + * @shmem: Transmit/Receive shared memory area + * @func_id: smc/hvc call function id + */ + +struct scmi_smc { + struct scmi_chan_info *cinfo; + struct scmi_shared_mem __iomem *shmem; + u32 func_id; +}; + +static DEFINE_MUTEX(smc_mutex); + +static bool smc_chan_available(struct device *dev, int idx) +{ + return true; +} + +static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, + bool tx) +{ + struct device *cdev = cinfo->dev; + struct scmi_smc *scmi_info; + resource_size_t size; + struct resource res; + struct device_node *np; + u32 func_id; + int ret; + + if (!tx) + return -ENODEV; + + scmi_info = devm_kzalloc(dev, sizeof(*scmi_info), GFP_KERNEL); + if (!scmi_info) + return -ENOMEM; + + np = of_parse_phandle(cdev->of_node, "shmem", 0); + if (!np) + np = of_parse_phandle(dev->of_node, "shmem", 0); + ret = of_address_to_resource(np, 0, &res); + of_node_put(np); + if (ret) { + dev_err(cdev, "failed to get SCMI Tx shared memory\n"); + return ret; + } + + size = resource_size(&res); + scmi_info->shmem = devm_ioremap(dev, res.start, size); + if (!scmi_info->shmem) { + dev_err(dev, "failed to ioremap SCMI Tx shared memory\n"); + return -EADDRNOTAVAIL; + } + + ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id); + if (ret < 0) + return ret; + + scmi_info->func_id = func_id; + scmi_info->cinfo = cinfo; + cinfo->transport_info = scmi_info; + + return 0; +} + +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; + + cinfo->transport_info = NULL; + scmi_info->cinfo = NULL; + + scmi_free_channel(cinfo, data, id); + + return 0; +} + +static int smc_send_message(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_smc *scmi_info = cinfo->transport_info; + struct arm_smccc_res res; + + mutex_lock(&smc_mutex); + + shmem_tx_prepare(scmi_info->shmem, xfer); + + arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); + scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem)); + + mutex_unlock(&smc_mutex); + + return res.a0; +} + +static void smc_mark_txdone(struct scmi_chan_info *cinfo, int ret) +{ +} + +static void smc_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_smc *scmi_info = cinfo->transport_info; + + shmem_fetch_response(scmi_info->shmem, xfer); +} + +static bool +smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) +{ + struct scmi_smc *scmi_info = cinfo->transport_info; + + return shmem_poll_done(scmi_info->shmem, xfer); +} + +static struct scmi_transport_ops scmi_smc_ops = { + .chan_available = smc_chan_available, + .chan_setup = smc_chan_setup, + .chan_free = smc_chan_free, + .send_message = smc_send_message, + .mark_txdone = smc_mark_txdone, + .fetch_response = smc_fetch_response, + .poll_done = smc_poll_done, +}; + +const struct scmi_desc scmi_smc_desc = { + .ops = &scmi_smc_ops, + .max_rx_timeout_ms = 30, + .max_msg = 1, + .max_msg_size = 128, +}; -- cgit v1.2.3 From 18988265b87615b93cd4cf15001c8c883236a137 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 16:36:51 +0000 Subject: firmware: arm_scmi: Make mutex channel specific In order to support multiple SMC/HVC transport channels with associated shared memory, it is better to maintain the mutex per channel instead of existing global one. Move the smc_mutex into the scmi_smc structure and also rename it to shmem_lock which is more appropriate for it's use. Link: https://lore.kernel.org/r/20200327163654.13389-2-sudeep.holla@arm.com Tested-by: Peng Fan Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 336168e40f49..6dc8a88cc91b 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -27,11 +27,10 @@ struct scmi_smc { struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; + struct mutex shmem_lock; u32 func_id; }; -static DEFINE_MUTEX(smc_mutex); - static bool smc_chan_available(struct device *dev, int idx) { return true; @@ -78,6 +77,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, scmi_info->func_id = func_id; scmi_info->cinfo = cinfo; + mutex_init(&scmi_info->shmem_lock); cinfo->transport_info = scmi_info; return 0; @@ -102,14 +102,14 @@ static int smc_send_message(struct scmi_chan_info *cinfo, struct scmi_smc *scmi_info = cinfo->transport_info; struct arm_smccc_res res; - mutex_lock(&smc_mutex); + mutex_lock(&scmi_info->shmem_lock); shmem_tx_prepare(scmi_info->shmem, xfer); arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem)); - mutex_unlock(&smc_mutex); + mutex_unlock(&scmi_info->shmem_lock); return res.a0; } -- cgit v1.2.3 From b9d15ee21cd8cf088ac574649ae7ac2fefaa440a Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 16:36:52 +0000 Subject: firmware: arm_scmi: Drop empty stub for smc_mark_txdone The scmi protocol core driver check for non NULL mark_txdone before invoking the same. There is no need to provide a empty stub. SMC/HVC calls are synchronous and the call return indicates the completion. Link: https://lore.kernel.org/r/20200327163654.13389-3-sudeep.holla@arm.com Tested-by: Peng Fan Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 6dc8a88cc91b..dd4b54c29679 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -114,10 +114,6 @@ static int smc_send_message(struct scmi_chan_info *cinfo, return res.a0; } -static void smc_mark_txdone(struct scmi_chan_info *cinfo, int ret) -{ -} - static void smc_fetch_response(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { @@ -139,7 +135,6 @@ static struct scmi_transport_ops scmi_smc_ops = { .chan_setup = smc_chan_setup, .chan_free = smc_chan_free, .send_message = smc_send_message, - .mark_txdone = smc_mark_txdone, .fetch_response = smc_fetch_response, .poll_done = smc_poll_done, }; -- cgit v1.2.3 From 8aa6e12bbf9dd6cf1ebf2aad7b09389863a4c2c6 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 16:36:53 +0000 Subject: firmware: arm_scmi: Check shmem property for channel availablity Instead of declaring the channel availabilty unconditionally, let us check for the presence of "shmem" property and return the channel availablity accordingly. Link: https://lore.kernel.org/r/20200327163654.13389-4-sudeep.holla@arm.com Tested-by: Peng Fan Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index dd4b54c29679..5929c668dc1d 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -33,6 +33,11 @@ struct scmi_smc { static bool smc_chan_available(struct device *dev, int idx) { + struct device_node *np = of_parse_phandle(dev->of_node, "shmem", 0); + if (!np) + return false; + + of_node_put(np); return true; } -- cgit v1.2.3 From 4e44590ee4a917df5ce557df4ad831ec5e82b4a6 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 16:36:54 +0000 Subject: firmware: arm_scmi: Drop checking for shmem property in parent node The scmi protocol core driver checks for the channel availability before evaluating the shmem property. If the individual protocols don't have separate channel assigned to them, the channel alloted for the BASE protocol is reused automatically. Therefore there is no need to check for the shmem property in the parent node if it is absent in the child protocol node. Link: https://lore.kernel.org/r/20200327163654.13389-5-sudeep.holla@arm.com Tested-by: Peng Fan Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 5929c668dc1d..833e793b5391 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -60,8 +60,6 @@ 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 (!np) - np = of_parse_phandle(dev->of_node, "shmem", 0); ret = of_address_to_resource(np, 0, &res); of_node_put(np); if (ret) { -- cgit v1.2.3 From 4ebd8f6dea813660740d004bde3caaf54bd79019 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 14:34:26 +0000 Subject: firmware: arm_scmi: Add receive buffer support for notifications With all the plumbing in place, let's just add the separate dedicated receive buffers to handle notifications that can arrive asynchronously from the platform firmware to OS. Also add one check to see if the platform supports any receive channels before allocating the receive buffers: since those buffers are optionally supported though, the whole xfer initialization is also postponed to be able to check for their existence in advance. Link: https://lore.kernel.org/r/20200327143438.5382-2-cristian.marussi@arm.com Reviewed-by: Jonathan Cameron [Changed parameters in __scmi_xfer_info_init()] Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 6ef61e52eef7..ba9617dbe93f 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -76,6 +76,7 @@ struct scmi_xfers_info { * implementation version and (sub-)vendor identification. * @handle: Instance of SCMI handle to send to clients * @tx_minfo: Universal Transmit Message management info + * @rx_minfo: Universal Receive 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 * @protocols_imp: List of protocols implemented, currently maximum of @@ -89,6 +90,7 @@ struct scmi_info { struct scmi_revision_info version; struct scmi_handle handle; struct scmi_xfers_info tx_minfo; + struct scmi_xfers_info rx_minfo; struct idr tx_idr; struct idr rx_idr; u8 *protocols_imp; @@ -525,13 +527,13 @@ int scmi_handle_put(const struct scmi_handle *handle) return 0; } -static int scmi_xfer_info_init(struct scmi_info *sinfo) +static int __scmi_xfer_info_init(struct scmi_info *sinfo, + struct scmi_xfers_info *info) { int i; struct scmi_xfer *xfer; struct device *dev = sinfo->dev; const struct scmi_desc *desc = sinfo->desc; - struct scmi_xfers_info *info = &sinfo->tx_minfo; /* Pre-allocated messages, no more than what hdr.seq can support */ if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { @@ -566,6 +568,16 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) return 0; } +static int scmi_xfer_info_init(struct scmi_info *sinfo) +{ + int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo); + + if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE)) + ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo); + + return ret; +} + static int scmi_chan_setup(struct scmi_info *info, struct device *dev, int prot_id, bool tx) { @@ -699,10 +711,6 @@ static int scmi_probe(struct platform_device *pdev) info->desc = desc; INIT_LIST_HEAD(&info->node); - ret = scmi_xfer_info_init(info); - if (ret) - return ret; - platform_set_drvdata(pdev, info); idr_init(&info->tx_idr); idr_init(&info->rx_idr); @@ -715,6 +723,10 @@ static int scmi_probe(struct platform_device *pdev) if (ret) return ret; + ret = scmi_xfer_info_init(info); + if (ret) + return ret; + ret = scmi_base_protocol_init(handle); if (ret) { dev_err(dev, "unable to communicate with SCMI(%d)\n", ret); -- cgit v1.2.3 From 2264417a9a99babcc45960f28057a04c24919efb Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 14:34:27 +0000 Subject: firmware: arm_scmi: Update protocol commands and notification list Add commands' enumerations and messages definitions for all existing notify-enable commands across all protocols. Link: https://lore.kernel.org/r/20200327143438.5382-3-cristian.marussi@arm.com Reviewed-by: Jonathan Cameron Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 7 +++++++ drivers/firmware/arm_scmi/perf.c | 5 +++++ drivers/firmware/arm_scmi/power.c | 6 ++++++ drivers/firmware/arm_scmi/sensors.c | 4 ++++ 4 files changed, 22 insertions(+) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index f804e8af6521..ce7d9203e41b 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd { BASE_DISCOVER_LIST_PROTOCOLS = 0x6, BASE_DISCOVER_AGENT = 0x7, BASE_NOTIFY_ERRORS = 0x8, + BASE_SET_DEVICE_PERMISSIONS = 0x9, + BASE_SET_PROTOCOL_PERMISSIONS = 0xa, + BASE_RESET_AGENT_CONFIGURATION = 0xb, +}; + +enum scmi_base_protocol_notify { + BASE_ERROR_EVENT = 0x0, }; struct scmi_msg_resp_base_attributes { diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 34f3a917dd8d..eadc171e254b 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -27,6 +27,11 @@ enum scmi_performance_protocol_cmd { PERF_DESCRIBE_FASTCHANNEL = 0xb, }; +enum scmi_performance_protocol_notify { + PERFORMANCE_LIMITS_CHANGED = 0x0, + PERFORMANCE_LEVEL_CHANGED = 0x1, +}; + struct scmi_opp { u32 perf; u32 power; diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 214886ce84f1..cf7f0312381b 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -12,6 +12,12 @@ enum scmi_power_protocol_cmd { POWER_STATE_SET = 0x4, POWER_STATE_GET = 0x5, POWER_STATE_NOTIFY = 0x6, + POWER_STATE_CHANGE_REQUESTED_NOTIFY = 0x7, +}; + +enum scmi_power_protocol_notify { + POWER_STATE_CHANGED = 0x0, + POWER_STATE_CHANGE_REQUESTED = 0x1, }; struct scmi_msg_resp_power_attributes { diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index eba61b9c1f53..db1b1ab303da 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -14,6 +14,10 @@ enum scmi_sensor_protocol_cmd { SENSOR_READING_GET = 0x6, }; +enum scmi_sensor_protocol_notify { + SENSOR_TRIP_POINT_EVENT = 0x0, +}; + struct scmi_msg_resp_sensor_attributes { __le16 num_sensors; u8 max_requests; -- cgit v1.2.3 From d5141f37c42e0b833863f157ac4cee203b2ba3d2 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 27 Mar 2020 14:34:28 +0000 Subject: firmware: arm_scmi: Add notifications support in transport layer Add common transport-layer methods to: - fetch a notification instead of a response - clear a pending notification Add also all the needed support in mailbox/shmem transports. Link: https://lore.kernel.org/r/20200327143438.5382-4-cristian.marussi@arm.com Reviewed-by: Jonathan Cameron Reviewed-by: Viresh Kumar Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/common.h | 8 ++++++++ drivers/firmware/arm_scmi/mailbox.c | 17 +++++++++++++++++ drivers/firmware/arm_scmi/shmem.c | 15 +++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 34bfadca14cc..07eb33c1576b 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -178,6 +178,8 @@ struct scmi_chan_info { * @send_message: Callback to send a message * @mark_txdone: Callback to mark tx as done * @fetch_response: Callback to fetch response + * @fetch_notification: Callback to fetch notification + * @clear_notification: Callback to clear a pending notification * @poll_done: Callback to poll transfer status */ struct scmi_transport_ops { @@ -190,6 +192,9 @@ struct scmi_transport_ops { void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); void (*fetch_response)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); + void (*fetch_notification)(struct scmi_chan_info *cinfo, + size_t max_len, struct scmi_xfer *xfer); + void (*clear_notification)(struct scmi_chan_info *cinfo); bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); }; @@ -225,5 +230,8 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); +void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, + size_t max_len, struct scmi_xfer *xfer); +void shmem_clear_notification(struct scmi_shared_mem __iomem *shmem); bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 73077bbc4ad9..19ee058f9f44 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -158,6 +158,21 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo, shmem_fetch_response(smbox->shmem, xfer); } +static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, + size_t max_len, struct scmi_xfer *xfer) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + + shmem_fetch_notification(smbox->shmem, max_len, xfer); +} + +static void mailbox_clear_notification(struct scmi_chan_info *cinfo) +{ + struct scmi_mailbox *smbox = cinfo->transport_info; + + shmem_clear_notification(smbox->shmem); +} + static bool mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { @@ -173,6 +188,8 @@ static struct scmi_transport_ops scmi_mailbox_ops = { .send_message = mailbox_send_message, .mark_txdone = mailbox_mark_txdone, .fetch_response = mailbox_fetch_response, + .fetch_notification = mailbox_fetch_notification, + .clear_notification = mailbox_clear_notification, .poll_done = mailbox_poll_done, }; diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index e1e816e0018c..a5a5d0f6bf86 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -67,6 +67,21 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, memcpy_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) +{ + /* Skip only the length of header in shmem area i.e 4 bytes */ + xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4); + + /* Take a copy to the rx buffer.. */ + memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); +} + +void shmem_clear_notification(struct scmi_shared_mem __iomem *shmem) +{ + iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status); +} + bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer) { -- cgit v1.2.3 From 4d09852b6f0126cafc45d070de589c7bf64fdd17 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 27 Mar 2020 14:34:29 +0000 Subject: firmware: arm_scmi: Add support for notifications message processing Add the mechanisms to distinguish notifications from delayed responses and command responses. Also add support to properly fetch notification messages upon reception. Notifications processing does not continue any further after the fetch phase. Link: https://lore.kernel.org/r/20200327143438.5382-5-cristian.marussi@arm.com Reviewed-by: Jonathan Cameron [Reworked/renamed scmi_handle_xfer_delayed_resp()] Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 84 +++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 20 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ba9617dbe93f..332edf1b09fc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -202,29 +202,42 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) spin_unlock_irqrestore(&minfo->xfer_lock, flags); } -/** - * scmi_rx_callback() - callback for receiving messages - * - * @cinfo: SCMI channel info - * @msg_hdr: Message header - * - * Processes one received message to appropriate transfer information and - * signals completion of the transfer. - * - * NOTE: This function will be invoked in IRQ context, hence should be - * as optimal as possible. - */ -void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) +static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) { - struct scmi_info *info = handle_to_scmi_info(cinfo->handle); - struct scmi_xfers_info *minfo = &info->tx_minfo; - 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; + struct device *dev = cinfo->dev; + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); + struct scmi_xfers_info *minfo = &info->rx_minfo; + + xfer = scmi_xfer_get(cinfo->handle, minfo); + if (IS_ERR(xfer)) { + dev_err(dev, "failed to get free message slot (%ld)\n", + PTR_ERR(xfer)); + info->desc->ops->clear_notification(cinfo); + return; + } + + unpack_scmi_header(msg_hdr, &xfer->hdr); + scmi_dump_header_dbg(dev, &xfer->hdr); + info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size, + xfer); + + trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id, + xfer->hdr.protocol_id, xfer->hdr.seq, + MSG_TYPE_NOTIFICATION); - if (msg_type == MSG_TYPE_NOTIFICATION) - return; /* Notifications not yet supported */ + __scmi_xfer_put(minfo, xfer); + + info->desc->ops->clear_notification(cinfo); +} + +static void scmi_handle_response(struct scmi_chan_info *cinfo, + u16 xfer_id, u8 msg_type) +{ + struct scmi_xfer *xfer; + struct device *dev = cinfo->dev; + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); + struct scmi_xfers_info *minfo = &info->tx_minfo; /* Are we even expecting this? */ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { @@ -248,6 +261,37 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) complete(&xfer->done); } +/** + * scmi_rx_callback() - callback for receiving messages + * + * @cinfo: SCMI channel info + * @msg_hdr: Message header + * + * Processes one received message to appropriate transfer information and + * signals completion of the transfer. + * + * NOTE: This function will be invoked in IRQ context, hence should be + * as optimal as possible. + */ +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) +{ + u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr); + u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); + + switch (msg_type) { + case MSG_TYPE_NOTIFICATION: + scmi_handle_notification(cinfo, msg_hdr); + break; + case MSG_TYPE_COMMAND: + case MSG_TYPE_DELAYED_RESP: + scmi_handle_response(cinfo, xfer_id, msg_type); + break; + default: + WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type); + break; + } +} + /** * scmi_xfer_put() - Release a transmit message * -- cgit v1.2.3 From 87dff4e63cf2910f2e4a32d1cb3e4a1a25406eb7 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 20 Apr 2020 16:23:12 +0100 Subject: firmware: arm_scmi: Rename .clear_notification() transport_ops SCMI transport operation .clear_notification() is indeed a generic method to clear the channel in a transport dependent way, as such it could be a useful helper also in other contexts. Rename such method as .clear_channel(), renaming accordingly also its already existent call-sites. No functional change. Link: https://lore.kernel.org/r/20200420152315.21008-2-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/common.h | 6 +++--- drivers/firmware/arm_scmi/driver.c | 4 ++-- drivers/firmware/arm_scmi/mailbox.c | 6 +++--- drivers/firmware/arm_scmi/shmem.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 07eb33c1576b..31fe5a22a011 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -179,7 +179,7 @@ struct scmi_chan_info { * @mark_txdone: Callback to mark tx as done * @fetch_response: Callback to fetch response * @fetch_notification: Callback to fetch notification - * @clear_notification: Callback to clear a pending notification + * @clear_channel: Callback to clear a channel * @poll_done: Callback to poll transfer status */ struct scmi_transport_ops { @@ -194,7 +194,7 @@ struct scmi_transport_ops { struct scmi_xfer *xfer); void (*fetch_notification)(struct scmi_chan_info *cinfo, size_t max_len, struct scmi_xfer *xfer); - void (*clear_notification)(struct scmi_chan_info *cinfo); + void (*clear_channel)(struct scmi_chan_info *cinfo); bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); }; @@ -232,6 +232,6 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, size_t max_len, struct scmi_xfer *xfer); -void shmem_clear_notification(struct scmi_shared_mem __iomem *shmem); +void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem); bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 332edf1b09fc..90c7a0bb62ef 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -213,7 +213,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) if (IS_ERR(xfer)) { dev_err(dev, "failed to get free message slot (%ld)\n", PTR_ERR(xfer)); - info->desc->ops->clear_notification(cinfo); + info->desc->ops->clear_channel(cinfo); return; } @@ -228,7 +228,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) __scmi_xfer_put(minfo, xfer); - info->desc->ops->clear_notification(cinfo); + info->desc->ops->clear_channel(cinfo); } static void scmi_handle_response(struct scmi_chan_info *cinfo, diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 19ee058f9f44..6998dc86b5ce 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -166,11 +166,11 @@ static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, shmem_fetch_notification(smbox->shmem, max_len, xfer); } -static void mailbox_clear_notification(struct scmi_chan_info *cinfo) +static void mailbox_clear_channel(struct scmi_chan_info *cinfo) { struct scmi_mailbox *smbox = cinfo->transport_info; - shmem_clear_notification(smbox->shmem); + shmem_clear_channel(smbox->shmem); } static bool @@ -189,7 +189,7 @@ static struct scmi_transport_ops scmi_mailbox_ops = { .mark_txdone = mailbox_mark_txdone, .fetch_response = mailbox_fetch_response, .fetch_notification = mailbox_fetch_notification, - .clear_notification = mailbox_clear_notification, + .clear_channel = mailbox_clear_channel, .poll_done = mailbox_poll_done, }; diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index a5a5d0f6bf86..0e3eaea5d852 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -77,7 +77,7 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); } -void shmem_clear_notification(struct scmi_shared_mem __iomem *shmem) +void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem) { iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status); } -- cgit v1.2.3 From b37f5cc8d243479d7572445010fb6c9a4dff6dc4 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 20 Apr 2020 16:23:13 +0100 Subject: firmware: arm_scmi: Clear channel on reception of unexpected responses When an unexpected response message is received we currently warn the user and bail-out, ensure to also free the channel by invoking the transport independent operation .clear_channel() Link: https://lore.kernel.org/r/20200420152315.21008-3-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 90c7a0bb62ef..31c6a89a6edd 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -242,6 +242,7 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, /* Are we even expecting this? */ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { dev_err(dev, "message for %d is not expected!\n", xfer_id); + info->desc->ops->clear_channel(cinfo); return; } -- cgit v1.2.3 From d04fb2b2ddefad7c00edd29c1ed40188ce8f12a2 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 20 Apr 2020 16:23:14 +0100 Subject: firmware: arm_scmi: Clear channel for delayed responses Clear channel properly when done processing a delayed response. This will let the platform firmware know that the channel is now free to use it for any new delayed response or notification. Link: https://lore.kernel.org/r/20200420152315.21008-4-cristian.marussi@arm.com Signed-off-by: Cristian Marussi (sudeep.holla: Updated commit log to reflect that channel is now free for platform to use) Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 31c6a89a6edd..07de196f15aa 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -256,10 +256,12 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, xfer->hdr.protocol_id, xfer->hdr.seq, msg_type); - if (msg_type == MSG_TYPE_DELAYED_RESP) + if (msg_type == MSG_TYPE_DELAYED_RESP) { + info->desc->ops->clear_channel(cinfo); complete(xfer->async_done); - else + } else { complete(&xfer->done); + } } /** -- cgit v1.2.3 From c5bceb98ce0e4ae8057a386c5171a868213fe226 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 20 Apr 2020 16:23:15 +0100 Subject: firmware: arm_scmi: Fix handling of unexpected delayed responses Upon reception of an unexpected bogus delayed response, clear the channel and bail-out safely. Link: https://lore.kernel.org/r/20200420152315.21008-5-cristian.marussi@arm.com Fixes: 4d09852b6f01 ("firmware: arm_scmi: Add support for notifications message processing") Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 07de196f15aa..fec308e47b9d 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -247,6 +247,21 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, } xfer = &minfo->xfer_block[xfer_id]; + /* + * Even if a response was indeed expected on this slot at this point, + * a buggy platform could wrongly reply feeding us an unexpected + * delayed response we're not prepared to handle: bail-out safely + * blaming firmware. + */ + if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) { + dev_err(dev, + "Delayed Response for %d not expected! Buggy F/W ?\n", + xfer_id); + info->desc->ops->clear_channel(cinfo); + /* It was unexpected, so nobody will clear the xfer if not us */ + __scmi_xfer_put(minfo, xfer); + return; + } scmi_dump_header_dbg(dev, &xfer->hdr); -- cgit v1.2.3 From f7199cf489027ae38a9a82312d13025f7aefa0b8 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 17 Apr 2020 11:32:32 +0100 Subject: firmware: arm_scmi: Fix return error code in smc_send_message SMCCC can return NOT_SUPPORTED(-1). Map it to appropriate Linux error codes namely -EOPNOTSUPP. Link: https://lore.kernel.org/r/20200417103232.6896-1-sudeep.holla@arm.com Reported-and-Tested-by:: Etienne Carriere Reviewed-by: Etienne Carriere Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 833e793b5391..49bc4b0e8428 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -114,7 +114,10 @@ static int smc_send_message(struct scmi_chan_info *cinfo, mutex_unlock(&scmi_info->shmem_lock); - return res.a0; + /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */ + if (res.a0) + return -EOPNOTSUPP; + return 0; } static void smc_fetch_response(struct scmi_chan_info *cinfo, -- cgit v1.2.3 From 5a897e3ab42942e0fff51738357b1e0f91690dcf Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 7 May 2020 15:49:05 +0100 Subject: firmware: arm_scmi: fix psci dependency When CONFIG_ARM_PSCI_FW is disabled but CONFIG_HAVE_ARM_SMCCC is enabled, arm-scmi runs into a link failure: arm-linux-gnueabi-ld: drivers/firmware/arm_scmi/smc.o: in function `smc_send_message': smc.c:(.text+0x200): undefined reference to `arm_smccc_1_1_get_conduit' Change from HAVE_ARM_SMCCC to ARM_PSCI_FW config dependency for now. We rely on PSCI bindings anyways for the conduit and this should be fine. Link: https://lore.kernel.org/r/20200507144905.11397-1-sudeep.holla@arm.com Fixes: 1dc6558062da ("firmware: arm_scmi: Add smc/hvc transport") Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/driver.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/firmware/arm_scmi') diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 11b238f81923..1cad32b38b29 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -4,6 +4,6 @@ scmi-bus-y = bus.o scmi-driver-y = driver.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o -scmi-transport-$(CONFIG_HAVE_ARM_SMCCC) += smc.o +scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.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/driver.c b/drivers/firmware/arm_scmi/driver.c index fec308e47b9d..7483cacf63f9 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -901,7 +901,7 @@ ATTRIBUTE_GROUPS(versions); /* Each compatible listed below must have descriptor associated with it */ static const struct of_device_id scmi_of_match[] = { { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, -#ifdef CONFIG_HAVE_ARM_SMCCC +#ifdef CONFIG_ARM_PSCI_FW { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, #endif { /* Sentinel */ }, -- cgit v1.2.3