Commit a4f7f931 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'scmi-updates-5.19' of...

Merge tag 'scmi-updates-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/drivers

Arm SCMI firmware driver updates/fixes for v5.19

The main theme for most of the changes this time is around the addition
of the support for SCMI v3.1 specification changes. Though one of the main
addition in the specification is the powercap protocol, that is still
work in progress and this set includes all other changes bit and pieces
scattered all around the different parts of the specification. There are
few bugs discovered during the process and associated fixes and some
refactoring to simplify the addition of v3.1 support. It mainly includes
the support for extended names, few newly added notifications and async
command support.

Apart from v3.1 SCMI changes, OPTEE transport gets support for dynamic
shared memory.

* tag 'scmi-updates-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: (24 commits)
  firmware: arm_scmi: Fix late checks on pointer dereference
  firmware: arm_scmi: Support optee shared memory in the optee transport
  firmware: arm_scmi: Add SCMI v3.1 VOLTAGE_LEVEL_SET_COMPLETE
  firmware: arm_scmi: Add SCMI v3.1 clock notifications
  firmware: arm_scmi: Add checks for min/max limits in PERFORMANCE_LIMITS_SET
  firmware: arm_scmi: Add SCMI v3.1 perf power-cost in microwatts
  firmware: arm_scmi: Use common iterators in the perf protocol
  firmware: arm_scmi: Use common iterators in the voltage protocol
  firmware: arm_scmi: Use common iterators in the clock protocol
  firmware: arm_scmi: Add SCMI v3.1 SENSOR_AXIS_NAME_GET support
  firmware: arm_scmi: Use common iterators in the sensor protocol
  firmware: arm_scmi: Add iterators for multi-part commands
  firmware: arm_scmi: Parse clock_enable_latency conditionally
  firmware: arm_scmi: Set clock latency to U32_MAX if it is not supported
  firmware: arm_scmi: Add SCMI v3.1 protocol extended names support
  firmware: arm_scmi: Introduce a common SCMI v3.1 .extended_name_get helper
  firmware: arm_scmi: Split protocol specific definitions in a dedicated header
  firmware: arm_scmi: Remove unneeded NULL termination of clk name
  firmware: arm_scmi: Check CLOCK_RATE_SET_COMPLETE async response
  firmware: arm_scmi: Make name_get operations return a const
  ...

Link: https://lore.kernel.org/r/20220504112906.3491985-1-sudeep.holla@arm.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 91f92d70 c7f8852d
...@@ -59,6 +59,7 @@ config ARM_SCMI_TRANSPORT_OPTEE ...@@ -59,6 +59,7 @@ config ARM_SCMI_TRANSPORT_OPTEE
depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL
select ARM_SCMI_HAVE_TRANSPORT select ARM_SCMI_HAVE_TRANSPORT
select ARM_SCMI_HAVE_SHMEM select ARM_SCMI_HAVE_SHMEM
select ARM_SCMI_HAVE_MSG
default y default y
help help
This enables the OP-TEE service based transport for SCMI. This enables the OP-TEE service based transport for SCMI.
......
...@@ -178,6 +178,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, ...@@ -178,6 +178,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
__le32 *num_skip, *num_ret; __le32 *num_skip, *num_ret;
u32 tot_num_ret = 0, loop_num_ret; u32 tot_num_ret = 0, loop_num_ret;
struct device *dev = ph->dev; struct device *dev = ph->dev;
struct scmi_revision_info *rev = ph->get_priv(ph);
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS, ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
sizeof(*num_skip), 0, &t); sizeof(*num_skip), 0, &t);
...@@ -189,6 +190,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, ...@@ -189,6 +190,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
list = t->rx.buf + sizeof(*num_ret); list = t->rx.buf + sizeof(*num_ret);
do { do {
size_t real_list_sz;
u32 calc_list_sz;
/* Set the number of protocols to be skipped/already read */ /* Set the number of protocols to be skipped/already read */
*num_skip = cpu_to_le32(tot_num_ret); *num_skip = cpu_to_le32(tot_num_ret);
...@@ -197,8 +201,30 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, ...@@ -197,8 +201,30 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
break; break;
loop_num_ret = le32_to_cpu(*num_ret); loop_num_ret = le32_to_cpu(*num_ret);
if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) { if (!loop_num_ret)
dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); break;
if (loop_num_ret > rev->num_protocols - tot_num_ret) {
dev_err(dev,
"No. Returned protocols > Total protocols.\n");
break;
}
if (t->rx.len < (sizeof(u32) * 2)) {
dev_err(dev, "Truncated reply - rx.len:%zd\n",
t->rx.len);
ret = -EPROTO;
break;
}
real_list_sz = t->rx.len - sizeof(u32);
calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) *
sizeof(u32);
if (calc_list_sz != real_list_sz) {
dev_err(dev,
"Malformed reply - real_sz:%zd calc_sz:%u\n",
real_list_sz, calc_list_sz);
ret = -EPROTO;
break; break;
} }
...@@ -208,7 +234,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, ...@@ -208,7 +234,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
tot_num_ret += loop_num_ret; tot_num_ret += loop_num_ret;
ph->xops->reset_rx_to_maxsz(ph, t); ph->xops->reset_rx_to_maxsz(ph, t);
} while (loop_num_ret); } while (tot_num_ret < rev->num_protocols);
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
...@@ -351,15 +377,19 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -351,15 +377,19 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
if (ret) if (ret)
return ret; return ret;
prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
if (!prot_imp)
return -ENOMEM;
rev->major_ver = PROTOCOL_REV_MAJOR(version), rev->major_ver = PROTOCOL_REV_MAJOR(version),
rev->minor_ver = PROTOCOL_REV_MINOR(version); rev->minor_ver = PROTOCOL_REV_MINOR(version);
ph->set_priv(ph, rev); ph->set_priv(ph, rev);
scmi_base_attributes_get(ph); ret = scmi_base_attributes_get(ph);
if (ret)
return ret;
prot_imp = devm_kcalloc(dev, rev->num_protocols, sizeof(u8),
GFP_KERNEL);
if (!prot_imp)
return -ENOMEM;
scmi_base_vendor_id_get(ph, false); scmi_base_vendor_id_get(ph, false);
scmi_base_vendor_id_get(ph, true); scmi_base_vendor_id_get(ph, true);
scmi_base_implementation_version_get(ph); scmi_base_implementation_version_get(ph);
......
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
/* /*
* System Control and Management Interface (SCMI) Clock Protocol * System Control and Management Interface (SCMI) Clock Protocol
* *
* Copyright (C) 2018-2021 ARM Ltd. * Copyright (C) 2018-2022 ARM Ltd.
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/limits.h>
#include <linux/sort.h> #include <linux/sort.h>
#include "common.h" #include "protocols.h"
#include "notify.h"
enum scmi_clock_protocol_cmd { enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3, CLOCK_ATTRIBUTES = 0x3,
...@@ -16,6 +18,9 @@ enum scmi_clock_protocol_cmd { ...@@ -16,6 +18,9 @@ enum scmi_clock_protocol_cmd {
CLOCK_RATE_SET = 0x5, CLOCK_RATE_SET = 0x5,
CLOCK_RATE_GET = 0x6, CLOCK_RATE_GET = 0x6,
CLOCK_CONFIG_SET = 0x7, CLOCK_CONFIG_SET = 0x7,
CLOCK_NAME_GET = 0x8,
CLOCK_RATE_NOTIFY = 0x9,
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA,
}; };
struct scmi_msg_resp_clock_protocol_attributes { struct scmi_msg_resp_clock_protocol_attributes {
...@@ -27,7 +32,10 @@ struct scmi_msg_resp_clock_protocol_attributes { ...@@ -27,7 +32,10 @@ struct scmi_msg_resp_clock_protocol_attributes {
struct scmi_msg_resp_clock_attributes { struct scmi_msg_resp_clock_attributes {
__le32 attributes; __le32 attributes;
#define CLOCK_ENABLE BIT(0) #define CLOCK_ENABLE BIT(0)
u8 name[SCMI_MAX_STR_SIZE]; #define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31))
#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
__le32 clock_enable_latency; __le32 clock_enable_latency;
}; };
...@@ -68,6 +76,24 @@ struct scmi_clock_set_rate { ...@@ -68,6 +76,24 @@ struct scmi_clock_set_rate {
__le32 value_high; __le32 value_high;
}; };
struct scmi_msg_resp_set_rate_complete {
__le32 id;
__le32 rate_low;
__le32 rate_high;
};
struct scmi_msg_clock_rate_notify {
__le32 clk_id;
__le32 notify_enable;
};
struct scmi_clock_rate_notify_payld {
__le32 agent_id;
__le32 clock_id;
__le32 rate_low;
__le32 rate_high;
};
struct clock_info { struct clock_info {
u32 version; u32 version;
int num_clocks; int num_clocks;
...@@ -76,6 +102,11 @@ struct clock_info { ...@@ -76,6 +102,11 @@ struct clock_info {
struct scmi_clock_info *clk; struct scmi_clock_info *clk;
}; };
static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
CLOCK_RATE_NOTIFY,
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
};
static int static int
scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
struct clock_info *ci) struct clock_info *ci)
...@@ -102,9 +133,11 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -102,9 +133,11 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
} }
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
u32 clk_id, struct scmi_clock_info *clk) u32 clk_id, struct scmi_clock_info *clk,
u32 version)
{ {
int ret; int ret;
u32 attributes;
struct scmi_xfer *t; struct scmi_xfer *t;
struct scmi_msg_resp_clock_attributes *attr; struct scmi_msg_resp_clock_attributes *attr;
...@@ -118,16 +151,33 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -118,16 +151,33 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
ret = ph->xops->do_xfer(ph, t); ret = ph->xops->do_xfer(ph, t);
if (!ret) { if (!ret) {
u32 latency = 0;
attributes = le32_to_cpu(attr->attributes);
strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
/* Is optional field clock_enable_latency provided ? */ /* clock_enable_latency field is present only since SCMI v3.1 */
if (t->rx.len == sizeof(*attr)) if (PROTOCOL_REV_MAJOR(version) >= 0x2)
clk->enable_latency = latency = le32_to_cpu(attr->clock_enable_latency);
le32_to_cpu(attr->clock_enable_latency); clk->enable_latency = latency ? : U32_MAX;
} else {
clk->name[0] = '\0';
} }
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) {
if (SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
clk->name,
SCMI_MAX_STR_SIZE);
if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
clk->rate_changed_notifications = true;
if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
clk->rate_change_requested_notifications = true;
}
return ret; return ret;
} }
...@@ -143,81 +193,111 @@ static int rate_cmp_func(const void *_r1, const void *_r2) ...@@ -143,81 +193,111 @@ static int rate_cmp_func(const void *_r1, const void *_r2)
return 1; return 1;
} }
static int struct scmi_clk_ipriv {
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id, u32 clk_id;
struct scmi_clock_info *clk) struct scmi_clock_info *clk;
{ };
u64 *rate = NULL;
int ret, cnt;
bool rate_discrete = false;
u32 tot_rate_cnt = 0, rates_flag;
u16 num_returned, num_remaining;
struct scmi_xfer *t;
struct scmi_msg_clock_describe_rates *clk_desc;
struct scmi_msg_resp_clock_describe_rates *rlist;
ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES, static void iter_clk_describe_prepare_message(void *message,
sizeof(*clk_desc), 0, &t); const unsigned int desc_index,
if (ret) const void *priv)
return ret; {
struct scmi_msg_clock_describe_rates *msg = message;
const struct scmi_clk_ipriv *p = priv;
clk_desc = t->tx.buf; msg->id = cpu_to_le32(p->clk_id);
rlist = t->rx.buf; /* Set the number of rates to be skipped/already read */
msg->rate_index = cpu_to_le32(desc_index);
}
do { static int
clk_desc->id = cpu_to_le32(clk_id); iter_clk_describe_update_state(struct scmi_iterator_state *st,
/* Set the number of rates to be skipped/already read */ const void *response, void *priv)
clk_desc->rate_index = cpu_to_le32(tot_rate_cnt); {
u32 flags;
struct scmi_clk_ipriv *p = priv;
const struct scmi_msg_resp_clock_describe_rates *r = response;
ret = ph->xops->do_xfer(ph, t); flags = le32_to_cpu(r->num_rates_flags);
if (ret) st->num_remaining = NUM_REMAINING(flags);
goto err; st->num_returned = NUM_RETURNED(flags);
p->clk->rate_discrete = RATE_DISCRETE(flags);
rates_flag = le32_to_cpu(rlist->num_rates_flags); return 0;
num_remaining = NUM_REMAINING(rates_flag); }
rate_discrete = RATE_DISCRETE(rates_flag);
num_returned = NUM_RETURNED(rates_flag);
if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) { static int
dev_err(ph->dev, "No. of rates > MAX_NUM_RATES"); iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv)
{
int ret = 0;
struct scmi_clk_ipriv *p = priv;
const struct scmi_msg_resp_clock_describe_rates *r = response;
if (!p->clk->rate_discrete) {
switch (st->desc_index + st->loop_idx) {
case 0:
p->clk->range.min_rate = RATE_TO_U64(r->rate[0]);
break; break;
} case 1:
p->clk->range.max_rate = RATE_TO_U64(r->rate[1]);
if (!rate_discrete) { break;
clk->range.min_rate = RATE_TO_U64(rlist->rate[0]); case 2:
clk->range.max_rate = RATE_TO_U64(rlist->rate[1]); p->clk->range.step_size = RATE_TO_U64(r->rate[2]);
clk->range.step_size = RATE_TO_U64(rlist->rate[2]); break;
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n", default:
clk->range.min_rate, clk->range.max_rate, ret = -EINVAL;
clk->range.step_size);
break; break;
} }
} else {
u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx];
rate = &clk->list.rates[tot_rate_cnt]; *rate = RATE_TO_U64(r->rate[st->loop_idx]);
for (cnt = 0; cnt < num_returned; cnt++, rate++) { p->clk->list.num_rates++;
*rate = RATE_TO_U64(rlist->rate[cnt]); //XXX dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
dev_dbg(ph->dev, "Rate %llu Hz\n", *rate); }
}
tot_rate_cnt += num_returned; return ret;
}
static int
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
struct scmi_clock_info *clk)
{
int ret;
ph->xops->reset_rx_to_maxsz(ph, t); void *iter;
/* struct scmi_msg_clock_describe_rates *msg;
* check for both returned and remaining to avoid infinite struct scmi_iterator_ops ops = {
* loop due to buggy firmware .prepare_message = iter_clk_describe_prepare_message,
*/ .update_state = iter_clk_describe_update_state,
} while (num_returned && num_remaining); .process_response = iter_clk_describe_process_response,
};
struct scmi_clk_ipriv cpriv = {
.clk_id = clk_id,
.clk = clk,
};
iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES,
CLOCK_DESCRIBE_RATES,
sizeof(*msg), &cpriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
ret = ph->hops->iter_response_run(iter);
if (ret)
return ret;
if (rate_discrete && rate) { if (!clk->rate_discrete) {
clk->list.num_rates = tot_rate_cnt; dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
sort(clk->list.rates, tot_rate_cnt, sizeof(*rate), clk->range.min_rate, clk->range.max_rate,
rate_cmp_func, NULL); clk->range.step_size);
} else if (clk->list.num_rates) {
sort(clk->list.rates, clk->list.num_rates,
sizeof(clk->list.rates[0]), rate_cmp_func, NULL);
} }
clk->rate_discrete = rate_discrete;
err:
ph->xops->xfer_put(ph, t);
return ret; return ret;
} }
...@@ -266,10 +346,22 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, ...@@ -266,10 +346,22 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
cfg->value_low = cpu_to_le32(rate & 0xffffffff); cfg->value_low = cpu_to_le32(rate & 0xffffffff);
cfg->value_high = cpu_to_le32(rate >> 32); cfg->value_high = cpu_to_le32(rate >> 32);
if (flags & CLOCK_SET_ASYNC) if (flags & CLOCK_SET_ASYNC) {
ret = ph->xops->do_xfer_with_response(ph, t); ret = ph->xops->do_xfer_with_response(ph, t);
else if (!ret) {
struct scmi_msg_resp_set_rate_complete *resp;
resp = t->rx.buf;
if (le32_to_cpu(resp->id) == clk_id)
dev_dbg(ph->dev,
"Clk ID %d set async to %llu\n", clk_id,
get_unaligned_le64(&resp->rate_low));
else
ret = -EPROTO;
}
} else {
ret = ph->xops->do_xfer(ph, t); ret = ph->xops->do_xfer(ph, t);
}
if (ci->max_async_req) if (ci->max_async_req)
atomic_dec(&ci->cur_async_req); atomic_dec(&ci->cur_async_req);
...@@ -355,13 +447,111 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { ...@@ -355,13 +447,111 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
.disable_atomic = scmi_clock_disable_atomic, .disable_atomic = scmi_clock_disable_atomic,
}; };
static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
u32 clk_id, int message_id, bool enable)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_clock_rate_notify *notify;
ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*notify), 0, &t);
if (ret)
return ret;
notify = t->tx.buf;
notify->clk_id = cpu_to_le32(clk_id);
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
return ret;
}
static int scmi_clk_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
int ret, cmd_id;
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
return -EINVAL;
cmd_id = evt_2_cmd[evt_id];
ret = scmi_clk_rate_notify(ph, src_id, cmd_id, enable);
if (ret)
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
evt_id, src_id, ret);
return ret;
}
static void *scmi_clk_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)
{
const struct scmi_clock_rate_notify_payld *p = payld;
struct scmi_clock_rate_notif_report *r = report;
if (sizeof(*p) != payld_sz ||
(evt_id != SCMI_EVENT_CLOCK_RATE_CHANGED &&
evt_id != SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED))
return NULL;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->clock_id = le32_to_cpu(p->clock_id);
r->rate = get_unaligned_le64(&p->rate_low);
*src_id = r->clock_id;
return r;
}
static int scmi_clk_get_num_sources(const struct scmi_protocol_handle *ph)
{
struct clock_info *ci = ph->get_priv(ph);
if (!ci)
return -EINVAL;
return ci->num_clocks;
}
static const struct scmi_event clk_events[] = {
{
.id = SCMI_EVENT_CLOCK_RATE_CHANGED,
.max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld),
.max_report_sz = sizeof(struct scmi_clock_rate_notif_report),
},
{
.id = SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED,
.max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld),
.max_report_sz = sizeof(struct scmi_clock_rate_notif_report),
},
};
static const struct scmi_event_ops clk_event_ops = {
.get_num_sources = scmi_clk_get_num_sources,
.set_notify_enabled = scmi_clk_set_notify_enabled,
.fill_custom_report = scmi_clk_fill_custom_report,
};
static const struct scmi_protocol_events clk_protocol_events = {
.queue_sz = SCMI_PROTO_QUEUE_SZ,
.ops = &clk_event_ops,
.evts = clk_events,
.num_events = ARRAY_SIZE(clk_events),
};
static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
{ {
u32 version; u32 version;
int clkid, ret; int clkid, ret;
struct clock_info *cinfo; struct clock_info *cinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Clock Version %d.%d\n", dev_dbg(ph->dev, "Clock Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
...@@ -370,7 +560,9 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -370,7 +560,9 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
if (!cinfo) if (!cinfo)
return -ENOMEM; return -ENOMEM;
scmi_clock_protocol_attributes_get(ph, cinfo); ret = scmi_clock_protocol_attributes_get(ph, cinfo);
if (ret)
return ret;
cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks, cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
sizeof(*cinfo->clk), GFP_KERNEL); sizeof(*cinfo->clk), GFP_KERNEL);
...@@ -380,7 +572,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -380,7 +572,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) { for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
struct scmi_clock_info *clk = cinfo->clk + clkid; struct scmi_clock_info *clk = cinfo->clk + clkid;
ret = scmi_clock_attributes_get(ph, clkid, clk); ret = scmi_clock_attributes_get(ph, clkid, clk, version);
if (!ret) if (!ret)
scmi_clock_describe_rates_get(ph, clkid, clk); scmi_clock_describe_rates_get(ph, clkid, clk);
} }
...@@ -394,6 +586,7 @@ static const struct scmi_protocol scmi_clock = { ...@@ -394,6 +586,7 @@ static const struct scmi_protocol scmi_clock = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.instance_init = &scmi_clock_protocol_init, .instance_init = &scmi_clock_protocol_init,
.ops = &clk_proto_ops, .ops = &clk_proto_ops,
.events = &clk_protocol_events,
}; };
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock) DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock)
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* driver common header file containing some definitions, structures * driver common header file containing some definitions, structures
* and function prototypes used in all the different SCMI protocols. * and function prototypes used in all the different SCMI protocols.
* *
* Copyright (C) 2018-2021 ARM Ltd. * Copyright (C) 2018-2022 ARM Ltd.
*/ */
#ifndef _SCMI_COMMON_H #ifndef _SCMI_COMMON_H
#define _SCMI_COMMON_H #define _SCMI_COMMON_H
...@@ -24,38 +24,9 @@ ...@@ -24,38 +24,9 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "protocols.h"
#include "notify.h" #include "notify.h"
#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
#define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x)))
#define MAX_PROTOCOLS_IMP 16
#define MAX_OPPS 16
enum scmi_common_cmd {
PROTOCOL_VERSION = 0x0,
PROTOCOL_ATTRIBUTES = 0x1,
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
};
/**
* struct scmi_msg_resp_prot_version - Response for a message
*
* @minor_version: Minor version of the ABI that firmware supports
* @major_version: Major version of the ABI that firmware supports
*
* In general, ABI version changes follow the rule that minor version increments
* are backward compatible. Major revision changes in ABI may not be
* backward compatible.
*
* Response to a generic message with message type SCMI_MSG_VERSION
*/
struct scmi_msg_resp_prot_version {
__le16 minor_version;
__le16 major_version;
};
#define MSG_ID_MASK GENMASK(7, 0) #define MSG_ID_MASK GENMASK(7, 0)
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) #define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
#define MSG_TYPE_MASK GENMASK(9, 8) #define MSG_TYPE_MASK GENMASK(9, 8)
...@@ -79,28 +50,6 @@ struct scmi_msg_resp_prot_version { ...@@ -79,28 +50,6 @@ struct scmi_msg_resp_prot_version {
*/ */
#define SCMI_PENDING_XFERS_HT_ORDER_SZ 9 #define SCMI_PENDING_XFERS_HT_ORDER_SZ 9
/**
* struct scmi_msg_hdr - Message(Tx/Rx) header
*
* @id: The identifier of the message being sent
* @protocol_id: The identifier of the protocol used to send @id message
* @type: The SCMI type for this message
* @seq: The token to identify the message. When a message returns, the
* platform returns the whole message header unmodified including the
* token
* @status: Status of the transfer once it's complete
* @poll_completion: Indicate if the transfer needs to be polled for
* completion or interrupt mode is used
*/
struct scmi_msg_hdr {
u8 id;
u8 protocol_id;
u8 type;
u16 seq;
u32 status;
bool poll_completion;
};
/** /**
* pack_scmi_header() - packs and returns 32-bit header * pack_scmi_header() - packs and returns 32-bit header
* *
...@@ -130,72 +79,6 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) ...@@ -130,72 +79,6 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
hdr->type = MSG_XTRACT_TYPE(msg_hdr); hdr->type = MSG_XTRACT_TYPE(msg_hdr);
} }
/**
* struct scmi_msg - Message(Tx/Rx) structure
*
* @buf: Buffer pointer
* @len: Length of data in the Buffer
*/
struct scmi_msg {
void *buf;
size_t len;
};
/**
* struct scmi_xfer - Structure representing a message flow
*
* @transfer_id: Unique ID for debug & profiling purpose
* @hdr: Transmit message header
* @tx: Transmit message
* @rx: Receive message, the buffer should be pre-allocated to store
* message. If request-ACK protocol is used, we can reuse the same
* buffer for the rx path as we use for the tx path.
* @done: command message transmit completion event
* @async_done: pointer to delayed response message received event completion
* @pending: True for xfers added to @pending_xfers hashtable
* @node: An hlist_node reference used to store this xfer, alternatively, on
* the free list @free_xfers or in the @pending_xfers hashtable
* @users: A refcount to track the active users for this xfer.
* This is meant to protect against the possibility that, when a command
* transaction times out concurrently with the reception of a valid
* response message, the xfer could be finally put on the TX path, and
* so vanish, while on the RX path scmi_rx_callback() is still
* processing it: in such a case this refcounting will ensure that, even
* though the timed-out transaction will anyway cause the command
* request to be reported as failed by time-out, the underlying xfer
* cannot be discarded and possibly reused until the last one user on
* the RX path has released it.
* @busy: An atomic flag to ensure exclusive write access to this xfer
* @state: The current state of this transfer, with states transitions deemed
* valid being:
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
* (Missing synchronous response is assumed OK and ignored)
* @lock: A spinlock to protect state and busy fields.
* @priv: A pointer for transport private usage.
*/
struct scmi_xfer {
int transfer_id;
struct scmi_msg_hdr hdr;
struct scmi_msg tx;
struct scmi_msg rx;
struct completion done;
struct completion *async_done;
bool pending;
struct hlist_node node;
refcount_t users;
#define SCMI_XFER_FREE 0
#define SCMI_XFER_BUSY 1
atomic_t busy;
#define SCMI_XFER_SENT_OK 0
#define SCMI_XFER_RESP_OK 1
#define SCMI_XFER_DRESP_OK 2
int state;
/* A lock to protect state and busy fields */
spinlock_t lock;
void *priv;
};
/* /*
* An helper macro to lookup an xfer from the @pending_xfers hashtable * An helper macro to lookup an xfer from the @pending_xfers hashtable
* using the message sequence number token as a key. * using the message sequence number token as a key.
...@@ -211,64 +94,6 @@ struct scmi_xfer { ...@@ -211,64 +94,6 @@ struct scmi_xfer {
xfer_; \ xfer_; \
}) })
struct scmi_xfer_ops;
/**
* struct scmi_protocol_handle - Reference to an initialized protocol instance
*
* @dev: A reference to the associated SCMI instance device (handle->dev).
* @xops: A reference to a struct holding refs to the core xfer operations that
* can be used by the protocol implementation to generate SCMI messages.
* @set_priv: A method to set protocol private data for this instance.
* @get_priv: A method to get protocol private data previously set.
*
* This structure represents a protocol initialized against specific SCMI
* instance and it will be used as follows:
* - as a parameter fed from the core to the protocol initialization code so
* that it can access the core xfer operations to build and generate SCMI
* messages exclusively for the specific underlying protocol instance.
* - as an opaque handle fed by an SCMI driver user when it tries to access
* this protocol through its own protocol operations.
* In this case this handle will be returned as an opaque object together
* with the related protocol operations when the SCMI driver tries to access
* the protocol.
*/
struct scmi_protocol_handle {
struct device *dev;
const struct scmi_xfer_ops *xops;
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
void *(*get_priv)(const struct scmi_protocol_handle *ph);
};
/**
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
* @version_get: Get this version protocol.
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
* @reset_rx_to_maxsz: Reset rx size to max transport size.
* @do_xfer: Do the SCMI transfer.
* @do_xfer_with_response: Do the SCMI transfer waiting for a response.
* @xfer_put: Free the xfer slot.
*
* Note that all this operations expect a protocol handle as first parameter;
* they then internally use it to infer the underlying protocol number: this
* way is not possible for a protocol implementation to forge messages for
* another protocol.
*/
struct scmi_xfer_ops {
int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
size_t tx_size, size_t rx_size,
struct scmi_xfer **p);
void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
int (*do_xfer)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
void (*xfer_put)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
};
struct scmi_revision_info * struct scmi_revision_info *
scmi_revision_area_get(const struct scmi_protocol_handle *ph); scmi_revision_area_get(const struct scmi_protocol_handle *ph);
int scmi_handle_put(const struct scmi_handle *handle); int scmi_handle_put(const struct scmi_handle *handle);
...@@ -277,55 +102,9 @@ void scmi_set_handle(struct scmi_device *scmi_dev); ...@@ -277,55 +102,9 @@ void scmi_set_handle(struct scmi_device *scmi_dev);
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
u8 *prot_imp); u8 *prot_imp);
typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
/**
* struct scmi_protocol - Protocol descriptor
* @id: Protocol ID.
* @owner: Module reference if any.
* @instance_init: Mandatory protocol initialization function.
* @instance_deinit: Optional protocol de-initialization function.
* @ops: Optional reference to the operations provided by the protocol and
* exposed in scmi_protocol.h.
* @events: An optional reference to the events supported by this protocol.
*/
struct scmi_protocol {
const u8 id;
struct module *owner;
const scmi_prot_init_ph_fn_t instance_init;
const scmi_prot_init_ph_fn_t instance_deinit;
const void *ops;
const struct scmi_protocol_events *events;
};
int __init scmi_bus_init(void); int __init scmi_bus_init(void);
void __exit scmi_bus_exit(void); void __exit scmi_bus_exit(void);
#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \
int __init scmi_##func##_register(void); \
void __exit scmi_##func##_unregister(void)
DECLARE_SCMI_REGISTER_UNREGISTER(base);
DECLARE_SCMI_REGISTER_UNREGISTER(clock);
DECLARE_SCMI_REGISTER_UNREGISTER(perf);
DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
DECLARE_SCMI_REGISTER_UNREGISTER(system);
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
static const struct scmi_protocol *__this_proto = &(proto); \
\
int __init scmi_##name##_register(void) \
{ \
return scmi_protocol_register(__this_proto); \
} \
\
void __exit scmi_##name##_unregister(void) \
{ \
scmi_protocol_unregister(__this_proto); \
}
const struct scmi_protocol *scmi_protocol_get(int protocol_id); const struct scmi_protocol *scmi_protocol_get(int protocol_id);
void scmi_protocol_put(int protocol_id); void scmi_protocol_put(int protocol_id);
......
...@@ -128,7 +128,8 @@ struct scmi_protocol_instance { ...@@ -128,7 +128,8 @@ struct scmi_protocol_instance {
* usage. * usage.
* @protocols_mtx: A mutex to protect protocols instances initialization. * @protocols_mtx: A mutex to protect protocols instances initialization.
* @protocols_imp: List of protocols implemented, currently maximum of * @protocols_imp: List of protocols implemented, currently maximum of
* MAX_PROTOCOLS_IMP elements allocated by the base protocol * scmi_revision_info.num_protocols elements allocated by the
* base protocol
* @active_protocols: IDR storing device_nodes for protocols actually defined * @active_protocols: IDR storing device_nodes for protocols actually defined
* in the DT and confirmed as implemented by fw. * in the DT and confirmed as implemented by fw.
* @atomic_threshold: Optional system wide DT-configured threshold, expressed * @atomic_threshold: Optional system wide DT-configured threshold, expressed
...@@ -1102,6 +1103,167 @@ static const struct scmi_xfer_ops xfer_ops = { ...@@ -1102,6 +1103,167 @@ static const struct scmi_xfer_ops xfer_ops = {
.xfer_put = xfer_put, .xfer_put = xfer_put,
}; };
struct scmi_msg_resp_domain_name_get {
__le32 flags;
u8 name[SCMI_MAX_STR_SIZE];
};
/**
* scmi_common_extended_name_get - Common helper to get extended resources name
* @ph: A protocol handle reference.
* @cmd_id: The specific command ID to use.
* @res_id: The specific resource ID to use.
* @name: A pointer to the preallocated area where the retrieved name will be
* stored as a NULL terminated string.
* @len: The len in bytes of the @name char array.
*
* Return: 0 on Succcess
*/
static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph,
u8 cmd_id, u32 res_id, char *name,
size_t len)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_resp_domain_name_get *resp;
ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(res_id),
sizeof(*resp), &t);
if (ret)
goto out;
put_unaligned_le32(res_id, t->tx.buf);
resp = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
if (!ret)
strscpy(name, resp->name, len);
ph->xops->xfer_put(ph, t);
out:
if (ret)
dev_warn(ph->dev,
"Failed to get extended name - id:%u (ret:%d). Using %s\n",
res_id, ret, name);
return ret;
}
/**
* struct scmi_iterator - Iterator descriptor
* @msg: A reference to the message TX buffer; filled by @prepare_message with
* a proper custom command payload for each multi-part command request.
* @resp: A reference to the response RX buffer; used by @update_state and
* @process_response to parse the multi-part replies.
* @t: A reference to the underlying xfer initialized and used transparently by
* the iterator internal routines.
* @ph: A reference to the associated protocol handle to be used.
* @ops: A reference to the custom provided iterator operations.
* @state: The current iterator state; used and updated in turn by the iterators
* internal routines and by the caller-provided @scmi_iterator_ops.
* @priv: A reference to optional private data as provided by the caller and
* passed back to the @@scmi_iterator_ops.
*/
struct scmi_iterator {
void *msg;
void *resp;
struct scmi_xfer *t;
const struct scmi_protocol_handle *ph;
struct scmi_iterator_ops *ops;
struct scmi_iterator_state state;
void *priv;
};
static void *scmi_iterator_init(const struct scmi_protocol_handle *ph,
struct scmi_iterator_ops *ops,
unsigned int max_resources, u8 msg_id,
size_t tx_size, void *priv)
{
int ret;
struct scmi_iterator *i;
i = devm_kzalloc(ph->dev, sizeof(*i), GFP_KERNEL);
if (!i)
return ERR_PTR(-ENOMEM);
i->ph = ph;
i->ops = ops;
i->priv = priv;
ret = ph->xops->xfer_get_init(ph, msg_id, tx_size, 0, &i->t);
if (ret) {
devm_kfree(ph->dev, i);
return ERR_PTR(ret);
}
i->state.max_resources = max_resources;
i->msg = i->t->tx.buf;
i->resp = i->t->rx.buf;
return i;
}
static int scmi_iterator_run(void *iter)
{
int ret = -EINVAL;
struct scmi_iterator_ops *iops;
const struct scmi_protocol_handle *ph;
struct scmi_iterator_state *st;
struct scmi_iterator *i = iter;
if (!i || !i->ops || !i->ph)
return ret;
iops = i->ops;
ph = i->ph;
st = &i->state;
do {
iops->prepare_message(i->msg, st->desc_index, i->priv);
ret = ph->xops->do_xfer(ph, i->t);
if (ret)
break;
ret = iops->update_state(st, i->resp, i->priv);
if (ret)
break;
if (st->num_returned > st->max_resources - st->desc_index) {
dev_err(ph->dev,
"No. of resources can't exceed %d\n",
st->max_resources);
ret = -EINVAL;
break;
}
for (st->loop_idx = 0; st->loop_idx < st->num_returned;
st->loop_idx++) {
ret = iops->process_response(ph, i->resp, st, i->priv);
if (ret)
goto out;
}
st->desc_index += st->num_returned;
ph->xops->reset_rx_to_maxsz(ph, i->t);
/*
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
*/
} while (st->num_returned && st->num_remaining);
out:
/* Finalize and destroy iterator */
ph->xops->xfer_put(ph, i->t);
devm_kfree(ph->dev, i);
return ret;
}
static const struct scmi_proto_helpers_ops helpers_ops = {
.extended_name_get = scmi_common_extended_name_get,
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
};
/** /**
* scmi_revision_area_get - Retrieve version memory area. * scmi_revision_area_get - Retrieve version memory area.
* *
...@@ -1162,6 +1324,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, ...@@ -1162,6 +1324,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
pi->handle = handle; pi->handle = handle;
pi->ph.dev = handle->dev; pi->ph.dev = handle->dev;
pi->ph.xops = &xfer_ops; pi->ph.xops = &xfer_ops;
pi->ph.hops = &helpers_ops;
pi->ph.set_priv = scmi_set_protocol_priv; pi->ph.set_priv = scmi_set_protocol_priv;
pi->ph.get_priv = scmi_get_protocol_priv; pi->ph.get_priv = scmi_get_protocol_priv;
refcount_set(&pi->users, 1); refcount_set(&pi->users, 1);
...@@ -1310,11 +1473,12 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id) ...@@ -1310,11 +1473,12 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
{ {
int i; int i;
struct scmi_info *info = handle_to_scmi_info(handle); struct scmi_info *info = handle_to_scmi_info(handle);
struct scmi_revision_info *rev = handle->version;
if (!info->protocols_imp) if (!info->protocols_imp)
return false; return false;
for (i = 0; i < MAX_PROTOCOLS_IMP; i++) for (i = 0; i < rev->num_protocols; i++)
if (info->protocols_imp[i] == prot_id) if (info->protocols_imp[i] == prot_id)
return true; return true;
return false; return false;
......
...@@ -64,6 +64,22 @@ enum scmi_optee_pta_cmd { ...@@ -64,6 +64,22 @@ enum scmi_optee_pta_cmd {
* [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps)
*/ */
PTA_SCMI_CMD_GET_CHANNEL = 3, PTA_SCMI_CMD_GET_CHANNEL = 3,
/*
* PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG
* buffer pointed by memref parameters
*
* [in] value[0].a: Channel handle
* [in] memref[1]: Message buffer (MSG and SCMI payload)
* [out] memref[2]: Response buffer (MSG and SCMI payload)
*
* Shared memories used for SCMI message/response are MSG buffers
* referenced by param[1] and param[2]. MSG transport protocol
* uses a 32bit header to carry SCMI meta-data (protocol ID and
* protocol message ID) followed by the effective SCMI message
* payload.
*/
PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4,
}; };
/* /*
...@@ -72,9 +88,17 @@ enum scmi_optee_pta_cmd { ...@@ -72,9 +88,17 @@ enum scmi_optee_pta_cmd {
* PTA_SCMI_CAPS_SMT_HEADER * PTA_SCMI_CAPS_SMT_HEADER
* When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in
* shared memory buffers to carry SCMI protocol synchronisation information. * shared memory buffers to carry SCMI protocol synchronisation information.
*
* PTA_SCMI_CAPS_MSG_HEADER
* When set, OP-TEE supports command using MSG header protocol in an OP-TEE
* shared memory to carry SCMI protocol synchronisation information and SCMI
* message payload.
*/ */
#define PTA_SCMI_CAPS_NONE 0 #define PTA_SCMI_CAPS_NONE 0
#define PTA_SCMI_CAPS_SMT_HEADER BIT(0) #define PTA_SCMI_CAPS_SMT_HEADER BIT(0)
#define PTA_SCMI_CAPS_MSG_HEADER BIT(1)
#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \
PTA_SCMI_CAPS_MSG_HEADER)
/** /**
* struct scmi_optee_channel - Description of an OP-TEE SCMI channel * struct scmi_optee_channel - Description of an OP-TEE SCMI channel
...@@ -85,7 +109,8 @@ enum scmi_optee_pta_cmd { ...@@ -85,7 +109,8 @@ enum scmi_optee_pta_cmd {
* @mu: Mutex protection on channel access * @mu: Mutex protection on channel access
* @cinfo: SCMI channel information * @cinfo: SCMI channel information
* @shmem: Virtual base address of the shared memory * @shmem: Virtual base address of the shared memory
* @tee_shm: Reference to TEE shared memory or NULL if using static shmem * @req: Shared memory protocol handle for SCMI request and synchronous response
* @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
* @link: Reference in agent's channel list * @link: Reference in agent's channel list
*/ */
struct scmi_optee_channel { struct scmi_optee_channel {
...@@ -94,7 +119,10 @@ struct scmi_optee_channel { ...@@ -94,7 +119,10 @@ struct scmi_optee_channel {
u32 caps; u32 caps;
struct mutex mu; struct mutex mu;
struct scmi_chan_info *cinfo; struct scmi_chan_info *cinfo;
struct scmi_shared_mem __iomem *shmem; union {
struct scmi_shared_mem __iomem *shmem;
struct scmi_msg_payld *msg;
} req;
struct tee_shm *tee_shm; struct tee_shm *tee_shm;
struct list_head link; struct list_head link;
}; };
...@@ -178,8 +206,8 @@ static int get_capabilities(struct scmi_optee_agent *agent) ...@@ -178,8 +206,8 @@ static int get_capabilities(struct scmi_optee_agent *agent)
caps = param[0].u.value.a; caps = param[0].u.value.a;
if (!(caps & PTA_SCMI_CAPS_SMT_HEADER)) { if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) {
dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT\n"); dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -193,9 +221,14 @@ static int get_channel(struct scmi_optee_channel *channel) ...@@ -193,9 +221,14 @@ static int get_channel(struct scmi_optee_channel *channel)
struct device *dev = scmi_optee_private->dev; struct device *dev = scmi_optee_private->dev;
struct tee_ioctl_invoke_arg arg = { }; struct tee_ioctl_invoke_arg arg = { };
struct tee_param param[1] = { }; struct tee_param param[1] = { };
unsigned int caps = PTA_SCMI_CAPS_SMT_HEADER; unsigned int caps = 0;
int ret; int ret;
if (channel->tee_shm)
caps = PTA_SCMI_CAPS_MSG_HEADER;
else
caps = PTA_SCMI_CAPS_SMT_HEADER;
arg.func = PTA_SCMI_CMD_GET_CHANNEL; arg.func = PTA_SCMI_CMD_GET_CHANNEL;
arg.session = channel->tee_session; arg.session = channel->tee_session;
arg.num_params = 1; arg.num_params = 1;
...@@ -220,25 +253,48 @@ static int get_channel(struct scmi_optee_channel *channel) ...@@ -220,25 +253,48 @@ static int get_channel(struct scmi_optee_channel *channel)
static int invoke_process_smt_channel(struct scmi_optee_channel *channel) static int invoke_process_smt_channel(struct scmi_optee_channel *channel)
{ {
struct tee_ioctl_invoke_arg arg = { }; struct tee_ioctl_invoke_arg arg = {
struct tee_param param[2] = { }; .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL,
.session = channel->tee_session,
.num_params = 1,
};
struct tee_param param[1] = { };
int ret; int ret;
arg.session = channel->tee_session;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = channel->channel_id; param[0].u.value.a = channel->channel_id;
if (channel->tee_shm) { ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; if (ret < 0 || arg.ret) {
param[1].u.memref.shm = channel->tee_shm; dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
param[1].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; channel->channel_id, ret, arg.ret);
arg.num_params = 2; return -EIO;
arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE;
} else {
arg.num_params = 1;
arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL;
} }
return 0;
}
static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size)
{
struct tee_ioctl_invoke_arg arg = {
.func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL,
.session = channel->tee_session,
.num_params = 3,
};
struct tee_param param[3] = { };
int ret;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = channel->channel_id;
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
param[1].u.memref.shm = channel->tee_shm;
param[1].u.memref.size = msg_size;
param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
param[2].u.memref.shm = channel->tee_shm;
param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE;
ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
if (ret < 0 || arg.ret) { if (ret < 0 || arg.ret) {
dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
...@@ -279,7 +335,26 @@ static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo) ...@@ -279,7 +335,26 @@ static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo)
{ {
struct scmi_optee_channel *channel = cinfo->transport_info; struct scmi_optee_channel *channel = cinfo->transport_info;
shmem_clear_channel(channel->shmem); if (!channel->tee_shm)
shmem_clear_channel(channel->req.shmem);
}
static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel)
{
const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE;
void *shbuf;
channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size);
if (IS_ERR(channel->tee_shm)) {
dev_err(channel->cinfo->dev, "shmem allocation failed\n");
return -ENOMEM;
}
shbuf = tee_shm_get_va(channel->tee_shm, 0);
memset(shbuf, 0, msg_size);
channel->req.msg = shbuf;
return 0;
} }
static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
...@@ -304,8 +379,8 @@ static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, ...@@ -304,8 +379,8 @@ static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
size = resource_size(&res); size = resource_size(&res);
channel->shmem = devm_ioremap(dev, res.start, size); channel->req.shmem = devm_ioremap(dev, res.start, size);
if (!channel->shmem) { if (!channel->req.shmem) {
dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n"); dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n");
ret = -EADDRNOTAVAIL; ret = -EADDRNOTAVAIL;
goto out; goto out;
...@@ -325,7 +400,7 @@ static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo, ...@@ -325,7 +400,7 @@ static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo,
if (of_find_property(cinfo->dev->of_node, "shmem", NULL)) if (of_find_property(cinfo->dev->of_node, "shmem", NULL))
return setup_static_shmem(dev, cinfo, channel); return setup_static_shmem(dev, cinfo, channel);
else else
return -ENOMEM; return setup_dynamic_shmem(dev, channel);
} }
static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx)
...@@ -405,27 +480,22 @@ static int scmi_optee_chan_free(int id, void *p, void *data) ...@@ -405,27 +480,22 @@ static int scmi_optee_chan_free(int id, void *p, void *data)
return 0; return 0;
} }
static struct scmi_shared_mem __iomem *
get_channel_shm(struct scmi_optee_channel *chan, struct scmi_xfer *xfer)
{
if (!chan)
return NULL;
return chan->shmem;
}
static int scmi_optee_send_message(struct scmi_chan_info *cinfo, static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer) struct scmi_xfer *xfer)
{ {
struct scmi_optee_channel *channel = cinfo->transport_info; struct scmi_optee_channel *channel = cinfo->transport_info;
struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer);
int ret; int ret;
mutex_lock(&channel->mu); mutex_lock(&channel->mu);
shmem_tx_prepare(shmem, xfer);
ret = invoke_process_smt_channel(channel); if (channel->tee_shm) {
msg_tx_prepare(channel->req.msg, xfer);
ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
} else {
shmem_tx_prepare(channel->req.shmem, xfer);
ret = invoke_process_smt_channel(channel);
}
if (ret) if (ret)
mutex_unlock(&channel->mu); mutex_unlock(&channel->mu);
...@@ -436,9 +506,11 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, ...@@ -436,9 +506,11 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer) struct scmi_xfer *xfer)
{ {
struct scmi_optee_channel *channel = cinfo->transport_info; struct scmi_optee_channel *channel = cinfo->transport_info;
struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer);
shmem_fetch_response(shmem, xfer); if (channel->tee_shm)
msg_fetch_response(channel->req.msg, SCMI_OPTEE_MAX_MSG_SIZE, xfer);
else
shmem_fetch_response(channel->req.shmem, xfer);
} }
static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* System Control and Management Interface (SCMI) Performance Protocol * System Control and Management Interface (SCMI) Performance Protocol
* *
* Copyright (C) 2018-2021 ARM Ltd. * Copyright (C) 2018-2022 ARM Ltd.
*/ */
#define pr_fmt(fmt) "SCMI Notifications PERF - " fmt #define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include <linux/sort.h> #include <linux/sort.h>
#include "common.h" #include "protocols.h"
#include "notify.h" #include "notify.h"
#define MAX_OPPS 16
enum scmi_performance_protocol_cmd { enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3, PERF_DOMAIN_ATTRIBUTES = 0x3,
PERF_DESCRIBE_LEVELS = 0x4, PERF_DESCRIBE_LEVELS = 0x4,
...@@ -30,6 +32,7 @@ enum scmi_performance_protocol_cmd { ...@@ -30,6 +32,7 @@ enum scmi_performance_protocol_cmd {
PERF_NOTIFY_LIMITS = 0x9, PERF_NOTIFY_LIMITS = 0x9,
PERF_NOTIFY_LEVEL = 0xa, PERF_NOTIFY_LEVEL = 0xa,
PERF_DESCRIBE_FASTCHANNEL = 0xb, PERF_DESCRIBE_FASTCHANNEL = 0xb,
PERF_DOMAIN_NAME_GET = 0xc,
}; };
struct scmi_opp { struct scmi_opp {
...@@ -42,6 +45,7 @@ struct scmi_msg_resp_perf_attributes { ...@@ -42,6 +45,7 @@ struct scmi_msg_resp_perf_attributes {
__le16 num_domains; __le16 num_domains;
__le16 flags; __le16 flags;
#define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0)) #define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0))
#define POWER_SCALE_IN_MICROWATT(x) ((x) & BIT(1))
__le32 stats_addr_low; __le32 stats_addr_low;
__le32 stats_addr_high; __le32 stats_addr_high;
__le32 stats_size; __le32 stats_size;
...@@ -54,10 +58,11 @@ struct scmi_msg_resp_perf_domain_attributes { ...@@ -54,10 +58,11 @@ struct scmi_msg_resp_perf_domain_attributes {
#define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29)) #define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
#define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) #define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26))
__le32 rate_limit_us; __le32 rate_limit_us;
__le32 sustained_freq_khz; __le32 sustained_freq_khz;
__le32 sustained_perf_level; __le32 sustained_perf_level;
u8 name[SCMI_MAX_STR_SIZE]; u8 name[SCMI_SHORT_NAME_MAX_SIZE];
}; };
struct scmi_msg_perf_describe_levels { struct scmi_msg_perf_describe_levels {
...@@ -166,6 +171,7 @@ struct scmi_perf_info { ...@@ -166,6 +171,7 @@ struct scmi_perf_info {
u32 version; u32 version;
int num_domains; int num_domains;
bool power_scale_mw; bool power_scale_mw;
bool power_scale_uw;
u64 stats_addr; u64 stats_addr;
u32 stats_size; u32 stats_size;
struct perf_dom_info *dom_info; struct perf_dom_info *dom_info;
...@@ -196,6 +202,8 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -196,6 +202,8 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
pi->num_domains = le16_to_cpu(attr->num_domains); pi->num_domains = le16_to_cpu(attr->num_domains);
pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags); pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
pi->power_scale_uw = POWER_SCALE_IN_MICROWATT(flags);
pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
(u64)le32_to_cpu(attr->stats_addr_high) << 32; (u64)le32_to_cpu(attr->stats_addr_high) << 32;
pi->stats_size = le32_to_cpu(attr->stats_size); pi->stats_size = le32_to_cpu(attr->stats_size);
...@@ -207,9 +215,11 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -207,9 +215,11 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
static int static int
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct perf_dom_info *dom_info) u32 domain, struct perf_dom_info *dom_info,
u32 version)
{ {
int ret; int ret;
u32 flags;
struct scmi_xfer *t; struct scmi_xfer *t;
struct scmi_msg_resp_perf_domain_attributes *attr; struct scmi_msg_resp_perf_domain_attributes *attr;
...@@ -223,7 +233,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -223,7 +233,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
ret = ph->xops->do_xfer(ph, t); ret = ph->xops->do_xfer(ph, t);
if (!ret) { if (!ret) {
u32 flags = le32_to_cpu(attr->flags); flags = le32_to_cpu(attr->flags);
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags); dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags); dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
...@@ -246,6 +256,16 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -246,6 +256,16 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
} }
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags))
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, domain,
dom_info->name, SCMI_MAX_STR_SIZE);
return ret; return ret;
} }
...@@ -256,66 +276,87 @@ static int opp_cmp_func(const void *opp1, const void *opp2) ...@@ -256,66 +276,87 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
return t1->perf - t2->perf; return t1->perf - t2->perf;
} }
static int struct scmi_perf_ipriv {
scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, u32 domain;
struct perf_dom_info *perf_dom) struct perf_dom_info *perf_dom;
};
static void iter_perf_levels_prepare_message(void *message,
unsigned int desc_index,
const void *priv)
{ {
int ret, cnt; struct scmi_msg_perf_describe_levels *msg = message;
u32 tot_opp_cnt = 0; const struct scmi_perf_ipriv *p = priv;
u16 num_returned, num_remaining;
struct scmi_xfer *t;
struct scmi_opp *opp;
struct scmi_msg_perf_describe_levels *dom_info;
struct scmi_msg_resp_perf_describe_levels *level_info;
ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_LEVELS, msg->domain = cpu_to_le32(p->domain);
sizeof(*dom_info), 0, &t); /* Set the number of OPPs to be skipped/already read */
if (ret) msg->level_index = cpu_to_le32(desc_index);
return ret; }
dom_info = t->tx.buf; static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
level_info = t->rx.buf; const void *response, void *priv)
{
const struct scmi_msg_resp_perf_describe_levels *r = response;
do { st->num_returned = le16_to_cpu(r->num_returned);
dom_info->domain = cpu_to_le32(domain); st->num_remaining = le16_to_cpu(r->num_remaining);
/* Set the number of OPPs to be skipped/already read */
dom_info->level_index = cpu_to_le32(tot_opp_cnt);
ret = ph->xops->do_xfer(ph, t); return 0;
if (ret) }
break;
num_returned = le16_to_cpu(level_info->num_returned); static int
num_remaining = le16_to_cpu(level_info->num_remaining); iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
if (tot_opp_cnt + num_returned > MAX_OPPS) { const void *response,
dev_err(ph->dev, "No. of OPPs exceeded MAX_OPPS"); struct scmi_iterator_state *st, void *priv)
break; {
} struct scmi_opp *opp;
const struct scmi_msg_resp_perf_describe_levels *r = response;
struct scmi_perf_ipriv *p = priv;
opp = &perf_dom->opp[tot_opp_cnt]; opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
for (cnt = 0; cnt < num_returned; cnt++, opp++) { opp->perf = le32_to_cpu(r->opp[st->loop_idx].perf_val);
opp->perf = le32_to_cpu(level_info->opp[cnt].perf_val); opp->power = le32_to_cpu(r->opp[st->loop_idx].power);
opp->power = le32_to_cpu(level_info->opp[cnt].power); opp->trans_latency_us =
opp->trans_latency_us = le16_to_cpu le16_to_cpu(r->opp[st->loop_idx].transition_latency_us);
(level_info->opp[cnt].transition_latency_us); p->perf_dom->opp_count++;
dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n", dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n",
opp->perf, opp->power, opp->trans_latency_us); opp->perf, opp->power, opp->trans_latency_us);
}
tot_opp_cnt += num_returned; return 0;
}
ph->xops->reset_rx_to_maxsz(ph, t); static int
/* scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain,
* check for both returned and remaining to avoid infinite struct perf_dom_info *perf_dom)
* loop due to buggy firmware {
*/ int ret;
} while (num_returned && num_remaining); void *iter;
struct scmi_msg_perf_describe_levels *msg;
struct scmi_iterator_ops ops = {
.prepare_message = iter_perf_levels_prepare_message,
.update_state = iter_perf_levels_update_state,
.process_response = iter_perf_levels_process_response,
};
struct scmi_perf_ipriv ppriv = {
.domain = domain,
.perf_dom = perf_dom,
};
iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS,
PERF_DESCRIBE_LEVELS,
sizeof(*msg), &ppriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
ret = ph->hops->iter_response_run(iter);
if (ret)
return ret;
perf_dom->opp_count = tot_opp_cnt; if (perf_dom->opp_count)
ph->xops->xfer_put(ph, t); sort(perf_dom->opp, perf_dom->opp_count,
sizeof(struct scmi_opp), opp_cmp_func, NULL);
sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
return ret; return ret;
} }
...@@ -382,6 +423,9 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, ...@@ -382,6 +423,9 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
struct scmi_perf_info *pi = ph->get_priv(ph); struct scmi_perf_info *pi = ph->get_priv(ph);
struct perf_dom_info *dom = pi->dom_info + domain; struct perf_dom_info *dom = pi->dom_info + domain;
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf)
return -EINVAL;
if (dom->fc_info && dom->fc_info->limit_set_addr) { if (dom->fc_info && dom->fc_info->limit_set_addr) {
iowrite32(max_perf, dom->fc_info->limit_set_addr); iowrite32(max_perf, dom->fc_info->limit_set_addr);
iowrite32(min_perf, dom->fc_info->limit_set_addr + 4); iowrite32(min_perf, dom->fc_info->limit_set_addr + 4);
...@@ -873,11 +917,13 @@ static const struct scmi_protocol_events perf_protocol_events = { ...@@ -873,11 +917,13 @@ static const struct scmi_protocol_events perf_protocol_events = {
static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
{ {
int domain; int domain, ret;
u32 version; u32 version;
struct scmi_perf_info *pinfo; struct scmi_perf_info *pinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Performance Version %d.%d\n", dev_dbg(ph->dev, "Performance Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
...@@ -886,7 +932,9 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -886,7 +932,9 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo) if (!pinfo)
return -ENOMEM; return -ENOMEM;
scmi_perf_attributes_get(ph, pinfo); ret = scmi_perf_attributes_get(ph, pinfo);
if (ret)
return ret;
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
sizeof(*pinfo->dom_info), GFP_KERNEL); sizeof(*pinfo->dom_info), GFP_KERNEL);
...@@ -896,7 +944,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -896,7 +944,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) { for (domain = 0; domain < pinfo->num_domains; domain++) {
struct perf_dom_info *dom = pinfo->dom_info + domain; struct perf_dom_info *dom = pinfo->dom_info + domain;
scmi_perf_domain_attributes_get(ph, domain, dom); scmi_perf_domain_attributes_get(ph, domain, dom, version);
scmi_perf_describe_levels_get(ph, domain, dom); scmi_perf_describe_levels_get(ph, domain, dom);
if (dom->perf_fastchannels) if (dom->perf_fastchannels)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* System Control and Management Interface (SCMI) Power Protocol * System Control and Management Interface (SCMI) Power Protocol
* *
* Copyright (C) 2018-2021 ARM Ltd. * Copyright (C) 2018-2022 ARM Ltd.
*/ */
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include "common.h" #include "protocols.h"
#include "notify.h" #include "notify.h"
enum scmi_power_protocol_cmd { enum scmi_power_protocol_cmd {
...@@ -18,6 +18,7 @@ enum scmi_power_protocol_cmd { ...@@ -18,6 +18,7 @@ enum scmi_power_protocol_cmd {
POWER_STATE_SET = 0x4, POWER_STATE_SET = 0x4,
POWER_STATE_GET = 0x5, POWER_STATE_GET = 0x5,
POWER_STATE_NOTIFY = 0x6, POWER_STATE_NOTIFY = 0x6,
POWER_DOMAIN_NAME_GET = 0x8,
}; };
struct scmi_msg_resp_power_attributes { struct scmi_msg_resp_power_attributes {
...@@ -33,7 +34,8 @@ struct scmi_msg_resp_power_domain_attributes { ...@@ -33,7 +34,8 @@ struct scmi_msg_resp_power_domain_attributes {
#define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31)) #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31))
#define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30)) #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30))
#define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29)) #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29))
u8 name[SCMI_MAX_STR_SIZE]; #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27))
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
}; };
struct scmi_power_set_state { struct scmi_power_set_state {
...@@ -97,9 +99,11 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -97,9 +99,11 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
static int static int
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct power_dom_info *dom_info) u32 domain, struct power_dom_info *dom_info,
u32 version)
{ {
int ret; int ret;
u32 flags;
struct scmi_xfer *t; struct scmi_xfer *t;
struct scmi_msg_resp_power_domain_attributes *attr; struct scmi_msg_resp_power_domain_attributes *attr;
...@@ -113,15 +117,26 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -113,15 +117,26 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
ret = ph->xops->do_xfer(ph, t); ret = ph->xops->do_xfer(ph, t);
if (!ret) { if (!ret) {
u32 flags = le32_to_cpu(attr->flags); flags = le32_to_cpu(attr->flags);
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(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_async = SUPPORTS_STATE_SET_ASYNC(flags);
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
} }
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags)) {
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
domain, dom_info->name,
SCMI_MAX_STR_SIZE);
}
return ret; return ret;
} }
...@@ -174,8 +189,9 @@ static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph) ...@@ -174,8 +189,9 @@ static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
return pi->num_domains; return pi->num_domains;
} }
static char *scmi_power_name_get(const struct scmi_protocol_handle *ph, static const char *
u32 domain) scmi_power_name_get(const struct scmi_protocol_handle *ph,
u32 domain)
{ {
struct scmi_power_info *pi = ph->get_priv(ph); struct scmi_power_info *pi = ph->get_priv(ph);
struct power_dom_info *dom = pi->dom_info + domain; struct power_dom_info *dom = pi->dom_info + domain;
...@@ -280,11 +296,13 @@ static const struct scmi_protocol_events power_protocol_events = { ...@@ -280,11 +296,13 @@ static const struct scmi_protocol_events power_protocol_events = {
static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
{ {
int domain; int domain, ret;
u32 version; u32 version;
struct scmi_power_info *pinfo; struct scmi_power_info *pinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Power Version %d.%d\n", dev_dbg(ph->dev, "Power Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
...@@ -293,7 +311,9 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -293,7 +311,9 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo) if (!pinfo)
return -ENOMEM; return -ENOMEM;
scmi_power_attributes_get(ph, pinfo); ret = scmi_power_attributes_get(ph, pinfo);
if (ret)
return ret;
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
sizeof(*pinfo->dom_info), GFP_KERNEL); sizeof(*pinfo->dom_info), GFP_KERNEL);
...@@ -303,7 +323,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -303,7 +323,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) { for (domain = 0; domain < pinfo->num_domains; domain++) {
struct power_dom_info *dom = pinfo->dom_info + domain; struct power_dom_info *dom = pinfo->dom_info + domain;
scmi_power_domain_attributes_get(ph, domain, dom); scmi_power_domain_attributes_get(ph, domain, dom, version);
} }
pinfo->version = version; pinfo->version = version;
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* System Control and Management Interface (SCMI) Message Protocol
* protocols common header file containing some definitions, structures
* and function prototypes used in all the different SCMI protocols.
*
* Copyright (C) 2022 ARM Ltd.
*/
#ifndef _SCMI_PROTOCOLS_H
#define _SCMI_PROTOCOLS_H
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/hashtable.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/refcount.h>
#include <linux/scmi_protocol.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#define SCMI_SHORT_NAME_MAX_SIZE 16
#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))))
#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))))
enum scmi_common_cmd {
PROTOCOL_VERSION = 0x0,
PROTOCOL_ATTRIBUTES = 0x1,
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
};
/**
* struct scmi_msg_resp_prot_version - Response for a message
*
* @minor_version: Minor version of the ABI that firmware supports
* @major_version: Major version of the ABI that firmware supports
*
* In general, ABI version changes follow the rule that minor version increments
* are backward compatible. Major revision changes in ABI may not be
* backward compatible.
*
* Response to a generic message with message type SCMI_MSG_VERSION
*/
struct scmi_msg_resp_prot_version {
__le16 minor_version;
__le16 major_version;
};
/**
* struct scmi_msg - Message(Tx/Rx) structure
*
* @buf: Buffer pointer
* @len: Length of data in the Buffer
*/
struct scmi_msg {
void *buf;
size_t len;
};
/**
* struct scmi_msg_hdr - Message(Tx/Rx) header
*
* @id: The identifier of the message being sent
* @protocol_id: The identifier of the protocol used to send @id message
* @type: The SCMI type for this message
* @seq: The token to identify the message. When a message returns, the
* platform returns the whole message header unmodified including the
* token
* @status: Status of the transfer once it's complete
* @poll_completion: Indicate if the transfer needs to be polled for
* completion or interrupt mode is used
*/
struct scmi_msg_hdr {
u8 id;
u8 protocol_id;
u8 type;
u16 seq;
u32 status;
bool poll_completion;
};
/**
* struct scmi_xfer - Structure representing a message flow
*
* @transfer_id: Unique ID for debug & profiling purpose
* @hdr: Transmit message header
* @tx: Transmit message
* @rx: Receive message, the buffer should be pre-allocated to store
* message. If request-ACK protocol is used, we can reuse the same
* buffer for the rx path as we use for the tx path.
* @done: command message transmit completion event
* @async_done: pointer to delayed response message received event completion
* @pending: True for xfers added to @pending_xfers hashtable
* @node: An hlist_node reference used to store this xfer, alternatively, on
* the free list @free_xfers or in the @pending_xfers hashtable
* @users: A refcount to track the active users for this xfer.
* This is meant to protect against the possibility that, when a command
* transaction times out concurrently with the reception of a valid
* response message, the xfer could be finally put on the TX path, and
* so vanish, while on the RX path scmi_rx_callback() is still
* processing it: in such a case this refcounting will ensure that, even
* though the timed-out transaction will anyway cause the command
* request to be reported as failed by time-out, the underlying xfer
* cannot be discarded and possibly reused until the last one user on
* the RX path has released it.
* @busy: An atomic flag to ensure exclusive write access to this xfer
* @state: The current state of this transfer, with states transitions deemed
* valid being:
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
* (Missing synchronous response is assumed OK and ignored)
* @lock: A spinlock to protect state and busy fields.
* @priv: A pointer for transport private usage.
*/
struct scmi_xfer {
int transfer_id;
struct scmi_msg_hdr hdr;
struct scmi_msg tx;
struct scmi_msg rx;
struct completion done;
struct completion *async_done;
bool pending;
struct hlist_node node;
refcount_t users;
#define SCMI_XFER_FREE 0
#define SCMI_XFER_BUSY 1
atomic_t busy;
#define SCMI_XFER_SENT_OK 0
#define SCMI_XFER_RESP_OK 1
#define SCMI_XFER_DRESP_OK 2
int state;
/* A lock to protect state and busy fields */
spinlock_t lock;
void *priv;
};
struct scmi_xfer_ops;
struct scmi_proto_helpers_ops;
/**
* struct scmi_protocol_handle - Reference to an initialized protocol instance
*
* @dev: A reference to the associated SCMI instance device (handle->dev).
* @xops: A reference to a struct holding refs to the core xfer operations that
* can be used by the protocol implementation to generate SCMI messages.
* @set_priv: A method to set protocol private data for this instance.
* @get_priv: A method to get protocol private data previously set.
*
* This structure represents a protocol initialized against specific SCMI
* instance and it will be used as follows:
* - as a parameter fed from the core to the protocol initialization code so
* that it can access the core xfer operations to build and generate SCMI
* messages exclusively for the specific underlying protocol instance.
* - as an opaque handle fed by an SCMI driver user when it tries to access
* this protocol through its own protocol operations.
* In this case this handle will be returned as an opaque object together
* with the related protocol operations when the SCMI driver tries to access
* the protocol.
*/
struct scmi_protocol_handle {
struct device *dev;
const struct scmi_xfer_ops *xops;
const struct scmi_proto_helpers_ops *hops;
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
void *(*get_priv)(const struct scmi_protocol_handle *ph);
};
/**
* struct scmi_iterator_state - Iterator current state descriptor
* @desc_index: Starting index for the current mulit-part request.
* @num_returned: Number of returned items in the last multi-part reply.
* @num_remaining: Number of remaining items in the multi-part message.
* @max_resources: Maximum acceptable number of items, configured by the caller
* depending on the underlying resources that it is querying.
* @loop_idx: The iterator loop index in the current multi-part reply.
* @priv: Optional pointer to some additional state-related private data setup
* by the caller during the iterations.
*/
struct scmi_iterator_state {
unsigned int desc_index;
unsigned int num_returned;
unsigned int num_remaining;
unsigned int max_resources;
unsigned int loop_idx;
void *priv;
};
/**
* struct scmi_iterator_ops - Custom iterator operations
* @prepare_message: An operation to provide the custom logic to fill in the
* SCMI command request pointed by @message. @desc_index is
* a reference to the next index to use in the multi-part
* request.
* @update_state: An operation to provide the custom logic to update the
* iterator state from the actual message response.
* @process_response: An operation to provide the custom logic needed to process
* each chunk of the multi-part message.
*/
struct scmi_iterator_ops {
void (*prepare_message)(void *message, unsigned int desc_index,
const void *priv);
int (*update_state)(struct scmi_iterator_state *st,
const void *response, void *priv);
int (*process_response)(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv);
};
/**
* struct scmi_proto_helpers_ops - References to common protocol helpers
* @extended_name_get: A common helper function to retrieve extended naming
* for the specified resource using the specified command.
* Result is returned as a NULL terminated string in the
* pre-allocated area pointed to by @name with maximum
* capacity of @len bytes.
* @iter_response_init: A common helper to initialize a generic iterator to
* parse multi-message responses: when run the iterator
* will take care to send the initial command request as
* specified by @msg_id and @tx_size and then to parse the
* multi-part responses using the custom operations
* provided in @ops.
* @iter_response_run: A common helper to trigger the run of a previously
* initialized iterator.
*/
struct scmi_proto_helpers_ops {
int (*extended_name_get)(const struct scmi_protocol_handle *ph,
u8 cmd_id, u32 res_id, char *name, size_t len);
void *(*iter_response_init)(const struct scmi_protocol_handle *ph,
struct scmi_iterator_ops *ops,
unsigned int max_resources, u8 msg_id,
size_t tx_size, void *priv);
int (*iter_response_run)(void *iter);
};
/**
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
* @version_get: Get this version protocol.
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
* @reset_rx_to_maxsz: Reset rx size to max transport size.
* @do_xfer: Do the SCMI transfer.
* @do_xfer_with_response: Do the SCMI transfer waiting for a response.
* @xfer_put: Free the xfer slot.
*
* Note that all this operations expect a protocol handle as first parameter;
* they then internally use it to infer the underlying protocol number: this
* way is not possible for a protocol implementation to forge messages for
* another protocol.
*/
struct scmi_xfer_ops {
int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
size_t tx_size, size_t rx_size,
struct scmi_xfer **p);
void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
int (*do_xfer)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
void (*xfer_put)(const struct scmi_protocol_handle *ph,
struct scmi_xfer *xfer);
};
typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
/**
* struct scmi_protocol - Protocol descriptor
* @id: Protocol ID.
* @owner: Module reference if any.
* @instance_init: Mandatory protocol initialization function.
* @instance_deinit: Optional protocol de-initialization function.
* @ops: Optional reference to the operations provided by the protocol and
* exposed in scmi_protocol.h.
* @events: An optional reference to the events supported by this protocol.
*/
struct scmi_protocol {
const u8 id;
struct module *owner;
const scmi_prot_init_ph_fn_t instance_init;
const scmi_prot_init_ph_fn_t instance_deinit;
const void *ops;
const struct scmi_protocol_events *events;
};
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
static const struct scmi_protocol *__this_proto = &(proto); \
\
int __init scmi_##name##_register(void) \
{ \
return scmi_protocol_register(__this_proto); \
} \
\
void __exit scmi_##name##_unregister(void) \
{ \
scmi_protocol_unregister(__this_proto); \
}
#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \
int __init scmi_##func##_register(void); \
void __exit scmi_##func##_unregister(void)
DECLARE_SCMI_REGISTER_UNREGISTER(base);
DECLARE_SCMI_REGISTER_UNREGISTER(clock);
DECLARE_SCMI_REGISTER_UNREGISTER(perf);
DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
DECLARE_SCMI_REGISTER_UNREGISTER(system);
#endif /* _SCMI_PROTOCOLS_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* System Control and Management Interface (SCMI) Reset Protocol * System Control and Management Interface (SCMI) Reset Protocol
* *
* Copyright (C) 2019-2021 ARM Ltd. * Copyright (C) 2019-2022 ARM Ltd.
*/ */
#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt #define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
...@@ -10,13 +10,14 @@ ...@@ -10,13 +10,14 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include "common.h" #include "protocols.h"
#include "notify.h" #include "notify.h"
enum scmi_reset_protocol_cmd { enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3, RESET_DOMAIN_ATTRIBUTES = 0x3,
RESET = 0x4, RESET = 0x4,
RESET_NOTIFY = 0x5, RESET_NOTIFY = 0x5,
RESET_DOMAIN_NAME_GET = 0x6,
}; };
#define NUM_RESET_DOMAIN_MASK 0xffff #define NUM_RESET_DOMAIN_MASK 0xffff
...@@ -26,8 +27,9 @@ struct scmi_msg_resp_reset_domain_attributes { ...@@ -26,8 +27,9 @@ struct scmi_msg_resp_reset_domain_attributes {
__le32 attributes; __le32 attributes;
#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31)) #define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30)) #define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
__le32 latency; __le32 latency;
u8 name[SCMI_MAX_STR_SIZE]; u8 name[SCMI_SHORT_NAME_MAX_SIZE];
}; };
struct scmi_msg_reset_domain_reset { struct scmi_msg_reset_domain_reset {
...@@ -89,9 +91,11 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -89,9 +91,11 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
static int static int
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct reset_dom_info *dom_info) u32 domain, struct reset_dom_info *dom_info,
u32 version)
{ {
int ret; int ret;
u32 attributes;
struct scmi_xfer *t; struct scmi_xfer *t;
struct scmi_msg_resp_reset_domain_attributes *attr; struct scmi_msg_resp_reset_domain_attributes *attr;
...@@ -105,7 +109,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -105,7 +109,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
ret = ph->xops->do_xfer(ph, t); ret = ph->xops->do_xfer(ph, t);
if (!ret) { if (!ret) {
u32 attributes = le32_to_cpu(attr->attributes); attributes = le32_to_cpu(attr->attributes);
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes); dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
...@@ -116,6 +120,16 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -116,6 +120,16 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
} }
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
dom_info->name, SCMI_MAX_STR_SIZE);
return ret; return ret;
} }
...@@ -126,8 +140,8 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph) ...@@ -126,8 +140,8 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
return pi->num_domains; return pi->num_domains;
} }
static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph, static const char *
u32 domain) scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
{ {
struct scmi_reset_info *pi = ph->get_priv(ph); struct scmi_reset_info *pi = ph->get_priv(ph);
...@@ -293,11 +307,13 @@ static const struct scmi_protocol_events reset_protocol_events = { ...@@ -293,11 +307,13 @@ static const struct scmi_protocol_events reset_protocol_events = {
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
{ {
int domain; int domain, ret;
u32 version; u32 version;
struct scmi_reset_info *pinfo; struct scmi_reset_info *pinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Reset Version %d.%d\n", dev_dbg(ph->dev, "Reset Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
...@@ -306,7 +322,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -306,7 +322,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo) if (!pinfo)
return -ENOMEM; return -ENOMEM;
scmi_reset_attributes_get(ph, pinfo); ret = scmi_reset_attributes_get(ph, pinfo);
if (ret)
return ret;
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
sizeof(*pinfo->dom_info), GFP_KERNEL); sizeof(*pinfo->dom_info), GFP_KERNEL);
...@@ -316,7 +334,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -316,7 +334,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) { for (domain = 0; domain < pinfo->num_domains; domain++) {
struct reset_dom_info *dom = pinfo->dom_info + domain; struct reset_dom_info *dom = pinfo->dom_info + domain;
scmi_reset_domain_attributes_get(ph, domain, dom); scmi_reset_domain_attributes_get(ph, domain, dom, version);
} }
pinfo->version = version; pinfo->version = version;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* System Control and Management Interface (SCMI) Sensor Protocol * System Control and Management Interface (SCMI) Sensor Protocol
* *
* Copyright (C) 2018-2021 ARM Ltd. * Copyright (C) 2018-2022 ARM Ltd.
*/ */
#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt #define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include "common.h" #include "protocols.h"
#include "notify.h" #include "notify.h"
#define SCMI_MAX_NUM_SENSOR_AXIS 63 #define SCMI_MAX_NUM_SENSOR_AXIS 63
...@@ -27,6 +27,8 @@ enum scmi_sensor_protocol_cmd { ...@@ -27,6 +27,8 @@ enum scmi_sensor_protocol_cmd {
SENSOR_CONFIG_GET = 0x9, SENSOR_CONFIG_GET = 0x9,
SENSOR_CONFIG_SET = 0xA, SENSOR_CONFIG_SET = 0xA,
SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB, SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB,
SENSOR_NAME_GET = 0xC,
SENSOR_AXIS_NAME_GET = 0xD,
}; };
struct scmi_msg_resp_sensor_attributes { struct scmi_msg_resp_sensor_attributes {
...@@ -63,6 +65,10 @@ struct scmi_msg_resp_attrs { ...@@ -63,6 +65,10 @@ struct scmi_msg_resp_attrs {
__le32 max_range_high; __le32 max_range_high;
}; };
struct scmi_msg_sensor_description {
__le32 desc_index;
};
struct scmi_msg_resp_sensor_description { struct scmi_msg_resp_sensor_description {
__le16 num_returned; __le16 num_returned;
__le16 num_remaining; __le16 num_remaining;
...@@ -71,6 +77,7 @@ struct scmi_msg_resp_sensor_description { ...@@ -71,6 +77,7 @@ struct scmi_msg_resp_sensor_description {
__le32 attributes_low; __le32 attributes_low;
/* Common attributes_low macros */ /* Common attributes_low macros */
#define SUPPORTS_ASYNC_READ(x) FIELD_GET(BIT(31), (x)) #define SUPPORTS_ASYNC_READ(x) FIELD_GET(BIT(31), (x))
#define SUPPORTS_EXTENDED_NAMES(x) FIELD_GET(BIT(29), (x))
#define NUM_TRIP_POINTS(x) FIELD_GET(GENMASK(7, 0), (x)) #define NUM_TRIP_POINTS(x) FIELD_GET(GENMASK(7, 0), (x))
__le32 attributes_high; __le32 attributes_high;
/* Common attributes_high macros */ /* Common attributes_high macros */
...@@ -78,7 +85,7 @@ struct scmi_msg_resp_sensor_description { ...@@ -78,7 +85,7 @@ struct scmi_msg_resp_sensor_description {
#define SENSOR_SCALE_SIGN BIT(4) #define SENSOR_SCALE_SIGN BIT(4)
#define SENSOR_SCALE_EXTEND GENMASK(31, 5) #define SENSOR_SCALE_EXTEND GENMASK(31, 5)
#define SENSOR_TYPE(x) FIELD_GET(GENMASK(7, 0), (x)) #define SENSOR_TYPE(x) FIELD_GET(GENMASK(7, 0), (x))
u8 name[SCMI_MAX_STR_SIZE]; u8 name[SCMI_SHORT_NAME_MAX_SIZE];
/* only for version > 2.0 */ /* only for version > 2.0 */
__le32 power; __le32 power;
__le32 resolution; __le32 resolution;
...@@ -111,13 +118,22 @@ struct scmi_msg_resp_sensor_axis_description { ...@@ -111,13 +118,22 @@ struct scmi_msg_resp_sensor_axis_description {
struct scmi_axis_descriptor { struct scmi_axis_descriptor {
__le32 id; __le32 id;
__le32 attributes_low; __le32 attributes_low;
#define SUPPORTS_EXTENDED_AXIS_NAMES(x) FIELD_GET(BIT(9), (x))
__le32 attributes_high; __le32 attributes_high;
u8 name[SCMI_MAX_STR_SIZE]; u8 name[SCMI_SHORT_NAME_MAX_SIZE];
__le32 resolution; __le32 resolution;
struct scmi_msg_resp_attrs attrs; struct scmi_msg_resp_attrs attrs;
} desc[]; } desc[];
}; };
struct scmi_msg_resp_sensor_axis_names_description {
__le32 num_axis_flags;
struct scmi_sensor_axis_name_descriptor {
__le32 axis_id;
u8 name[SCMI_MAX_STR_SIZE];
} desc[];
};
/* Base scmi_axis_descriptor size excluding extended attrs after name */ /* Base scmi_axis_descriptor size excluding extended attrs after name */
#define SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ 28 #define SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ 28
...@@ -231,335 +247,414 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph, ...@@ -231,335 +247,414 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
} }
static inline void scmi_parse_range_attrs(struct scmi_range_attrs *out, static inline void scmi_parse_range_attrs(struct scmi_range_attrs *out,
struct scmi_msg_resp_attrs *in) const struct scmi_msg_resp_attrs *in)
{ {
out->min_range = get_unaligned_le64((void *)&in->min_range_low); out->min_range = get_unaligned_le64((void *)&in->min_range_low);
out->max_range = get_unaligned_le64((void *)&in->max_range_low); out->max_range = get_unaligned_le64((void *)&in->max_range_low);
} }
static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph, struct scmi_sens_ipriv {
struct scmi_sensor_info *s) void *priv;
{ struct device *dev;
int ret, cnt; };
u32 desc_index = 0;
u16 num_returned, num_remaining;
struct scmi_xfer *ti;
struct scmi_msg_resp_sensor_list_update_intervals *buf;
struct scmi_msg_sensor_list_update_intervals *msg;
ret = ph->xops->xfer_get_init(ph, SENSOR_LIST_UPDATE_INTERVALS,
sizeof(*msg), 0, &ti);
if (ret)
return ret;
buf = ti->rx.buf;
do {
u32 flags;
msg = ti->tx.buf;
/* Set the number of sensors to be skipped/already read */
msg->id = cpu_to_le32(s->id);
msg->index = cpu_to_le32(desc_index);
ret = ph->xops->do_xfer(ph, ti); static void iter_intervals_prepare_message(void *message,
if (ret) unsigned int desc_index,
break; const void *p)
{
struct scmi_msg_sensor_list_update_intervals *msg = message;
const struct scmi_sensor_info *s;
flags = le32_to_cpu(buf->num_intervals_flags); s = ((const struct scmi_sens_ipriv *)p)->priv;
num_returned = NUM_INTERVALS_RETURNED(flags); /* Set the number of sensors to be skipped/already read */
num_remaining = NUM_INTERVALS_REMAINING(flags); msg->id = cpu_to_le32(s->id);
msg->index = cpu_to_le32(desc_index);
}
/* static int iter_intervals_update_state(struct scmi_iterator_state *st,
* Max intervals is not declared previously anywhere so we const void *response, void *p)
* assume it's returned+remaining. {
*/ u32 flags;
if (!s->intervals.count) { struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags); struct device *dev = ((struct scmi_sens_ipriv *)p)->dev;
s->intervals.count = num_returned + num_remaining; const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
/* segmented intervals are reported in one triplet */
if (s->intervals.segmented && flags = le32_to_cpu(r->num_intervals_flags);
(num_remaining || num_returned != 3)) { st->num_returned = NUM_INTERVALS_RETURNED(flags);
dev_err(ph->dev, st->num_remaining = NUM_INTERVALS_REMAINING(flags);
"Sensor ID:%d advertises an invalid segmented interval (%d)\n",
s->id, s->intervals.count); /*
* Max intervals is not declared previously anywhere so we
* assume it's returned+remaining on first call.
*/
if (!st->max_resources) {
s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags);
s->intervals.count = st->num_returned + st->num_remaining;
/* segmented intervals are reported in one triplet */
if (s->intervals.segmented &&
(st->num_remaining || st->num_returned != 3)) {
dev_err(dev,
"Sensor ID:%d advertises an invalid segmented interval (%d)\n",
s->id, s->intervals.count);
s->intervals.segmented = false;
s->intervals.count = 0;
return -EINVAL;
}
/* Direct allocation when exceeding pre-allocated */
if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) {
s->intervals.desc =
devm_kcalloc(dev,
s->intervals.count,
sizeof(*s->intervals.desc),
GFP_KERNEL);
if (!s->intervals.desc) {
s->intervals.segmented = false; s->intervals.segmented = false;
s->intervals.count = 0; s->intervals.count = 0;
ret = -EINVAL; return -ENOMEM;
break;
}
/* Direct allocation when exceeding pre-allocated */
if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) {
s->intervals.desc =
devm_kcalloc(ph->dev,
s->intervals.count,
sizeof(*s->intervals.desc),
GFP_KERNEL);
if (!s->intervals.desc) {
s->intervals.segmented = false;
s->intervals.count = 0;
ret = -ENOMEM;
break;
}
} }
} else if (desc_index + num_returned > s->intervals.count) {
dev_err(ph->dev,
"No. of update intervals can't exceed %d\n",
s->intervals.count);
ret = -EINVAL;
break;
} }
for (cnt = 0; cnt < num_returned; cnt++) st->max_resources = s->intervals.count;
s->intervals.desc[desc_index + cnt] = }
le32_to_cpu(buf->intervals[cnt]);
return 0;
}
desc_index += num_returned; static int
iter_intervals_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *p)
{
const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
ph->xops->reset_rx_to_maxsz(ph, ti); s->intervals.desc[st->desc_index + st->loop_idx] =
/* le32_to_cpu(r->intervals[st->loop_idx]);
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
*/
} while (num_returned && num_remaining);
ph->xops->xfer_put(ph, ti); return 0;
return ret;
} }
static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph,
struct scmi_sensor_info *s) struct scmi_sensor_info *s)
{ {
int ret, cnt; void *iter;
u32 desc_index = 0; struct scmi_msg_sensor_list_update_intervals *msg;
u16 num_returned, num_remaining; struct scmi_iterator_ops ops = {
struct scmi_xfer *te; .prepare_message = iter_intervals_prepare_message,
struct scmi_msg_resp_sensor_axis_description *buf; .update_state = iter_intervals_update_state,
struct scmi_msg_sensor_axis_description_get *msg; .process_response = iter_intervals_process_response,
};
struct scmi_sens_ipriv upriv = {
.priv = s,
.dev = ph->dev,
};
iter = ph->hops->iter_response_init(ph, &ops, s->intervals.count,
SENSOR_LIST_UPDATE_INTERVALS,
sizeof(*msg), &upriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
return ph->hops->iter_response_run(iter);
}
s->axis = devm_kcalloc(ph->dev, s->num_axis, static void iter_axes_desc_prepare_message(void *message,
sizeof(*s->axis), GFP_KERNEL); const unsigned int desc_index,
if (!s->axis) const void *priv)
return -ENOMEM; {
struct scmi_msg_sensor_axis_description_get *msg = message;
const struct scmi_sensor_info *s = priv;
ret = ph->xops->xfer_get_init(ph, SENSOR_AXIS_DESCRIPTION_GET, /* Set the number of sensors to be skipped/already read */
sizeof(*msg), 0, &te); msg->id = cpu_to_le32(s->id);
if (ret) msg->axis_desc_index = cpu_to_le32(desc_index);
return ret; }
buf = te->rx.buf; static int
do { iter_axes_desc_update_state(struct scmi_iterator_state *st,
u32 flags; const void *response, void *priv)
struct scmi_axis_descriptor *adesc; {
u32 flags;
const struct scmi_msg_resp_sensor_axis_description *r = response;
msg = te->tx.buf; flags = le32_to_cpu(r->num_axis_flags);
/* Set the number of sensors to be skipped/already read */ st->num_returned = NUM_AXIS_RETURNED(flags);
msg->id = cpu_to_le32(s->id); st->num_remaining = NUM_AXIS_REMAINING(flags);
msg->axis_desc_index = cpu_to_le32(desc_index); st->priv = (void *)&r->desc[0];
ret = ph->xops->do_xfer(ph, te); return 0;
if (ret) }
break;
flags = le32_to_cpu(buf->num_axis_flags); static int
num_returned = NUM_AXIS_RETURNED(flags); iter_axes_desc_process_response(const struct scmi_protocol_handle *ph,
num_remaining = NUM_AXIS_REMAINING(flags); const void *response,
struct scmi_iterator_state *st, void *priv)
{
u32 attrh, attrl;
struct scmi_sensor_axis_info *a;
size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ;
struct scmi_sensor_info *s = priv;
const struct scmi_axis_descriptor *adesc = st->priv;
if (desc_index + num_returned > s->num_axis) { attrl = le32_to_cpu(adesc->attributes_low);
dev_err(ph->dev, "No. of axis can't exceed %d\n",
s->num_axis);
break;
}
adesc = &buf->desc[0]; a = &s->axis[st->desc_index + st->loop_idx];
for (cnt = 0; cnt < num_returned; cnt++) { a->id = le32_to_cpu(adesc->id);
u32 attrh, attrl; a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
struct scmi_sensor_axis_info *a;
size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ;
attrl = le32_to_cpu(adesc->attributes_low); attrh = le32_to_cpu(adesc->attributes_high);
a->scale = S32_EXT(SENSOR_SCALE(attrh));
a->type = SENSOR_TYPE(attrh);
strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
a = &s->axis[desc_index + cnt]; if (a->extended_attrs) {
unsigned int ares = le32_to_cpu(adesc->resolution);
a->id = le32_to_cpu(adesc->id); a->resolution = SENSOR_RES(ares);
a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl); a->exponent = S32_EXT(SENSOR_RES_EXP(ares));
dsize += sizeof(adesc->resolution);
attrh = le32_to_cpu(adesc->attributes_high); scmi_parse_range_attrs(&a->attrs, &adesc->attrs);
a->scale = S32_EXT(SENSOR_SCALE(attrh)); dsize += sizeof(adesc->attrs);
a->type = SENSOR_TYPE(attrh); }
strlcpy(a->name, adesc->name, SCMI_MAX_STR_SIZE); st->priv = ((u8 *)adesc + dsize);
return 0;
}
if (a->extended_attrs) { static int
unsigned int ares = iter_axes_extended_name_update_state(struct scmi_iterator_state *st,
le32_to_cpu(adesc->resolution); const void *response, void *priv)
{
u32 flags;
const struct scmi_msg_resp_sensor_axis_names_description *r = response;
a->resolution = SENSOR_RES(ares); flags = le32_to_cpu(r->num_axis_flags);
a->exponent = st->num_returned = NUM_AXIS_RETURNED(flags);
S32_EXT(SENSOR_RES_EXP(ares)); st->num_remaining = NUM_AXIS_REMAINING(flags);
dsize += sizeof(adesc->resolution); st->priv = (void *)&r->desc[0];
scmi_parse_range_attrs(&a->attrs, return 0;
&adesc->attrs); }
dsize += sizeof(adesc->attrs);
}
adesc = (typeof(adesc))((u8 *)adesc + dsize); static int
} iter_axes_extended_name_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st,
void *priv)
{
struct scmi_sensor_axis_info *a;
const struct scmi_sensor_info *s = priv;
struct scmi_sensor_axis_name_descriptor *adesc = st->priv;
desc_index += num_returned; a = &s->axis[st->desc_index + st->loop_idx];
strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
st->priv = ++adesc;
ph->xops->reset_rx_to_maxsz(ph, te); return 0;
/* }
* check for both returned and remaining to avoid infinite
* loop due to buggy firmware
*/
} while (num_returned && num_remaining);
ph->xops->xfer_put(ph, te); static int
return ret; scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph,
struct scmi_sensor_info *s)
{
void *iter;
struct scmi_msg_sensor_axis_description_get *msg;
struct scmi_iterator_ops ops = {
.prepare_message = iter_axes_desc_prepare_message,
.update_state = iter_axes_extended_name_update_state,
.process_response = iter_axes_extended_name_process_response,
};
iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
SENSOR_AXIS_NAME_GET,
sizeof(*msg), s);
if (IS_ERR(iter))
return PTR_ERR(iter);
return ph->hops->iter_response_run(iter);
} }
static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph, static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
struct sensors_info *si) struct scmi_sensor_info *s,
u32 version)
{ {
int ret, cnt; int ret;
u32 desc_index = 0; void *iter;
u16 num_returned, num_remaining; struct scmi_msg_sensor_axis_description_get *msg;
struct scmi_xfer *t; struct scmi_iterator_ops ops = {
struct scmi_msg_resp_sensor_description *buf; .prepare_message = iter_axes_desc_prepare_message,
.update_state = iter_axes_desc_update_state,
.process_response = iter_axes_desc_process_response,
};
ret = ph->xops->xfer_get_init(ph, SENSOR_DESCRIPTION_GET, s->axis = devm_kcalloc(ph->dev, s->num_axis,
sizeof(__le32), 0, &t); sizeof(*s->axis), GFP_KERNEL);
if (!s->axis)
return -ENOMEM;
iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
SENSOR_AXIS_DESCRIPTION_GET,
sizeof(*msg), s);
if (IS_ERR(iter))
return PTR_ERR(iter);
ret = ph->hops->iter_response_run(iter);
if (ret) if (ret)
return ret; return ret;
buf = t->rx.buf; if (PROTOCOL_REV_MAJOR(version) >= 0x3)
ret = scmi_sensor_axis_extended_names_get(ph, s);
do {
struct scmi_sensor_descriptor *sdesc;
/* Set the number of sensors to be skipped/already read */ return ret;
put_unaligned_le32(desc_index, t->tx.buf); }
ret = ph->xops->do_xfer(ph, t); static void iter_sens_descr_prepare_message(void *message,
if (ret) unsigned int desc_index,
break; const void *priv)
{
struct scmi_msg_sensor_description *msg = message;
num_returned = le16_to_cpu(buf->num_returned); msg->desc_index = cpu_to_le32(desc_index);
num_remaining = le16_to_cpu(buf->num_remaining); }
if (desc_index + num_returned > si->num_sensors) { static int iter_sens_descr_update_state(struct scmi_iterator_state *st,
dev_err(ph->dev, "No. of sensors can't exceed %d", const void *response, void *priv)
si->num_sensors); {
break; const struct scmi_msg_resp_sensor_description *r = response;
}
sdesc = &buf->desc[0]; st->num_returned = le16_to_cpu(r->num_returned);
for (cnt = 0; cnt < num_returned; cnt++) { st->num_remaining = le16_to_cpu(r->num_remaining);
u32 attrh, attrl; st->priv = (void *)&r->desc[0];
struct scmi_sensor_info *s;
size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ;
s = &si->sensors[desc_index + cnt];
s->id = le32_to_cpu(sdesc->id);
attrl = le32_to_cpu(sdesc->attributes_low);
/* common bitfields parsing */
s->async = SUPPORTS_ASYNC_READ(attrl);
s->num_trip_points = NUM_TRIP_POINTS(attrl);
/**
* only SCMIv3.0 specific bitfield below.
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
if (s->timestamped)
s->tstamp_scale =
S32_EXT(SENSOR_TSTAMP_EXP(attrl));
s->extended_scalar_attrs =
SUPPORTS_EXTEND_ATTRS(attrl);
attrh = le32_to_cpu(sdesc->attributes_high);
/* common bitfields parsing */
s->scale = S32_EXT(SENSOR_SCALE(attrh));
s->type = SENSOR_TYPE(attrh);
/* Use pre-allocated pool wherever possible */
s->intervals.desc = s->intervals.prealloc_pool;
if (si->version == SCMIv2_SENSOR_PROTOCOL) {
s->intervals.segmented = false;
s->intervals.count = 1;
/*
* Convert SCMIv2.0 update interval format to
* SCMIv3.0 to be used as the common exposed
* descriptor, accessible via common macros.
*/
s->intervals.desc[0] =
(SENSOR_UPDATE_BASE(attrh) << 5) |
SENSOR_UPDATE_SCALE(attrh);
} else {
/*
* From SCMIv3.0 update intervals are retrieved
* via a dedicated (optional) command.
* Since the command is optional, on error carry
* on without any update interval.
*/
if (scmi_sensor_update_intervals(ph, s))
dev_dbg(ph->dev,
"Update Intervals not available for sensor ID:%d\n",
s->id);
}
/**
* only > SCMIv2.0 specific bitfield below.
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
s->num_axis = min_t(unsigned int,
SUPPORTS_AXIS(attrh) ?
SENSOR_AXIS_NUMBER(attrh) : 0,
SCMI_MAX_NUM_SENSOR_AXIS);
strlcpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE);
if (s->extended_scalar_attrs) {
s->sensor_power = le32_to_cpu(sdesc->power);
dsize += sizeof(sdesc->power);
/* Only for sensors reporting scalar values */
if (s->num_axis == 0) {
unsigned int sres =
le32_to_cpu(sdesc->resolution);
s->resolution = SENSOR_RES(sres);
s->exponent =
S32_EXT(SENSOR_RES_EXP(sres));
dsize += sizeof(sdesc->resolution);
scmi_parse_range_attrs(&s->scalar_attrs,
&sdesc->scalar_attrs);
dsize += sizeof(sdesc->scalar_attrs);
}
}
if (s->num_axis > 0) {
ret = scmi_sensor_axis_description(ph, s);
if (ret)
goto out;
}
sdesc = (typeof(sdesc))((u8 *)sdesc + dsize); return 0;
} }
desc_index += num_returned; static int
iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv)
ph->xops->reset_rx_to_maxsz(ph, t); {
int ret = 0;
u32 attrh, attrl;
size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ;
struct scmi_sensor_info *s;
struct sensors_info *si = priv;
const struct scmi_sensor_descriptor *sdesc = st->priv;
s = &si->sensors[st->desc_index + st->loop_idx];
s->id = le32_to_cpu(sdesc->id);
attrl = le32_to_cpu(sdesc->attributes_low);
/* common bitfields parsing */
s->async = SUPPORTS_ASYNC_READ(attrl);
s->num_trip_points = NUM_TRIP_POINTS(attrl);
/**
* only SCMIv3.0 specific bitfield below.
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
if (s->timestamped)
s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
s->extended_scalar_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
attrh = le32_to_cpu(sdesc->attributes_high);
/* common bitfields parsing */
s->scale = S32_EXT(SENSOR_SCALE(attrh));
s->type = SENSOR_TYPE(attrh);
/* Use pre-allocated pool wherever possible */
s->intervals.desc = s->intervals.prealloc_pool;
if (si->version == SCMIv2_SENSOR_PROTOCOL) {
s->intervals.segmented = false;
s->intervals.count = 1;
/*
* Convert SCMIv2.0 update interval format to
* SCMIv3.0 to be used as the common exposed
* descriptor, accessible via common macros.
*/
s->intervals.desc[0] = (SENSOR_UPDATE_BASE(attrh) << 5) |
SENSOR_UPDATE_SCALE(attrh);
} else {
/* /*
* check for both returned and remaining to avoid infinite * From SCMIv3.0 update intervals are retrieved
* loop due to buggy firmware * via a dedicated (optional) command.
* Since the command is optional, on error carry
* on without any update interval.
*/ */
} while (num_returned && num_remaining); if (scmi_sensor_update_intervals(ph, s))
dev_dbg(ph->dev,
"Update Intervals not available for sensor ID:%d\n",
s->id);
}
/**
* only > SCMIv2.0 specific bitfield below.
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
s->num_axis = min_t(unsigned int,
SUPPORTS_AXIS(attrh) ?
SENSOR_AXIS_NUMBER(attrh) : 0,
SCMI_MAX_NUM_SENSOR_AXIS);
strscpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE);
/*
* If supported overwrite short name with the extended
* one; on error just carry on and use already provided
* short name.
*/
if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attrl))
ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
s->name, SCMI_MAX_STR_SIZE);
if (s->extended_scalar_attrs) {
s->sensor_power = le32_to_cpu(sdesc->power);
dsize += sizeof(sdesc->power);
/* Only for sensors reporting scalar values */
if (s->num_axis == 0) {
unsigned int sres = le32_to_cpu(sdesc->resolution);
s->resolution = SENSOR_RES(sres);
s->exponent = S32_EXT(SENSOR_RES_EXP(sres));
dsize += sizeof(sdesc->resolution);
scmi_parse_range_attrs(&s->scalar_attrs,
&sdesc->scalar_attrs);
dsize += sizeof(sdesc->scalar_attrs);
}
}
if (s->num_axis > 0)
ret = scmi_sensor_axis_description(ph, s, si->version);
st->priv = ((u8 *)sdesc + dsize);
out:
ph->xops->xfer_put(ph, t);
return ret; return ret;
} }
static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph,
struct sensors_info *si)
{
void *iter;
struct scmi_iterator_ops ops = {
.prepare_message = iter_sens_descr_prepare_message,
.update_state = iter_sens_descr_update_state,
.process_response = iter_sens_descr_process_response,
};
iter = ph->hops->iter_response_init(ph, &ops, si->num_sensors,
SENSOR_DESCRIPTION_GET,
sizeof(__le32), si);
if (IS_ERR(iter))
return PTR_ERR(iter);
return ph->hops->iter_response_run(iter);
}
static inline int static inline int
scmi_sensor_request_notify(const struct scmi_protocol_handle *ph, u32 sensor_id, scmi_sensor_request_notify(const struct scmi_protocol_handle *ph, u32 sensor_id,
u8 message_id, bool enable) u8 message_id, bool enable)
...@@ -966,7 +1061,9 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph) ...@@ -966,7 +1061,9 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
int ret; int ret;
struct sensors_info *sinfo; struct sensors_info *sinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Sensor Version %d.%d\n", dev_dbg(ph->dev, "Sensor Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* System Control and Management Interface (SCMI) System Power Protocol * System Control and Management Interface (SCMI) System Power Protocol
* *
* Copyright (C) 2020-2021 ARM Ltd. * Copyright (C) 2020-2022 ARM Ltd.
*/ */
#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include "common.h" #include "protocols.h"
#include "notify.h" #include "notify.h"
#define SCMI_SYSTEM_NUM_SOURCES 1 #define SCMI_SYSTEM_NUM_SOURCES 1
...@@ -113,10 +113,13 @@ static const struct scmi_protocol_events system_protocol_events = { ...@@ -113,10 +113,13 @@ static const struct scmi_protocol_events system_protocol_events = {
static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
{ {
int ret;
u32 version; u32 version;
struct scmi_system_info *pinfo; struct scmi_system_info *pinfo;
ph->xops->version_get(ph, &version); ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "System Power Version %d.%d\n", dev_dbg(ph->dev, "System Power Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
/* /*
* System Control and Management Interface (SCMI) Voltage Protocol * System Control and Management Interface (SCMI) Voltage Protocol
* *
* Copyright (C) 2020-2021 ARM Ltd. * Copyright (C) 2020-2022 ARM Ltd.
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include "common.h" #include "protocols.h"
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) #define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
#define REMAINING_LEVELS_MASK GENMASK(31, 16) #define REMAINING_LEVELS_MASK GENMASK(31, 16)
...@@ -21,13 +21,16 @@ enum scmi_voltage_protocol_cmd { ...@@ -21,13 +21,16 @@ enum scmi_voltage_protocol_cmd {
VOLTAGE_CONFIG_GET = 0x6, VOLTAGE_CONFIG_GET = 0x6,
VOLTAGE_LEVEL_SET = 0x7, VOLTAGE_LEVEL_SET = 0x7,
VOLTAGE_LEVEL_GET = 0x8, VOLTAGE_LEVEL_GET = 0x8,
VOLTAGE_DOMAIN_NAME_GET = 0x09,
}; };
#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) #define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
struct scmi_msg_resp_domain_attributes { struct scmi_msg_resp_domain_attributes {
__le32 attr; __le32 attr;
u8 name[SCMI_MAX_STR_SIZE]; #define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30))
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
}; };
struct scmi_msg_cmd_describe_levels { struct scmi_msg_cmd_describe_levels {
...@@ -54,6 +57,11 @@ struct scmi_msg_cmd_level_set { ...@@ -54,6 +57,11 @@ struct scmi_msg_cmd_level_set {
__le32 voltage_level; __le32 voltage_level;
}; };
struct scmi_resp_voltage_level_set_complete {
__le32 domain_id;
__le32 voltage_level;
};
struct voltage_info { struct voltage_info {
unsigned int version; unsigned int version;
unsigned int num_domains; unsigned int num_domains;
...@@ -110,14 +118,100 @@ static int scmi_init_voltage_levels(struct device *dev, ...@@ -110,14 +118,100 @@ static int scmi_init_voltage_levels(struct device *dev,
return 0; return 0;
} }
struct scmi_volt_ipriv {
struct device *dev;
struct scmi_voltage_info *v;
};
static void iter_volt_levels_prepare_message(void *message,
unsigned int desc_index,
const void *priv)
{
struct scmi_msg_cmd_describe_levels *msg = message;
const struct scmi_volt_ipriv *p = priv;
msg->domain_id = cpu_to_le32(p->v->id);
msg->level_index = cpu_to_le32(desc_index);
}
static int iter_volt_levels_update_state(struct scmi_iterator_state *st,
const void *response, void *priv)
{
int ret = 0;
u32 flags;
const struct scmi_msg_resp_describe_levels *r = response;
struct scmi_volt_ipriv *p = priv;
flags = le32_to_cpu(r->flags);
st->num_returned = NUM_RETURNED_LEVELS(flags);
st->num_remaining = NUM_REMAINING_LEVELS(flags);
/* Allocate space for num_levels if not already done */
if (!p->v->num_levels) {
ret = scmi_init_voltage_levels(p->dev, p->v, st->num_returned,
st->num_remaining,
SUPPORTS_SEGMENTED_LEVELS(flags));
if (!ret)
st->max_resources = p->v->num_levels;
}
return ret;
}
static int
iter_volt_levels_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv)
{
s32 val;
const struct scmi_msg_resp_describe_levels *r = response;
struct scmi_volt_ipriv *p = priv;
val = (s32)le32_to_cpu(r->voltage[st->loop_idx]);
p->v->levels_uv[st->desc_index + st->loop_idx] = val;
if (val < 0)
p->v->negative_volts_allowed = true;
return 0;
}
static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph,
struct scmi_voltage_info *v)
{
int ret;
void *iter;
struct scmi_msg_cmd_describe_levels *msg;
struct scmi_iterator_ops ops = {
.prepare_message = iter_volt_levels_prepare_message,
.update_state = iter_volt_levels_update_state,
.process_response = iter_volt_levels_process_response,
};
struct scmi_volt_ipriv vpriv = {
.dev = ph->dev,
.v = v,
};
iter = ph->hops->iter_response_init(ph, &ops, v->num_levels,
VOLTAGE_DESCRIBE_LEVELS,
sizeof(*msg), &vpriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
ret = ph->hops->iter_response_run(iter);
if (ret) {
v->num_levels = 0;
devm_kfree(ph->dev, v->levels_uv);
}
return ret;
}
static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
struct voltage_info *vinfo) struct voltage_info *vinfo)
{ {
int ret, dom; int ret, dom;
struct scmi_xfer *td, *tl; struct scmi_xfer *td;
struct device *dev = ph->dev;
struct scmi_msg_resp_domain_attributes *resp_dom; struct scmi_msg_resp_domain_attributes *resp_dom;
struct scmi_msg_resp_describe_levels *resp_levels;
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES, ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
sizeof(__le32), sizeof(*resp_dom), &td); sizeof(__le32), sizeof(*resp_dom), &td);
...@@ -125,16 +219,8 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, ...@@ -125,16 +219,8 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
return ret; return ret;
resp_dom = td->rx.buf; resp_dom = td->rx.buf;
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
sizeof(__le64), 0, &tl);
if (ret)
goto outd;
resp_levels = tl->rx.buf;
for (dom = 0; dom < vinfo->num_domains; dom++) { for (dom = 0; dom < vinfo->num_domains; dom++) {
u32 desc_index = 0; u32 attributes;
u16 num_returned = 0, num_remaining = 0;
struct scmi_msg_cmd_describe_levels *cmd;
struct scmi_voltage_info *v; struct scmi_voltage_info *v;
/* Retrieve domain attributes at first ... */ /* Retrieve domain attributes at first ... */
...@@ -146,69 +232,31 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, ...@@ -146,69 +232,31 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
v = vinfo->domains + dom; v = vinfo->domains + dom;
v->id = dom; v->id = dom;
v->attributes = le32_to_cpu(resp_dom->attr); attributes = le32_to_cpu(resp_dom->attr);
strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
cmd = tl->tx.buf; /*
/* ...then retrieve domain levels descriptions */ * If supported overwrite short name with the extended one;
do { * on error just carry on and use already provided short name.
u32 flags; */
int cnt; if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
if (SUPPORTS_EXTENDED_NAMES(attributes))
cmd->domain_id = cpu_to_le32(v->id); ph->hops->extended_name_get(ph,
cmd->level_index = cpu_to_le32(desc_index); VOLTAGE_DOMAIN_NAME_GET,
ret = ph->xops->do_xfer(ph, tl); v->id, v->name,
if (ret) SCMI_MAX_STR_SIZE);
break; if (SUPPORTS_ASYNC_LEVEL_SET(attributes))
v->async_level_set = true;
flags = le32_to_cpu(resp_levels->flags);
num_returned = NUM_RETURNED_LEVELS(flags);
num_remaining = NUM_REMAINING_LEVELS(flags);
/* Allocate space for num_levels if not already done */
if (!v->num_levels) {
ret = scmi_init_voltage_levels(dev, v,
num_returned,
num_remaining,
SUPPORTS_SEGMENTED_LEVELS(flags));
if (ret)
break;
}
if (desc_index + num_returned > v->num_levels) {
dev_err(ph->dev,
"No. of voltage levels can't exceed %d\n",
v->num_levels);
ret = -EINVAL;
break;
}
for (cnt = 0; cnt < num_returned; cnt++) {
s32 val;
val =
(s32)le32_to_cpu(resp_levels->voltage[cnt]);
v->levels_uv[desc_index + cnt] = val;
if (val < 0)
v->negative_volts_allowed = true;
}
desc_index += num_returned;
ph->xops->reset_rx_to_maxsz(ph, tl);
/* check both to avoid infinite loop due to buggy fw */
} while (num_returned && num_remaining);
if (ret) {
v->num_levels = 0;
devm_kfree(dev, v->levels_uv);
} }
ret = scmi_voltage_levels_get(ph, v);
/* Skip invalid voltage descriptors */
if (ret)
continue;
ph->xops->reset_rx_to_maxsz(ph, td); ph->xops->reset_rx_to_maxsz(ph, td);
} }
ph->xops->xfer_put(ph, tl);
outd:
ph->xops->xfer_put(ph, td); ph->xops->xfer_put(ph, td);
return ret; return ret;
...@@ -271,12 +319,15 @@ static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph, ...@@ -271,12 +319,15 @@ static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
} }
static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 flags, s32 volt_uV) u32 domain_id,
enum scmi_voltage_level_mode mode,
s32 volt_uV)
{ {
int ret; int ret;
struct scmi_xfer *t; struct scmi_xfer *t;
struct voltage_info *vinfo = ph->get_priv(ph); struct voltage_info *vinfo = ph->get_priv(ph);
struct scmi_msg_cmd_level_set *cmd; struct scmi_msg_cmd_level_set *cmd;
struct scmi_voltage_info *v;
if (domain_id >= vinfo->num_domains) if (domain_id >= vinfo->num_domains)
return -EINVAL; return -EINVAL;
...@@ -286,12 +337,31 @@ static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, ...@@ -286,12 +337,31 @@ static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
if (ret) if (ret)
return ret; return ret;
v = vinfo->domains + domain_id;
cmd = t->tx.buf; cmd = t->tx.buf;
cmd->domain_id = cpu_to_le32(domain_id); cmd->domain_id = cpu_to_le32(domain_id);
cmd->flags = cpu_to_le32(flags);
cmd->voltage_level = cpu_to_le32(volt_uV); cmd->voltage_level = cpu_to_le32(volt_uV);
ret = ph->xops->do_xfer(ph, t); if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) {
cmd->flags = cpu_to_le32(0x0);
ret = ph->xops->do_xfer(ph, t);
} else {
cmd->flags = cpu_to_le32(0x1);
ret = ph->xops->do_xfer_with_response(ph, t);
if (!ret) {
struct scmi_resp_voltage_level_set_complete *resp;
resp = t->rx.buf;
if (le32_to_cpu(resp->domain_id) == domain_id)
dev_dbg(ph->dev,
"Voltage domain %d set async to %d\n",
v->id,
le32_to_cpu(resp->voltage_level));
else
ret = -EPROTO;
}
}
ph->xops->xfer_put(ph, t); ph->xops->xfer_put(ph, t);
return ret; return ret;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/types.h> #include <linux/types.h>
#define SCMI_MAX_STR_SIZE 16 #define SCMI_MAX_STR_SIZE 64
#define SCMI_MAX_NUM_RATES 16 #define SCMI_MAX_NUM_RATES 16
/** /**
...@@ -44,6 +44,8 @@ struct scmi_clock_info { ...@@ -44,6 +44,8 @@ struct scmi_clock_info {
char name[SCMI_MAX_STR_SIZE]; char name[SCMI_MAX_STR_SIZE];
unsigned int enable_latency; unsigned int enable_latency;
bool rate_discrete; bool rate_discrete;
bool rate_changed_notifications;
bool rate_change_requested_notifications;
union { union {
struct { struct {
int num_rates; int num_rates;
...@@ -146,7 +148,8 @@ struct scmi_perf_proto_ops { ...@@ -146,7 +148,8 @@ struct scmi_perf_proto_ops {
*/ */
struct scmi_power_proto_ops { struct scmi_power_proto_ops {
int (*num_domains_get)(const struct scmi_protocol_handle *ph); int (*num_domains_get)(const struct scmi_protocol_handle *ph);
char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain); const char *(*name_get)(const struct scmi_protocol_handle *ph,
u32 domain);
#define SCMI_POWER_STATE_TYPE_SHIFT 30 #define SCMI_POWER_STATE_TYPE_SHIFT 30
#define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1) #define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1)
#define SCMI_POWER_STATE_PARAM(type, id) \ #define SCMI_POWER_STATE_PARAM(type, id) \
...@@ -484,13 +487,19 @@ struct scmi_sensor_proto_ops { ...@@ -484,13 +487,19 @@ struct scmi_sensor_proto_ops {
*/ */
struct scmi_reset_proto_ops { struct scmi_reset_proto_ops {
int (*num_domains_get)(const struct scmi_protocol_handle *ph); int (*num_domains_get)(const struct scmi_protocol_handle *ph);
char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain); const char *(*name_get)(const struct scmi_protocol_handle *ph,
u32 domain);
int (*latency_get)(const struct scmi_protocol_handle *ph, u32 domain); int (*latency_get)(const struct scmi_protocol_handle *ph, u32 domain);
int (*reset)(const struct scmi_protocol_handle *ph, u32 domain); int (*reset)(const struct scmi_protocol_handle *ph, u32 domain);
int (*assert)(const struct scmi_protocol_handle *ph, u32 domain); int (*assert)(const struct scmi_protocol_handle *ph, u32 domain);
int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain); int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain);
}; };
enum scmi_voltage_level_mode {
SCMI_VOLTAGE_LEVEL_SET_AUTO,
SCMI_VOLTAGE_LEVEL_SET_SYNC,
};
/** /**
* struct scmi_voltage_info - describe one available SCMI Voltage Domain * struct scmi_voltage_info - describe one available SCMI Voltage Domain
* *
...@@ -503,7 +512,8 @@ struct scmi_reset_proto_ops { ...@@ -503,7 +512,8 @@ struct scmi_reset_proto_ops {
* supported voltage level * supported voltage level
* @negative_volts_allowed: True if any of the entries of @levels_uv represent * @negative_volts_allowed: True if any of the entries of @levels_uv represent
* a negative voltage. * a negative voltage.
* @attributes: represents Voltage Domain advertised attributes * @async_level_set: True when the voltage domain supports asynchronous level
* set commands.
* @name: name assigned to the Voltage Domain by platform * @name: name assigned to the Voltage Domain by platform
* @num_levels: number of total entries in @levels_uv. * @num_levels: number of total entries in @levels_uv.
* @levels_uv: array of entries describing the available voltage levels for * @levels_uv: array of entries describing the available voltage levels for
...@@ -513,7 +523,7 @@ struct scmi_voltage_info { ...@@ -513,7 +523,7 @@ struct scmi_voltage_info {
unsigned int id; unsigned int id;
bool segmented; bool segmented;
bool negative_volts_allowed; bool negative_volts_allowed;
unsigned int attributes; bool async_level_set;
char name[SCMI_MAX_STR_SIZE]; char name[SCMI_MAX_STR_SIZE];
unsigned int num_levels; unsigned int num_levels;
#define SCMI_VOLTAGE_SEGMENT_LOW 0 #define SCMI_VOLTAGE_SEGMENT_LOW 0
...@@ -544,7 +554,7 @@ struct scmi_voltage_proto_ops { ...@@ -544,7 +554,7 @@ struct scmi_voltage_proto_ops {
int (*config_get)(const struct scmi_protocol_handle *ph, u32 domain_id, int (*config_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
u32 *config); u32 *config);
int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain_id, int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
u32 flags, s32 volt_uV); enum scmi_voltage_level_mode mode, s32 volt_uV);
int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain_id, int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
s32 *volt_uV); s32 *volt_uV);
}; };
...@@ -742,6 +752,8 @@ void scmi_protocol_unregister(const struct scmi_protocol *proto); ...@@ -742,6 +752,8 @@ void scmi_protocol_unregister(const struct scmi_protocol *proto);
/* SCMI Notification API - Custom Event Reports */ /* SCMI Notification API - Custom Event Reports */
enum scmi_notification_events { enum scmi_notification_events {
SCMI_EVENT_POWER_STATE_CHANGED = 0x0, SCMI_EVENT_POWER_STATE_CHANGED = 0x0,
SCMI_EVENT_CLOCK_RATE_CHANGED = 0x0,
SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED = 0x1,
SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED = 0x0, SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED = 0x0,
SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED = 0x1, SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED = 0x1,
SCMI_EVENT_SENSOR_TRIP_POINT_EVENT = 0x0, SCMI_EVENT_SENSOR_TRIP_POINT_EVENT = 0x0,
...@@ -758,6 +770,13 @@ struct scmi_power_state_changed_report { ...@@ -758,6 +770,13 @@ struct scmi_power_state_changed_report {
unsigned int power_state; unsigned int power_state;
}; };
struct scmi_clock_rate_notif_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int clock_id;
unsigned long long rate;
};
struct scmi_system_power_state_notifier_report { struct scmi_system_power_state_notifier_report {
ktime_t timestamp; ktime_t timestamp;
unsigned int agent_id; unsigned int agent_id;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment