aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rpmsg')
-rw-r--r--drivers/rpmsg/Kconfig1
-rw-r--r--drivers/rpmsg/qcom_smd.c68
-rw-r--r--drivers/rpmsg/rpmsg_core.c39
3 files changed, 89 insertions, 19 deletions
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 65a9f6b892f0..9b14090e3603 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -39,6 +39,7 @@ config RPMSG_QCOM_GLINK_SMEM
config RPMSG_QCOM_SMD
tristate "Qualcomm Shared Memory Driver (SMD)"
+ depends on MAILBOX
depends on QCOM_SMEM
select RPMSG
help
diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c
index 5ce9bf7b897d..b07e9a1a9d22 100644
--- a/drivers/rpmsg/qcom_smd.c
+++ b/drivers/rpmsg/qcom_smd.c
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/mailbox_client.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_irq.h>
@@ -107,6 +108,8 @@ static const struct {
* @ipc_regmap: regmap handle holding the outgoing ipc register
* @ipc_offset: offset within @ipc_regmap of the register for ipc
* @ipc_bit: bit in the register at @ipc_offset of @ipc_regmap
+ * @mbox_client: mailbox client handle
+ * @mbox_chan: apcs ipc mailbox channel handle
* @channels: list of all channels detected on this edge
* @channels_lock: guard for modifications of @channels
* @allocated: array of bitmaps representing already allocated channels
@@ -129,6 +132,9 @@ struct qcom_smd_edge {
int ipc_offset;
int ipc_bit;
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+
struct list_head channels;
spinlock_t channels_lock;
@@ -366,7 +372,17 @@ static void qcom_smd_signal_channel(struct qcom_smd_channel *channel)
{
struct qcom_smd_edge *edge = channel->edge;
- regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit));
+ if (edge->mbox_chan) {
+ /*
+ * We can ignore a failing mbox_send_message() as the only
+ * possible cause is that the FIFO in the framework is full of
+ * other writes to the same bit.
+ */
+ mbox_send_message(edge->mbox_chan, NULL);
+ mbox_client_txdone(edge->mbox_chan, 0);
+ } else {
+ regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit));
+ }
}
/*
@@ -1326,27 +1342,37 @@ static int qcom_smd_parse_edge(struct device *dev,
key = "qcom,remote-pid";
of_property_read_u32(node, key, &edge->remote_pid);
- syscon_np = of_parse_phandle(node, "qcom,ipc", 0);
- if (!syscon_np) {
- dev_err(dev, "no qcom,ipc node\n");
- return -ENODEV;
- }
+ edge->mbox_client.dev = dev;
+ edge->mbox_client.knows_txdone = true;
+ edge->mbox_chan = mbox_request_channel(&edge->mbox_client, 0);
+ if (IS_ERR(edge->mbox_chan)) {
+ if (PTR_ERR(edge->mbox_chan) != -ENODEV)
+ return PTR_ERR(edge->mbox_chan);
- edge->ipc_regmap = syscon_node_to_regmap(syscon_np);
- if (IS_ERR(edge->ipc_regmap))
- return PTR_ERR(edge->ipc_regmap);
+ edge->mbox_chan = NULL;
- key = "qcom,ipc";
- ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset);
- if (ret < 0) {
- dev_err(dev, "no offset in %s\n", key);
- return -EINVAL;
- }
+ syscon_np = of_parse_phandle(node, "qcom,ipc", 0);
+ if (!syscon_np) {
+ dev_err(dev, "no qcom,ipc node\n");
+ return -ENODEV;
+ }
- ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit);
- if (ret < 0) {
- dev_err(dev, "no bit in %s\n", key);
- return -EINVAL;
+ edge->ipc_regmap = syscon_node_to_regmap(syscon_np);
+ if (IS_ERR(edge->ipc_regmap))
+ return PTR_ERR(edge->ipc_regmap);
+
+ key = "qcom,ipc";
+ ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset);
+ if (ret < 0) {
+ dev_err(dev, "no offset in %s\n", key);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit);
+ if (ret < 0) {
+ dev_err(dev, "no bit in %s\n", key);
+ return -EINVAL;
+ }
}
ret = of_property_read_string(node, "label", &edge->name);
@@ -1453,6 +1479,9 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
return edge;
unregister_dev:
+ if (!IS_ERR_OR_NULL(edge->mbox_chan))
+ mbox_free_channel(edge->mbox_chan);
+
device_unregister(&edge->dev);
return ERR_PTR(ret);
}
@@ -1481,6 +1510,7 @@ int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
if (ret)
dev_warn(&edge->dev, "can't remove smd device: %d\n", ret);
+ mbox_free_channel(edge->mbox_chan);
device_unregister(&edge->dev);
return 0;
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 920a02f0462c..bea7c74e12eb 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -333,11 +333,49 @@ field##_show(struct device *dev, \
} \
static DEVICE_ATTR_RO(field);
+#define rpmsg_string_attr(field, member) \
+static ssize_t \
+field##_store(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t sz) \
+{ \
+ struct rpmsg_device *rpdev = to_rpmsg_device(dev); \
+ char *new, *old; \
+ \
+ new = kstrndup(buf, sz, GFP_KERNEL); \
+ if (!new) \
+ return -ENOMEM; \
+ new[strcspn(new, "\n")] = '\0'; \
+ \
+ device_lock(dev); \
+ old = rpdev->member; \
+ if (strlen(new)) { \
+ rpdev->member = new; \
+ } else { \
+ kfree(new); \
+ rpdev->member = NULL; \
+ } \
+ device_unlock(dev); \
+ \
+ kfree(old); \
+ \
+ return sz; \
+} \
+static ssize_t \
+field##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct rpmsg_device *rpdev = to_rpmsg_device(dev); \
+ \
+ return sprintf(buf, "%s\n", rpdev->member); \
+} \
+static DEVICE_ATTR_RW(field)
+
/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
rpmsg_show_attr(name, id.name, "%s\n");
rpmsg_show_attr(src, src, "0x%x\n");
rpmsg_show_attr(dst, dst, "0x%x\n");
rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
+rpmsg_string_attr(driver_override, driver_override);
static ssize_t modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -359,6 +397,7 @@ static struct attribute *rpmsg_dev_attrs[] = {
&dev_attr_dst.attr,
&dev_attr_src.attr,
&dev_attr_announce.attr,
+ &dev_attr_driver_override.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpmsg_dev);