// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 MediaTek Inc. */ #define MBOX_TIMESTAMP #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF_RESERVED_MEM #include #endif #ifdef CONFIG_MTK_SCMI_TIMEOUT_HOOK #include #endif #ifdef MBOX_TIMESTAMP #include #define FIXED_MBOX_SIZE 128 #define T_SEND_OFFSET_H (FIXED_MBOX_SIZE - 16) #define T_SEND_OFFSET_L (FIXED_MBOX_SIZE - 12) #define T_IRQ_OFFSET_H (FIXED_MBOX_SIZE - 8) #define T_IRQ_OFFSET_L (FIXED_MBOX_SIZE - 4) #endif #define INTR_SET_OFS 0x0 #define INTR_CLR_OFS 0x4 #define MBOX_CHANS 2 #define MBOX_DEBUG 0 struct mhu_link { unsigned irq; void __iomem *tx_reg; void __iomem *rx_reg; #ifdef MBOX_TIMESTAMP void __iomem *shmem; resource_size_t shmem_size; #endif }; struct mtk_mbu { void __iomem *base; struct mhu_link mlink[MBOX_CHANS]; struct mbox_chan chan[MBOX_CHANS]; struct mbox_controller mbox; }; static irqreturn_t tinysys_mbox_rx_interrupt(int irq, void *p) { struct mbox_chan *chan = p; struct mhu_link *mlink = chan->con_priv; u32 val; #ifdef MBOX_TIMESTAMP u64 tv; tv = __arch_counter_get_cntvct(); writel_relaxed((u32)((tv & 0xFFFFFFFF00000000LL) >> 32), mlink->shmem + T_IRQ_OFFSET_H); writel_relaxed((u32)(tv & 0xFFFFFFFFLL), mlink->shmem + T_IRQ_OFFSET_L); #endif val = readl_relaxed(mlink->rx_reg + INTR_CLR_OFS); #if MBOX_DEBUG dev_notice(chan->mbox->dev, "[scmi] tinysys_mbox_rx_interrupt chan:%px txdone_method:%x\n", chan, chan->txdone_method); #endif if (!val) return IRQ_NONE; writel_relaxed(1, mlink->rx_reg + INTR_CLR_OFS); mbox_chan_received_data(chan, (void *)&val); return IRQ_HANDLED; } static bool tinysys_mbox_last_tx_done(struct mbox_chan *chan) { struct mhu_link *mlink = chan->con_priv; u32 val = readl_relaxed(mlink->tx_reg + INTR_SET_OFS); #if MBOX_DEBUG if (val == 0) dev_notice(chan->mbox->dev, "[scmi] last_tx_done\n"); #endif return (val == 0); } static int tinysys_mbox_send_data(struct mbox_chan *chan, void *data) { struct mhu_link *mlink = chan->con_priv; #ifdef MBOX_TIMESTAMP u64 tv; #endif #if MBOX_DEBUG u32 *arg = data; dev_notice(chan->mbox->dev, "[scmi] send_data %x\n", *arg); #endif smp_mb(); #ifdef MBOX_TIMESTAMP tv = __arch_counter_get_cntvct(); writel_relaxed((u32)((tv & 0xFFFFFFFF00000000LL) >> 32), mlink->shmem + T_SEND_OFFSET_H); writel_relaxed((u32)(tv & 0xFFFFFFFFLL), mlink->shmem + T_SEND_OFFSET_L); #endif writel_relaxed(1, mlink->tx_reg + INTR_SET_OFS); return 0; } #ifdef CONFIG_MTK_SCMI_TIMEOUT_HOOK static void scmi_timeout_set(void *ignore, int *timeout) { int rx_timeout = msecs_to_jiffies(2000); *timeout = rx_timeout; } #endif static int tinysys_mbox_startup(struct mbox_chan *chan) { struct mhu_link *mlink = chan->con_priv; int ret; ret = request_irq(mlink->irq, tinysys_mbox_rx_interrupt, IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, "mtk_tinysys_mbox", chan); if (ret) { dev_notice(chan->mbox->dev, "Unable to acquire IRQ %d\n", mlink->irq); return ret; } return 0; } static void tinysys_mbox_shutdown(struct mbox_chan *chan) { struct mhu_link *mlink = chan->con_priv; free_irq(mlink->irq, chan); } static const struct mbox_chan_ops tinysys_mbox_chan_ops = { .send_data = tinysys_mbox_send_data, .startup = tinysys_mbox_startup, .shutdown = tinysys_mbox_shutdown, .last_tx_done = tinysys_mbox_last_tx_done, }; static int tinysys_mbox_probe(struct platform_device *pdev) { int err, i; struct mtk_mbu *mbu; struct device *dev = &pdev->dev; struct resource *res; struct tinysys_mbox_plat *plat_data; #ifdef MBOX_TIMESTAMP struct device_node *shmem; struct resource shmem_res; resource_size_t size; #endif #ifdef CONFIG_MTK_SCMI_TIMEOUT_HOOK int ret; #endif /* Allocate memory for device */ mbu = devm_kzalloc(dev, sizeof(*mbu), GFP_KERNEL); if (!mbu) return -ENOMEM; #ifdef CONFIG_MTK_SCMI_TIMEOUT_HOOK /* register tracepoint of scmi mailbox rx timeout */ ret = register_trace_android_vh_scmi_timeout_sync(scmi_timeout_set, NULL); if (ret) { dev_notice(dev, "scmi register hooks fail"); return ret; } #endif for (i = 0; i < MBOX_CHANS; i++) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); mbu->base = devm_ioremap_resource(dev, res); if (IS_ERR(mbu->base)) { dev_notice(dev, "failed to ioremap mbu"); return PTR_ERR(mbu->base); } #ifdef MBOX_TIMESTAMP shmem = of_parse_phandle(dev->of_node, "shmem", i); err = of_address_to_resource(shmem, 0, &shmem_res); of_node_put(shmem); if (err) { dev_err(dev, "failed to get scmi profile memory\n"); return err; } size = resource_size(&shmem_res); mbu->mlink[i].shmem = devm_ioremap(dev, shmem_res.start, size); mbu->mlink[i].shmem_size = size; #endif mbu->chan[i].con_priv = &mbu->mlink[i]; mbu->mlink[i].irq = platform_get_irq(pdev, i); if (!mbu->mlink[i].irq) dev_notice(dev, "failed to get irq"); mbu->mlink[i].rx_reg = mbu->base; mbu->mlink[i].tx_reg = mbu->base; } plat_data = (struct tinysys_mbox_plat *)of_device_get_match_data(dev); if (!plat_data) { dev_notice(dev, "failed to get match data\n"); return -EINVAL; } mbu->mbox.dev = dev; mbu->mbox.chans = &mbu->chan[0]; mbu->mbox.num_chans = MBOX_CHANS; mbu->mbox.ops = &tinysys_mbox_chan_ops; mbu->mbox.txdone_irq = false; mbu->mbox.txdone_poll = true; mbu->mbox.txpoll_period = 1; platform_set_drvdata(pdev, mbu); err = devm_mbox_controller_register(dev, &mbu->mbox); if (err) { dev_notice(dev, "Failed to register mailboxes %d\n", err); return err; } dev_info(dev, "MTK MBOX Mailbox registered\n"); return 0; } struct tinysys_mbox_plat { u32 thread_nr; u32 bfs; }; static const struct tinysys_mbox_plat tinysys_mbox_plat_v1 = {.thread_nr = 1, .bfs = 64}; static const struct of_device_id tinysys_mbox_of_ids[] = { {.compatible = "mediatek,tinysys_mbox", .data = (void *)&tinysys_mbox_plat_v1}, {} }; static int tinysys_mbox_remove(struct platform_device *pdev) { #ifdef CONFIG_MTK_SCMI_TIMEOUT_HOOK unregister_trace_android_vh_scmi_timeout_sync(scmi_timeout_set, NULL); #endif return 0; } static struct platform_driver tinysys_mbox_drv = { .probe = tinysys_mbox_probe, .remove = tinysys_mbox_remove, .driver = { .name = "mtk_tinysys_mbox", .of_match_table = tinysys_mbox_of_ids, } }; static __init int mtk_tinysys_mbox_driver(void) { u32 err = 0; err = platform_driver_register(&tinysys_mbox_drv); if (err) { pr_notice("mtk_tinysys_mbox_driver failed:%d", err); return err; } return 0; } module_init(mtk_tinysys_mbox_driver); MODULE_LICENSE("GPL v2");