Commit 35ddb06a authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 6aa293d8 d69e1164
......@@ -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:
......
......@@ -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);
......
......@@ -1665,7 +1665,7 @@ static int flexrm_mbox_probe(struct platform_device *pdev)
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);
......
......@@ -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;
......
// 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);
......
......@@ -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,
......
......@@ -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)
......
......@@ -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;
......
......@@ -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 @@ static int altera_mbox_probe(struct platform_device *pdev)
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,
......
......@@ -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,
......
......@@ -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),
......
......@@ -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);
......@@ -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;
......
......@@ -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);
......
......@@ -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,
......
......@@ -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;
......
......@@ -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),
......
......@@ -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);
......
/*
* 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)
......
......@@ -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),
......
......@@ -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
......@@ -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 */
......
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment