diff options
Diffstat (limited to 'drivers/rpmsg/qcom_glink_smem.c')
-rw-r--r-- | drivers/rpmsg/qcom_glink_smem.c | 102 |
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); |