OSDN Git Service

firmware: arm_scmi: Add system power protocol support
authorCristian Marussi <cristian.marussi@arm.com>
Mon, 7 Sep 2020 17:46:55 +0000 (18:46 +0100)
committerSudeep Holla <sudeep.holla@arm.com>
Tue, 8 Sep 2020 15:00:55 +0000 (16:00 +0100)
Add bare protocol support for SCMI system power protocol as needed by
an OSPM agent: basic initialization and SYSTEM_POWER_STATE_NOTIFIER
core notification support.

No event-handling logic is attached to such notification..

Link: https://lore.kernel.org/r/20200907174657.32466-2-cristian.marussi@arm.com
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/Makefile
drivers/firmware/arm_scmi/system.c [new file with mode: 0644]
include/linux/scmi_protocol.h

index 6f9cbc4..643f232 100644 (file)
@@ -5,5 +5,5 @@ scmi-driver-y = driver.o notify.o
 scmi-transport-y = shmem.o
 scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
 scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
-scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
+scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
 obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
new file mode 100644 (file)
index 0000000..aa1e74f
--- /dev/null
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) System Power Protocol
+ *
+ * Copyright (C) 2020 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
+
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+#include "notify.h"
+
+#define SCMI_SYSTEM_NUM_SOURCES                1
+
+enum scmi_system_protocol_cmd {
+       SYSTEM_POWER_STATE_NOTIFY = 0x5,
+};
+
+struct scmi_system_power_state_notify {
+       __le32 notify_enable;
+};
+
+struct scmi_system_power_state_notifier_payld {
+       __le32 agent_id;
+       __le32 flags;
+       __le32 system_state;
+};
+
+struct scmi_system_info {
+       u32 version;
+};
+
+static int scmi_system_request_notify(const struct scmi_handle *handle,
+                                     bool enable)
+{
+       int ret;
+       struct scmi_xfer *t;
+       struct scmi_system_power_state_notify *notify;
+
+       ret = scmi_xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY,
+                                SCMI_PROTOCOL_SYSTEM, sizeof(*notify), 0, &t);
+       if (ret)
+               return ret;
+
+       notify = t->tx.buf;
+       notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
+
+       ret = scmi_do_xfer(handle, t);
+
+       scmi_xfer_put(handle, t);
+       return ret;
+}
+
+static int scmi_system_set_notify_enabled(const struct scmi_handle *handle,
+                                         u8 evt_id, u32 src_id, bool enable)
+{
+       int ret;
+
+       ret = scmi_system_request_notify(handle, enable);
+       if (ret)
+               pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
+
+       return ret;
+}
+
+static void *scmi_system_fill_custom_report(const struct scmi_handle *handle,
+                                           u8 evt_id, ktime_t timestamp,
+                                           const void *payld, size_t payld_sz,
+                                           void *report, u32 *src_id)
+{
+       const struct scmi_system_power_state_notifier_payld *p = payld;
+       struct scmi_system_power_state_notifier_report *r = report;
+
+       if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
+           sizeof(*p) != payld_sz)
+               return NULL;
+
+       r->timestamp = timestamp;
+       r->agent_id = le32_to_cpu(p->agent_id);
+       r->flags = le32_to_cpu(p->flags);
+       r->system_state = le32_to_cpu(p->system_state);
+       *src_id = 0;
+
+       return r;
+}
+
+static const struct scmi_event system_events[] = {
+       {
+               .id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
+               .max_payld_sz =
+                       sizeof(struct scmi_system_power_state_notifier_payld),
+               .max_report_sz =
+                       sizeof(struct scmi_system_power_state_notifier_report),
+       },
+};
+
+static const struct scmi_event_ops system_event_ops = {
+       .set_notify_enabled = scmi_system_set_notify_enabled,
+       .fill_custom_report = scmi_system_fill_custom_report,
+};
+
+static int scmi_system_protocol_init(struct scmi_handle *handle)
+{
+       u32 version;
+       struct scmi_system_info *pinfo;
+
+       scmi_version_get(handle, SCMI_PROTOCOL_SYSTEM, &version);
+
+       dev_dbg(handle->dev, "System Power Version %d.%d\n",
+               PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+       pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+       if (!pinfo)
+               return -ENOMEM;
+
+       scmi_register_protocol_events(handle,
+                                     SCMI_PROTOCOL_SYSTEM, SCMI_PROTO_QUEUE_SZ,
+                                     &system_event_ops,
+                                     system_events,
+                                     ARRAY_SIZE(system_events),
+                                     SCMI_SYSTEM_NUM_SOURCES);
+
+       pinfo->version = version;
+       handle->system_priv = pinfo;
+
+       return 0;
+}
+
+static int __init scmi_system_init(void)
+{
+       return scmi_protocol_register(SCMI_PROTOCOL_SYSTEM,
+                                     &scmi_system_protocol_init);
+}
+subsys_initcall(scmi_system_init);
index 05570af..4b10093 100644 (file)
@@ -292,6 +292,7 @@ struct scmi_handle {
        void *sensor_priv;
        void *reset_priv;
        void *notify_priv;
+       void *system_priv;
 };
 
 enum scmi_std_protocol {
@@ -304,6 +305,15 @@ enum scmi_std_protocol {
        SCMI_PROTOCOL_RESET = 0x16,
 };
 
+enum scmi_system_events {
+       SCMI_SYSTEM_SHUTDOWN,
+       SCMI_SYSTEM_COLDRESET,
+       SCMI_SYSTEM_WARMRESET,
+       SCMI_SYSTEM_POWERUP,
+       SCMI_SYSTEM_SUSPEND,
+       SCMI_SYSTEM_MAX
+};
+
 struct scmi_device {
        u32 id;
        u8 protocol_id;
@@ -378,6 +388,7 @@ enum scmi_notification_events {
        SCMI_EVENT_SENSOR_TRIP_POINT_EVENT = 0x0,
        SCMI_EVENT_RESET_ISSUED = 0x0,
        SCMI_EVENT_BASE_ERROR_EVENT = 0x0,
+       SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER = 0x0,
 };
 
 struct scmi_power_state_changed_report {
@@ -387,6 +398,13 @@ struct scmi_power_state_changed_report {
        unsigned int    power_state;
 };
 
+struct scmi_system_power_state_notifier_report {
+       ktime_t         timestamp;
+       unsigned int    agent_id;
+       unsigned int    flags;
+       unsigned int    system_state;
+};
+
 struct scmi_perf_limits_report {
        ktime_t         timestamp;
        unsigned int    agent_id;