1// SPDX-License-Identifier: GPL-2.0
2/*
3 * System Control and Management Interface (SCMI) System Power Protocol
4 *
5 * Copyright (C) 2020 ARM Ltd.
6 */
7
8#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
9
10#include <linux/scmi_protocol.h>
11
12#include "common.h"
13#include "notify.h"
14
15#define SCMI_SYSTEM_NUM_SOURCES		1
16
17enum scmi_system_protocol_cmd {
18	SYSTEM_POWER_STATE_NOTIFY = 0x5,
19};
20
21struct scmi_system_power_state_notify {
22	__le32 notify_enable;
23};
24
25struct scmi_system_power_state_notifier_payld {
26	__le32 agent_id;
27	__le32 flags;
28	__le32 system_state;
29};
30
31struct scmi_system_info {
32	u32 version;
33};
34
35static int scmi_system_request_notify(const struct scmi_handle *handle,
36				      bool enable)
37{
38	int ret;
39	struct scmi_xfer *t;
40	struct scmi_system_power_state_notify *notify;
41
42	ret = scmi_xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY,
43				 SCMI_PROTOCOL_SYSTEM, sizeof(*notify), 0, &t);
44	if (ret)
45		return ret;
46
47	notify = t->tx.buf;
48	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
49
50	ret = scmi_do_xfer(handle, t);
51
52	scmi_xfer_put(handle, t);
53	return ret;
54}
55
56static int scmi_system_set_notify_enabled(const struct scmi_handle *handle,
57					  u8 evt_id, u32 src_id, bool enable)
58{
59	int ret;
60
61	ret = scmi_system_request_notify(handle, enable);
62	if (ret)
63		pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
64
65	return ret;
66}
67
68static void *scmi_system_fill_custom_report(const struct scmi_handle *handle,
69					    u8 evt_id, ktime_t timestamp,
70					    const void *payld, size_t payld_sz,
71					    void *report, u32 *src_id)
72{
73	const struct scmi_system_power_state_notifier_payld *p = payld;
74	struct scmi_system_power_state_notifier_report *r = report;
75
76	if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
77	    sizeof(*p) != payld_sz)
78		return NULL;
79
80	r->timestamp = timestamp;
81	r->agent_id = le32_to_cpu(p->agent_id);
82	r->flags = le32_to_cpu(p->flags);
83	r->system_state = le32_to_cpu(p->system_state);
84	*src_id = 0;
85
86	return r;
87}
88
89static const struct scmi_event system_events[] = {
90	{
91		.id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
92		.max_payld_sz =
93			sizeof(struct scmi_system_power_state_notifier_payld),
94		.max_report_sz =
95			sizeof(struct scmi_system_power_state_notifier_report),
96	},
97};
98
99static const struct scmi_event_ops system_event_ops = {
100	.set_notify_enabled = scmi_system_set_notify_enabled,
101	.fill_custom_report = scmi_system_fill_custom_report,
102};
103
104static int scmi_system_protocol_init(struct scmi_handle *handle)
105{
106	u32 version;
107	struct scmi_system_info *pinfo;
108
109	scmi_version_get(handle, SCMI_PROTOCOL_SYSTEM, &version);
110
111	dev_dbg(handle->dev, "System Power Version %d.%d\n",
112		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
113
114	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
115	if (!pinfo)
116		return -ENOMEM;
117
118	scmi_register_protocol_events(handle,
119				      SCMI_PROTOCOL_SYSTEM, SCMI_PROTO_QUEUE_SZ,
120				      &system_event_ops,
121				      system_events,
122				      ARRAY_SIZE(system_events),
123				      SCMI_SYSTEM_NUM_SOURCES);
124
125	pinfo->version = version;
126	handle->system_priv = pinfo;
127
128	return 0;
129}
130
131DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_SYSTEM, system)
132