diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-03 05:41:38 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-03 05:41:38 +0300 |
commit | 35ddb06a467538434b4139fbf5c02a2ef073162a (patch) | |
tree | 20bf25b62f6d360adffd4233a971ef53b02019be | |
parent | 6aa293d8ff0939802a6c86cee6cd152c1b0a7a0d (diff) | |
parent | d69e11648e486ee0f21cb246f687b083f0d4e124 (diff) | |
download | linux-35ddb06a467538434b4139fbf5c02a2ef073162a.tar.xz |
Merge tag 'mailbox-v4.21' of git://git.linaro.org/landing-teams/working/fujitsu/integration
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, 612 insertions, 247 deletions
diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt index b99d25fc2f26..ff3eafc5a882 100644 --- a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt +++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt @@ -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: diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index 99befa76e37c..64d85c6a2bdf 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -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); diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index d7a8ed7d8097..d713271ebf7c 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -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); diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index 4fe7be0bdd11..ccf3d62af7e7 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -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; diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c index e92bbc533821..39761d190545 100644 --- a/drivers/mailbox/bcm2835-mailbox.c +++ b/drivers/mailbox/bcm2835-mailbox.c @@ -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); diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c index 3eea6b642484..53f4bc2488c5 100644 --- a/drivers/mailbox/hi3660-mailbox.c +++ b/drivers/mailbox/hi3660-mailbox.c @@ -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, diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c index 4fa9803cd204..c32cbfaf223a 100644 --- a/drivers/mailbox/hi6220-mailbox.c +++ b/drivers/mailbox/hi6220-mailbox.c @@ -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) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 363d35d5e49d..774362a05159 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -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; diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index bcb29df9549e..397e25ddae29 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -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, diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c index 779d41262ef0..adf82b85dbb2 100644 --- a/drivers/mailbox/mailbox-sti.c +++ b/drivers/mailbox/mailbox-sti.c @@ -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, diff --git a/drivers/mailbox/mailbox-xgene-slimpro.c b/drivers/mailbox/mailbox-xgene-slimpro.c index b8b2b3533f46..8f397da1150b 100644 --- a/drivers/mailbox/mailbox-xgene-slimpro.c +++ b/drivers/mailbox/mailbox-xgene-slimpro.c @@ -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), diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 674b35f402f5..c6a7d4582dc6 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -284,6 +284,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. * @index: Index of mailbox specifier in 'mboxes' property. @@ -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); diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index f7cc29c00302..22811784dc7d 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -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; diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index db66e952a871..ca50177a33f2 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -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); diff --git a/drivers/mailbox/platform_mhu.c b/drivers/mailbox/platform_mhu.c index e13201a5cec6..d2502c5be130 100644 --- a/drivers/mailbox/platform_mhu.c +++ b/drivers/mailbox/platform_mhu.c @@ -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, diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index aed23ac9550d..3cf2937be149 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -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; diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c index d702a204f5c1..f24a77b1a0ff 100644 --- a/drivers/mailbox/rockchip-mailbox.c +++ b/drivers/mailbox/rockchip-mailbox.c @@ -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), diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index 533b0da5235d..a338bd4cd7db 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -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); diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 0cde356c11ab..e443f6a2ec4b 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -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, @@ -11,16 +11,29 @@ * 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 @@ -34,6 +47,11 @@ #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) diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 713d701b6568..88047d835211 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -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), diff --git a/include/dt-bindings/mailbox/tegra186-hsp.h b/include/dt-bindings/mailbox/tegra186-hsp.h index bcab5b7ca785..3bdec7a84d35 100644 --- a/include/dt-bindings/mailbox/tegra186-hsp.h +++ b/include/dt-bindings/mailbox/tegra186-hsp.h @@ -22,4 +22,15 @@ #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 diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h index 44348710953f..faa7da3c9c8b 100644 --- a/include/linux/mailbox_client.h +++ b/include/linux/mailbox_client.h @@ -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 */ diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index 74deadb42d76..4994a438444c 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -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 */ |