// SPDX-License-Identifier: GPL-2.0 /* * System Control and Management Interface (SCMI) System Power Protocol * * Copyright (C) 2020-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt #include #include #include "protocols.h" #include "notify.h" /* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001 #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; __le32 timeout; }; struct scmi_system_info { u32 version; bool graceful_timeout_supported; bool power_state_notify_cmd; }; static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id) { struct scmi_system_info *pinfo = ph->get_priv(ph); if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER) return false; return pinfo->power_state_notify_cmd; } static int scmi_system_request_notify(const struct scmi_protocol_handle *ph, bool enable) { int ret; struct scmi_xfer *t; struct scmi_system_power_state_notify *notify; ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY, sizeof(*notify), 0, &t); if (ret) return ret; notify = t->tx.buf; notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { int ret; ret = scmi_system_request_notify(ph, 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_protocol_handle *ph, u8 evt_id, ktime_t timestamp, const void *payld, size_t payld_sz, void *report, u32 *src_id) { size_t expected_sz; const struct scmi_system_power_state_notifier_payld *p = payld; struct scmi_system_power_state_notifier_report *r = report; struct scmi_system_info *pinfo = ph->get_priv(ph); expected_sz = pinfo->graceful_timeout_supported ? sizeof(*p) : sizeof(*p) - sizeof(__le32); if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER || payld_sz != expected_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); if (pinfo->graceful_timeout_supported && r->system_state == SCMI_SYSTEM_SHUTDOWN && SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags)) r->timeout = le32_to_cpu(p->timeout); else r->timeout = 0x00; *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 = { .is_notify_supported = scmi_system_notify_supported, .set_notify_enabled = scmi_system_set_notify_enabled, .fill_custom_report = scmi_system_fill_custom_report, }; static const struct scmi_protocol_events system_protocol_events = { .queue_sz = SCMI_PROTO_QUEUE_SZ, .ops = &system_event_ops, .evts = system_events, .num_events = ARRAY_SIZE(system_events), .num_sources = SCMI_SYSTEM_NUM_SOURCES, }; static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) { int ret; u32 version; struct scmi_system_info *pinfo; ret = ph->xops->version_get(ph, &version); if (ret) return ret; dev_dbg(ph->dev, "System Power Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; pinfo->version = version; if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2) pinfo->graceful_timeout_supported = true; if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL)) pinfo->power_state_notify_cmd = true; return ph->set_priv(ph, pinfo, version); } static const struct scmi_protocol scmi_system = { .id = SCMI_PROTOCOL_SYSTEM, .owner = THIS_MODULE, .instance_init = &scmi_system_protocol_init, .ops = NULL, .events = &system_protocol_events, .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, }; DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)