Skip to content

Commit ac8aaf3

Browse files
committed
firmware: arm_scmi: Add discovery of SCMI v2.0 performance fastchannels
SCMI v2.0 adds support for "FastChannel", a lightweight unidirectional channel that is dedicated to a single SCMI message type for controlling a specific platform resource. They do not use a message header as they are specialized for a single message. Only PERFORMANCE_LIMITS_{SET,GET} and PERFORMANCE_LEVEL_{SET,GET} commands are supported over fastchannels. As they are optional, they need to be discovered by PERFORMANCE_DESCRIBE_FASTCHANNEL command. Further {LIMIT,LEVEL}_SET commands can have optional doorbell support. Add support for discovery of these fastchannels. Cc: Ionela Voinescu <[email protected]> Cc: Chris Redpath <[email protected]> Cc: Quentin Perret <[email protected]> Reviewed-by: Peng Fan <[email protected]> Signed-off-by: Sudeep Holla <[email protected]>
1 parent aa90ac4 commit ac8aaf3

File tree

1 file changed

+149
-4
lines changed
  • drivers/firmware/arm_scmi

1 file changed

+149
-4
lines changed

drivers/firmware/arm_scmi/perf.c

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
* Copyright (C) 2018 ARM Ltd.
66
*/
77

8+
#include <linux/bits.h>
89
#include <linux/of.h>
10+
#include <linux/io.h>
911
#include <linux/platform_device.h>
1012
#include <linux/pm_opp.h>
1113
#include <linux/sort.h>
@@ -21,6 +23,7 @@ enum scmi_performance_protocol_cmd {
2123
PERF_LEVEL_GET = 0x8,
2224
PERF_NOTIFY_LIMITS = 0x9,
2325
PERF_NOTIFY_LEVEL = 0xa,
26+
PERF_DESCRIBE_FASTCHANNEL = 0xb,
2427
};
2528

2629
struct scmi_opp {
@@ -44,6 +47,7 @@ struct scmi_msg_resp_perf_domain_attributes {
4447
#define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30))
4548
#define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
4649
#define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
50+
#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27))
4751
__le32 rate_limit_us;
4852
__le32 sustained_freq_khz;
4953
__le32 sustained_perf_level;
@@ -87,17 +91,56 @@ struct scmi_msg_resp_perf_describe_levels {
8791
} opp[0];
8892
};
8993

94+
struct scmi_perf_get_fc_info {
95+
__le32 domain;
96+
__le32 message_id;
97+
};
98+
99+
struct scmi_msg_resp_perf_desc_fc {
100+
__le32 attr;
101+
#define SUPPORTS_DOORBELL(x) ((x) & BIT(0))
102+
#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x))
103+
__le32 rate_limit;
104+
__le32 chan_addr_low;
105+
__le32 chan_addr_high;
106+
__le32 chan_size;
107+
__le32 db_addr_low;
108+
__le32 db_addr_high;
109+
__le32 db_set_lmask;
110+
__le32 db_set_hmask;
111+
__le32 db_preserve_lmask;
112+
__le32 db_preserve_hmask;
113+
};
114+
115+
struct scmi_fc_db_info {
116+
int width;
117+
u64 set;
118+
u64 mask;
119+
void __iomem *addr;
120+
};
121+
122+
struct scmi_fc_info {
123+
void __iomem *level_set_addr;
124+
void __iomem *limit_set_addr;
125+
void __iomem *level_get_addr;
126+
void __iomem *limit_get_addr;
127+
struct scmi_fc_db_info *level_set_db;
128+
struct scmi_fc_db_info *limit_set_db;
129+
};
130+
90131
struct perf_dom_info {
91132
bool set_limits;
92133
bool set_perf;
93134
bool perf_limit_notify;
94135
bool perf_level_notify;
136+
bool perf_fastchannels;
95137
u32 opp_count;
96138
u32 sustained_freq_khz;
97139
u32 sustained_perf_level;
98140
u32 mult_factor;
99141
char name[SCMI_MAX_STR_SIZE];
100142
struct scmi_opp opp[MAX_OPPS];
143+
struct scmi_fc_info *fc_info;
101144
};
102145

103146
struct scmi_perf_info {
@@ -162,6 +205,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
162205
dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
163206
dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
164207
dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
208+
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
165209
dom_info->sustained_freq_khz =
166210
le32_to_cpu(attr->sustained_freq_khz);
167211
dom_info->sustained_perf_level =
@@ -250,7 +294,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
250294
}
251295

252296
static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
253-
u32 max_perf, u32 min_perf)
297+
u32 max_perf, u32 min_perf)
254298
{
255299
int ret;
256300
struct scmi_xfer *t;
@@ -273,7 +317,7 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
273317
}
274318

275319
static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
276-
u32 *max_perf, u32 *min_perf)
320+
u32 *max_perf, u32 *min_perf)
277321
{
278322
int ret;
279323
struct scmi_xfer *t;
@@ -299,7 +343,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
299343
}
300344

301345
static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
302-
u32 level, bool poll)
346+
u32 level, bool poll)
303347
{
304348
int ret;
305349
struct scmi_xfer *t;
@@ -322,7 +366,7 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
322366
}
323367

324368
static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
325-
u32 *level, bool poll)
369+
u32 *level, bool poll)
326370
{
327371
int ret;
328372
struct scmi_xfer *t;
@@ -343,6 +387,104 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
343387
return ret;
344388
}
345389

390+
static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
391+
{
392+
if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
393+
return true;
394+
if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8)
395+
return true;
396+
return false;
397+
}
398+
399+
static void
400+
scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
401+
u32 message_id, void __iomem **p_addr,
402+
struct scmi_fc_db_info **p_db)
403+
{
404+
int ret;
405+
u32 flags;
406+
u64 phys_addr;
407+
u8 size;
408+
void __iomem *addr;
409+
struct scmi_xfer *t;
410+
struct scmi_fc_db_info *db;
411+
struct scmi_perf_get_fc_info *info;
412+
struct scmi_msg_resp_perf_desc_fc *resp;
413+
414+
if (!p_addr)
415+
return;
416+
417+
ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL,
418+
SCMI_PROTOCOL_PERF,
419+
sizeof(*info), sizeof(*resp), &t);
420+
if (ret)
421+
return;
422+
423+
info = t->tx.buf;
424+
info->domain = cpu_to_le32(domain);
425+
info->message_id = cpu_to_le32(message_id);
426+
427+
ret = scmi_do_xfer(handle, t);
428+
if (ret)
429+
goto err_xfer;
430+
431+
resp = t->rx.buf;
432+
flags = le32_to_cpu(resp->attr);
433+
size = le32_to_cpu(resp->chan_size);
434+
if (!scmi_perf_fc_size_is_valid(message_id, size))
435+
goto err_xfer;
436+
437+
phys_addr = le32_to_cpu(resp->chan_addr_low);
438+
phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
439+
addr = devm_ioremap(handle->dev, phys_addr, size);
440+
if (!addr)
441+
goto err_xfer;
442+
*p_addr = addr;
443+
444+
if (p_db && SUPPORTS_DOORBELL(flags)) {
445+
db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL);
446+
if (!db)
447+
goto err_xfer;
448+
449+
size = 1 << DOORBELL_REG_WIDTH(flags);
450+
phys_addr = le32_to_cpu(resp->db_addr_low);
451+
phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
452+
addr = devm_ioremap(handle->dev, phys_addr, size);
453+
if (!addr)
454+
goto err_xfer;
455+
456+
db->addr = addr;
457+
db->width = size;
458+
db->set = le32_to_cpu(resp->db_set_lmask);
459+
db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
460+
db->mask = le32_to_cpu(resp->db_preserve_lmask);
461+
db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
462+
*p_db = db;
463+
}
464+
err_xfer:
465+
scmi_xfer_put(handle, t);
466+
}
467+
468+
static void scmi_perf_domain_init_fc(const struct scmi_handle *handle,
469+
u32 domain, struct scmi_fc_info **p_fc)
470+
{
471+
struct scmi_fc_info *fc;
472+
473+
fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL);
474+
if (!fc)
475+
return;
476+
477+
scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET,
478+
&fc->level_set_addr, &fc->level_set_db);
479+
scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET,
480+
&fc->level_get_addr, NULL);
481+
scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET,
482+
&fc->limit_set_addr, &fc->limit_set_db);
483+
scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET,
484+
&fc->limit_get_addr, NULL);
485+
*p_fc = fc;
486+
}
487+
346488
/* Device specific ops */
347489
static int scmi_dev_domain_id(struct device *dev)
348490
{
@@ -494,6 +636,9 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
494636

495637
scmi_perf_domain_attributes_get(handle, domain, dom);
496638
scmi_perf_describe_levels_get(handle, domain, dom);
639+
640+
if (dom->perf_fastchannels)
641+
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
497642
}
498643

499644
handle->perf_ops = &perf_ops;

0 commit comments

Comments
 (0)