power.c 7.4 KB
Newer Older
1 2 3 4
// SPDX-License-Identifier: GPL-2.0
/*
 * System Control and Management Interface (SCMI) Power Protocol
 *
5
 * Copyright (C) 2018-2021 ARM Ltd.
6 7
 */

8 9
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt

10
#include <linux/module.h>
11 12
#include <linux/scmi_protocol.h>

13
#include "common.h"
14
#include "notify.h"
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

enum scmi_power_protocol_cmd {
	POWER_DOMAIN_ATTRIBUTES = 0x3,
	POWER_STATE_SET = 0x4,
	POWER_STATE_GET = 0x5,
	POWER_STATE_NOTIFY = 0x6,
};

struct scmi_msg_resp_power_attributes {
	__le16 num_domains;
	__le16 reserved;
	__le32 stats_addr_low;
	__le32 stats_addr_high;
	__le32 stats_size;
};

struct scmi_msg_resp_power_domain_attributes {
	__le32 flags;
#define SUPPORTS_STATE_SET_NOTIFY(x)	((x) & BIT(31))
#define SUPPORTS_STATE_SET_ASYNC(x)	((x) & BIT(30))
#define SUPPORTS_STATE_SET_SYNC(x)	((x) & BIT(29))
	    u8 name[SCMI_MAX_STR_SIZE];
};

struct scmi_power_set_state {
	__le32 flags;
#define STATE_SET_ASYNC		BIT(0)
	__le32 domain;
	__le32 state;
};

struct scmi_power_state_notify {
	__le32 domain;
	__le32 notify_enable;
};

51 52 53 54 55 56
struct scmi_power_state_notify_payld {
	__le32 agent_id;
	__le32 domain_id;
	__le32 power_state;
};

57 58 59 60 61 62 63 64
struct power_dom_info {
	bool state_set_sync;
	bool state_set_async;
	bool state_set_notify;
	char name[SCMI_MAX_STR_SIZE];
};

struct scmi_power_info {
65
	u32 version;
66 67 68 69 70 71
	int num_domains;
	u64 stats_addr;
	u32 stats_size;
	struct power_dom_info *dom_info;
};

72
static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
73 74 75 76 77 78
				     struct scmi_power_info *pi)
{
	int ret;
	struct scmi_xfer *t;
	struct scmi_msg_resp_power_attributes *attr;

79 80
	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
				      0, sizeof(*attr), &t);
81 82 83 84 85
	if (ret)
		return ret;

	attr = t->rx.buf;

86
	ret = ph->xops->do_xfer(ph, t);
87 88 89 90 91 92 93
	if (!ret) {
		pi->num_domains = le16_to_cpu(attr->num_domains);
		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
		pi->stats_size = le32_to_cpu(attr->stats_size);
	}

94
	ph->xops->xfer_put(ph, t);
95 96 97 98
	return ret;
}

static int
99 100
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
				 u32 domain, struct power_dom_info *dom_info)
101 102 103 104 105
{
	int ret;
	struct scmi_xfer *t;
	struct scmi_msg_resp_power_domain_attributes *attr;

106 107
	ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,
				      sizeof(domain), sizeof(*attr), &t);
108 109 110
	if (ret)
		return ret;

111
	put_unaligned_le32(domain, t->tx.buf);
112 113
	attr = t->rx.buf;

114
	ret = ph->xops->do_xfer(ph, t);
115 116 117 118 119 120
	if (!ret) {
		u32 flags = le32_to_cpu(attr->flags);

		dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
		dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
		dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
121
		strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
122 123
	}

124
	ph->xops->xfer_put(ph, t);
125 126 127
	return ret;
}

128 129
static int scmi_power_state_set(const struct scmi_protocol_handle *ph,
				u32 domain, u32 state)
130 131 132 133 134
{
	int ret;
	struct scmi_xfer *t;
	struct scmi_power_set_state *st;

135
	ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);
136 137 138 139 140 141 142 143
	if (ret)
		return ret;

	st = t->tx.buf;
	st->flags = cpu_to_le32(0);
	st->domain = cpu_to_le32(domain);
	st->state = cpu_to_le32(state);

144
	ret = ph->xops->do_xfer(ph, t);
145

146
	ph->xops->xfer_put(ph, t);
147 148 149
	return ret;
}

150 151
static int scmi_power_state_get(const struct scmi_protocol_handle *ph,
				u32 domain, u32 *state)
152 153 154 155
{
	int ret;
	struct scmi_xfer *t;

156
	ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);
157 158 159
	if (ret)
		return ret;

160
	put_unaligned_le32(domain, t->tx.buf);
161

162
	ret = ph->xops->do_xfer(ph, t);
163
	if (!ret)
164
		*state = get_unaligned_le32(t->rx.buf);
165

166
	ph->xops->xfer_put(ph, t);
167 168 169
	return ret;
}

170 171 172
static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
{
	struct scmi_power_info *pi = ph->get_priv(ph);
173 174 175 176

	return pi->num_domains;
}

177 178 179 180
static char *scmi_power_name_get(const struct scmi_protocol_handle *ph,
				 u32 domain)
{
	struct scmi_power_info *pi = ph->get_priv(ph);
181 182 183 184 185
	struct power_dom_info *dom = pi->dom_info + domain;

	return dom->name;
}

186
static const struct scmi_power_proto_ops power_proto_ops = {
187 188 189 190 191 192
	.num_domains_get = scmi_power_num_domains_get,
	.name_get = scmi_power_name_get,
	.state_set = scmi_power_state_set,
	.state_get = scmi_power_state_get,
};

193
static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
194 195 196 197 198 199
				     u32 domain, bool enable)
{
	int ret;
	struct scmi_xfer *t;
	struct scmi_power_state_notify *notify;

200 201
	ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY,
				      sizeof(*notify), 0, &t);
202 203 204 205 206 207 208
	if (ret)
		return ret;

	notify = t->tx.buf;
	notify->domain = cpu_to_le32(domain);
	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;

209
	ret = ph->xops->do_xfer(ph, t);
210

211
	ph->xops->xfer_put(ph, t);
212 213 214
	return ret;
}

215
static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
216 217 218 219
					 u8 evt_id, u32 src_id, bool enable)
{
	int ret;

220
	ret = scmi_power_request_notify(ph, src_id, enable);
221 222 223 224 225 226 227
	if (ret)
		pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
			 evt_id, src_id, ret);

	return ret;
}

228 229 230 231 232
static void *
scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph,
			      u8 evt_id, ktime_t timestamp,
			      const void *payld, size_t payld_sz,
			      void *report, u32 *src_id)
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
{
	const struct scmi_power_state_notify_payld *p = payld;
	struct scmi_power_state_changed_report *r = report;

	if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)
		return NULL;

	r->timestamp = timestamp;
	r->agent_id = le32_to_cpu(p->agent_id);
	r->domain_id = le32_to_cpu(p->domain_id);
	r->power_state = le32_to_cpu(p->power_state);
	*src_id = r->domain_id;

	return r;
}

249
static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph)
250
{
251
	struct scmi_power_info *pinfo = ph->get_priv(ph);
252 253 254 255 256 257 258

	if (!pinfo)
		return -EINVAL;

	return pinfo->num_domains;
}

259 260 261 262 263 264 265 266 267 268
static const struct scmi_event power_events[] = {
	{
		.id = SCMI_EVENT_POWER_STATE_CHANGED,
		.max_payld_sz = sizeof(struct scmi_power_state_notify_payld),
		.max_report_sz =
			sizeof(struct scmi_power_state_changed_report),
	},
};

static const struct scmi_event_ops power_event_ops = {
269
	.get_num_sources = scmi_power_get_num_sources,
270 271 272 273
	.set_notify_enabled = scmi_power_set_notify_enabled,
	.fill_custom_report = scmi_power_fill_custom_report,
};

274 275 276 277 278 279 280
static const struct scmi_protocol_events power_protocol_events = {
	.queue_sz = SCMI_PROTO_QUEUE_SZ,
	.ops = &power_event_ops,
	.evts = power_events,
	.num_events = ARRAY_SIZE(power_events),
};

281
static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
282 283 284 285 286
{
	int domain;
	u32 version;
	struct scmi_power_info *pinfo;

287
	ph->xops->version_get(ph, &version);
288

289
	dev_dbg(ph->dev, "Power Version %d.%d\n",
290 291
		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));

292
	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
293 294 295
	if (!pinfo)
		return -ENOMEM;

296
	scmi_power_attributes_get(ph, pinfo);
297

298
	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
299 300 301 302 303 304 305
				       sizeof(*pinfo->dom_info), GFP_KERNEL);
	if (!pinfo->dom_info)
		return -ENOMEM;

	for (domain = 0; domain < pinfo->num_domains; domain++) {
		struct power_dom_info *dom = pinfo->dom_info + domain;

306
		scmi_power_domain_attributes_get(ph, domain, dom);
307 308
	}

309
	pinfo->version = version;
310 311

	return ph->set_priv(ph, pinfo);
312 313
}

314 315
static const struct scmi_protocol scmi_power = {
	.id = SCMI_PROTOCOL_POWER,
316
	.owner = THIS_MODULE,
317 318
	.instance_init = &scmi_power_protocol_init,
	.ops = &power_proto_ops,
319
	.events = &power_protocol_events,
320 321 322
};

DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)