Merge tag 'mailbox-v4.21' of git://git.linaro.org/landing-teams/working/fujitsu/integ...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Jan 2019 02:41:38 +0000 (18:41 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Jan 2019 02:41:38 +0000 (18:41 -0800)
Pull mailbox updates from Jassi Brar:

 - Introduce device-managed registration
   devm_mbox_controller_un/register and convert drivers to use it

 - Introduce flush api to support clients that must busy-wait in atomic
   context

 - Support multiple controllers per device

 - Hi3660: a bugfix and constify ops structure

 - TI-MsgMgr: off by one bugfix.

 - BCM: switch to spdx license

 - Tegra-HSP: support for shared mailboxes and suspend/resume.

* tag 'mailbox-v4.21' of git://git.linaro.org/landing-teams/working/fujitsu/integration: (30 commits)
  mailbox: tegra-hsp: Use device-managed registration API
  mailbox: tegra-hsp: use devm_kstrdup_const()
  mailbox: tegra-hsp: Add suspend/resume support
  mailbox: tegra-hsp: Add support for shared mailboxes
  dt-bindings: tegra186-hsp: Add shared mailboxes
  mailbox: Allow multiple controllers per device
  mailbox: Support blocking transfers in atomic context
  mailbox: ti-msgmgr: Use device-managed registration API
  mailbox: stm32-ipcc: Use device-managed registration API
  mailbox: rockchip: Use device-managed registration API
  mailbox: qcom-apcs: Use device-managed registration API
  mailbox: platform-mhu: Use device-managed registration API
  mailbox: omap: Use device-managed registration API
  mailbox: mtk-cmdq: Remove needless devm_kfree() calls
  mailbox: mtk-cmdq: Use device-managed registration API
  mailbox: xgene-slimpro: Use device-managed registration API
  mailbox: sti: Use device-managed registration API
  mailbox: altera: Use device-managed registration API
  mailbox: imx: Use device-managed registration API
  mailbox: hi6220: Use device-managed registration API
  ...

23 files changed:
Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
drivers/mailbox/arm_mhu.c
drivers/mailbox/bcm-flexrm-mailbox.c
drivers/mailbox/bcm-pdc-mailbox.c
drivers/mailbox/bcm2835-mailbox.c
drivers/mailbox/hi3660-mailbox.c
drivers/mailbox/hi6220-mailbox.c
drivers/mailbox/imx-mailbox.c
drivers/mailbox/mailbox-altera.c
drivers/mailbox/mailbox-sti.c
drivers/mailbox/mailbox-xgene-slimpro.c
drivers/mailbox/mailbox.c
drivers/mailbox/mtk-cmdq-mailbox.c
drivers/mailbox/omap-mailbox.c
drivers/mailbox/platform_mhu.c
drivers/mailbox/qcom-apcs-ipc-mailbox.c
drivers/mailbox/rockchip-mailbox.c
drivers/mailbox/stm32-ipcc.c
drivers/mailbox/tegra-hsp.c
drivers/mailbox/ti-msgmgr.c
include/dt-bindings/mailbox/tegra186-hsp.h
include/linux/mailbox_client.h
include/linux/mailbox_controller.h

index b99d25fc2f26e7a2bd6a4548f0b7a8f9baae8723..ff3eafc5a882bca7cf04c581401fe36b5785277c 100644 (file)
@@ -15,12 +15,15 @@ Required properties:
     Array of strings.
     one of:
     - "nvidia,tegra186-hsp"
+    - "nvidia,tegra194-hsp", "nvidia,tegra186-hsp"
 - reg : Offset and length of the register set for the device.
 - interrupt-names
     Array of strings.
     Contains a list of names for the interrupts described by the interrupt
     property. May contain the following entries, in any order:
     - "doorbell"
+    - "sharedN", where 'N' is a number from zero up to the number of
+      external interrupts supported by the HSP instance minus one.
     Users of this binding MUST look up entries in the interrupt property
     by name, using this interrupt-names property to do so.
 - interrupts
@@ -29,12 +32,29 @@ Required properties:
     in a matching order.
 - #mbox-cells : Should be 2.
 
-The mbox specifier of the "mboxes" property in the client node should
-contain two data. The first one should be the HSP type and the second
-one should be the ID that the client is going to use. Those information
-can be found in the following file.
+The mbox specifier of the "mboxes" property in the client node should contain
+two cells. The first cell determines the HSP type and the second cell is used
+to identify the mailbox that the client is going to use.
 
-- <dt-bindings/mailbox/tegra186-hsp.h>.
+For doorbells, the second cell specifies the index of the doorbell to use.
+
+For shared mailboxes, the second cell is composed of two fields:
+- bits 31..24:
+    A bit mask of flags that further specify how the shared mailbox will be
+    used. Valid flags are:
+    - bit 31:
+        Defines the direction of the mailbox. If set, the mailbox will be used
+        as a producer (i.e. used to send data). If cleared, the mailbox is the
+        consumer of data sent by a producer.
+
+- bits 23.. 0:
+    The index of the shared mailbox to use. The number of available mailboxes
+    may vary by instance of the HSP block and SoC generation.
+
+The following file contains definitions that can be used to construct mailbox
+specifiers:
+
+    <dt-bindings/mailbox/tegra186-hsp.h>
 
 Example:
 
index 99befa76e37c70d70d7f89b0db0c7aa6e1a24701..64d85c6a2bdfa8af4e9a53fa85ca39675ff0e943 100644 (file)
@@ -152,7 +152,7 @@ static int mhu_probe(struct amba_device *adev, const struct amba_id *id)
 
        amba_set_drvdata(adev, mhu);
 
-       err = mbox_controller_register(&mhu->mbox);
+       err = devm_mbox_controller_register(dev, &mhu->mbox);
        if (err) {
                dev_err(dev, "Failed to register mailboxes %d\n", err);
                return err;
@@ -162,15 +162,6 @@ static int mhu_probe(struct amba_device *adev, const struct amba_id *id)
        return 0;
 }
 
-static int mhu_remove(struct amba_device *adev)
-{
-       struct arm_mhu *mhu = amba_get_drvdata(adev);
-
-       mbox_controller_unregister(&mhu->mbox);
-
-       return 0;
-}
-
 static struct amba_id mhu_ids[] = {
        {
                .id     = 0x1bb098,
@@ -186,7 +177,6 @@ static struct amba_driver arm_mhu_driver = {
        },
        .id_table       = mhu_ids,
        .probe          = mhu_probe,
-       .remove         = mhu_remove,
 };
 module_amba_driver(arm_mhu_driver);
 
index d7a8ed7d809789f6d55f28aa7935d026193b0db7..d713271ebf7c73f161456195e9f3ffc68db8c495 100644 (file)
@@ -1665,7 +1665,7 @@ skip_debugfs:
                mbox->controller.chans[index].con_priv = &mbox->rings[index];
 
        /* Register mailbox controller */
-       ret = mbox_controller_register(&mbox->controller);
+       ret = devm_mbox_controller_register(dev, &mbox->controller);
        if (ret)
                goto fail_free_debugfs_root;
 
@@ -1691,8 +1691,6 @@ static int flexrm_mbox_remove(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct flexrm_mbox *mbox = platform_get_drvdata(pdev);
 
-       mbox_controller_unregister(&mbox->controller);
-
        debugfs_remove_recursive(mbox->root);
 
        platform_msi_domain_free_irqs(dev);
index 4fe7be0bdd11f2e836897e9783b3c7fec8a7534d..ccf3d62af7e7b05a8d53ac8914cd9cbda4512bc0 100644 (file)
@@ -1471,7 +1471,7 @@ static int pdc_mb_init(struct pdc_state *pdcs)
                mbc->chans[chan_index].con_priv = pdcs;
 
        /* Register mailbox controller */
-       err = mbox_controller_register(mbc);
+       err = devm_mbox_controller_register(dev, mbc);
        if (err) {
                dev_crit(dev,
                         "Failed to register PDC mailbox controller. Error %d.",
@@ -1641,8 +1641,6 @@ static int pdc_remove(struct platform_device *pdev)
 
        pdc_hw_disable(pdcs);
 
-       mbox_controller_unregister(&pdcs->mbc);
-
        dma_pool_destroy(pdcs->rx_buf_pool);
        dma_pool_destroy(pdcs->ring_pool);
        return 0;
index e92bbc533821ad9877316bab29672ec05c266ac5..39761d19054594c2e4917d2218ec0346bffc79c8 100644 (file)
@@ -1,15 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  Copyright (C) 2010,2015 Broadcom
  *  Copyright (C) 2013-2014 Lubomir Rintel
  *  Copyright (C) 2013 Craig McGeachie
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This device provides a mechanism for writing to the mailboxes,
- * that are shared between the ARM and the VideoCore processor
- *
  * Parts of the driver are based on:
  *  - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was
  *    obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/
@@ -178,7 +172,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
        if (!mbox->controller.chans)
                return -ENOMEM;
 
-       ret = mbox_controller_register(&mbox->controller);
+       ret = devm_mbox_controller_register(dev, &mbox->controller);
        if (ret)
                return ret;
 
@@ -188,13 +182,6 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int bcm2835_mbox_remove(struct platform_device *pdev)
-{
-       struct bcm2835_mbox *mbox = platform_get_drvdata(pdev);
-       mbox_controller_unregister(&mbox->controller);
-       return 0;
-}
-
 static const struct of_device_id bcm2835_mbox_of_match[] = {
        { .compatible = "brcm,bcm2835-mbox", },
        {},
@@ -207,7 +194,6 @@ static struct platform_driver bcm2835_mbox_driver = {
                .of_match_table = bcm2835_mbox_of_match,
        },
        .probe          = bcm2835_mbox_probe,
-       .remove         = bcm2835_mbox_remove,
 };
 module_platform_driver(bcm2835_mbox_driver);
 
index 3eea6b6424841ad16a726382045a5e19fe8be0d0..53f4bc2488c50c3a91988f06195d3415dff9c54c 100644 (file)
@@ -38,6 +38,7 @@
 #define MBOX_AUTOMATIC_ACK             1
 
 #define MBOX_STATE_IDLE                        BIT(4)
+#define MBOX_STATE_READY               BIT(5)
 #define MBOX_STATE_ACK                 BIT(7)
 
 #define MBOX_MSG_LEN                   8
@@ -91,8 +92,8 @@ static int hi3660_mbox_check_state(struct mbox_chan *chan)
        unsigned long val;
        unsigned int ret;
 
-       /* Mailbox is idle so directly bail out */
-       if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE)
+       /* Mailbox is ready to use */
+       if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY)
                return 0;
 
        /* Wait for acknowledge from remote */
@@ -103,9 +104,9 @@ static int hi3660_mbox_check_state(struct mbox_chan *chan)
                return ret;
        }
 
-       /* Ensure channel is released */
-       writel(0xffffffff, base + MBOX_IMASK_REG);
-       writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
+       /* clear ack state, mailbox will get back to ready state */
+       writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG);
+
        return 0;
 }
 
@@ -160,10 +161,6 @@ static int hi3660_mbox_startup(struct mbox_chan *chan)
 {
        int ret;
 
-       ret = hi3660_mbox_check_state(chan);
-       if (ret)
-               return ret;
-
        ret = hi3660_mbox_unlock(chan);
        if (ret)
                return ret;
@@ -183,10 +180,11 @@ static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
        void __iomem *base = MBOX_BASE(mbox, ch);
        u32 *buf = msg;
        unsigned int i;
+       int ret;
 
-       /* Ensure channel is released */
-       writel_relaxed(0xffffffff, base + MBOX_IMASK_REG);
-       writel_relaxed(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
+       ret = hi3660_mbox_check_state(chan);
+       if (ret)
+               return ret;
 
        /* Clear mask for destination interrupt */
        writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);
@@ -206,7 +204,7 @@ static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
        return 0;
 }
 
-static struct mbox_chan_ops hi3660_mbox_ops = {
+static const struct mbox_chan_ops hi3660_mbox_ops = {
        .startup        = hi3660_mbox_startup,
        .send_data      = hi3660_mbox_send_data,
 };
@@ -267,7 +265,7 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
        for (ch = 0; ch < MBOX_CHAN_MAX; ch++)
                chan[ch].con_priv = (void *)ch;
 
-       err = mbox_controller_register(&mbox->controller);
+       err = devm_mbox_controller_register(dev, &mbox->controller);
        if (err) {
                dev_err(dev, "Failed to register mailbox %d\n", err);
                return err;
@@ -278,17 +276,8 @@ static int hi3660_mbox_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int hi3660_mbox_remove(struct platform_device *pdev)
-{
-       struct hi3660_mbox *mbox = platform_get_drvdata(pdev);
-
-       mbox_controller_unregister(&mbox->controller);
-       return 0;
-}
-
 static struct platform_driver hi3660_mbox_driver = {
        .probe  = hi3660_mbox_probe,
-       .remove = hi3660_mbox_remove,
        .driver = {
                .name = "hi3660-mbox",
                .of_match_table = hi3660_mbox_of_match,
index 4fa9803cd2041c035a0fdaff538b659f25a3327d..c32cbfaf223aa51ec4bf293eab511f9c7267daa1 100644 (file)
@@ -349,7 +349,7 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
                mbox->controller.txpoll_period = 5;
        }
 
-       err = mbox_controller_register(&mbox->controller);
+       err = devm_mbox_controller_register(dev, &mbox->controller);
        if (err) {
                dev_err(dev, "Failed to register mailbox %d\n", err);
                return err;
@@ -360,14 +360,6 @@ static int hi6220_mbox_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int hi6220_mbox_remove(struct platform_device *pdev)
-{
-       struct hi6220_mbox *mbox = platform_get_drvdata(pdev);
-
-       mbox_controller_unregister(&mbox->controller);
-       return 0;
-}
-
 static struct platform_driver hi6220_mbox_driver = {
        .driver = {
                .name = "hi6220-mbox",
@@ -375,7 +367,6 @@ static struct platform_driver hi6220_mbox_driver = {
                .of_match_table = hi6220_mbox_of_match,
        },
        .probe  = hi6220_mbox_probe,
-       .remove = hi6220_mbox_remove,
 };
 
 static int __init hi6220_mbox_init(void)
index 363d35d5e49dcb22bec03bf748536dee93b301d8..774362a05159a9e6c726dba4d31aff531de80bce 100644 (file)
@@ -324,14 +324,13 @@ static int imx_mu_probe(struct platform_device *pdev)
 
        imx_mu_init_generic(priv);
 
-       return mbox_controller_register(&priv->mbox);
+       return devm_mbox_controller_register(dev, &priv->mbox);
 }
 
 static int imx_mu_remove(struct platform_device *pdev)
 {
        struct imx_mu_priv *priv = platform_get_drvdata(pdev);
 
-       mbox_controller_unregister(&priv->mbox);
        clk_disable_unprepare(priv->clk);
 
        return 0;
index bcb29df9549eb80512a2c9b4c5d299e941a790c0..397e25ddae29f9edc7201e47d64bc132b2cc4e40 100644 (file)
@@ -341,7 +341,7 @@ static int altera_mbox_probe(struct platform_device *pdev)
                }
        }
 
-       ret = mbox_controller_register(&mbox->controller);
+       ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller);
        if (ret) {
                dev_err(&pdev->dev, "Register mailbox failed\n");
                goto err;
@@ -352,18 +352,6 @@ err:
        return ret;
 }
 
-static int altera_mbox_remove(struct platform_device *pdev)
-{
-       struct altera_mbox *mbox = platform_get_drvdata(pdev);
-
-       if (!mbox)
-               return -EINVAL;
-
-       mbox_controller_unregister(&mbox->controller);
-
-       return 0;
-}
-
 static const struct of_device_id altera_mbox_match[] = {
        { .compatible = "altr,mailbox-1.0" },
        { /* Sentinel */ }
@@ -373,7 +361,6 @@ MODULE_DEVICE_TABLE(of, altera_mbox_match);
 
 static struct platform_driver altera_mbox_driver = {
        .probe  = altera_mbox_probe,
-       .remove = altera_mbox_remove,
        .driver = {
                .name   = DRIVER_NAME,
                .of_match_table = altera_mbox_match,
index 779d41262ef0c8d9d7fde683efd7017b9eb1011c..adf82b85dbb2d931f49fa6c2e27426c2089018ab 100644 (file)
@@ -462,7 +462,7 @@ static int sti_mbox_probe(struct platform_device *pdev)
        mbox->chans             = chans;
        mbox->num_chans         = STI_MBOX_CHAN_MAX;
 
-       ret = mbox_controller_register(mbox);
+       ret = devm_mbox_controller_register(&pdev->dev, mbox);
        if (ret)
                return ret;
 
@@ -480,7 +480,6 @@ static int sti_mbox_probe(struct platform_device *pdev)
                                        IRQF_ONESHOT, mdev->name, mdev);
        if (ret) {
                dev_err(&pdev->dev, "Can't claim IRQ %d\n", irq);
-               mbox_controller_unregister(mbox);
                return -EINVAL;
        }
 
@@ -489,18 +488,8 @@ static int sti_mbox_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int sti_mbox_remove(struct platform_device *pdev)
-{
-       struct sti_mbox_device *mdev = platform_get_drvdata(pdev);
-
-       mbox_controller_unregister(mdev->mbox);
-
-       return 0;
-}
-
 static struct platform_driver sti_mbox_driver = {
        .probe = sti_mbox_probe,
-       .remove = sti_mbox_remove,
        .driver = {
                .name = "sti-mailbox",
                .of_match_table = sti_mailbox_match,
index b8b2b3533f466badbf0645efda9ea975c460c998..8f397da1150bb8a0a93f1a687a4090f16e880999 100644 (file)
@@ -224,7 +224,7 @@ static int slimpro_mbox_probe(struct platform_device *pdev)
        ctx->mb_ctrl.ops = &slimpro_mbox_ops;
        ctx->mb_ctrl.num_chans = i;
 
-       rc = mbox_controller_register(&ctx->mb_ctrl);
+       rc = devm_mbox_controller_register(&pdev->dev, &ctx->mb_ctrl);
        if (rc) {
                dev_err(&pdev->dev,
                        "APM X-Gene SLIMpro MailBox register failed:%d\n", rc);
@@ -235,14 +235,6 @@ static int slimpro_mbox_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int slimpro_mbox_remove(struct platform_device *pdev)
-{
-       struct slimpro_mbox *smb = platform_get_drvdata(pdev);
-
-       mbox_controller_unregister(&smb->mb_ctrl);
-       return 0;
-}
-
 static const struct of_device_id slimpro_of_match[] = {
        {.compatible = "apm,xgene-slimpro-mbox" },
        { },
@@ -259,7 +251,6 @@ MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);
 
 static struct platform_driver slimpro_mbox_driver = {
        .probe  = slimpro_mbox_probe,
-       .remove = slimpro_mbox_remove,
        .driver = {
                .name = "xgene-slimpro-mbox",
                .of_match_table = of_match_ptr(slimpro_of_match),
index 674b35f402f5e939833bdbde4a39399689d653f9..c6a7d4582dc6790be3a50f545faa36bc07ea8a7f 100644 (file)
@@ -283,6 +283,34 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
 }
 EXPORT_SYMBOL_GPL(mbox_send_message);
 
+/**
+ * mbox_flush - flush a mailbox channel
+ * @chan: mailbox channel to flush
+ * @timeout: time, in milliseconds, to allow the flush operation to succeed
+ *
+ * Mailbox controllers that need to work in atomic context can implement the
+ * ->flush() callback to busy loop until a transmission has been completed.
+ * The implementation must call mbox_chan_txdone() upon success. Clients can
+ * call the mbox_flush() function at any time after mbox_send_message() to
+ * flush the transmission. After the function returns success, the mailbox
+ * transmission is guaranteed to have completed.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int mbox_flush(struct mbox_chan *chan, unsigned long timeout)
+{
+       int ret;
+
+       if (!chan->mbox->ops->flush)
+               return -ENOTSUPP;
+
+       ret = chan->mbox->ops->flush(chan, timeout);
+       if (ret < 0)
+               tx_tick(chan, ret);
+
+       return ret;
+}
+
 /**
  * mbox_request_channel - Request a mailbox channel.
  * @cl: Identity of the client requesting the channel.
@@ -327,7 +355,8 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
        list_for_each_entry(mbox, &mbox_cons, node)
                if (mbox->dev->of_node == spec.np) {
                        chan = mbox->of_xlate(mbox, &spec);
-                       break;
+                       if (!IS_ERR(chan))
+                               break;
                }
 
        of_node_put(spec.np);
@@ -515,3 +544,73 @@ void mbox_controller_unregister(struct mbox_controller *mbox)
        mutex_unlock(&con_mutex);
 }
 EXPORT_SYMBOL_GPL(mbox_controller_unregister);
+
+static void __devm_mbox_controller_unregister(struct device *dev, void *res)
+{
+       struct mbox_controller **mbox = res;
+
+       mbox_controller_unregister(*mbox);
+}
+
+static int devm_mbox_controller_match(struct device *dev, void *res, void *data)
+{
+       struct mbox_controller **mbox = res;
+
+       if (WARN_ON(!mbox || !*mbox))
+               return 0;
+
+       return *mbox == data;
+}
+
+/**
+ * devm_mbox_controller_register() - managed mbox_controller_register()
+ * @dev: device owning the mailbox controller being registered
+ * @mbox: mailbox controller being registered
+ *
+ * This function adds a device-managed resource that will make sure that the
+ * mailbox controller, which is registered using mbox_controller_register()
+ * as part of this function, will be unregistered along with the rest of
+ * device-managed resources upon driver probe failure or driver removal.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_mbox_controller_register(struct device *dev,
+                                 struct mbox_controller *mbox)
+{
+       struct mbox_controller **ptr;
+       int err;
+
+       ptr = devres_alloc(__devm_mbox_controller_unregister, sizeof(*ptr),
+                          GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       err = mbox_controller_register(mbox);
+       if (err < 0) {
+               devres_free(ptr);
+               return err;
+       }
+
+       devres_add(dev, ptr);
+       *ptr = mbox;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devm_mbox_controller_register);
+
+/**
+ * devm_mbox_controller_unregister() - managed mbox_controller_unregister()
+ * @dev: device owning the mailbox controller being unregistered
+ * @mbox: mailbox controller being unregistered
+ *
+ * This function unregisters the mailbox controller and removes the device-
+ * managed resource that was set up to automatically unregister the mailbox
+ * controller on driver probe failure or driver removal. It's typically not
+ * necessary to call this function.
+ */
+void devm_mbox_controller_unregister(struct device *dev, struct mbox_controller *mbox)
+{
+       WARN_ON(devres_release(dev, __devm_mbox_controller_unregister,
+                              devm_mbox_controller_match, mbox));
+}
+EXPORT_SYMBOL_GPL(devm_mbox_controller_unregister);
index f7cc29c00302a493ee4136fe41273775b706c35a..22811784dc7de9527ddbe60221a8cbb6bac3ca87 100644 (file)
@@ -337,17 +337,8 @@ static int cmdq_remove(struct platform_device *pdev)
 {
        struct cmdq *cmdq = platform_get_drvdata(pdev);
 
-       mbox_controller_unregister(&cmdq->mbox);
        clk_unprepare(cmdq->clock);
 
-       if (cmdq->mbox.chans)
-               devm_kfree(&pdev->dev, cmdq->mbox.chans);
-
-       if (cmdq->thread)
-               devm_kfree(&pdev->dev, cmdq->thread);
-
-       devm_kfree(&pdev->dev, cmdq);
-
        return 0;
 }
 
@@ -524,7 +515,7 @@ static int cmdq_probe(struct platform_device *pdev)
                cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i];
        }
 
-       err = mbox_controller_register(&cmdq->mbox);
+       err = devm_mbox_controller_register(dev, &cmdq->mbox);
        if (err < 0) {
                dev_err(dev, "failed to register mailbox: %d\n", err);
                return err;
index db66e952a87153e77df761a0ef04e53e04880330..ca50177a33f29ecd7c4ce4151a5c3b606417256f 100644 (file)
@@ -486,7 +486,7 @@ static int omap_mbox_register(struct omap_mbox_device *mdev)
        list_add(&mdev->elem, &omap_mbox_devices);
        mutex_unlock(&omap_mbox_devices_lock);
 
-       ret = mbox_controller_register(&mdev->controller);
+       ret = devm_mbox_controller_register(mdev->dev, &mdev->controller);
 
 err_out:
        if (ret) {
@@ -508,8 +508,6 @@ static int omap_mbox_unregister(struct omap_mbox_device *mdev)
        list_del(&mdev->elem);
        mutex_unlock(&omap_mbox_devices_lock);
 
-       mbox_controller_unregister(&mdev->controller);
-
        mboxes = mdev->mboxes;
        for (i = 0; mboxes[i]; i++)
                device_unregister(mboxes[i]->dev);
index e13201a5cec686a63e7d82c30544ab6ee3e13ef9..d2502c5be13092d6a61349f8f2747746673b2ad1 100644 (file)
@@ -163,7 +163,7 @@ static int platform_mhu_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, mhu);
 
-       err = mbox_controller_register(&mhu->mbox);
+       err = devm_mbox_controller_register(dev, &mhu->mbox);
        if (err) {
                dev_err(dev, "Failed to register mailboxes %d\n", err);
                return err;
@@ -173,15 +173,6 @@ static int platform_mhu_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int platform_mhu_remove(struct platform_device *pdev)
-{
-       struct platform_mhu *mhu = platform_get_drvdata(pdev);
-
-       mbox_controller_unregister(&mhu->mbox);
-
-       return 0;
-}
-
 static const struct of_device_id platform_mhu_dt_ids[] = {
        { .compatible = "amlogic,meson-gxbb-mhu", },
        { /* sentinel */ },
@@ -190,7 +181,6 @@ MODULE_DEVICE_TABLE(of, platform_mhu_dt_ids);
 
 static struct platform_driver platform_mhu_driver = {
        .probe  = platform_mhu_probe,
-       .remove = platform_mhu_remove,
        .driver = {
                .name = "platform-mhu",
                .of_match_table = platform_mhu_dt_ids,
index aed23ac9550dff8ef24761a18776ca650d082c7a..3cf2937be14912a70d5c82a5cc79e09dee8bf995 100644 (file)
@@ -91,7 +91,7 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
        apcs->mbox.chans = apcs->mbox_chans;
        apcs->mbox.num_chans = ARRAY_SIZE(apcs->mbox_chans);
 
-       ret = mbox_controller_register(&apcs->mbox);
+       ret = devm_mbox_controller_register(&pdev->dev, &apcs->mbox);
        if (ret) {
                dev_err(&pdev->dev, "failed to register APCS IPC controller\n");
                return ret;
@@ -115,7 +115,6 @@ static int qcom_apcs_ipc_remove(struct platform_device *pdev)
        struct qcom_apcs_ipc *apcs = platform_get_drvdata(pdev);
        struct platform_device *clk = apcs->clk;
 
-       mbox_controller_unregister(&apcs->mbox);
        platform_device_unregister(clk);
 
        return 0;
index d702a204f5c10ef5dc0d46946b9ff7495390caeb..f24a77b1a0ff626429ca0f4455bff171374bd2f1 100644 (file)
@@ -247,28 +247,15 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
                mb->chans[i].msg = NULL;
        }
 
-       ret = mbox_controller_register(&mb->mbox);
+       ret = devm_mbox_controller_register(&pdev->dev, &mb->mbox);
        if (ret < 0)
                dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret);
 
        return ret;
 }
 
-static int rockchip_mbox_remove(struct platform_device *pdev)
-{
-       struct rockchip_mbox *mb = platform_get_drvdata(pdev);
-
-       if (!mb)
-               return -EINVAL;
-
-       mbox_controller_unregister(&mb->mbox);
-
-       return 0;
-}
-
 static struct platform_driver rockchip_mbox_driver = {
        .probe  = rockchip_mbox_probe,
-       .remove = rockchip_mbox_remove,
        .driver = {
                .name = "rockchip-mailbox",
                .of_match_table = of_match_ptr(rockchip_mbox_of_match),
index 533b0da5235d43b52e1f75ab68537ff5f7ab1e35..a338bd4cd7db69ccd539af77b75c26a612d1c9eb 100644 (file)
@@ -299,7 +299,7 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
        for (i = 0; i < ipcc->controller.num_chans; i++)
                ipcc->controller.chans[i].con_priv = (void *)i;
 
-       ret = mbox_controller_register(&ipcc->controller);
+       ret = devm_mbox_controller_register(dev, &ipcc->controller);
        if (ret)
                goto err_irq_wkp;
 
@@ -329,8 +329,6 @@ static int stm32_ipcc_remove(struct platform_device *pdev)
 {
        struct stm32_ipcc *ipcc = platform_get_drvdata(pdev);
 
-       mbox_controller_unregister(&ipcc->controller);
-
        if (ipcc->wkp)
                dev_pm_clear_wake_irq(&pdev->dev);
 
index 0cde356c11abcb5cc0d1596e9ef0888acf883662..e443f6a2ec4bf81eb17770bf06ed062e12c952b6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2016-2018, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * more details.
  */
 
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/mailbox_controller.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
 
 #include <dt-bindings/mailbox/tegra186-hsp.h>
 
+#include "mailbox.h"
+
+#define HSP_INT_IE(x)          (0x100 + ((x) * 4))
+#define HSP_INT_IV             0x300
+#define HSP_INT_IR             0x304
+
+#define HSP_INT_EMPTY_SHIFT    0
+#define HSP_INT_EMPTY_MASK     0xff
+#define HSP_INT_FULL_SHIFT     8
+#define HSP_INT_FULL_MASK      0xff
+
 #define HSP_INT_DIMENSIONING   0x380
 #define HSP_nSM_SHIFT          0
 #define HSP_nSS_SHIFT          4
 #define HSP_DB_RAW     0x8
 #define HSP_DB_PENDING 0xc
 
+#define HSP_SM_SHRD_MBOX       0x0
+#define HSP_SM_SHRD_MBOX_FULL  BIT(31)
+#define HSP_SM_SHRD_MBOX_FULL_INT_IE   0x04
+#define HSP_SM_SHRD_MBOX_EMPTY_INT_IE  0x08
+
 #define HSP_DB_CCPLEX          1
 #define HSP_DB_BPMP            3
 #define HSP_DB_MAX             7
@@ -55,6 +73,12 @@ struct tegra_hsp_doorbell {
        unsigned int index;
 };
 
+struct tegra_hsp_mailbox {
+       struct tegra_hsp_channel channel;
+       unsigned int index;
+       bool producer;
+};
+
 struct tegra_hsp_db_map {
        const char *name;
        unsigned int master;
@@ -63,13 +87,18 @@ struct tegra_hsp_db_map {
 
 struct tegra_hsp_soc {
        const struct tegra_hsp_db_map *map;
+       bool has_per_mb_ie;
 };
 
 struct tegra_hsp {
+       struct device *dev;
        const struct tegra_hsp_soc *soc;
-       struct mbox_controller mbox;
+       struct mbox_controller mbox_db;
+       struct mbox_controller mbox_sm;
        void __iomem *regs;
-       unsigned int irq;
+       unsigned int doorbell_irq;
+       unsigned int *shared_irqs;
+       unsigned int shared_irq;
        unsigned int num_sm;
        unsigned int num_as;
        unsigned int num_ss;
@@ -78,13 +107,10 @@ struct tegra_hsp {
        spinlock_t lock;
 
        struct list_head doorbells;
-};
+       struct tegra_hsp_mailbox *mailboxes;
 
-static inline struct tegra_hsp *
-to_tegra_hsp(struct mbox_controller *mbox)
-{
-       return container_of(mbox, struct tegra_hsp, mbox);
-}
+       unsigned long mask;
+};
 
 static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
 {
@@ -158,7 +184,7 @@ static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
 
        spin_lock(&hsp->lock);
 
-       for_each_set_bit(master, &value, hsp->mbox.num_chans) {
+       for_each_set_bit(master, &value, hsp->mbox_db.num_chans) {
                struct tegra_hsp_doorbell *db;
 
                db = __tegra_hsp_doorbell_get(hsp, master);
@@ -182,6 +208,71 @@ static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t tegra_hsp_shared_irq(int irq, void *data)
+{
+       struct tegra_hsp *hsp = data;
+       unsigned long bit, mask;
+       u32 status, value;
+       void *msg;
+
+       status = tegra_hsp_readl(hsp, HSP_INT_IR) & hsp->mask;
+
+       /* process EMPTY interrupts first */
+       mask = (status >> HSP_INT_EMPTY_SHIFT) & HSP_INT_EMPTY_MASK;
+
+       for_each_set_bit(bit, &mask, hsp->num_sm) {
+               struct tegra_hsp_mailbox *mb = &hsp->mailboxes[bit];
+
+               if (mb->producer) {
+                       /*
+                        * Disable EMPTY interrupts until data is sent with
+                        * the next message. These interrupts are level-
+                        * triggered, so if we kept them enabled they would
+                        * constantly trigger until we next write data into
+                        * the message.
+                        */
+                       spin_lock(&hsp->lock);
+
+                       hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
+                       tegra_hsp_writel(hsp, hsp->mask,
+                                        HSP_INT_IE(hsp->shared_irq));
+
+                       spin_unlock(&hsp->lock);
+
+                       mbox_chan_txdone(mb->channel.chan, 0);
+               }
+       }
+
+       /* process FULL interrupts */
+       mask = (status >> HSP_INT_FULL_SHIFT) & HSP_INT_FULL_MASK;
+
+       for_each_set_bit(bit, &mask, hsp->num_sm) {
+               struct tegra_hsp_mailbox *mb = &hsp->mailboxes[bit];
+
+               if (!mb->producer) {
+                       value = tegra_hsp_channel_readl(&mb->channel,
+                                                       HSP_SM_SHRD_MBOX);
+                       value &= ~HSP_SM_SHRD_MBOX_FULL;
+                       msg = (void *)(unsigned long)value;
+                       mbox_chan_received_data(mb->channel.chan, msg);
+
+                       /*
+                        * Need to clear all bits here since some producers,
+                        * such as TCU, depend on fields in the register
+                        * getting cleared by the consumer.
+                        *
+                        * The mailbox API doesn't give the consumers a way
+                        * of doing that explicitly, so we have to make sure
+                        * we cover all possible cases.
+                        */
+                       tegra_hsp_channel_writel(&mb->channel, 0x0,
+                                                HSP_SM_SHRD_MBOX);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
 static struct tegra_hsp_channel *
 tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
                          unsigned int master, unsigned int index)
@@ -190,17 +281,17 @@ tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
        unsigned int offset;
        unsigned long flags;
 
-       db = kzalloc(sizeof(*db), GFP_KERNEL);
+       db = devm_kzalloc(hsp->dev, sizeof(*db), GFP_KERNEL);
        if (!db)
                return ERR_PTR(-ENOMEM);
 
-       offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
+       offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) * SZ_64K;
        offset += index * 0x100;
 
        db->channel.regs = hsp->regs + offset;
        db->channel.hsp = hsp;
 
-       db->name = kstrdup_const(name, GFP_KERNEL);
+       db->name = devm_kstrdup_const(hsp->dev, name, GFP_KERNEL);
        db->master = master;
        db->index = index;
 
@@ -211,13 +302,6 @@ tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
        return &db->channel;
 }
 
-static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
-{
-       list_del(&db->list);
-       kfree_const(db->name);
-       kfree(db);
-}
-
 static int tegra_hsp_doorbell_send_data(struct mbox_chan *chan, void *data)
 {
        struct tegra_hsp_doorbell *db = chan->con_priv;
@@ -235,8 +319,8 @@ static int tegra_hsp_doorbell_startup(struct mbox_chan *chan)
        unsigned long flags;
        u32 value;
 
-       if (db->master >= hsp->mbox.num_chans) {
-               dev_err(hsp->mbox.dev,
+       if (db->master >= chan->mbox->num_chans) {
+               dev_err(chan->mbox->dev,
                        "invalid master ID %u for HSP channel\n",
                        db->master);
                return -EINVAL;
@@ -281,46 +365,167 @@ static void tegra_hsp_doorbell_shutdown(struct mbox_chan *chan)
        spin_unlock_irqrestore(&hsp->lock, flags);
 }
 
-static const struct mbox_chan_ops tegra_hsp_doorbell_ops = {
+static const struct mbox_chan_ops tegra_hsp_db_ops = {
        .send_data = tegra_hsp_doorbell_send_data,
        .startup = tegra_hsp_doorbell_startup,
        .shutdown = tegra_hsp_doorbell_shutdown,
 };
 
-static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
+static int tegra_hsp_mailbox_send_data(struct mbox_chan *chan, void *data)
+{
+       struct tegra_hsp_mailbox *mb = chan->con_priv;
+       struct tegra_hsp *hsp = mb->channel.hsp;
+       unsigned long flags;
+       u32 value;
+
+       if (WARN_ON(!mb->producer))
+               return -EPERM;
+
+       /* copy data and mark mailbox full */
+       value = (u32)(unsigned long)data;
+       value |= HSP_SM_SHRD_MBOX_FULL;
+
+       tegra_hsp_channel_writel(&mb->channel, value, HSP_SM_SHRD_MBOX);
+
+       /* enable EMPTY interrupt for the shared mailbox */
+       spin_lock_irqsave(&hsp->lock, flags);
+
+       hsp->mask |= BIT(HSP_INT_EMPTY_SHIFT + mb->index);
+       tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
+
+       spin_unlock_irqrestore(&hsp->lock, flags);
+
+       return 0;
+}
+
+static int tegra_hsp_mailbox_flush(struct mbox_chan *chan,
+                                  unsigned long timeout)
+{
+       struct tegra_hsp_mailbox *mb = chan->con_priv;
+       struct tegra_hsp_channel *ch = &mb->channel;
+       u32 value;
+
+       timeout = jiffies + msecs_to_jiffies(timeout);
+
+       while (time_before(jiffies, timeout)) {
+               value = tegra_hsp_channel_readl(ch, HSP_SM_SHRD_MBOX);
+               if ((value & HSP_SM_SHRD_MBOX_FULL) == 0) {
+                       mbox_chan_txdone(chan, 0);
+                       return 0;
+               }
+
+               udelay(1);
+       }
+
+       return -ETIME;
+}
+
+static int tegra_hsp_mailbox_startup(struct mbox_chan *chan)
+{
+       struct tegra_hsp_mailbox *mb = chan->con_priv;
+       struct tegra_hsp_channel *ch = &mb->channel;
+       struct tegra_hsp *hsp = mb->channel.hsp;
+       unsigned long flags;
+
+       chan->txdone_method = TXDONE_BY_IRQ;
+
+       /*
+        * Shared mailboxes start out as consumers by default. FULL and EMPTY
+        * interrupts are coalesced at the same shared interrupt.
+        *
+        * Keep EMPTY interrupts disabled at startup and only enable them when
+        * the mailbox is actually full. This is required because the FULL and
+        * EMPTY interrupts are level-triggered, so keeping EMPTY interrupts
+        * enabled all the time would cause an interrupt storm while mailboxes
+        * are idle.
+        */
+
+       spin_lock_irqsave(&hsp->lock, flags);
+
+       if (mb->producer)
+               hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
+       else
+               hsp->mask |= BIT(HSP_INT_FULL_SHIFT + mb->index);
+
+       tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
+
+       spin_unlock_irqrestore(&hsp->lock, flags);
+
+       if (hsp->soc->has_per_mb_ie) {
+               if (mb->producer)
+                       tegra_hsp_channel_writel(ch, 0x0,
+                                                HSP_SM_SHRD_MBOX_EMPTY_INT_IE);
+               else
+                       tegra_hsp_channel_writel(ch, 0x1,
+                                                HSP_SM_SHRD_MBOX_FULL_INT_IE);
+       }
+
+       return 0;
+}
+
+static void tegra_hsp_mailbox_shutdown(struct mbox_chan *chan)
+{
+       struct tegra_hsp_mailbox *mb = chan->con_priv;
+       struct tegra_hsp_channel *ch = &mb->channel;
+       struct tegra_hsp *hsp = mb->channel.hsp;
+       unsigned long flags;
+
+       if (hsp->soc->has_per_mb_ie) {
+               if (mb->producer)
+                       tegra_hsp_channel_writel(ch, 0x0,
+                                                HSP_SM_SHRD_MBOX_EMPTY_INT_IE);
+               else
+                       tegra_hsp_channel_writel(ch, 0x0,
+                                                HSP_SM_SHRD_MBOX_FULL_INT_IE);
+       }
+
+       spin_lock_irqsave(&hsp->lock, flags);
+
+       if (mb->producer)
+               hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
+       else
+               hsp->mask &= ~BIT(HSP_INT_FULL_SHIFT + mb->index);
+
+       tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
+
+       spin_unlock_irqrestore(&hsp->lock, flags);
+}
+
+static const struct mbox_chan_ops tegra_hsp_sm_ops = {
+       .send_data = tegra_hsp_mailbox_send_data,
+       .flush = tegra_hsp_mailbox_flush,
+       .startup = tegra_hsp_mailbox_startup,
+       .shutdown = tegra_hsp_mailbox_shutdown,
+};
+
+static struct mbox_chan *tegra_hsp_db_xlate(struct mbox_controller *mbox,
                                            const struct of_phandle_args *args)
 {
+       struct tegra_hsp *hsp = container_of(mbox, struct tegra_hsp, mbox_db);
+       unsigned int type = args->args[0], master = args->args[1];
        struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
-       struct tegra_hsp *hsp = to_tegra_hsp(mbox);
-       unsigned int type = args->args[0];
-       unsigned int master = args->args[1];
        struct tegra_hsp_doorbell *db;
        struct mbox_chan *chan;
        unsigned long flags;
        unsigned int i;
 
-       switch (type) {
-       case TEGRA_HSP_MBOX_TYPE_DB:
-               db = tegra_hsp_doorbell_get(hsp, master);
-               if (db)
-                       channel = &db->channel;
-
-               break;
+       if (type != TEGRA_HSP_MBOX_TYPE_DB || !hsp->doorbell_irq)
+               return ERR_PTR(-ENODEV);
 
-       default:
-               break;
-       }
+       db = tegra_hsp_doorbell_get(hsp, master);
+       if (db)
+               channel = &db->channel;
 
        if (IS_ERR(channel))
                return ERR_CAST(channel);
 
        spin_lock_irqsave(&hsp->lock, flags);
 
-       for (i = 0; i < hsp->mbox.num_chans; i++) {
-               chan = &hsp->mbox.chans[i];
+       for (i = 0; i < mbox->num_chans; i++) {
+               chan = &mbox->chans[i];
                if (!chan->con_priv) {
-                       chan->con_priv = channel;
                        channel->chan = chan;
+                       chan->con_priv = db;
                        break;
                }
 
@@ -332,17 +537,27 @@ static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
        return chan ?: ERR_PTR(-EBUSY);
 }
 
-static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
+static struct mbox_chan *tegra_hsp_sm_xlate(struct mbox_controller *mbox,
+                                           const struct of_phandle_args *args)
 {
-       struct tegra_hsp_doorbell *db, *tmp;
-       unsigned long flags;
+       struct tegra_hsp *hsp = container_of(mbox, struct tegra_hsp, mbox_sm);
+       unsigned int type = args->args[0], index;
+       struct tegra_hsp_mailbox *mb;
 
-       spin_lock_irqsave(&hsp->lock, flags);
+       index = args->args[1] & TEGRA_HSP_SM_MASK;
 
-       list_for_each_entry_safe(db, tmp, &hsp->doorbells, list)
-               __tegra_hsp_doorbell_destroy(db);
+       if (type != TEGRA_HSP_MBOX_TYPE_SM || !hsp->shared_irqs ||
+           index >= hsp->num_sm)
+               return ERR_PTR(-ENODEV);
 
-       spin_unlock_irqrestore(&hsp->lock, flags);
+       mb = &hsp->mailboxes[index];
+
+       if ((args->args[1] & TEGRA_HSP_SM_FLAG_TX) == 0)
+               mb->producer = false;
+       else
+               mb->producer = true;
+
+       return mb->channel.chan;
 }
 
 static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
@@ -353,10 +568,8 @@ static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
        while (map->name) {
                channel = tegra_hsp_doorbell_create(hsp, map->name,
                                                    map->master, map->index);
-               if (IS_ERR(channel)) {
-                       tegra_hsp_remove_doorbells(hsp);
+               if (IS_ERR(channel))
                        return PTR_ERR(channel);
-               }
 
                map++;
        }
@@ -364,10 +577,70 @@ static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
        return 0;
 }
 
+static int tegra_hsp_add_mailboxes(struct tegra_hsp *hsp, struct device *dev)
+{
+       int i;
+
+       hsp->mailboxes = devm_kcalloc(dev, hsp->num_sm, sizeof(*hsp->mailboxes),
+                                     GFP_KERNEL);
+       if (!hsp->mailboxes)
+               return -ENOMEM;
+
+       for (i = 0; i < hsp->num_sm; i++) {
+               struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i];
+
+               mb->index = i;
+
+               mb->channel.hsp = hsp;
+               mb->channel.regs = hsp->regs + SZ_64K + i * SZ_32K;
+               mb->channel.chan = &hsp->mbox_sm.chans[i];
+               mb->channel.chan->con_priv = mb;
+       }
+
+       return 0;
+}
+
+static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
+{
+       unsigned int i, irq = 0;
+       int err;
+
+       for (i = 0; i < hsp->num_si; i++) {
+               irq = hsp->shared_irqs[i];
+               if (irq <= 0)
+                       continue;
+
+               err = devm_request_irq(hsp->dev, irq, tegra_hsp_shared_irq, 0,
+                                      dev_name(hsp->dev), hsp);
+               if (err < 0) {
+                       dev_err(hsp->dev, "failed to request interrupt: %d\n",
+                               err);
+                       continue;
+               }
+
+               hsp->shared_irq = i;
+
+               /* disable all interrupts */
+               tegra_hsp_writel(hsp, 0, HSP_INT_IE(hsp->shared_irq));
+
+               dev_dbg(hsp->dev, "interrupt requested: %u\n", irq);
+
+               break;
+       }
+
+       if (i == hsp->num_si) {
+               dev_err(hsp->dev, "failed to find available interrupt\n");
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
 static int tegra_hsp_probe(struct platform_device *pdev)
 {
        struct tegra_hsp *hsp;
        struct resource *res;
+       unsigned int i;
        u32 value;
        int err;
 
@@ -375,6 +648,7 @@ static int tegra_hsp_probe(struct platform_device *pdev)
        if (!hsp)
                return -ENOMEM;
 
+       hsp->dev = &pdev->dev;
        hsp->soc = of_device_get_match_data(&pdev->dev);
        INIT_LIST_HEAD(&hsp->doorbells);
        spin_lock_init(&hsp->lock);
@@ -392,62 +666,136 @@ static int tegra_hsp_probe(struct platform_device *pdev)
        hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
 
        err = platform_get_irq_byname(pdev, "doorbell");
+       if (err >= 0)
+               hsp->doorbell_irq = err;
+
+       if (hsp->num_si > 0) {
+               unsigned int count = 0;
+
+               hsp->shared_irqs = devm_kcalloc(&pdev->dev, hsp->num_si,
+                                               sizeof(*hsp->shared_irqs),
+                                               GFP_KERNEL);
+               if (!hsp->shared_irqs)
+                       return -ENOMEM;
+
+               for (i = 0; i < hsp->num_si; i++) {
+                       char *name;
+
+                       name = kasprintf(GFP_KERNEL, "shared%u", i);
+                       if (!name)
+                               return -ENOMEM;
+
+                       err = platform_get_irq_byname(pdev, name);
+                       if (err >= 0) {
+                               hsp->shared_irqs[i] = err;
+                               count++;
+                       }
+
+                       kfree(name);
+               }
+
+               if (count == 0) {
+                       devm_kfree(&pdev->dev, hsp->shared_irqs);
+                       hsp->shared_irqs = NULL;
+               }
+       }
+
+       /* setup the doorbell controller */
+       hsp->mbox_db.of_xlate = tegra_hsp_db_xlate;
+       hsp->mbox_db.num_chans = 32;
+       hsp->mbox_db.dev = &pdev->dev;
+       hsp->mbox_db.ops = &tegra_hsp_db_ops;
+
+       hsp->mbox_db.chans = devm_kcalloc(&pdev->dev, hsp->mbox_db.num_chans,
+                                         sizeof(*hsp->mbox_db.chans),
+                                         GFP_KERNEL);
+       if (!hsp->mbox_db.chans)
+               return -ENOMEM;
+
+       if (hsp->doorbell_irq) {
+               err = tegra_hsp_add_doorbells(hsp);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "failed to add doorbells: %d\n",
+                               err);
+                       return err;
+               }
+       }
+
+       err = devm_mbox_controller_register(&pdev->dev, &hsp->mbox_db);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
+               dev_err(&pdev->dev, "failed to register doorbell mailbox: %d\n",
+                       err);
                return err;
        }
 
-       hsp->irq = err;
-
-       hsp->mbox.of_xlate = of_tegra_hsp_xlate;
-       hsp->mbox.num_chans = 32;
-       hsp->mbox.dev = &pdev->dev;
-       hsp->mbox.txdone_irq = false;
-       hsp->mbox.txdone_poll = false;
-       hsp->mbox.ops = &tegra_hsp_doorbell_ops;
+       /* setup the shared mailbox controller */
+       hsp->mbox_sm.of_xlate = tegra_hsp_sm_xlate;
+       hsp->mbox_sm.num_chans = hsp->num_sm;
+       hsp->mbox_sm.dev = &pdev->dev;
+       hsp->mbox_sm.ops = &tegra_hsp_sm_ops;
 
-       hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
-                                       sizeof(*hsp->mbox.chans),
-                                       GFP_KERNEL);
-       if (!hsp->mbox.chans)
+       hsp->mbox_sm.chans = devm_kcalloc(&pdev->dev, hsp->mbox_sm.num_chans,
+                                         sizeof(*hsp->mbox_sm.chans),
+                                         GFP_KERNEL);
+       if (!hsp->mbox_sm.chans)
                return -ENOMEM;
 
-       err = tegra_hsp_add_doorbells(hsp);
+       if (hsp->shared_irqs) {
+               err = tegra_hsp_add_mailboxes(hsp, &pdev->dev);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "failed to add mailboxes: %d\n",
+                               err);
+                       return err;
+               }
+       }
+
+       err = devm_mbox_controller_register(&pdev->dev, &hsp->mbox_sm);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
+               dev_err(&pdev->dev, "failed to register shared mailbox: %d\n",
+                       err);
                return err;
        }
 
        platform_set_drvdata(pdev, hsp);
 
-       err = mbox_controller_register(&hsp->mbox);
-       if (err) {
-               dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
-               tegra_hsp_remove_doorbells(hsp);
-               return err;
+       if (hsp->doorbell_irq) {
+               err = devm_request_irq(&pdev->dev, hsp->doorbell_irq,
+                                      tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND,
+                                      dev_name(&pdev->dev), hsp);
+               if (err < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to request doorbell IRQ#%u: %d\n",
+                               hsp->doorbell_irq, err);
+                       return err;
+               }
        }
 
-       err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
-                              IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
-                       hsp->irq, err);
-               return err;
+       if (hsp->shared_irqs) {
+               err = tegra_hsp_request_shared_irq(hsp);
+               if (err < 0)
+                       return err;
        }
 
        return 0;
 }
 
-static int tegra_hsp_remove(struct platform_device *pdev)
+static int tegra_hsp_resume(struct device *dev)
 {
-       struct tegra_hsp *hsp = platform_get_drvdata(pdev);
+       struct tegra_hsp *hsp = dev_get_drvdata(dev);
+       unsigned int i;
 
-       mbox_controller_unregister(&hsp->mbox);
-       tegra_hsp_remove_doorbells(hsp);
+       for (i = 0; i < hsp->num_sm; i++) {
+               struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i];
+
+               if (mb->channel.chan->cl)
+                       tegra_hsp_mailbox_startup(mb->channel.chan);
+       }
 
        return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(tegra_hsp_pm_ops, NULL, tegra_hsp_resume);
+
 static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
        { "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX, },
        { "bpmp",   TEGRA_HSP_DB_MASTER_BPMP,   HSP_DB_BPMP,   },
@@ -456,10 +804,17 @@ static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
 
 static const struct tegra_hsp_soc tegra186_hsp_soc = {
        .map = tegra186_hsp_db_map,
+       .has_per_mb_ie = false,
+};
+
+static const struct tegra_hsp_soc tegra194_hsp_soc = {
+       .map = tegra186_hsp_db_map,
+       .has_per_mb_ie = true,
 };
 
 static const struct of_device_id tegra_hsp_match[] = {
        { .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
+       { .compatible = "nvidia,tegra194-hsp", .data = &tegra194_hsp_soc },
        { }
 };
 
@@ -467,9 +822,9 @@ static struct platform_driver tegra_hsp_driver = {
        .driver = {
                .name = "tegra-hsp",
                .of_match_table = tegra_hsp_match,
+               .pm = &tegra_hsp_pm_ops,
        },
        .probe = tegra_hsp_probe,
-       .remove = tegra_hsp_remove,
 };
 
 static int __init tegra_hsp_init(void)
index 713d701b656892d2f9fe5d7008f30808d21936c8..88047d835211c2d05c756daaa6d2be79f36d7e1e 100644 (file)
@@ -547,7 +547,7 @@ static struct mbox_chan *ti_msgmgr_of_xlate(struct mbox_controller *mbox,
        }
 
        if (d->is_sproxy) {
-               if (req_pid > d->num_valid_queues)
+               if (req_pid >= d->num_valid_queues)
                        goto err;
                qinst = &inst->qinsts[req_pid];
                return qinst->chan;
@@ -817,26 +817,15 @@ static int ti_msgmgr_probe(struct platform_device *pdev)
        mbox->of_xlate = ti_msgmgr_of_xlate;
 
        platform_set_drvdata(pdev, inst);
-       ret = mbox_controller_register(mbox);
+       ret = devm_mbox_controller_register(dev, mbox);
        if (ret)
                dev_err(dev, "Failed to register mbox_controller(%d)\n", ret);
 
        return ret;
 }
 
-static int ti_msgmgr_remove(struct platform_device *pdev)
-{
-       struct ti_msgmgr_inst *inst;
-
-       inst = platform_get_drvdata(pdev);
-       mbox_controller_unregister(&inst->mbox);
-
-       return 0;
-}
-
 static struct platform_driver ti_msgmgr_driver = {
        .probe = ti_msgmgr_probe,
-       .remove = ti_msgmgr_remove,
        .driver = {
                   .name = "ti-msgmgr",
                   .of_match_table = of_match_ptr(ti_msgmgr_of_match),
index bcab5b7ca7852c2f157fbfe4d98983e9e5f9ac2c..3bdec7a84d35cfd98c2d203aff7b11792ca3e4f4 100644 (file)
 #define TEGRA_HSP_DB_MASTER_CCPLEX 17
 #define TEGRA_HSP_DB_MASTER_BPMP 19
 
+/*
+ * Shared mailboxes are unidirectional, so the direction needs to be specified
+ * in the device tree.
+ */
+#define TEGRA_HSP_SM_MASK 0x00ffffff
+#define TEGRA_HSP_SM_FLAG_RX (0 << 31)
+#define TEGRA_HSP_SM_FLAG_TX (1 << 31)
+
+#define TEGRA_HSP_SM_RX(x) (TEGRA_HSP_SM_FLAG_RX | ((x) & TEGRA_HSP_SM_MASK))
+#define TEGRA_HSP_SM_TX(x) (TEGRA_HSP_SM_FLAG_TX | ((x) & TEGRA_HSP_SM_MASK))
+
 #endif
index 44348710953f277576975b1bfe37375fc50e899c..faa7da3c9c8b65a7a4b6246e1a1ac0f66111165e 100644 (file)
@@ -44,6 +44,7 @@ struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
                                              const char *name);
 struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
 int mbox_send_message(struct mbox_chan *chan, void *mssg);
+int mbox_flush(struct mbox_chan *chan, unsigned long timeout);
 void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
 bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
 void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
index 74deadb42d76747e8f8f66b32bc839b052ed20a8..4994a438444c7c8afe82d2fd9b06d90438540537 100644 (file)
@@ -24,6 +24,9 @@ struct mbox_chan;
  *             transmission of data is reported by the controller via
  *             mbox_chan_txdone (if it has some TX ACK irq). It must not
  *             sleep.
+ * @flush:     Called when a client requests transmissions to be blocking but
+ *             the context doesn't allow sleeping. Typically the controller
+ *             will implement a busy loop waiting for the data to flush out.
  * @startup:   Called when a client requests the chan. The controller
  *             could ask clients for additional parameters of communication
  *             to be provided via client's chan_data. This call may
@@ -46,6 +49,7 @@ struct mbox_chan;
  */
 struct mbox_chan_ops {
        int (*send_data)(struct mbox_chan *chan, void *data);
+       int (*flush)(struct mbox_chan *chan, unsigned long timeout);
        int (*startup)(struct mbox_chan *chan);
        void (*shutdown)(struct mbox_chan *chan);
        bool (*last_tx_done)(struct mbox_chan *chan);
@@ -131,4 +135,9 @@ void mbox_controller_unregister(struct mbox_controller *mbox); /* can sleep */
 void mbox_chan_received_data(struct mbox_chan *chan, void *data); /* atomic */
 void mbox_chan_txdone(struct mbox_chan *chan, int r); /* atomic */
 
+int devm_mbox_controller_register(struct device *dev,
+                                 struct mbox_controller *mbox);
+void devm_mbox_controller_unregister(struct device *dev,
+                                    struct mbox_controller *mbox);
+
 #endif /* __MAILBOX_CONTROLLER_H */