cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

power.c (8149B)


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