brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 59aa164 Raw
371 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) Power Protocol4 *5 * Copyright (C) 2018-2022 ARM Ltd.6 */7 8#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt9 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		0x3000118 19enum scmi_power_protocol_cmd {20	POWER_DOMAIN_ATTRIBUTES = 0x3,21	POWER_STATE_SET = 0x4,22	POWER_STATE_GET = 0x5,23	POWER_STATE_NOTIFY = 0x6,24	POWER_DOMAIN_NAME_GET = 0x8,25};26 27struct scmi_msg_resp_power_attributes {28	__le16 num_domains;29	__le16 reserved;30	__le32 stats_addr_low;31	__le32 stats_addr_high;32	__le32 stats_size;33};34 35struct scmi_msg_resp_power_domain_attributes {36	__le32 flags;37#define SUPPORTS_STATE_SET_NOTIFY(x)	((x) & BIT(31))38#define SUPPORTS_STATE_SET_ASYNC(x)	((x) & BIT(30))39#define SUPPORTS_STATE_SET_SYNC(x)	((x) & BIT(29))40#define SUPPORTS_EXTENDED_NAMES(x)	((x) & BIT(27))41	    u8 name[SCMI_SHORT_NAME_MAX_SIZE];42};43 44struct scmi_power_set_state {45	__le32 flags;46#define STATE_SET_ASYNC		BIT(0)47	__le32 domain;48	__le32 state;49};50 51struct scmi_power_state_notify {52	__le32 domain;53	__le32 notify_enable;54};55 56struct scmi_power_state_notify_payld {57	__le32 agent_id;58	__le32 domain_id;59	__le32 power_state;60};61 62struct power_dom_info {63	bool state_set_sync;64	bool state_set_async;65	bool state_set_notify;66	char name[SCMI_MAX_STR_SIZE];67};68 69struct scmi_power_info {70	u32 version;71	bool notify_state_change_cmd;72	int num_domains;73	u64 stats_addr;74	u32 stats_size;75	struct power_dom_info *dom_info;76};77 78static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,79				     struct scmi_power_info *pi)80{81	int ret;82	struct scmi_xfer *t;83	struct scmi_msg_resp_power_attributes *attr;84 85	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,86				      0, sizeof(*attr), &t);87	if (ret)88		return ret;89 90	attr = t->rx.buf;91 92	ret = ph->xops->do_xfer(ph, t);93	if (!ret) {94		pi->num_domains = le16_to_cpu(attr->num_domains);95		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |96				(u64)le32_to_cpu(attr->stats_addr_high) << 32;97		pi->stats_size = le32_to_cpu(attr->stats_size);98	}99 100	ph->xops->xfer_put(ph, t);101 102	if (!ret)103		if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))104			pi->notify_state_change_cmd = true;105 106	return ret;107}108 109static int110scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,111				 u32 domain, struct power_dom_info *dom_info,112				 u32 version, bool notify_state_change_cmd)113{114	int ret;115	u32 flags;116	struct scmi_xfer *t;117	struct scmi_msg_resp_power_domain_attributes *attr;118 119	ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,120				      sizeof(domain), sizeof(*attr), &t);121	if (ret)122		return ret;123 124	put_unaligned_le32(domain, t->tx.buf);125	attr = t->rx.buf;126 127	ret = ph->xops->do_xfer(ph, t);128	if (!ret) {129		flags = le32_to_cpu(attr->flags);130 131		if (notify_state_change_cmd)132			dom_info->state_set_notify =133				SUPPORTS_STATE_SET_NOTIFY(flags);134		dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);135		dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);136		strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);137	}138	ph->xops->xfer_put(ph, t);139 140	/*141	 * If supported overwrite short name with the extended one;142	 * on error just carry on and use already provided short name.143	 */144	if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&145	    SUPPORTS_EXTENDED_NAMES(flags)) {146		ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,147					    domain, NULL, dom_info->name,148					    SCMI_MAX_STR_SIZE);149	}150 151	return ret;152}153 154static int scmi_power_state_set(const struct scmi_protocol_handle *ph,155				u32 domain, u32 state)156{157	int ret;158	struct scmi_xfer *t;159	struct scmi_power_set_state *st;160 161	ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);162	if (ret)163		return ret;164 165	st = t->tx.buf;166	st->flags = cpu_to_le32(0);167	st->domain = cpu_to_le32(domain);168	st->state = cpu_to_le32(state);169 170	ret = ph->xops->do_xfer(ph, t);171 172	ph->xops->xfer_put(ph, t);173	return ret;174}175 176static int scmi_power_state_get(const struct scmi_protocol_handle *ph,177				u32 domain, u32 *state)178{179	int ret;180	struct scmi_xfer *t;181 182	ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);183	if (ret)184		return ret;185 186	put_unaligned_le32(domain, t->tx.buf);187 188	ret = ph->xops->do_xfer(ph, t);189	if (!ret)190		*state = get_unaligned_le32(t->rx.buf);191 192	ph->xops->xfer_put(ph, t);193	return ret;194}195 196static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)197{198	struct scmi_power_info *pi = ph->get_priv(ph);199 200	return pi->num_domains;201}202 203static const char *204scmi_power_name_get(const struct scmi_protocol_handle *ph,205		    u32 domain)206{207	struct scmi_power_info *pi = ph->get_priv(ph);208	struct power_dom_info *dom = pi->dom_info + domain;209 210	return dom->name;211}212 213static const struct scmi_power_proto_ops power_proto_ops = {214	.num_domains_get = scmi_power_num_domains_get,215	.name_get = scmi_power_name_get,216	.state_set = scmi_power_state_set,217	.state_get = scmi_power_state_get,218};219 220static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,221				     u32 domain, bool enable)222{223	int ret;224	struct scmi_xfer *t;225	struct scmi_power_state_notify *notify;226 227	ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY,228				      sizeof(*notify), 0, &t);229	if (ret)230		return ret;231 232	notify = t->tx.buf;233	notify->domain = cpu_to_le32(domain);234	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;235 236	ret = ph->xops->do_xfer(ph, t);237 238	ph->xops->xfer_put(ph, t);239	return ret;240}241 242static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,243					u8 evt_id, u32 src_id)244{245	struct power_dom_info *dom;246	struct scmi_power_info *pinfo = ph->get_priv(ph);247 248	if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||249	    src_id >= pinfo->num_domains)250		return false;251 252	dom = pinfo->dom_info + src_id;253	return dom->state_set_notify;254}255 256static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,257					 u8 evt_id, u32 src_id, bool enable)258{259	int ret;260 261	ret = scmi_power_request_notify(ph, src_id, enable);262	if (ret)263		pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",264			 evt_id, src_id, ret);265 266	return ret;267}268 269static void *270scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph,271			      u8 evt_id, ktime_t timestamp,272			      const void *payld, size_t payld_sz,273			      void *report, u32 *src_id)274{275	const struct scmi_power_state_notify_payld *p = payld;276	struct scmi_power_state_changed_report *r = report;277 278	if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)279		return NULL;280 281	r->timestamp = timestamp;282	r->agent_id = le32_to_cpu(p->agent_id);283	r->domain_id = le32_to_cpu(p->domain_id);284	r->power_state = le32_to_cpu(p->power_state);285	*src_id = r->domain_id;286 287	return r;288}289 290static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph)291{292	struct scmi_power_info *pinfo = ph->get_priv(ph);293 294	if (!pinfo)295		return -EINVAL;296 297	return pinfo->num_domains;298}299 300static const struct scmi_event power_events[] = {301	{302		.id = SCMI_EVENT_POWER_STATE_CHANGED,303		.max_payld_sz = sizeof(struct scmi_power_state_notify_payld),304		.max_report_sz =305			sizeof(struct scmi_power_state_changed_report),306	},307};308 309static const struct scmi_event_ops power_event_ops = {310	.is_notify_supported = scmi_power_notify_supported,311	.get_num_sources = scmi_power_get_num_sources,312	.set_notify_enabled = scmi_power_set_notify_enabled,313	.fill_custom_report = scmi_power_fill_custom_report,314};315 316static const struct scmi_protocol_events power_protocol_events = {317	.queue_sz = SCMI_PROTO_QUEUE_SZ,318	.ops = &power_event_ops,319	.evts = power_events,320	.num_events = ARRAY_SIZE(power_events),321};322 323static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)324{325	int domain, ret;326	u32 version;327	struct scmi_power_info *pinfo;328 329	ret = ph->xops->version_get(ph, &version);330	if (ret)331		return ret;332 333	dev_dbg(ph->dev, "Power Version %d.%d\n",334		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));335 336	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);337	if (!pinfo)338		return -ENOMEM;339 340	ret = scmi_power_attributes_get(ph, pinfo);341	if (ret)342		return ret;343 344	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,345				       sizeof(*pinfo->dom_info), GFP_KERNEL);346	if (!pinfo->dom_info)347		return -ENOMEM;348 349	for (domain = 0; domain < pinfo->num_domains; domain++) {350		struct power_dom_info *dom = pinfo->dom_info + domain;351 352		scmi_power_domain_attributes_get(ph, domain, dom, version,353						 pinfo->notify_state_change_cmd);354	}355 356	pinfo->version = version;357 358	return ph->set_priv(ph, pinfo, version);359}360 361static const struct scmi_protocol scmi_power = {362	.id = SCMI_PROTOCOL_POWER,363	.owner = THIS_MODULE,364	.instance_init = &scmi_power_protocol_init,365	.ops = &power_proto_ops,366	.events = &power_protocol_events,367	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,368};369 370DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)371