Commit 409d01fb authored by Arnd Bergmann's avatar Arnd Bergmann

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

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

ARM SCMI/SCPI updates for v5.9

The main addition for this time is the support for platform notifications.
SCMI protocol specification allows the platform to signal events to the
interested agents via notification messages. We are adding support for
the dispatch and delivery of such notifications to the interested users
inside the kernel.

Other than that, there are minor changes like checking and using the
fast_switch capability quering the firmware instead of doing it
unconditionally(using polling mode transfer), cosmetic trace update,
use of HAVE_ARM_SMCCC_DISCOVERY instead of ARM_PSCI_FW and a fix in
scmi clock registration logic for all the clocks with discrete rates.

* tag 'scmi-updates-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: Remove fixed size fields from reports/scmi_event_header
  firmware: arm_scmi: Remove unneeded __packed attribute
  firmware: arm_scmi: Remove zero-length array in SCMI notifications
  firmware: arm_scmi: Provide a missing function param description
  clk: scmi: Fix min and max rate when registering clocks with discrete rates
  firmware: arm_scmi: Keep the discrete clock rates sorted
  firmware: arm_scmi: Add base notifications support
  firmware: arm_scmi: Add reset notifications support
  firmware: arm_scmi: Add sensor notifications support
  firmware: arm_scmi: Add perf notifications support
  firmware: arm_scmi: Add power notifications support
  firmware: arm_scmi: Enable notification core
  firmware: arm_scmi: Add notification dispatch and delivery
  firmware: arm_scmi: Add notification callbacks-registration
  firmware: arm_scmi: Add notification protocol-registration
  firmware: arm_scmi: Fix SCMI genpd domain probing
  firmware: arm_scmi: Use HAVE_ARM_SMCCC_DISCOVERY instead of ARM_PSCI_FW
  cpufreq: arm_scmi: Set fast_switch_possible conditionally
  firmware: arm_scmi: Add fast_switch_possible() interface
  firmware: arm_scmi: Use signed integer to report transfer status

Link: https://lore.kernel.org/r/20200713161410.12324-1-sudeep.holla@arm.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 38d9dff1 72a5eb9d
......@@ -103,6 +103,8 @@ static const struct clk_ops scmi_clk_ops = {
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
{
int ret;
unsigned long min_rate, max_rate;
struct clk_init_data init = {
.flags = CLK_GET_RATE_NOCACHE,
.num_parents = 0,
......@@ -112,9 +114,23 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
sclk->hw.init = &init;
ret = devm_clk_hw_register(dev, &sclk->hw);
if (!ret)
clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate,
sclk->info->range.max_rate);
if (ret)
return ret;
if (sclk->info->rate_discrete) {
int num_rates = sclk->info->list.num_rates;
if (num_rates <= 0)
return -EINVAL;
min_rate = sclk->info->list.rates[0];
max_rate = sclk->info->list.rates[num_rates - 1];
} else {
min_rate = sclk->info->range.min_rate;
max_rate = sclk->info->range.max_rate;
}
clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate);
return ret;
}
......
......@@ -198,7 +198,8 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = latency;
policy->fast_switch_possible = true;
policy->fast_switch_possible =
handle->perf_ops->fast_switch_possible(handle, cpu_dev);
em_register_perf_domain(policy->cpus, nr_opp, &em_cb);
......
# SPDX-License-Identifier: GPL-2.0-only
obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o
scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-driver-y = driver.o notify.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
......@@ -5,7 +5,15 @@
* Copyright (C) 2018 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt
#include <linux/scmi_protocol.h>
#include "common.h"
#include "notify.h"
#define SCMI_BASE_NUM_SOURCES 1
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
enum scmi_base_protocol_cmd {
BASE_DISCOVER_VENDOR = 0x3,
......@@ -19,16 +27,25 @@ enum scmi_base_protocol_cmd {
BASE_RESET_AGENT_CONFIGURATION = 0xb,
};
enum scmi_base_protocol_notify {
BASE_ERROR_EVENT = 0x0,
};
struct scmi_msg_resp_base_attributes {
u8 num_protocols;
u8 num_agents;
__le16 reserved;
};
struct scmi_msg_base_error_notify {
__le32 event_control;
#define BASE_TP_NOTIFY_ALL BIT(0)
};
struct scmi_base_error_notify_payld {
__le32 agent_id;
__le32 error_status;
#define IS_FATAL_ERROR(x) ((x) & BIT(31))
#define ERROR_CMD_COUNT(x) FIELD_GET(GENMASK(9, 0), (x))
__le64 msg_reports[SCMI_BASE_MAX_CMD_ERR_COUNT];
};
/**
* scmi_base_attributes_get() - gets the implementation details
* that are associated with the base protocol.
......@@ -222,6 +239,83 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
return ret;
}
static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
{
int ret;
u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0;
struct scmi_xfer *t;
struct scmi_msg_base_error_notify *cfg;
ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS,
SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t);
if (ret)
return ret;
cfg = t->tx.buf;
cfg->event_control = cpu_to_le32(evt_cntl);
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_base_set_notify_enabled(const struct scmi_handle *handle,
u8 evt_id, u32 src_id, bool enable)
{
int ret;
ret = scmi_base_error_notify(handle, enable);
if (ret)
pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret);
return ret;
}
static void *scmi_base_fill_custom_report(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
int i;
const struct scmi_base_error_notify_payld *p = payld;
struct scmi_base_error_report *r = report;
/*
* BaseError notification payload is variable in size but
* up to a maximum length determined by the struct ponted by p.
* Instead payld_sz is the effective length of this notification
* payload so cannot be greater of the maximum allowed size as
* pointed by p.
*/
if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz)
return NULL;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status));
r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status));
for (i = 0; i < r->cmd_count; i++)
r->reports[i] = le64_to_cpu(p->msg_reports[i]);
*src_id = 0;
return r;
}
static const struct scmi_event base_events[] = {
{
.id = SCMI_EVENT_BASE_ERROR_EVENT,
.max_payld_sz = sizeof(struct scmi_base_error_notify_payld),
.max_report_sz = sizeof(struct scmi_base_error_report) +
SCMI_BASE_MAX_CMD_ERR_COUNT * sizeof(u64),
},
};
static const struct scmi_event_ops base_event_ops = {
.set_notify_enabled = scmi_base_set_notify_enabled,
.fill_custom_report = scmi_base_fill_custom_report,
};
int scmi_base_protocol_init(struct scmi_handle *h)
{
int id, ret;
......@@ -256,6 +350,12 @@ int scmi_base_protocol_init(struct scmi_handle *h)
dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
rev->num_agents);
scmi_register_protocol_events(handle, SCMI_PROTOCOL_BASE,
(4 * SCMI_PROTO_QUEUE_SZ),
&base_event_ops, base_events,
ARRAY_SIZE(base_events),
SCMI_BASE_NUM_SOURCES);
for (id = 0; id < rev->num_agents; id++) {
scmi_base_discover_agent_get(handle, id, name);
dev_dbg(dev, "Agent %d: %s\n", id, name);
......
......@@ -5,6 +5,8 @@
* Copyright (C) 2018 ARM Ltd.
*/
#include <linux/sort.h>
#include "common.h"
enum scmi_clock_protocol_cmd {
......@@ -121,11 +123,23 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
return ret;
}
static int rate_cmp_func(const void *_r1, const void *_r2)
{
const u64 *r1 = _r1, *r2 = _r2;
if (*r1 < *r2)
return -1;
else if (*r1 == *r2)
return 0;
else
return 1;
}
static int
scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
struct scmi_clock_info *clk)
{
u64 *rate;
u64 *rate = 0;
int ret, cnt;
bool rate_discrete = false;
u32 tot_rate_cnt = 0, rates_flag;
......@@ -184,8 +198,10 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
*/
} while (num_returned && num_remaining);
if (rate_discrete)
if (rate_discrete && rate) {
clk->list.num_rates = tot_rate_cnt;
sort(rate, tot_rate_cnt, sizeof(*rate), rate_cmp_func, NULL);
}
clk->rate_discrete = rate_discrete;
......
......@@ -6,6 +6,8 @@
*
* Copyright (C) 2018 ARM Ltd.
*/
#ifndef _SCMI_COMMON_H
#define _SCMI_COMMON_H
#include <linux/bitfield.h>
#include <linux/completion.h>
......@@ -235,3 +237,5 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer);
#endif /* _SCMI_COMMON_H */
......@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include "common.h"
#include "notify.h"
#define CREATE_TRACE_POINTS
#include <trace/events/scmi.h>
......@@ -208,7 +209,9 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
struct device *dev = cinfo->dev;
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
struct scmi_xfers_info *minfo = &info->rx_minfo;
ktime_t ts;
ts = ktime_get_boottime();
xfer = scmi_xfer_get(cinfo->handle, minfo);
if (IS_ERR(xfer)) {
dev_err(dev, "failed to get free message slot (%ld)\n",
......@@ -221,6 +224,8 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
scmi_dump_header_dbg(dev, &xfer->hdr);
info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
xfer);
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
......@@ -392,8 +397,7 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
info->desc->ops->mark_txdone(cinfo, ret);
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.status);
xfer->hdr.protocol_id, xfer->hdr.seq, ret);
return ret;
}
......@@ -789,6 +793,9 @@ static int scmi_probe(struct platform_device *pdev)
if (ret)
return ret;
if (scmi_notification_init(handle))
dev_err(dev, "SCMI Notifications NOT available.\n");
ret = scmi_base_protocol_init(handle);
if (ret) {
dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
......@@ -831,6 +838,8 @@ static int scmi_remove(struct platform_device *pdev)
struct scmi_info *info = platform_get_drvdata(pdev);
struct idr *idr = &info->tx_idr;
scmi_notification_exit(&info->handle);
mutex_lock(&scmi_list_mutex);
if (info->users)
ret = -EBUSY;
......@@ -901,7 +910,7 @@ ATTRIBUTE_GROUPS(versions);
/* Each compatible listed below must have descriptor associated with it */
static const struct of_device_id scmi_of_match[] = {
{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
#ifdef CONFIG_ARM_PSCI_FW
#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
#endif
{ /* Sentinel */ },
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* System Control and Management Interface (SCMI) Message Protocol
* notification header file containing some definitions, structures
* and function prototypes related to SCMI Notification handling.
*
* Copyright (C) 2020 ARM Ltd.
*/
#ifndef _SCMI_NOTIFY_H
#define _SCMI_NOTIFY_H
#include <linux/device.h>
#include <linux/ktime.h>
#include <linux/types.h>
#define SCMI_PROTO_QUEUE_SZ 4096
/**
* struct scmi_event - Describes an event to be supported
* @id: Event ID
* @max_payld_sz: Max possible size for the payload of a notification message
* @max_report_sz: Max possible size for the report of a notification message
*
* Each SCMI protocol, during its initialization phase, can describe the events
* it wishes to support in a few struct scmi_event and pass them to the core
* using scmi_register_protocol_events().
*/
struct scmi_event {
u8 id;
size_t max_payld_sz;
size_t max_report_sz;
};
/**
* struct scmi_event_ops - Protocol helpers called by the notification core.
* @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
* using the proper custom protocol commands.
* Return 0 on Success
* @fill_custom_report: fills a custom event report from the provided
* event message payld identifying the event
* specific src_id.
* Return NULL on failure otherwise @report now fully
* populated
*
* Context: Helpers described in &struct scmi_event_ops are called only in
* process context.
*/
struct scmi_event_ops {
int (*set_notify_enabled)(const struct scmi_handle *handle,
u8 evt_id, u32 src_id, bool enabled);
void *(*fill_custom_report)(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id);
};
int scmi_notification_init(struct scmi_handle *handle);
void scmi_notification_exit(struct scmi_handle *handle);
int scmi_register_protocol_events(const struct scmi_handle *handle,
u8 proto_id, size_t queue_sz,
const struct scmi_event_ops *ops,
const struct scmi_event *evt, int num_events,
int num_sources);
int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
const void *buf, size_t len, ktime_t ts);
#endif /* _SCMI_NOTIFY_H */
......@@ -5,15 +5,19 @@
* Copyright (C) 2018 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
#include <linux/bits.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/scmi_protocol.h>
#include <linux/sort.h>
#include "common.h"
#include "notify.h"
enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3,
......@@ -27,11 +31,6 @@ enum scmi_performance_protocol_cmd {
PERF_DESCRIBE_FASTCHANNEL = 0xb,
};
enum scmi_performance_protocol_notify {
PERFORMANCE_LIMITS_CHANGED = 0x0,
PERFORMANCE_LEVEL_CHANGED = 0x1,
};
struct scmi_opp {
u32 perf;
u32 power;
......@@ -86,6 +85,19 @@ struct scmi_perf_notify_level_or_limits {
__le32 notify_enable;
};
struct scmi_perf_limits_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 range_max;
__le32 range_min;
};
struct scmi_perf_level_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 performance_level;
};
struct scmi_msg_resp_perf_describe_levels {
__le16 num_returned;
__le16 num_remaining;
......@@ -158,6 +170,11 @@ struct scmi_perf_info {
struct perf_dom_info *dom_info;
};
static enum scmi_performance_protocol_cmd evt_2_cmd[] = {
PERF_NOTIFY_LIMITS,
PERF_NOTIFY_LEVEL,
};
static int scmi_perf_attributes_get(const struct scmi_handle *handle,
struct scmi_perf_info *pi)
{
......@@ -488,6 +505,29 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
return scmi_perf_mb_level_get(handle, domain, level, poll);
}
static int scmi_perf_level_limits_notify(const struct scmi_handle *handle,
u32 domain, int message_id,
bool enable)
{
int ret;
struct scmi_xfer *t;
struct scmi_perf_notify_level_or_limits *notify;
ret = scmi_xfer_get_init(handle, message_id, SCMI_PROTOCOL_PERF,
sizeof(*notify), 0, &t);
if (ret)
return ret;
notify = t->tx.buf;
notify->domain = cpu_to_le32(domain);
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
{
if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
......@@ -697,6 +737,17 @@ static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
return ret;
}
static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
struct device *dev)
{
struct perf_dom_info *dom;
struct scmi_perf_info *pi = handle->perf_priv;
dom = pi->dom_info + scmi_dev_domain_id(dev);
return dom->fc_info && dom->fc_info->level_set_addr;
}
static struct scmi_perf_ops perf_ops = {
.limits_set = scmi_perf_limits_set,
.limits_get = scmi_perf_limits_get,
......@@ -708,6 +759,90 @@ static struct scmi_perf_ops perf_ops = {
.freq_set = scmi_dvfs_freq_set,
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible,
};
static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
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_perf_level_limits_notify(handle, 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_perf_fill_custom_report(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
void *rep = NULL;
switch (evt_id) {
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
{
const struct scmi_perf_limits_notify_payld *p = payld;
struct scmi_perf_limits_report *r = report;
if (sizeof(*p) != payld_sz)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->range_max = le32_to_cpu(p->range_max);
r->range_min = le32_to_cpu(p->range_min);
*src_id = r->domain_id;
rep = r;
break;
}
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
{
const struct scmi_perf_level_notify_payld *p = payld;
struct scmi_perf_level_report *r = report;
if (sizeof(*p) != payld_sz)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->performance_level = le32_to_cpu(p->performance_level);
*src_id = r->domain_id;
rep = r;
break;
}
default:
break;
}
return rep;
}
static const struct scmi_event perf_events[] = {
{
.id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
.max_payld_sz = sizeof(struct scmi_perf_limits_notify_payld),
.max_report_sz = sizeof(struct scmi_perf_limits_report),
},
{
.id = SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED,
.max_payld_sz = sizeof(struct scmi_perf_level_notify_payld),
.max_report_sz = sizeof(struct scmi_perf_level_report),
},
};
static const struct scmi_event_ops perf_event_ops = {
.set_notify_enabled = scmi_perf_set_notify_enabled,
.fill_custom_report = scmi_perf_fill_custom_report,
};
static int scmi_perf_protocol_init(struct scmi_handle *handle)
......@@ -742,6 +877,12 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
}
scmi_register_protocol_events(handle,
SCMI_PROTOCOL_PERF, SCMI_PROTO_QUEUE_SZ,
&perf_event_ops, perf_events,
ARRAY_SIZE(perf_events),
pinfo->num_domains);
pinfo->version = version;
handle->perf_ops = &perf_ops;
handle->perf_priv = pinfo;
......
......@@ -5,19 +5,18 @@
* Copyright (C) 2018 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
#include <linux/scmi_protocol.h>
#include "common.h"
#include "notify.h"
enum scmi_power_protocol_cmd {
POWER_DOMAIN_ATTRIBUTES = 0x3,
POWER_STATE_SET = 0x4,
POWER_STATE_GET = 0x5,
POWER_STATE_NOTIFY = 0x6,
POWER_STATE_CHANGE_REQUESTED_NOTIFY = 0x7,
};
enum scmi_power_protocol_notify {
POWER_STATE_CHANGED = 0x0,
POWER_STATE_CHANGE_REQUESTED = 0x1,
};
struct scmi_msg_resp_power_attributes {
......@@ -48,6 +47,12 @@ struct scmi_power_state_notify {
__le32 notify_enable;
};
struct scmi_power_state_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 power_state;
};
struct power_dom_info {
bool state_set_sync;
bool state_set_async;
......@@ -186,6 +191,75 @@ static struct scmi_power_ops power_ops = {
.state_get = scmi_power_state_get,
};
static int scmi_power_request_notify(const struct scmi_handle *handle,
u32 domain, bool enable)
{
int ret;
struct scmi_xfer *t;
struct scmi_power_state_notify *notify;
ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY,
SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
if (ret)
return ret;
notify = t->tx.buf;
notify->domain = cpu_to_le32(domain);
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_power_set_notify_enabled(const struct scmi_handle *handle,
u8 evt_id, u32 src_id, bool enable)
{
int ret;
ret = scmi_power_request_notify(handle, src_id, enable);
if (ret)
pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
evt_id, src_id, ret);
return ret;
}
static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
const struct scmi_power_state_notify_payld *p = payld;
struct scmi_power_state_changed_report *r = report;
if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)
return NULL;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->power_state = le32_to_cpu(p->power_state);
*src_id = r->domain_id;
return r;
}
static const struct scmi_event power_events[] = {
{
.id = SCMI_EVENT_POWER_STATE_CHANGED,
.max_payld_sz = sizeof(struct scmi_power_state_notify_payld),
.max_report_sz =
sizeof(struct scmi_power_state_changed_report),
},
};
static const struct scmi_event_ops power_event_ops = {
.set_notify_enabled = scmi_power_set_notify_enabled,
.fill_custom_report = scmi_power_fill_custom_report,
};
static int scmi_power_protocol_init(struct scmi_handle *handle)
{
int domain;
......@@ -214,6 +288,12 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
scmi_power_domain_attributes_get(handle, domain, dom);
}
scmi_register_protocol_events(handle,
SCMI_PROTOCOL_POWER, SCMI_PROTO_QUEUE_SZ,
&power_event_ops, power_events,
ARRAY_SIZE(power_events),
pinfo->num_domains);
pinfo->version = version;
handle->power_ops = &power_ops;
handle->power_priv = pinfo;
......
......@@ -5,7 +5,12 @@
* Copyright (C) 2019 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
#include <linux/scmi_protocol.h>
#include "common.h"
#include "notify.h"
enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3,
......@@ -13,10 +18,6 @@ enum scmi_reset_protocol_cmd {
RESET_NOTIFY = 0x5,
};
enum scmi_reset_protocol_notify {
RESET_ISSUED = 0x0,
};
#define NUM_RESET_DOMAIN_MASK 0xffff
#define RESET_NOTIFY_ENABLE BIT(0)
......@@ -40,6 +41,18 @@ struct scmi_msg_reset_domain_reset {
#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE)
};
struct scmi_msg_reset_notify {
__le32 id;
__le32 event_control;
#define RESET_TP_NOTIFY_ALL BIT(0)
};
struct scmi_reset_issued_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 reset_state;
};
struct reset_dom_info {
bool async_reset;
bool reset_notify;
......@@ -190,6 +203,75 @@ static struct scmi_reset_ops reset_ops = {
.deassert = scmi_reset_domain_deassert,
};
static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id,
bool enable)
{
int ret;
u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;
struct scmi_xfer *t;
struct scmi_msg_reset_notify *cfg;
ret = scmi_xfer_get_init(handle, RESET_NOTIFY,
SCMI_PROTOCOL_RESET, sizeof(*cfg), 0, &t);
if (ret)
return ret;
cfg = t->tx.buf;
cfg->id = cpu_to_le32(domain_id);
cfg->event_control = cpu_to_le32(evt_cntl);
ret = scmi_do_xfer(handle, t);
scmi_xfer_put(handle, t);
return ret;
}
static int scmi_reset_set_notify_enabled(const struct scmi_handle *handle,
u8 evt_id, u32 src_id, bool enable)
{
int ret;
ret = scmi_reset_notify(handle, src_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_reset_fill_custom_report(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
const struct scmi_reset_issued_notify_payld *p = payld;
struct scmi_reset_issued_report *r = report;
if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz)
return NULL;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->reset_state = le32_to_cpu(p->reset_state);
*src_id = r->domain_id;
return r;
}
static const struct scmi_event reset_events[] = {
{
.id = SCMI_EVENT_RESET_ISSUED,
.max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld),
.max_report_sz = sizeof(struct scmi_reset_issued_report),
},
};
static const struct scmi_event_ops reset_event_ops = {
.set_notify_enabled = scmi_reset_set_notify_enabled,
.fill_custom_report = scmi_reset_fill_custom_report,
};
static int scmi_reset_protocol_init(struct scmi_handle *handle)
{
int domain;
......@@ -218,6 +300,12 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
scmi_reset_domain_attributes_get(handle, domain, dom);
}
scmi_register_protocol_events(handle,
SCMI_PROTOCOL_RESET, SCMI_PROTO_QUEUE_SZ,
&reset_event_ops, reset_events,
ARRAY_SIZE(reset_events),
pinfo->num_domains);
pinfo->version = version;
handle->reset_ops = &reset_ops;
handle->reset_priv = pinfo;
......
......@@ -85,7 +85,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
for (i = 0; i < num_domains; i++, scmi_pd++) {
u32 state;
domains[i] = &scmi_pd->genpd;
if (handle->power_ops->state_get(handle, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
}
scmi_pd->domain = i;
scmi_pd->handle = handle;
......@@ -94,13 +97,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
if (handle->power_ops->state_get(handle, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
}
pm_genpd_init(&scmi_pd->genpd, NULL,
state == SCMI_POWER_STATE_GENERIC_OFF);
domains[i] = &scmi_pd->genpd;
}
scmi_pd_data->domains = domains;
......
......@@ -5,7 +5,12 @@
* Copyright (C) 2018 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
#include <linux/scmi_protocol.h>
#include "common.h"
#include "notify.h"
enum scmi_sensor_protocol_cmd {
SENSOR_DESCRIPTION_GET = 0x3,
......@@ -14,10 +19,6 @@ enum scmi_sensor_protocol_cmd {
SENSOR_READING_GET = 0x6,
};
enum scmi_sensor_protocol_notify {
SENSOR_TRIP_POINT_EVENT = 0x0,
};
struct scmi_msg_resp_sensor_attributes {
__le16 num_sensors;
u8 max_requests;
......@@ -71,6 +72,12 @@ struct scmi_msg_sensor_reading_get {
#define SENSOR_READ_ASYNC BIT(0)
};
struct scmi_sensor_trip_notify_payld {
__le32 agent_id;
__le32 sensor_id;
__le32 trip_point_desc;
};
struct sensors_info {
u32 version;
int num_sensors;
......@@ -271,11 +278,57 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle)
static struct scmi_sensor_ops sensor_ops = {
.count_get = scmi_sensor_count_get,
.info_get = scmi_sensor_info_get,
.trip_point_notify = scmi_sensor_trip_point_notify,
.trip_point_config = scmi_sensor_trip_point_config,
.reading_get = scmi_sensor_reading_get,
};
static int scmi_sensor_set_notify_enabled(const struct scmi_handle *handle,
u8 evt_id, u32 src_id, bool enable)
{
int ret;
ret = scmi_sensor_trip_point_notify(handle, src_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_sensor_fill_custom_report(const struct scmi_handle *handle,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
const struct scmi_sensor_trip_notify_payld *p = payld;
struct scmi_sensor_trip_point_report *r = report;
if (evt_id != SCMI_EVENT_SENSOR_TRIP_POINT_EVENT ||
sizeof(*p) != payld_sz)
return NULL;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->sensor_id = le32_to_cpu(p->sensor_id);
r->trip_point_desc = le32_to_cpu(p->trip_point_desc);
*src_id = r->sensor_id;
return r;
}
static const struct scmi_event sensor_events[] = {
{
.id = SCMI_EVENT_SENSOR_TRIP_POINT_EVENT,
.max_payld_sz = sizeof(struct scmi_sensor_trip_notify_payld),
.max_report_sz = sizeof(struct scmi_sensor_trip_point_report),
},
};
static const struct scmi_event_ops sensor_event_ops = {
.set_notify_enabled = scmi_sensor_set_notify_enabled,
.fill_custom_report = scmi_sensor_fill_custom_report,
};
static int scmi_sensors_protocol_init(struct scmi_handle *handle)
{
u32 version;
......@@ -299,6 +352,12 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
scmi_sensor_description_get(handle, sinfo);
scmi_register_protocol_events(handle,
SCMI_PROTOCOL_SENSOR, SCMI_PROTO_QUEUE_SZ,
&sensor_event_ops, sensor_events,
ARRAY_SIZE(sensor_events),
sinfo->num_sensors);
sinfo->version = version;
handle->sensor_ops = &sensor_ops;
handle->sensor_priv = sinfo;
......
......@@ -21,6 +21,7 @@
*
* @cinfo: SCMI channel info
* @shmem: Transmit/Receive shared memory area
* @shmem_lock: Lock to protect access to Tx/Rx shared memory area
* @func_id: smc/hvc call function id
*/
......
......@@ -9,6 +9,7 @@
#define _LINUX_SCMI_PROTOCOL_H
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/types.h>
#define SCMI_MAX_STR_SIZE 16
......@@ -118,6 +119,8 @@ struct scmi_perf_ops {
unsigned long *rate, bool poll);
int (*est_power_get)(const struct scmi_handle *handle, u32 domain,
unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_handle *handle,
struct device *dev);
};
/**
......@@ -173,18 +176,13 @@ enum scmi_sensor_class {
*
* @count_get: get the count of sensors provided by SCMI
* @info_get: get the information of the specified sensor
* @trip_point_notify: control notifications on cross-over events for
* the trip-points
* @trip_point_config: selects and configures a trip-point of interest
* @reading_get: gets the current value of the sensor
*/
struct scmi_sensor_ops {
int (*count_get)(const struct scmi_handle *handle);
const struct scmi_sensor_info *(*info_get)
(const struct scmi_handle *handle, u32 sensor_id);
int (*trip_point_notify)(const struct scmi_handle *handle,
u32 sensor_id, bool enable);
int (*trip_point_config)(const struct scmi_handle *handle,
u32 sensor_id, u8 trip_id, u64 trip_value);
int (*reading_get)(const struct scmi_handle *handle, u32 sensor_id,
......@@ -211,6 +209,49 @@ struct scmi_reset_ops {
int (*deassert)(const struct scmi_handle *handle, u32 domain);
};
/**
* struct scmi_notify_ops - represents notifications' operations provided by
* SCMI core
* @register_event_notifier: Register a notifier_block for the requested event
* @unregister_event_notifier: Unregister a notifier_block for the requested
* event
*
* A user can register/unregister its own notifier_block against the wanted
* platform instance regarding the desired event identified by the
* tuple: (proto_id, evt_id, src_id) using the provided register/unregister
* interface where:
*
* @handle: The handle identifying the platform instance to use
* @proto_id: The protocol ID as in SCMI Specification
* @evt_id: The message ID of the desired event as in SCMI Specification
* @src_id: A pointer to the desired source ID if different sources are
* possible for the protocol (like domain_id, sensor_id...etc)
*
* @src_id can be provided as NULL if it simply does NOT make sense for
* the protocol at hand, OR if the user is explicitly interested in
* receiving notifications from ANY existent source associated to the
* specified proto_id / evt_id.
*
* Received notifications are finally delivered to the registered users,
* invoking the callback provided with the notifier_block *nb as follows:
*
* int user_cb(nb, evt_id, report)
*
* with:
*
* @nb: The notifier block provided by the user
* @evt_id: The message ID of the delivered event
* @report: A custom struct describing the specific event delivered
*/
struct scmi_notify_ops {
int (*register_event_notifier)(const struct scmi_handle *handle,
u8 proto_id, u8 evt_id, u32 *src_id,
struct notifier_block *nb);
int (*unregister_event_notifier)(const struct scmi_handle *handle,
u8 proto_id, u8 evt_id, u32 *src_id,
struct notifier_block *nb);
};
/**
* struct scmi_handle - Handle returned to ARM SCMI clients for usage.
*
......@@ -221,6 +262,7 @@ struct scmi_reset_ops {
* @clk_ops: pointer to set of clock protocol operations
* @sensor_ops: pointer to set of sensor protocol operations
* @reset_ops: pointer to set of reset protocol operations
* @notify_ops: pointer to set of notifications related operations
* @perf_priv: pointer to private data structure specific to performance
* protocol(for internal use only)
* @clk_priv: pointer to private data structure specific to clock
......@@ -231,6 +273,8 @@ struct scmi_reset_ops {
* protocol(for internal use only)
* @reset_priv: pointer to private data structure specific to reset
* protocol(for internal use only)
* @notify_priv: pointer to private data structure specific to notifications
* (for internal use only)
*/
struct scmi_handle {
struct device *dev;
......@@ -240,12 +284,14 @@ struct scmi_handle {
struct scmi_power_ops *power_ops;
struct scmi_sensor_ops *sensor_ops;
struct scmi_reset_ops *reset_ops;
struct scmi_notify_ops *notify_ops;
/* for protocol internal use */
void *perf_priv;
void *clk_priv;
void *power_priv;
void *sensor_priv;
void *reset_priv;
void *notify_priv;
};
enum scmi_std_protocol {
......@@ -324,4 +370,58 @@ typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
void scmi_protocol_unregister(int protocol_id);
/* SCMI Notification API - Custom Event Reports */
enum scmi_notification_events {
SCMI_EVENT_POWER_STATE_CHANGED = 0x0,
SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED = 0x0,
SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED = 0x1,
SCMI_EVENT_SENSOR_TRIP_POINT_EVENT = 0x0,
SCMI_EVENT_RESET_ISSUED = 0x0,
SCMI_EVENT_BASE_ERROR_EVENT = 0x0,
};
struct scmi_power_state_changed_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int domain_id;
unsigned int power_state;
};
struct scmi_perf_limits_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int domain_id;
unsigned int range_max;
unsigned int range_min;
};
struct scmi_perf_level_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int domain_id;
unsigned int performance_level;
};
struct scmi_sensor_trip_point_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int sensor_id;
unsigned int trip_point_desc;
};
struct scmi_reset_issued_report {
ktime_t timestamp;
unsigned int agent_id;
unsigned int domain_id;
unsigned int reset_state;
};
struct scmi_base_error_report {
ktime_t timestamp;
unsigned int agent_id;
bool fatal;
unsigned int cmd_count;
unsigned long long reports[];
};
#endif /* _LINUX_SCMI_PROTOCOL_H */
......@@ -35,7 +35,7 @@ TRACE_EVENT(scmi_xfer_begin,
TRACE_EVENT(scmi_xfer_end,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
u32 status),
int status),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, status),
TP_STRUCT__entry(
......@@ -43,7 +43,7 @@ TRACE_EVENT(scmi_xfer_end,
__field(u8, msg_id)
__field(u8, protocol_id)
__field(u16, seq)
__field(u32, status)
__field(int, status)
),
TP_fast_assign(
......@@ -54,7 +54,7 @@ TRACE_EVENT(scmi_xfer_end,
__entry->status = status;
),
TP_printk("transfer_id=%d msg_id=%u protocol_id=%u seq=%u status=%u",
TP_printk("transfer_id=%d msg_id=%u protocol_id=%u seq=%u status=%d",
__entry->transfer_id, __entry->msg_id, __entry->protocol_id,
__entry->seq, __entry->status)
);
......
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