Merge tag 'tag-chrome-platform-fixes-for-v6.3-rc4' of git://git.kernel.org/pub/scm...
[linux-2.6-block.git] / drivers / rpmsg / qcom_glink_smem.c
index 579bc4443f6dbf093c7144b6fa5ef2b249b67804..7a982c60a8ddc1c7442ae7c027f48f4a2d4fa511 100644 (file)
@@ -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>
 #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);