summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2024-06-27 17:40:33 +0300
committerArnd Bergmann <arnd@arndb.de>2024-06-27 17:40:33 +0300
commit7cab811dfe7bbdc75d34624d15e0f416e0f592ca (patch)
tree2e1c5a80e02a200278303b19092a1535f857077b /drivers/firmware
parentf2661062f16b2de5d7b6a5c42a9a5c96326b8454 (diff)
parentfa8b28ba22d95be4d2dd3c37fbdbdc066af82941 (diff)
downloadlinux-7cab811dfe7bbdc75d34624d15e0f416e0f592ca.tar.xz
Merge tag 'scmi-updates-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers
Arm SCMI driver updates for v6.11 The main addition this time is the support for platform to agent(p2a also referred sometimes as notification or Rx) channel completion via interrupt driven method. Currently, the OSPM agent clears or acknowledge the receipt of the norification or delayed response by updating the flags in the shared memory region which the platform is expected to poll. On some platforms that are completely interrupt driven, the OSPM agent is expected to send a response message instead. This change adds the support for the same. Other changes include addition of a separate mailing list specific to SCMI to allow open discussions about the interface itself in addition to the kernel driver updates and support for system suspend via the platform noification used on some systems. * tag 'scmi-updates-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: firmware: arm_scmi: Add support for platform to agent channel completion dt-bindings: firmware: arm,scmi: Add support for notification completion channel firmware: arm_scmi: Add support for system suspend in power control driver MAINTAINERS: Add mailing list for SCMI drivers Link: https://lore.kernel.org/r/20240620093924.375244-3-sudeep.holla@arm.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/arm_scmi/common.h1
-rw-r--r--drivers/firmware/arm_scmi/mailbox.c57
-rw-r--r--drivers/firmware/arm_scmi/scmi_power_control.c21
-rw-r--r--drivers/firmware/arm_scmi/shmem.c5
4 files changed, 77 insertions, 7 deletions
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index b5ac25dbc1ca..4b8c5250cdb5 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -326,6 +326,7 @@ 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);
+bool shmem_channel_intr_enabled(struct scmi_shared_mem __iomem *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 615a3b2ad83d..0219a12e3209 100644
--- a/drivers/firmware/arm_scmi/mailbox.c
+++ b/drivers/firmware/arm_scmi/mailbox.c
@@ -21,6 +21,7 @@
* @cl: Mailbox Client
* @chan: Transmit/Receive mailbox uni/bi-directional channel
* @chan_receiver: Optional Receiver mailbox unidirectional channel
+ * @chan_platform_receiver: Optional Platform Receiver mailbox unidirectional channel
* @cinfo: SCMI channel info
* @shmem: Transmit/Receive shared memory area
*/
@@ -28,6 +29,7 @@ struct scmi_mailbox {
struct mbox_client cl;
struct mbox_chan *chan;
struct mbox_chan *chan_receiver;
+ struct mbox_chan *chan_platform_receiver;
struct scmi_chan_info *cinfo;
struct scmi_shared_mem __iomem *shmem;
};
@@ -91,6 +93,8 @@ static bool mailbox_chan_available(struct device_node *of_node, int idx)
* for replies on the a2p channel. Set as zero if not present.
* @p2a_chan: A reference to the optional p2a channel.
* Set as zero if not present.
+ * @p2a_rx_chan: A reference to the optional p2a completion channel.
+ * Set as zero if not present.
*
* At first, validate the transport configuration as described in terms of
* 'mboxes' and 'shmem', then determin which mailbox channel indexes are
@@ -98,8 +102,8 @@ static bool mailbox_chan_available(struct device_node *of_node, int idx)
*
* Return: 0 on Success or error
*/
-static int mailbox_chan_validate(struct device *cdev,
- int *a2p_rx_chan, int *p2a_chan)
+static int mailbox_chan_validate(struct device *cdev, int *a2p_rx_chan,
+ int *p2a_chan, int *p2a_rx_chan)
{
int num_mb, num_sh, ret = 0;
struct device_node *np = cdev->of_node;
@@ -109,8 +113,9 @@ static int mailbox_chan_validate(struct device *cdev,
dev_dbg(cdev, "Found %d mboxes and %d shmems !\n", num_mb, num_sh);
/* Bail out if mboxes and shmem descriptors are inconsistent */
- if (num_mb <= 0 || num_sh <= 0 || num_sh > 2 || num_mb > 3 ||
- (num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2)) {
+ if (num_mb <= 0 || num_sh <= 0 || num_sh > 2 || num_mb > 4 ||
+ (num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2) ||
+ (num_mb == 4 && num_sh != 2)) {
dev_warn(cdev,
"Invalid channel descriptor for '%s' - mbs:%d shm:%d\n",
of_node_full_name(np), num_mb, num_sh);
@@ -139,6 +144,7 @@ static int mailbox_chan_validate(struct device *cdev,
case 1:
*a2p_rx_chan = 0;
*p2a_chan = 0;
+ *p2a_rx_chan = 0;
break;
case 2:
if (num_sh == 2) {
@@ -148,10 +154,17 @@ static int mailbox_chan_validate(struct device *cdev,
*a2p_rx_chan = 1;
*p2a_chan = 0;
}
+ *p2a_rx_chan = 0;
break;
case 3:
*a2p_rx_chan = 1;
*p2a_chan = 2;
+ *p2a_rx_chan = 0;
+ break;
+ case 4:
+ *a2p_rx_chan = 1;
+ *p2a_chan = 2;
+ *p2a_rx_chan = 3;
break;
}
}
@@ -166,12 +179,12 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
struct device *cdev = cinfo->dev;
struct scmi_mailbox *smbox;
struct device_node *shmem;
- int ret, a2p_rx_chan, p2a_chan, idx = tx ? 0 : 1;
+ int ret, a2p_rx_chan, p2a_chan, p2a_rx_chan, idx = tx ? 0 : 1;
struct mbox_client *cl;
resource_size_t size;
struct resource res;
- ret = mailbox_chan_validate(cdev, &a2p_rx_chan, &p2a_chan);
+ ret = mailbox_chan_validate(cdev, &a2p_rx_chan, &p2a_chan, &p2a_rx_chan);
if (ret)
return ret;
@@ -229,6 +242,17 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
}
}
+ if (!tx && p2a_rx_chan) {
+ smbox->chan_platform_receiver = mbox_request_channel(cl, p2a_rx_chan);
+ if (IS_ERR(smbox->chan_platform_receiver)) {
+ ret = PTR_ERR(smbox->chan_platform_receiver);
+ if (ret != -EPROBE_DEFER)
+ dev_err(cdev, "failed to request SCMI P2A Receiver mailbox\n");
+ return ret;
+ }
+ }
+
+
cinfo->transport_info = smbox;
smbox->cinfo = cinfo;
@@ -243,9 +267,11 @@ static int mailbox_chan_free(int id, void *p, void *data)
if (smbox && !IS_ERR(smbox->chan)) {
mbox_free_channel(smbox->chan);
mbox_free_channel(smbox->chan_receiver);
+ mbox_free_channel(smbox->chan_platform_receiver);
cinfo->transport_info = NULL;
smbox->chan = NULL;
smbox->chan_receiver = NULL;
+ smbox->chan_platform_receiver = NULL;
smbox->cinfo = NULL;
}
@@ -300,8 +326,27 @@ static void mailbox_fetch_notification(struct scmi_chan_info *cinfo,
static void mailbox_clear_channel(struct scmi_chan_info *cinfo)
{
struct scmi_mailbox *smbox = cinfo->transport_info;
+ struct mbox_chan *intr_chan;
+ int ret;
shmem_clear_channel(smbox->shmem);
+
+ if (!shmem_channel_intr_enabled(smbox->shmem))
+ return;
+
+ if (smbox->chan_platform_receiver)
+ intr_chan = smbox->chan_platform_receiver;
+ else if (smbox->chan)
+ intr_chan = smbox->chan;
+ else
+ return;
+
+ ret = mbox_send_message(intr_chan, NULL);
+ /* mbox_send_message returns non-negative value on success, so reset */
+ if (ret > 0)
+ ret = 0;
+
+ mbox_client_txdone(intr_chan, ret);
}
static bool
diff --git a/drivers/firmware/arm_scmi/scmi_power_control.c b/drivers/firmware/arm_scmi/scmi_power_control.c
index 6eb7d2a4b6b1..21f467a92942 100644
--- a/drivers/firmware/arm_scmi/scmi_power_control.c
+++ b/drivers/firmware/arm_scmi/scmi_power_control.c
@@ -50,6 +50,7 @@
#include <linux/reboot.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/time64.h>
#include <linux/timer.h>
#include <linux/types.h>
@@ -78,6 +79,7 @@ enum scmi_syspower_state {
* @reboot_nb: A notifier_block optionally used to track reboot progress
* @forceful_work: A worker used to trigger a forceful transition once a
* graceful has timed out.
+ * @suspend_work: A worker used to trigger system suspend
*/
struct scmi_syspower_conf {
struct device *dev;
@@ -90,6 +92,7 @@ struct scmi_syspower_conf {
struct notifier_block reboot_nb;
struct delayed_work forceful_work;
+ struct work_struct suspend_work;
};
#define userspace_nb_to_sconf(x) \
@@ -249,6 +252,9 @@ static void scmi_request_graceful_transition(struct scmi_syspower_conf *sc,
case SCMI_SYSTEM_WARMRESET:
orderly_reboot();
break;
+ case SCMI_SYSTEM_SUSPEND:
+ schedule_work(&sc->suspend_work);
+ break;
default:
break;
}
@@ -277,7 +283,8 @@ static int scmi_userspace_notifier(struct notifier_block *nb,
struct scmi_system_power_state_notifier_report *er = data;
struct scmi_syspower_conf *sc = userspace_nb_to_sconf(nb);
- if (er->system_state >= SCMI_SYSTEM_POWERUP) {
+ if (er->system_state >= SCMI_SYSTEM_MAX ||
+ er->system_state == SCMI_SYSTEM_POWERUP) {
dev_err(sc->dev, "Ignoring unsupported system_state: 0x%X\n",
er->system_state);
return NOTIFY_DONE;
@@ -315,6 +322,16 @@ static int scmi_userspace_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
+static void scmi_suspend_work_func(struct work_struct *work)
+{
+ struct scmi_syspower_conf *sc =
+ container_of(work, struct scmi_syspower_conf, suspend_work);
+
+ pm_suspend(PM_SUSPEND_MEM);
+
+ sc->state = SCMI_SYSPOWER_IDLE;
+}
+
static int scmi_syspower_probe(struct scmi_device *sdev)
{
int ret;
@@ -338,6 +355,8 @@ static int scmi_syspower_probe(struct scmi_device *sdev)
sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
sc->dev = &sdev->dev;
+ INIT_WORK(&sc->suspend_work, scmi_suspend_work_func);
+
return handle->notify_ops->devm_event_notifier_register(sdev,
SCMI_PROTOCOL_SYSTEM,
SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c
index 8bf495bcad09..b74e5a740f2c 100644
--- a/drivers/firmware/arm_scmi/shmem.c
+++ b/drivers/firmware/arm_scmi/shmem.c
@@ -128,3 +128,8 @@ bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem)
return (ioread32(&shmem->channel_status) &
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
}
+
+bool shmem_channel_intr_enabled(struct scmi_shared_mem __iomem *shmem)
+{
+ return ioread32(&shmem->flags) & SCMI_SHMEM_FLAG_INTR_ENABLED;
+}