summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2020-01-06 09:23:04 -0800
committerOlof Johansson <olof@lixom.net>2020-01-06 09:23:06 -0800
commit965af1cfbb81b2c0220760797c9d7c42d8859b11 (patch)
tree52d109b220f04fbc769238d477a6c527e41a9bda /drivers/firmware
parentd1eef1c619749b2a57e514a3fa67d9a516ffa919 (diff)
parent729d3530a50417a88f3f485ba2dc88ff8adfeacb (diff)
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@bogus Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/arm_scmi/bus.c29
-rw-r--r--drivers/firmware/arm_scmi/clock.c2
-rw-r--r--drivers/firmware/arm_scmi/common.h2
-rw-r--r--drivers/firmware/arm_scmi/driver.c110
-rw-r--r--drivers/firmware/arm_scmi/perf.c2
-rw-r--r--drivers/firmware/arm_scmi/power.c2
-rw-r--r--drivers/firmware/arm_scmi/reset.c2
-rw-r--r--drivers/firmware/arm_scmi/scmi_pm_domain.c2
-rw-r--r--drivers/firmware/arm_scmi/sensors.c2
9 files changed, 146 insertions, 7 deletions
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 7a30952b463d..db55c43a2cbd 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -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)
- return 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 @@ put_dev:
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);
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 32526a793f3a..4c2227662b26 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -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;
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 5237c2ff79fe..df35358ff324 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -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;
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 3eb0382491ce..2c96f6b5a7d8 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -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,
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 601af4edad5e..ec81e6f7e7a4 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -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;
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 5abef7079c0a..214886ce84f1 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -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;
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index ab42c21c5517..de73054554f3 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -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;
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
index 87f737e01473..bafbfe358f97 100644
--- a/drivers/firmware/arm_scmi/scmi_pm_domain.c
+++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c
@@ -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);
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index a400ea805fc2..eba61b9c1f53 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -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;