brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · 0aa82b9 Raw
383 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) Reset Protocol4 *5 * Copyright (C) 2019-2022 ARM Ltd.6 */7 8#define pr_fmt(fmt) "SCMI Notifications RESET - " 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_reset_protocol_cmd {20	RESET_DOMAIN_ATTRIBUTES = 0x3,21	RESET = 0x4,22	RESET_NOTIFY = 0x5,23	RESET_DOMAIN_NAME_GET = 0x6,24};25 26#define NUM_RESET_DOMAIN_MASK	0xffff27#define RESET_NOTIFY_ENABLE	BIT(0)28 29struct scmi_msg_resp_reset_domain_attributes {30	__le32 attributes;31#define SUPPORTS_ASYNC_RESET(x)		((x) & BIT(31))32#define SUPPORTS_NOTIFY_RESET(x)	((x) & BIT(30))33#define SUPPORTS_EXTENDED_NAMES(x)	((x) & BIT(29))34	__le32 latency;35	u8 name[SCMI_SHORT_NAME_MAX_SIZE];36};37 38struct scmi_msg_reset_domain_reset {39	__le32 domain_id;40	__le32 flags;41#define AUTONOMOUS_RESET	BIT(0)42#define EXPLICIT_RESET_ASSERT	BIT(1)43#define ASYNCHRONOUS_RESET	BIT(2)44	__le32 reset_state;45#define ARCH_COLD_RESET		046};47 48struct scmi_msg_reset_notify {49	__le32 id;50	__le32 event_control;51#define RESET_TP_NOTIFY_ALL	BIT(0)52};53 54struct scmi_reset_issued_notify_payld {55	__le32 agent_id;56	__le32 domain_id;57	__le32 reset_state;58};59 60struct reset_dom_info {61	bool async_reset;62	bool reset_notify;63	u32 latency_us;64	char name[SCMI_MAX_STR_SIZE];65};66 67struct scmi_reset_info {68	u32 version;69	int num_domains;70	bool notify_reset_cmd;71	struct reset_dom_info *dom_info;72};73 74static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,75				     struct scmi_reset_info *pi)76{77	int ret;78	struct scmi_xfer *t;79	u32 attr;80 81	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,82				      0, sizeof(attr), &t);83	if (ret)84		return ret;85 86	ret = ph->xops->do_xfer(ph, t);87	if (!ret) {88		attr = get_unaligned_le32(t->rx.buf);89		pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;90	}91 92	ph->xops->xfer_put(ph, t);93 94	if (!ret)95		if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))96			pi->notify_reset_cmd = true;97 98	return ret;99}100 101static int102scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,103				 struct scmi_reset_info *pinfo,104				 u32 domain, u32 version)105{106	int ret;107	u32 attributes;108	struct scmi_xfer *t;109	struct scmi_msg_resp_reset_domain_attributes *attr;110	struct reset_dom_info *dom_info = pinfo->dom_info + domain;111 112	ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,113				      sizeof(domain), sizeof(*attr), &t);114	if (ret)115		return ret;116 117	put_unaligned_le32(domain, t->tx.buf);118	attr = t->rx.buf;119 120	ret = ph->xops->do_xfer(ph, t);121	if (!ret) {122		attributes = le32_to_cpu(attr->attributes);123 124		dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);125		if (pinfo->notify_reset_cmd)126			dom_info->reset_notify =127				SUPPORTS_NOTIFY_RESET(attributes);128		dom_info->latency_us = le32_to_cpu(attr->latency);129		if (dom_info->latency_us == U32_MAX)130			dom_info->latency_us = 0;131		strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);132	}133 134	ph->xops->xfer_put(ph, t);135 136	/*137	 * If supported overwrite short name with the extended one;138	 * on error just carry on and use already provided short name.139	 */140	if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&141	    SUPPORTS_EXTENDED_NAMES(attributes))142		ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,143					    NULL, dom_info->name,144					    SCMI_MAX_STR_SIZE);145 146	return ret;147}148 149static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)150{151	struct scmi_reset_info *pi = ph->get_priv(ph);152 153	return pi->num_domains;154}155 156static const char *157scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)158{159	struct scmi_reset_info *pi = ph->get_priv(ph);160 161	struct reset_dom_info *dom = pi->dom_info + domain;162 163	return dom->name;164}165 166static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,167				  u32 domain)168{169	struct scmi_reset_info *pi = ph->get_priv(ph);170	struct reset_dom_info *dom = pi->dom_info + domain;171 172	return dom->latency_us;173}174 175static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,176			     u32 flags, u32 state)177{178	int ret;179	struct scmi_xfer *t;180	struct scmi_msg_reset_domain_reset *dom;181	struct scmi_reset_info *pi = ph->get_priv(ph);182	struct reset_dom_info *rdom;183 184	if (domain >= pi->num_domains)185		return -EINVAL;186 187	rdom = pi->dom_info + domain;188	if (rdom->async_reset && flags & AUTONOMOUS_RESET)189		flags |= ASYNCHRONOUS_RESET;190 191	ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);192	if (ret)193		return ret;194 195	dom = t->tx.buf;196	dom->domain_id = cpu_to_le32(domain);197	dom->flags = cpu_to_le32(flags);198	dom->reset_state = cpu_to_le32(state);199 200	if (flags & ASYNCHRONOUS_RESET)201		ret = ph->xops->do_xfer_with_response(ph, t);202	else203		ret = ph->xops->do_xfer(ph, t);204 205	ph->xops->xfer_put(ph, t);206	return ret;207}208 209static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph,210				   u32 domain)211{212	return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET,213				 ARCH_COLD_RESET);214}215 216static int217scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain)218{219	return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT,220				 ARCH_COLD_RESET);221}222 223static int224scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain)225{226	return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET);227}228 229static const struct scmi_reset_proto_ops reset_proto_ops = {230	.num_domains_get = scmi_reset_num_domains_get,231	.name_get = scmi_reset_name_get,232	.latency_get = scmi_reset_latency_get,233	.reset = scmi_reset_domain_reset,234	.assert = scmi_reset_domain_assert,235	.deassert = scmi_reset_domain_deassert,236};237 238static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,239					u8 evt_id, u32 src_id)240{241	struct reset_dom_info *dom;242	struct scmi_reset_info *pi = ph->get_priv(ph);243 244	if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)245		return false;246 247	dom = pi->dom_info + src_id;248 249	return dom->reset_notify;250}251 252static int scmi_reset_notify(const struct scmi_protocol_handle *ph,253			     u32 domain_id, bool enable)254{255	int ret;256	u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;257	struct scmi_xfer *t;258	struct scmi_msg_reset_notify *cfg;259 260	ret = ph->xops->xfer_get_init(ph, RESET_NOTIFY, sizeof(*cfg), 0, &t);261	if (ret)262		return ret;263 264	cfg = t->tx.buf;265	cfg->id = cpu_to_le32(domain_id);266	cfg->event_control = cpu_to_le32(evt_cntl);267 268	ret = ph->xops->do_xfer(ph, t);269 270	ph->xops->xfer_put(ph, t);271	return ret;272}273 274static int scmi_reset_set_notify_enabled(const struct scmi_protocol_handle *ph,275					 u8 evt_id, u32 src_id, bool enable)276{277	int ret;278 279	ret = scmi_reset_notify(ph, src_id, enable);280	if (ret)281		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",282			 evt_id, src_id, ret);283 284	return ret;285}286 287static void *288scmi_reset_fill_custom_report(const struct scmi_protocol_handle *ph,289			      u8 evt_id, ktime_t timestamp,290			      const void *payld, size_t payld_sz,291			      void *report, u32 *src_id)292{293	const struct scmi_reset_issued_notify_payld *p = payld;294	struct scmi_reset_issued_report *r = report;295 296	if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz)297		return NULL;298 299	r->timestamp = timestamp;300	r->agent_id = le32_to_cpu(p->agent_id);301	r->domain_id = le32_to_cpu(p->domain_id);302	r->reset_state = le32_to_cpu(p->reset_state);303	*src_id = r->domain_id;304 305	return r;306}307 308static int scmi_reset_get_num_sources(const struct scmi_protocol_handle *ph)309{310	struct scmi_reset_info *pinfo = ph->get_priv(ph);311 312	if (!pinfo)313		return -EINVAL;314 315	return pinfo->num_domains;316}317 318static const struct scmi_event reset_events[] = {319	{320		.id = SCMI_EVENT_RESET_ISSUED,321		.max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld),322		.max_report_sz = sizeof(struct scmi_reset_issued_report),323	},324};325 326static const struct scmi_event_ops reset_event_ops = {327	.is_notify_supported = scmi_reset_notify_supported,328	.get_num_sources = scmi_reset_get_num_sources,329	.set_notify_enabled = scmi_reset_set_notify_enabled,330	.fill_custom_report = scmi_reset_fill_custom_report,331};332 333static const struct scmi_protocol_events reset_protocol_events = {334	.queue_sz = SCMI_PROTO_QUEUE_SZ,335	.ops = &reset_event_ops,336	.evts = reset_events,337	.num_events = ARRAY_SIZE(reset_events),338};339 340static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)341{342	int domain, ret;343	u32 version;344	struct scmi_reset_info *pinfo;345 346	ret = ph->xops->version_get(ph, &version);347	if (ret)348		return ret;349 350	dev_dbg(ph->dev, "Reset Version %d.%d\n",351		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));352 353	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);354	if (!pinfo)355		return -ENOMEM;356 357	ret = scmi_reset_attributes_get(ph, pinfo);358	if (ret)359		return ret;360 361	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,362				       sizeof(*pinfo->dom_info), GFP_KERNEL);363	if (!pinfo->dom_info)364		return -ENOMEM;365 366	for (domain = 0; domain < pinfo->num_domains; domain++)367		scmi_reset_domain_attributes_get(ph, pinfo, domain, version);368 369	pinfo->version = version;370	return ph->set_priv(ph, pinfo, version);371}372 373static const struct scmi_protocol scmi_reset = {374	.id = SCMI_PROTOCOL_RESET,375	.owner = THIS_MODULE,376	.instance_init = &scmi_reset_protocol_init,377	.ops = &reset_proto_ops,378	.events = &reset_protocol_events,379	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,380};381 382DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)383