Commit 965af1cf authored by Olof Johansson's avatar Olof Johansson

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

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

ARM SCMI updates for v5.6

1. Addition of multiple device support per protocol to enable use of
   some procotols by multiple kernel subsystems simultaneously and
   corresponding updates to the existing scmi drivers
2. Addition of trace events around the scmi transfer code to measure
   any delays and capture anomalies that can also be used during
   investigation of some platform firmware related issues

* tag 'scmi-updates-5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  drivers: firmware: scmi: Extend SCMI transport layer by trace events
  include: trace: Add SCMI header with trace events
  reset: reset-scmi: Match scmi device by both name and protocol id
  hwmon: (scmi-hwmon) Match scmi device by both name and protocol id
  cpufreq: scmi: Match scmi device by both name and protocol id
  clk: scmi: Match scmi device by both name and protocol id
  firmware: arm_scmi: Skip protocol initialisation for additional devices
  firmware: arm_scmi: Stash version in protocol init functions
  firmware: arm_scmi: Match scmi device by both name and protocol id
  firmware: arm_scmi: Add versions and identifier attributes using dev_groups
  firmware: arm_scmi: Add names to scmi devices created
  firmware: arm_scmi: Skip scmi mbox channel setup for addtional devices
  firmware: arm_scmi: Add support for multiple device per protocol

Link: https://lore.kernel.org/r/20191230182956.GA29349@bogusSigned-off-by: default avatarOlof Johansson <olof@lixom.net>
parents d1eef1c6 729d3530
......@@ -15966,6 +15966,7 @@ F: drivers/firmware/arm_scpi.c
F: drivers/firmware/arm_scmi/
F: drivers/reset/reset-scmi.c
F: include/linux/sc[mp]i_protocol.h
F: include/trace/events/scmi.h
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <sre@kernel.org>
......
......@@ -176,7 +176,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_CLOCK },
{ SCMI_PROTOCOL_CLOCK, "clocks" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -261,7 +261,7 @@ static void scmi_cpufreq_remove(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_PERF },
{ SCMI_PROTOCOL_PERF, "cpufreq" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -28,8 +28,12 @@ scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
return NULL;
for (; id->protocol_id; id++)
if (id->protocol_id == scmi_dev->protocol_id)
if (id->protocol_id == scmi_dev->protocol_id) {
if (!id->name)
return id;
else if (!strcmp(id->name, scmi_dev->name))
return id;
}
return NULL;
}
......@@ -56,6 +60,11 @@ static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
return fn(handle);
}
static int scmi_protocol_dummy_init(struct scmi_handle *handle)
{
return 0;
}
static int scmi_dev_probe(struct device *dev)
{
struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
......@@ -74,6 +83,10 @@ static int scmi_dev_probe(struct device *dev)
if (ret)
return ret;
/* Skip protocol initialisation for additional devices */
idr_replace(&scmi_protocols, &scmi_protocol_dummy_init,
scmi_dev->protocol_id);
return scmi_drv->probe(scmi_dev);
}
......@@ -125,7 +138,8 @@ static void scmi_device_release(struct device *dev)
}
struct scmi_device *
scmi_device_create(struct device_node *np, struct device *parent, int protocol)
scmi_device_create(struct device_node *np, struct device *parent, int protocol,
const char *name)
{
int id, retval;
struct scmi_device *scmi_dev;
......@@ -134,8 +148,15 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
if (!scmi_dev)
return NULL;
scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL);
if (!scmi_dev->name) {
kfree(scmi_dev);
return NULL;
}
id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
if (id < 0) {
kfree_const(scmi_dev->name);
kfree(scmi_dev);
return NULL;
}
......@@ -154,6 +175,7 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
return scmi_dev;
put_dev:
kfree_const(scmi_dev->name);
put_device(&scmi_dev->dev);
ida_simple_remove(&scmi_bus_id, id);
return NULL;
......@@ -161,6 +183,7 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
void scmi_device_destroy(struct scmi_device *scmi_dev)
{
kfree_const(scmi_dev->name);
scmi_handle_put(scmi_dev->handle);
ida_simple_remove(&scmi_bus_id, scmi_dev->id);
device_unregister(&scmi_dev->dev);
......
......@@ -65,6 +65,7 @@ struct scmi_clock_set_rate {
};
struct clock_info {
u32 version;
int num_clocks;
int max_async_req;
atomic_t cur_async_req;
......@@ -340,6 +341,7 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
scmi_clock_describe_rates_get(handle, clkid, clk);
}
cinfo->version = version;
handle->clk_ops = &clk_ops;
handle->clk_priv = cinfo;
......
......@@ -81,6 +81,7 @@ struct scmi_msg {
/**
* 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
......@@ -90,6 +91,7 @@ struct scmi_msg {
* @async: pointer to delayed response message received event completion
*/
struct scmi_xfer {
int transfer_id;
struct scmi_msg_hdr hdr;
struct scmi_msg tx;
struct scmi_msg rx;
......
......@@ -29,6 +29,9 @@
#include "common.h"
#define CREATE_TRACE_POINTS
#include <trace/events/scmi.h>
#define MSG_ID_MASK GENMASK(7, 0)
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
#define MSG_TYPE_MASK GENMASK(9, 8)
......@@ -61,6 +64,8 @@ enum scmi_error_codes {
static LIST_HEAD(scmi_list);
/* Protection for the entire list */
static DEFINE_MUTEX(scmi_list_mutex);
/* Track the unique id for the transfers for debug & profiling purpose */
static atomic_t transfer_last_id;
/**
* struct scmi_xfers_info - Structure to manage transfer information
......@@ -304,6 +309,7 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
xfer = &minfo->xfer_block[xfer_id];
xfer->hdr.seq = xfer_id;
reinit_completion(&xfer->done);
xfer->transfer_id = atomic_inc_return(&transfer_last_id);
return xfer;
}
......@@ -374,6 +380,10 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m)
scmi_fetch_response(xfer, mem);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
msg_type);
if (msg_type == MSG_TYPE_DELAYED_RESP)
complete(xfer->async_done);
else
......@@ -439,6 +449,10 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
if (unlikely(!cinfo))
return -EINVAL;
trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
ret = mbox_send_message(cinfo->chan, xfer);
if (ret < 0) {
dev_dbg(dev, "mbox send fail %d\n", ret);
......@@ -478,6 +492,10 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
*/
mbox_client_txdone(cinfo->chan, ret);
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.status);
return ret;
}
......@@ -735,6 +753,11 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev,
idx = tx ? 0 : 1;
idr = tx ? &info->tx_idr : &info->rx_idr;
/* check if already allocated, used for multiple device per protocol */
cinfo = idr_find(idr, prot_id);
if (cinfo)
return 0;
if (scmi_mailbox_check(np, idx)) {
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
......@@ -803,11 +826,11 @@ scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id)
static inline void
scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
int prot_id)
int prot_id, const char *name)
{
struct scmi_device *sdev;
sdev = scmi_device_create(np, info->dev, prot_id);
sdev = scmi_device_create(np, info->dev, prot_id, name);
if (!sdev) {
dev_err(info->dev, "failed to create %d protocol device\n",
prot_id);
......@@ -824,6 +847,40 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
scmi_set_handle(sdev);
}
#define MAX_SCMI_DEV_PER_PROTOCOL 2
struct scmi_prot_devnames {
int protocol_id;
char *names[MAX_SCMI_DEV_PER_PROTOCOL];
};
static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_POWER, { "genpd" },},
{ SCMI_PROTOCOL_PERF, { "cpufreq" },},
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },},
};
static inline void
scmi_create_protocol_devices(struct device_node *np, struct scmi_info *info,
int prot_id)
{
int loop, cnt;
for (loop = 0; loop < ARRAY_SIZE(devnames); loop++) {
if (devnames[loop].protocol_id != prot_id)
continue;
for (cnt = 0; cnt < ARRAY_SIZE(devnames[loop].names); cnt++) {
const char *name = devnames[loop].names[cnt];
if (name)
scmi_create_protocol_device(np, info, prot_id,
name);
}
}
}
static int scmi_probe(struct platform_device *pdev)
{
int ret;
......@@ -892,7 +949,7 @@ static int scmi_probe(struct platform_device *pdev)
continue;
}
scmi_create_protocol_device(child, info, prot_id);
scmi_create_protocol_devices(child, info, prot_id);
}
return 0;
......@@ -940,6 +997,52 @@ static int scmi_remove(struct platform_device *pdev)
return ret;
}
static ssize_t protocol_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%u.%u\n", info->version.major_ver,
info->version.minor_ver);
}
static DEVICE_ATTR_RO(protocol_version);
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "0x%x\n", info->version.impl_ver);
}
static DEVICE_ATTR_RO(firmware_version);
static ssize_t vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", info->version.vendor_id);
}
static DEVICE_ATTR_RO(vendor_id);
static ssize_t sub_vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", info->version.sub_vendor_id);
}
static DEVICE_ATTR_RO(sub_vendor_id);
static struct attribute *versions_attrs[] = {
&dev_attr_firmware_version.attr,
&dev_attr_protocol_version.attr,
&dev_attr_vendor_id.attr,
&dev_attr_sub_vendor_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(versions);
static const struct scmi_desc scmi_generic_desc = {
.max_rx_timeout_ms = 30, /* We may increase this if required */
.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
......@@ -958,6 +1061,7 @@ static struct platform_driver scmi_driver = {
.driver = {
.name = "arm-scmi",
.of_match_table = scmi_of_match,
.dev_groups = versions_groups,
},
.probe = scmi_probe,
.remove = scmi_remove,
......
......@@ -145,6 +145,7 @@ struct perf_dom_info {
};
struct scmi_perf_info {
u32 version;
int num_domains;
bool power_scale_mw;
u64 stats_addr;
......@@ -736,6 +737,7 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
}
pinfo->version = version;
handle->perf_ops = &perf_ops;
handle->perf_priv = pinfo;
......
......@@ -50,6 +50,7 @@ struct power_dom_info {
};
struct scmi_power_info {
u32 version;
int num_domains;
u64 stats_addr;
u32 stats_size;
......@@ -207,6 +208,7 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
scmi_power_domain_attributes_get(handle, domain, dom);
}
pinfo->version = version;
handle->power_ops = &power_ops;
handle->power_priv = pinfo;
......
......@@ -48,6 +48,7 @@ struct reset_dom_info {
};
struct scmi_reset_info {
u32 version;
int num_domains;
struct reset_dom_info *dom_info;
};
......@@ -217,6 +218,7 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
scmi_reset_domain_attributes_get(handle, domain, dom);
}
pinfo->version = version;
handle->reset_ops = &reset_ops;
handle->reset_priv = pinfo;
......
......@@ -112,7 +112,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_POWER },
{ SCMI_PROTOCOL_POWER, "genpd" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -68,6 +68,7 @@ struct scmi_msg_sensor_reading_get {
};
struct sensors_info {
u32 version;
int num_sensors;
int max_requests;
u64 reg_addr;
......@@ -294,6 +295,7 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
scmi_sensor_description_get(handle, sinfo);
sinfo->version = version;
handle->sensor_ops = &sensor_ops;
handle->sensor_priv = sinfo;
......
......@@ -259,7 +259,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_SENSOR },
{ SCMI_PROTOCOL_SENSOR, "hwmon" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -108,7 +108,7 @@ static int scmi_reset_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_RESET },
{ SCMI_PROTOCOL_RESET, "reset" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -257,6 +257,7 @@ enum scmi_std_protocol {
struct scmi_device {
u32 id;
u8 protocol_id;
const char *name;
struct device dev;
struct scmi_handle *handle;
};
......@@ -264,11 +265,13 @@ struct scmi_device {
#define to_scmi_dev(d) container_of(d, struct scmi_device, dev)
struct scmi_device *
scmi_device_create(struct device_node *np, struct device *parent, int protocol);
scmi_device_create(struct device_node *np, struct device *parent, int protocol,
const char *name);
void scmi_device_destroy(struct scmi_device *scmi_dev);
struct scmi_device_id {
u8 protocol_id;
const char *name;
};
struct scmi_driver {
......
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM scmi
#if !defined(_TRACE_SCMI_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SCMI_H
#include <linux/tracepoint.h>
TRACE_EVENT(scmi_xfer_begin,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
bool poll),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, poll),
TP_STRUCT__entry(
__field(int, transfer_id)
__field(u8, msg_id)
__field(u8, protocol_id)
__field(u16, seq)
__field(bool, poll)
),
TP_fast_assign(
__entry->transfer_id = transfer_id;
__entry->msg_id = msg_id;
__entry->protocol_id = protocol_id;
__entry->seq = seq;
__entry->poll = poll;
),
TP_printk("transfer_id=%d msg_id=%u protocol_id=%u seq=%u poll=%u",
__entry->transfer_id, __entry->msg_id, __entry->protocol_id,
__entry->seq, __entry->poll)
);
TRACE_EVENT(scmi_xfer_end,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
u32 status),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, status),
TP_STRUCT__entry(
__field(int, transfer_id)
__field(u8, msg_id)
__field(u8, protocol_id)
__field(u16, seq)
__field(u32, status)
),
TP_fast_assign(
__entry->transfer_id = transfer_id;
__entry->msg_id = msg_id;
__entry->protocol_id = protocol_id;
__entry->seq = seq;
__entry->status = status;
),
TP_printk("transfer_id=%d msg_id=%u protocol_id=%u seq=%u status=%u",
__entry->transfer_id, __entry->msg_id, __entry->protocol_id,
__entry->seq, __entry->status)
);
TRACE_EVENT(scmi_rx_done,
TP_PROTO(int transfer_id, u8 msg_id, u8 protocol_id, u16 seq,
u8 msg_type),
TP_ARGS(transfer_id, msg_id, protocol_id, seq, msg_type),
TP_STRUCT__entry(
__field(int, transfer_id)
__field(u8, msg_id)
__field(u8, protocol_id)
__field(u16, seq)
__field(u8, msg_type)
),
TP_fast_assign(
__entry->transfer_id = transfer_id;
__entry->msg_id = msg_id;
__entry->protocol_id = protocol_id;
__entry->seq = seq;
__entry->msg_type = msg_type;
),
TP_printk("transfer_id=%d msg_id=%u protocol_id=%u seq=%u msg_type=%u",
__entry->transfer_id, __entry->msg_id, __entry->protocol_id,
__entry->seq, __entry->msg_type)
);
#endif /* _TRACE_SCMI_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
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