Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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		0x20000
 18
 19#define SCMI_SYSTEM_NUM_SOURCES		1
 20
 21enum scmi_system_protocol_cmd {
 22	SYSTEM_POWER_STATE_NOTIFY = 0x5,
 23};
 24
 25struct scmi_system_power_state_notify {
 26	__le32 notify_enable;
 27};
 28
 29struct scmi_system_power_state_notifier_payld {
 30	__le32 agent_id;
 31	__le32 flags;
 32	__le32 system_state;
 33	__le32 timeout;
 34};
 35
 36struct scmi_system_info {
 37	u32 version;
 38	bool graceful_timeout_supported;
 39};
 40
 41static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
 42				      bool enable)
 43{
 44	int ret;
 45	struct scmi_xfer *t;
 46	struct scmi_system_power_state_notify *notify;
 47
 48	ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY,
 49				      sizeof(*notify), 0, &t);
 50	if (ret)
 51		return ret;
 52
 53	notify = t->tx.buf;
 54	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
 55
 56	ret = ph->xops->do_xfer(ph, t);
 57
 58	ph->xops->xfer_put(ph, t);
 59	return ret;
 60}
 61
 62static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph,
 63					  u8 evt_id, u32 src_id, bool enable)
 64{
 65	int ret;
 66
 67	ret = scmi_system_request_notify(ph, enable);
 68	if (ret)
 69		pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
 70
 71	return ret;
 72}
 73
 74static void *
 75scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph,
 76			       u8 evt_id, ktime_t timestamp,
 77			       const void *payld, size_t payld_sz,
 78			       void *report, u32 *src_id)
 79{
 80	size_t expected_sz;
 81	const struct scmi_system_power_state_notifier_payld *p = payld;
 82	struct scmi_system_power_state_notifier_report *r = report;
 83	struct scmi_system_info *pinfo = ph->get_priv(ph);
 84
 85	expected_sz = pinfo->graceful_timeout_supported ?
 86			sizeof(*p) : sizeof(*p) - sizeof(__le32);
 87	if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
 88	    payld_sz != expected_sz)
 89		return NULL;
 90
 91	r->timestamp = timestamp;
 92	r->agent_id = le32_to_cpu(p->agent_id);
 93	r->flags = le32_to_cpu(p->flags);
 94	r->system_state = le32_to_cpu(p->system_state);
 95	if (pinfo->graceful_timeout_supported &&
 96	    r->system_state == SCMI_SYSTEM_SHUTDOWN &&
 97	    SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags))
 98		r->timeout = le32_to_cpu(p->timeout);
 99	else
100		r->timeout = 0x00;
101	*src_id = 0;
102
103	return r;
104}
105
106static const struct scmi_event system_events[] = {
107	{
108		.id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
109		.max_payld_sz =
110			sizeof(struct scmi_system_power_state_notifier_payld),
111		.max_report_sz =
112			sizeof(struct scmi_system_power_state_notifier_report),
113	},
114};
115
116static const struct scmi_event_ops system_event_ops = {
117	.set_notify_enabled = scmi_system_set_notify_enabled,
118	.fill_custom_report = scmi_system_fill_custom_report,
119};
120
121static const struct scmi_protocol_events system_protocol_events = {
122	.queue_sz = SCMI_PROTO_QUEUE_SZ,
123	.ops = &system_event_ops,
124	.evts = system_events,
125	.num_events = ARRAY_SIZE(system_events),
126	.num_sources = SCMI_SYSTEM_NUM_SOURCES,
127};
128
129static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
130{
131	int ret;
132	u32 version;
133	struct scmi_system_info *pinfo;
134
135	ret = ph->xops->version_get(ph, &version);
136	if (ret)
137		return ret;
138
139	dev_dbg(ph->dev, "System Power Version %d.%d\n",
140		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
141
142	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
143	if (!pinfo)
144		return -ENOMEM;
145
146	pinfo->version = version;
147	if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
148		pinfo->graceful_timeout_supported = true;
149
150	return ph->set_priv(ph, pinfo, version);
151}
152
153static const struct scmi_protocol scmi_system = {
154	.id = SCMI_PROTOCOL_SYSTEM,
155	.owner = THIS_MODULE,
156	.instance_init = &scmi_system_protocol_init,
157	.ops = NULL,
158	.events = &system_protocol_events,
159	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
160};
161
162DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)