system.c 3.2 KB
Newer Older
1 2 3 4
// SPDX-License-Identifier: GPL-2.0
/*
 * System Control and Management Interface (SCMI) System Power Protocol
 *
5
 * Copyright (C) 2020-2021 ARM Ltd.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
 */

#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;
}

131 132 133 134 135 136 137
static const struct scmi_protocol scmi_system = {
	.id = SCMI_PROTOCOL_SYSTEM,
	.init = &scmi_system_protocol_init,
	.ops = NULL,
};

DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)