summaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg/qcom_glink_smem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rpmsg/qcom_glink_smem.c')
-rw-r--r--drivers/rpmsg/qcom_glink_smem.c102
1 files changed, 87 insertions, 15 deletions
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 579bc4443f6d..7a982c60a8dd 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -7,8 +7,10 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/mailbox_client.h>
#include <linux/mfd/syscon.h>
#include <linux/slab.h>
#include <linux/rpmsg.h>
@@ -33,6 +35,18 @@
#define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479
#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480
+struct qcom_glink_smem {
+ struct device dev;
+
+ int irq;
+ struct qcom_glink *glink;
+
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+
+ u32 remote_pid;
+};
+
struct glink_smem_pipe {
struct qcom_glink_pipe native;
@@ -41,7 +55,7 @@ struct glink_smem_pipe {
void *fifo;
- int remote_pid;
+ struct qcom_glink_smem *smem;
};
#define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native)
@@ -49,13 +63,14 @@ struct glink_smem_pipe {
static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
{
struct glink_smem_pipe *pipe = to_smem_pipe(np);
+ struct qcom_glink_smem *smem = pipe->smem;
size_t len;
void *fifo;
u32 head;
u32 tail;
if (!pipe->fifo) {
- fifo = qcom_smem_get(pipe->remote_pid,
+ fifo = qcom_smem_get(smem->remote_pid,
SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len);
if (IS_ERR(fifo)) {
pr_err("failed to acquire RX fifo handle: %ld\n",
@@ -76,7 +91,7 @@ static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
return head - tail;
}
-static void glink_smem_rx_peak(struct qcom_glink_pipe *np,
+static void glink_smem_rx_peek(struct qcom_glink_pipe *np,
void *data, unsigned int offset, size_t count)
{
struct glink_smem_pipe *pipe = to_smem_pipe(np);
@@ -177,16 +192,37 @@ static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
*pipe->head = cpu_to_le32(head);
}
+static void glink_smem_tx_kick(struct qcom_glink_pipe *glink_pipe)
+{
+ struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe);
+ struct qcom_glink_smem *smem = pipe->smem;
+
+ mbox_send_message(smem->mbox_chan, NULL);
+ mbox_client_txdone(smem->mbox_chan, 0);
+}
+
+static irqreturn_t qcom_glink_smem_intr(int irq, void *data)
+{
+ struct qcom_glink_smem *smem = data;
+
+ qcom_glink_native_rx(smem->glink);
+
+ return IRQ_HANDLED;
+}
+
static void qcom_glink_smem_release(struct device *dev)
{
- kfree(dev);
+ struct qcom_glink_smem *smem = container_of(dev, struct qcom_glink_smem, dev);
+
+ kfree(smem);
}
-struct qcom_glink *qcom_glink_smem_register(struct device *parent,
- struct device_node *node)
+struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
+ struct device_node *node)
{
struct glink_smem_pipe *rx_pipe;
struct glink_smem_pipe *tx_pipe;
+ struct qcom_glink_smem *smem;
struct qcom_glink *glink;
struct device *dev;
u32 remote_pid;
@@ -194,10 +230,12 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
size_t size;
int ret;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ smem = kzalloc(sizeof(*smem), GFP_KERNEL);
+ if (!smem)
return ERR_PTR(-ENOMEM);
+ dev = &smem->dev;
+
dev->parent = parent;
dev->of_node = node;
dev->release = qcom_glink_smem_release;
@@ -216,6 +254,8 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
goto err_put_dev;
}
+ smem->remote_pid = remote_pid;
+
rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL);
tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL);
if (!rx_pipe || !tx_pipe) {
@@ -264,14 +304,33 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
goto err_put_dev;
}
+ smem->irq = of_irq_get(smem->dev.of_node, 0);
+ ret = devm_request_irq(&smem->dev, smem->irq, qcom_glink_smem_intr,
+ IRQF_NO_SUSPEND | IRQF_NO_AUTOEN,
+ "glink-smem", smem);
+ if (ret) {
+ dev_err(&smem->dev, "failed to request IRQ\n");
+ goto err_put_dev;
+ }
+
+ smem->mbox_client.dev = &smem->dev;
+ smem->mbox_client.knows_txdone = true;
+ smem->mbox_chan = mbox_request_channel(&smem->mbox_client, 0);
+ if (IS_ERR(smem->mbox_chan)) {
+ ret = dev_err_probe(&smem->dev, PTR_ERR(smem->mbox_chan),
+ "failed to acquire IPC channel\n");
+ goto err_put_dev;
+ }
+
+ rx_pipe->smem = smem;
rx_pipe->native.avail = glink_smem_rx_avail;
- rx_pipe->native.peak = glink_smem_rx_peak;
+ rx_pipe->native.peek = glink_smem_rx_peek;
rx_pipe->native.advance = glink_smem_rx_advance;
- rx_pipe->remote_pid = remote_pid;
+ tx_pipe->smem = smem;
tx_pipe->native.avail = glink_smem_tx_avail;
tx_pipe->native.write = glink_smem_tx_write;
- tx_pipe->remote_pid = remote_pid;
+ tx_pipe->native.kick = glink_smem_tx_kick;
*rx_pipe->tail = 0;
*tx_pipe->head = 0;
@@ -282,10 +341,17 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
false);
if (IS_ERR(glink)) {
ret = PTR_ERR(glink);
- goto err_put_dev;
+ goto err_free_mbox;
}
- return glink;
+ smem->glink = glink;
+
+ enable_irq(smem->irq);
+
+ return smem;
+
+err_free_mbox:
+ mbox_free_channel(smem->mbox_chan);
err_put_dev:
device_unregister(dev);
@@ -294,10 +360,16 @@ err_put_dev:
}
EXPORT_SYMBOL_GPL(qcom_glink_smem_register);
-void qcom_glink_smem_unregister(struct qcom_glink *glink)
+void qcom_glink_smem_unregister(struct qcom_glink_smem *smem)
{
+ struct qcom_glink *glink = smem->glink;
+
+ disable_irq(smem->irq);
+
qcom_glink_native_remove(glink);
- qcom_glink_native_unregister(glink);
+
+ mbox_free_channel(smem->mbox_chan);
+ device_unregister(&smem->dev);
}
EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister);