diff options
Diffstat (limited to 'drivers/mailbox')
-rw-r--r-- | drivers/mailbox/arm_mhuv2.c | 3 | ||||
-rw-r--r-- | drivers/mailbox/bcm-pdc-mailbox.c | 10 | ||||
-rw-r--r-- | drivers/mailbox/imx-mailbox.c | 32 | ||||
-rw-r--r-- | drivers/mailbox/mailbox-sti.c | 8 | ||||
-rw-r--r-- | drivers/mailbox/mtk-cmdq-mailbox.c | 80 | ||||
-rw-r--r-- | drivers/mailbox/pcc.c | 91 | ||||
-rw-r--r-- | drivers/mailbox/ti-msgmgr.c | 12 | ||||
-rw-r--r-- | drivers/mailbox/zynqmp-ipi-mailbox.c | 2 |
8 files changed, 188 insertions, 50 deletions
diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c index c6d4957c4d..0ec21dcdbd 100644 --- a/drivers/mailbox/arm_mhuv2.c +++ b/drivers/mailbox/arm_mhuv2.c @@ -553,7 +553,8 @@ static irqreturn_t mhuv2_sender_interrupt(int irq, void *data) priv = chan->con_priv; if (!IS_PROTOCOL_DOORBELL(priv)) { - writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + priv->windows - 1].int_clr); + for (i = 0; i < priv->windows; i++) + writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + i].int_clr); if (chan->cl) { mbox_chan_txdone(chan, 0); diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index d67db63b48..778faeced8 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -33,10 +33,9 @@ #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/mailbox_controller.h> #include <linux/mailbox/brcm-message.h> @@ -1494,7 +1493,6 @@ static int pdc_dt_read(struct platform_device *pdev, struct pdc_state *pdcs) { struct device *dev = &pdev->dev; struct device_node *dn = pdev->dev.of_node; - const struct of_device_id *match; const int *hw_type; int err; @@ -1509,11 +1507,9 @@ static int pdc_dt_read(struct platform_device *pdev, struct pdc_state *pdcs) pdcs->hw_type = PDC_HW; - match = of_match_device(of_match_ptr(pdc_mbox_of_match), dev); - if (match != NULL) { - hw_type = match->data; + hw_type = device_get_match_data(dev); + if (hw_type) pdcs->hw_type = *hw_type; - } return 0; } diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 3ef4dd8adf..0af739ab57 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -20,7 +20,9 @@ #include <linux/suspend.h> #include <linux/slab.h> -#define IMX_MU_CHANS 17 +#include "mailbox.h" + +#define IMX_MU_CHANS 24 /* TX0/RX0/RXDB[0-3] */ #define IMX_MU_SCU_CHANS 6 /* TX0/RX0 */ @@ -39,6 +41,7 @@ enum imx_mu_chan_type { IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */ IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */ IMX_MU_TYPE_RST = 4, /* Reset */ + IMX_MU_TYPE_TXDB_V2 = 5, /* Tx doorbell with S/W ACK */ }; enum imx_mu_xcr { @@ -226,6 +229,9 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); tasklet_schedule(&cp->txdb_tasklet); break; + case IMX_MU_TYPE_TXDB_V2: + imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); + break; default: dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); return -EINVAL; @@ -554,6 +560,9 @@ static int imx_mu_startup(struct mbox_chan *chan) int ret; pm_runtime_get_sync(priv->dev); + if (cp->type == IMX_MU_TYPE_TXDB_V2) + return 0; + if (cp->type == IMX_MU_TYPE_TXDB) { /* Tx doorbell don't have ACK support */ tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, @@ -595,6 +604,11 @@ static void imx_mu_shutdown(struct mbox_chan *chan) int ret; u32 sr; + if (cp->type == IMX_MU_TYPE_TXDB_V2) { + pm_runtime_put_sync(priv->dev); + return; + } + if (cp->type == IMX_MU_TYPE_TXDB) { tasklet_kill(&cp->txdb_tasklet); pm_runtime_put_sync(priv->dev); @@ -671,6 +685,7 @@ static struct mbox_chan *imx_mu_specific_xlate(struct mbox_controller *mbox, static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, const struct of_phandle_args *sp) { + struct mbox_chan *p_chan; u32 type, idx, chan; if (sp->args_count != 2) { @@ -680,14 +695,25 @@ static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, type = sp->args[0]; /* channel type */ idx = sp->args[1]; /* index */ - chan = type * 4 + idx; + /* RST only supports 1 channel */ + if ((type == IMX_MU_TYPE_RST) && idx) { + dev_err(mbox->dev, "Invalid RST channel %d\n", idx); + return ERR_PTR(-EINVAL); + } + + chan = type * 4 + idx; if (chan >= mbox->num_chans) { dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx); return ERR_PTR(-EINVAL); } - return &mbox->chans[chan]; + p_chan = &mbox->chans[chan]; + + if (type == IMX_MU_TYPE_TXDB_V2) + p_chan->txdone_method = TXDONE_BY_ACK; + + return p_chan; } static struct mbox_chan *imx_mu_seco_xlate(struct mbox_controller *mbox, diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c index 823061dd8c..b4b5bdd503 100644 --- a/drivers/mailbox/mailbox-sti.c +++ b/drivers/mailbox/mailbox-sti.c @@ -17,8 +17,8 @@ #include <linux/mailbox_controller.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include "mailbox.h" @@ -403,7 +403,6 @@ MODULE_DEVICE_TABLE(of, sti_mailbox_match); static int sti_mbox_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct mbox_controller *mbox; struct sti_mbox_device *mdev; struct device_node *np = pdev->dev.of_node; @@ -411,12 +410,11 @@ static int sti_mbox_probe(struct platform_device *pdev) int irq; int ret; - match = of_match_device(sti_mailbox_match, &pdev->dev); - if (!match) { + pdev->dev.platform_data = (struct sti_mbox_pdata *)device_get_match_data(&pdev->dev); + if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "No configuration found\n"); return -ENODEV; } - pdev->dev.platform_data = (struct sti_mbox_pdata *) match->data; mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); if (!mdev) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 4d62b07c14..de862e9137 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -13,10 +13,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/mailbox_controller.h> #include <linux/mailbox/mtk-cmdq-mailbox.h> #include <linux/of.h> +#define CMDQ_MBOX_AUTOSUSPEND_DELAY_MS 100 + #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT) #define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE) #define CMDQ_GCE_NUM_MAX (2) @@ -283,10 +286,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq, break; } - if (list_empty(&thread->task_busy_list)) { + if (list_empty(&thread->task_busy_list)) cmdq_thread_disable(cmdq, thread); - clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks); - } } static irqreturn_t cmdq_irq_handler(int irq, void *dev) @@ -307,9 +308,26 @@ static irqreturn_t cmdq_irq_handler(int irq, void *dev) spin_unlock_irqrestore(&thread->chan->lock, flags); } + pm_runtime_mark_last_busy(cmdq->mbox.dev); + return IRQ_HANDLED; } +static int cmdq_runtime_resume(struct device *dev) +{ + struct cmdq *cmdq = dev_get_drvdata(dev); + + return clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks); +} + +static int cmdq_runtime_suspend(struct device *dev) +{ + struct cmdq *cmdq = dev_get_drvdata(dev); + + clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks); + return 0; +} + static int cmdq_suspend(struct device *dev) { struct cmdq *cmdq = dev_get_drvdata(dev); @@ -333,16 +351,14 @@ static int cmdq_suspend(struct device *dev) if (cmdq->pdata->sw_ddr_en) cmdq_sw_ddr_enable(cmdq, false); - clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks); - - return 0; + return pm_runtime_force_suspend(dev); } static int cmdq_resume(struct device *dev) { struct cmdq *cmdq = dev_get_drvdata(dev); - WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks)); + WARN_ON(pm_runtime_force_resume(dev)); cmdq->suspended = false; if (cmdq->pdata->sw_ddr_en) @@ -358,6 +374,9 @@ static int cmdq_remove(struct platform_device *pdev) if (cmdq->pdata->sw_ddr_en) cmdq_sw_ddr_enable(cmdq, false); + if (!IS_ENABLED(CONFIG_PM)) + cmdq_runtime_suspend(&pdev->dev); + clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks); return 0; } @@ -369,13 +388,20 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev); struct cmdq_task *task; unsigned long curr_pa, end_pa; + int ret; /* Client should not flush new tasks if suspended. */ WARN_ON(cmdq->suspended); + ret = pm_runtime_get_sync(cmdq->mbox.dev); + if (ret < 0) + return ret; + task = kzalloc(sizeof(*task), GFP_ATOMIC); - if (!task) + if (!task) { + pm_runtime_put_autosuspend(cmdq->mbox.dev); return -ENOMEM; + } task->cmdq = cmdq; INIT_LIST_HEAD(&task->list_entry); @@ -384,8 +410,6 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) task->pkt = pkt; if (list_empty(&thread->task_busy_list)) { - WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks)); - /* * The thread reset will clear thread related register to 0, * including pc, end, priority, irq, suspend and enable. Thus @@ -424,6 +448,9 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) } list_move_tail(&task->list_entry, &thread->task_busy_list); + pm_runtime_mark_last_busy(cmdq->mbox.dev); + pm_runtime_put_autosuspend(cmdq->mbox.dev); + return 0; } @@ -439,6 +466,8 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan) struct cmdq_task *task, *tmp; unsigned long flags; + WARN_ON(pm_runtime_get_sync(cmdq->mbox.dev)); + spin_lock_irqsave(&thread->chan->lock, flags); if (list_empty(&thread->task_busy_list)) goto done; @@ -457,7 +486,6 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan) } cmdq_thread_disable(cmdq, thread); - clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks); done: /* @@ -467,6 +495,9 @@ done: * to do any operation here, only unlock and leave. */ spin_unlock_irqrestore(&thread->chan->lock, flags); + + pm_runtime_mark_last_busy(cmdq->mbox.dev); + pm_runtime_put_autosuspend(cmdq->mbox.dev); } static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) @@ -477,6 +508,11 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) struct cmdq_task *task, *tmp; unsigned long flags; u32 enable; + int ret; + + ret = pm_runtime_get_sync(cmdq->mbox.dev); + if (ret < 0) + return ret; spin_lock_irqsave(&thread->chan->lock, flags); if (list_empty(&thread->task_busy_list)) @@ -497,10 +533,12 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) cmdq_thread_resume(thread); cmdq_thread_disable(cmdq, thread); - clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks); out: spin_unlock_irqrestore(&thread->chan->lock, flags); + pm_runtime_mark_last_busy(cmdq->mbox.dev); + pm_runtime_put_autosuspend(cmdq->mbox.dev); + return 0; wait: @@ -513,6 +551,8 @@ wait: return -EFAULT; } + pm_runtime_mark_last_busy(cmdq->mbox.dev); + pm_runtime_put_autosuspend(cmdq->mbox.dev); return 0; } @@ -642,12 +682,28 @@ static int cmdq_probe(struct platform_device *pdev) return err; } + /* If Runtime PM is not available enable the clocks now. */ + if (!IS_ENABLED(CONFIG_PM)) { + err = cmdq_runtime_resume(dev); + if (err) + return err; + } + + err = devm_pm_runtime_enable(dev); + if (err) + return err; + + pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + return 0; } static const struct dev_pm_ops cmdq_pm_ops = { .suspend = cmdq_suspend, .resume = cmdq_resume, + SET_RUNTIME_PM_OPS(cmdq_runtime_suspend, + cmdq_runtime_resume, NULL) }; static const struct gce_plat gce_plat_v2 = { diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index a44d4b3e5b..94885e4110 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -91,6 +91,14 @@ struct pcc_chan_reg { * @cmd_update: PCC register bundle for the command complete update register * @error: PCC register bundle for the error status register * @plat_irq: platform interrupt + * @type: PCC subspace type + * @plat_irq_flags: platform interrupt flags + * @chan_in_use: this flag is used just to check if the interrupt needs + * handling when it is shared. Since only one transfer can occur + * at a time and mailbox takes care of locking, this flag can be + * accessed without a lock. Note: the type only support the + * communication from OSPM to Platform, like type3, use it, and + * other types completely ignore it. */ struct pcc_chan_info { struct pcc_mbox_chan chan; @@ -100,12 +108,17 @@ struct pcc_chan_info { struct pcc_chan_reg cmd_update; struct pcc_chan_reg error; int plat_irq; + u8 type; + unsigned int plat_irq_flags; + bool chan_in_use; }; #define to_pcc_chan_info(c) container_of(c, struct pcc_chan_info, chan) static struct pcc_chan_info *chan_info; static int pcc_chan_count; +static int pcc_send_data(struct mbox_chan *chan, void *data); + /* * PCC can be used with perf critical drivers such as CPPC * So it makes sense to locally cache the virtual address and @@ -221,6 +234,41 @@ static int pcc_map_interrupt(u32 interrupt, u32 flags) return acpi_register_gsi(NULL, interrupt, trigger, polarity); } +static bool pcc_chan_plat_irq_can_be_shared(struct pcc_chan_info *pchan) +{ + return (pchan->plat_irq_flags & ACPI_PCCT_INTERRUPT_MODE) == + ACPI_LEVEL_SENSITIVE; +} + +static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan) +{ + u64 val; + int ret; + + ret = pcc_chan_reg_read(&pchan->cmd_complete, &val); + if (ret) + return false; + + if (!pchan->cmd_complete.gas) + return true; + + /* + * Judge if the channel respond the interrupt based on the value of + * command complete. + */ + val &= pchan->cmd_complete.status_mask; + + /* + * If this is PCC slave subspace channel, and the command complete + * bit 0 indicates that Platform is sending a notification and OSPM + * needs to respond this interrupt to process this command. + */ + if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) + return !val; + + return !!val; +} + /** * pcc_mbox_irq - PCC mailbox interrupt handler * @irq: interrupt number @@ -236,16 +284,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) int ret; pchan = chan->con_priv; - - ret = pcc_chan_reg_read(&pchan->cmd_complete, &val); - if (ret) + if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE && + !pchan->chan_in_use) return IRQ_NONE; - if (val) { /* Ensure GAS exists and value is non-zero */ - val &= pchan->cmd_complete.status_mask; - if (!val) - return IRQ_NONE; - } + if (!pcc_mbox_cmd_complete_check(pchan)) + return IRQ_NONE; ret = pcc_chan_reg_read(&pchan->error, &val); if (ret) @@ -262,6 +306,16 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) mbox_chan_received_data(chan, NULL); + /* + * The PCC slave subspace channel needs to set the command complete bit + * and ring doorbell after processing message. + * + * The PCC master subspace channel clears chan_in_use to free channel. + */ + if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) + pcc_send_data(chan, NULL); + pchan->chan_in_use = false; + return IRQ_HANDLED; } @@ -340,7 +394,11 @@ static int pcc_send_data(struct mbox_chan *chan, void *data) if (ret) return ret; - return pcc_chan_reg_read_modify_write(&pchan->db); + ret = pcc_chan_reg_read_modify_write(&pchan->db); + if (!ret && pchan->plat_irq > 0) + pchan->chan_in_use = true; + + return ret; } /** @@ -353,11 +411,14 @@ static int pcc_send_data(struct mbox_chan *chan, void *data) static int pcc_startup(struct mbox_chan *chan) { struct pcc_chan_info *pchan = chan->con_priv; + unsigned long irqflags; int rc; if (pchan->plat_irq > 0) { - rc = devm_request_irq(chan->mbox->dev, pchan->plat_irq, pcc_mbox_irq, 0, - MBOX_IRQ_NAME, chan); + irqflags = pcc_chan_plat_irq_can_be_shared(pchan) ? + IRQF_SHARED | IRQF_ONESHOT : 0; + rc = devm_request_irq(chan->mbox->dev, pchan->plat_irq, pcc_mbox_irq, + irqflags, MBOX_IRQ_NAME, chan); if (unlikely(rc)) { dev_err(chan->mbox->dev, "failed to register PCC interrupt %d\n", pchan->plat_irq); @@ -463,6 +524,7 @@ static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan, pcct_ss->platform_interrupt); return -EINVAL; } + pchan->plat_irq_flags = pcct_ss->flags; if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss; @@ -484,6 +546,12 @@ static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan, "PLAT IRQ ACK"); } + if (pcc_chan_plat_irq_can_be_shared(pchan) && + !pchan->plat_irq_ack.gas) { + pr_err("PCC subspace has level IRQ with no ACK register\n"); + return -EINVAL; + } + return ret; } @@ -698,6 +766,7 @@ static int pcc_mbox_probe(struct platform_device *pdev) pcc_parse_subspace_shmem(pchan, pcct_entry); + pchan->type = pcct_entry->type; pcct_entry = (struct acpi_subtable_header *) ((unsigned long) pcct_entry + pcct_entry->length); } diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index a94577f16a..9d2d4ff6cd 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -15,10 +15,10 @@ #include <linux/kernel.h> #include <linux/mailbox_controller.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/soc/ti/ti-msgmgr.h> #define Q_DATA_OFFSET(proxy, queue, reg) \ @@ -810,7 +810,6 @@ MODULE_DEVICE_TABLE(of, ti_msgmgr_of_match); static int ti_msgmgr_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; struct device_node *np; const struct ti_msgmgr_desc *desc; struct ti_msgmgr_inst *inst; @@ -828,19 +827,12 @@ static int ti_msgmgr_probe(struct platform_device *pdev) } np = dev->of_node; - of_id = of_match_device(ti_msgmgr_of_match, dev); - if (!of_id) { - dev_err(dev, "OF data missing\n"); - return -EINVAL; - } - desc = of_id->data; - inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); if (!inst) return -ENOMEM; inst->dev = dev; - inst->desc = desc; + inst->desc = desc = device_get_match_data(dev); inst->queue_proxy_region = devm_platform_ioremap_resource_byname(pdev, desc->data_region_name); diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index e4fcac97db..7fa533e80d 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -108,7 +108,7 @@ struct zynqmp_ipi_pdata { unsigned int method; u32 local_id; int num_mboxes; - struct zynqmp_ipi_mbox ipi_mboxes[]; + struct zynqmp_ipi_mbox ipi_mboxes[] __counted_by(num_mboxes); }; static struct device_driver zynqmp_ipi_mbox_driver = { |