1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) System Power Protocol 4 * 5 * Copyright (C) 2020-2022 ARM Ltd. 6 */ 7 8 #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt 9 10 #include <linux/module.h> 11 #include <linux/scmi_protocol.h> 12 13 #include "protocols.h" 14 #include "notify.h" 15 16 /* Updated only after ALL the mandatory features for that version are merged */ 17 #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001 18 19 #define SCMI_SYSTEM_NUM_SOURCES 1 20 21 enum scmi_system_protocol_cmd { 22 SYSTEM_POWER_STATE_NOTIFY = 0x5, 23 }; 24 25 struct scmi_system_power_state_notify { 26 __le32 notify_enable; 27 }; 28 29 struct scmi_system_power_state_notifier_payld { 30 __le32 agent_id; 31 __le32 flags; 32 __le32 system_state; 33 __le32 timeout; 34 }; 35 36 struct scmi_system_info { 37 u32 version; 38 bool graceful_timeout_supported; 39 bool power_state_notify_cmd; 40 }; 41 42 static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph, 43 u8 evt_id, u32 src_id) 44 { 45 struct scmi_system_info *pinfo = ph->get_priv(ph); 46 47 if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER) 48 return false; 49 50 return pinfo->power_state_notify_cmd; 51 } 52 53 static int scmi_system_request_notify(const struct scmi_protocol_handle *ph, 54 bool enable) 55 { 56 int ret; 57 struct scmi_xfer *t; 58 struct scmi_system_power_state_notify *notify; 59 60 ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY, 61 sizeof(*notify), 0, &t); 62 if (ret) 63 return ret; 64 65 notify = t->tx.buf; 66 notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; 67 68 ret = ph->xops->do_xfer(ph, t); 69 70 ph->xops->xfer_put(ph, t); 71 return ret; 72 } 73 74 static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph, 75 u8 evt_id, u32 src_id, bool enable) 76 { 77 int ret; 78 79 ret = scmi_system_request_notify(ph, enable); 80 if (ret) 81 pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret); 82 83 return ret; 84 } 85 86 static void * 87 scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph, 88 u8 evt_id, ktime_t timestamp, 89 const void *payld, size_t payld_sz, 90 void *report, u32 *src_id) 91 { 92 size_t expected_sz; 93 const struct scmi_system_power_state_notifier_payld *p = payld; 94 struct scmi_system_power_state_notifier_report *r = report; 95 struct scmi_system_info *pinfo = ph->get_priv(ph); 96 97 expected_sz = pinfo->graceful_timeout_supported ? 98 sizeof(*p) : sizeof(*p) - sizeof(__le32); 99 if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER || 100 payld_sz != expected_sz) 101 return NULL; 102 103 r->timestamp = timestamp; 104 r->agent_id = le32_to_cpu(p->agent_id); 105 r->flags = le32_to_cpu(p->flags); 106 r->system_state = le32_to_cpu(p->system_state); 107 if (pinfo->graceful_timeout_supported && 108 r->system_state == SCMI_SYSTEM_SHUTDOWN && 109 SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags)) 110 r->timeout = le32_to_cpu(p->timeout); 111 else 112 r->timeout = 0x00; 113 *src_id = 0; 114 115 return r; 116 } 117 118 static const struct scmi_event system_events[] = { 119 { 120 .id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER, 121 .max_payld_sz = 122 sizeof(struct scmi_system_power_state_notifier_payld), 123 .max_report_sz = 124 sizeof(struct scmi_system_power_state_notifier_report), 125 }, 126 }; 127 128 static const struct scmi_event_ops system_event_ops = { 129 .is_notify_supported = scmi_system_notify_supported, 130 .set_notify_enabled = scmi_system_set_notify_enabled, 131 .fill_custom_report = scmi_system_fill_custom_report, 132 }; 133 134 static const struct scmi_protocol_events system_protocol_events = { 135 .queue_sz = SCMI_PROTO_QUEUE_SZ, 136 .ops = &system_event_ops, 137 .evts = system_events, 138 .num_events = ARRAY_SIZE(system_events), 139 .num_sources = SCMI_SYSTEM_NUM_SOURCES, 140 }; 141 142 static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) 143 { 144 int ret; 145 u32 version; 146 struct scmi_system_info *pinfo; 147 148 ret = ph->xops->version_get(ph, &version); 149 if (ret) 150 return ret; 151 152 dev_dbg(ph->dev, "System Power Version %d.%d\n", 153 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 154 155 pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 156 if (!pinfo) 157 return -ENOMEM; 158 159 pinfo->version = version; 160 if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2) 161 pinfo->graceful_timeout_supported = true; 162 163 if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL)) 164 pinfo->power_state_notify_cmd = true; 165 166 return ph->set_priv(ph, pinfo, version); 167 } 168 169 static const struct scmi_protocol scmi_system = { 170 .id = SCMI_PROTOCOL_SYSTEM, 171 .owner = THIS_MODULE, 172 .instance_init = &scmi_system_protocol_init, 173 .ops = NULL, 174 .events = &system_protocol_events, 175 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, 176 }; 177 178 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system) 179