18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd. 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd. 58c2ecf20Sopenharmony_ci * Based on ARM MHU driver by Jassi Brar <jaswinder.singh@linaro.org> 68c2ecf20Sopenharmony_ci * Copyright (C) 2020 ARM Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define INTR_STAT_OFS 0x0 218c2ecf20Sopenharmony_ci#define INTR_SET_OFS 0x8 228c2ecf20Sopenharmony_ci#define INTR_CLR_OFS 0x10 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MHU_LP_OFFSET 0x0 258c2ecf20Sopenharmony_ci#define MHU_HP_OFFSET 0x20 268c2ecf20Sopenharmony_ci#define MHU_SEC_OFFSET 0x200 278c2ecf20Sopenharmony_ci#define TX_REG_OFFSET 0x100 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MHU_CHANS 3 /* Secure, Non-Secure High and Low Priority */ 308c2ecf20Sopenharmony_ci#define MHU_CHAN_MAX 20 /* Max channels to save on unused RAM */ 318c2ecf20Sopenharmony_ci#define MHU_NUM_DOORBELLS 32 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct mhu_db_link { 348c2ecf20Sopenharmony_ci unsigned int irq; 358c2ecf20Sopenharmony_ci void __iomem *tx_reg; 368c2ecf20Sopenharmony_ci void __iomem *rx_reg; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct arm_mhu { 408c2ecf20Sopenharmony_ci void __iomem *base; 418c2ecf20Sopenharmony_ci struct mhu_db_link mlink[MHU_CHANS]; 428c2ecf20Sopenharmony_ci struct mbox_controller mbox; 438c2ecf20Sopenharmony_ci struct device *dev; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * ARM MHU Mailbox allocated channel information 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * @mhu: Pointer to parent mailbox device 508c2ecf20Sopenharmony_ci * @pchan: Physical channel within which this doorbell resides in 518c2ecf20Sopenharmony_ci * @doorbell: doorbell number pertaining to this channel 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistruct mhu_db_channel { 548c2ecf20Sopenharmony_ci struct arm_mhu *mhu; 558c2ecf20Sopenharmony_ci unsigned int pchan; 568c2ecf20Sopenharmony_ci unsigned int doorbell; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline struct mbox_chan * 608c2ecf20Sopenharmony_cimhu_db_mbox_to_channel(struct mbox_controller *mbox, unsigned int pchan, 618c2ecf20Sopenharmony_ci unsigned int doorbell) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) { 678c2ecf20Sopenharmony_ci chan_info = mbox->chans[i].con_priv; 688c2ecf20Sopenharmony_ci if (chan_info && chan_info->pchan == pchan && 698c2ecf20Sopenharmony_ci chan_info->doorbell == doorbell) 708c2ecf20Sopenharmony_ci return &mbox->chans[i]; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return NULL; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void mhu_db_mbox_clear_irq(struct mbox_chan *chan) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info = chan->con_priv; 798c2ecf20Sopenharmony_ci void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].rx_reg; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci writel_relaxed(BIT(chan_info->doorbell), base + INTR_CLR_OFS); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic unsigned int mhu_db_mbox_irq_to_pchan_num(struct arm_mhu *mhu, int irq) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned int pchan; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (pchan = 0; pchan < MHU_CHANS; pchan++) 898c2ecf20Sopenharmony_ci if (mhu->mlink[pchan].irq == irq) 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci return pchan; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct mbox_chan * 958c2ecf20Sopenharmony_cimhu_db_mbox_irq_to_channel(struct arm_mhu *mhu, unsigned int pchan) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci unsigned long bits; 988c2ecf20Sopenharmony_ci unsigned int doorbell; 998c2ecf20Sopenharmony_ci struct mbox_chan *chan = NULL; 1008c2ecf20Sopenharmony_ci struct mbox_controller *mbox = &mhu->mbox; 1018c2ecf20Sopenharmony_ci void __iomem *base = mhu->mlink[pchan].rx_reg; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci bits = readl_relaxed(base + INTR_STAT_OFS); 1048c2ecf20Sopenharmony_ci if (!bits) 1058c2ecf20Sopenharmony_ci /* No IRQs fired in specified physical channel */ 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* An IRQ has fired, find the associated channel */ 1098c2ecf20Sopenharmony_ci for (doorbell = 0; bits; doorbell++) { 1108c2ecf20Sopenharmony_ci if (!test_and_clear_bit(doorbell, &bits)) 1118c2ecf20Sopenharmony_ci continue; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell); 1148c2ecf20Sopenharmony_ci if (chan) 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci dev_err(mbox->dev, 1178c2ecf20Sopenharmony_ci "Channel not registered: pchan: %d doorbell: %d\n", 1188c2ecf20Sopenharmony_ci pchan, doorbell); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return chan; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic irqreturn_t mhu_db_mbox_rx_handler(int irq, void *data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1278c2ecf20Sopenharmony_ci struct arm_mhu *mhu = data; 1288c2ecf20Sopenharmony_ci unsigned int pchan = mhu_db_mbox_irq_to_pchan_num(mhu, irq); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci while (NULL != (chan = mhu_db_mbox_irq_to_channel(mhu, pchan))) { 1318c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, NULL); 1328c2ecf20Sopenharmony_ci mhu_db_mbox_clear_irq(chan); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic bool mhu_db_last_tx_done(struct mbox_chan *chan) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info = chan->con_priv; 1418c2ecf20Sopenharmony_ci void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (readl_relaxed(base + INTR_STAT_OFS) & BIT(chan_info->doorbell)) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return true; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int mhu_db_send_data(struct mbox_chan *chan, void *data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info = chan->con_priv; 1528c2ecf20Sopenharmony_ci void __iomem *base = chan_info->mhu->mlink[chan_info->pchan].tx_reg; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Send event to co-processor */ 1558c2ecf20Sopenharmony_ci writel_relaxed(BIT(chan_info->doorbell), base + INTR_SET_OFS); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int mhu_db_startup(struct mbox_chan *chan) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci mhu_db_mbox_clear_irq(chan); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void mhu_db_shutdown(struct mbox_chan *chan) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info = chan->con_priv; 1698c2ecf20Sopenharmony_ci struct mbox_controller *mbox = &chan_info->mhu->mbox; 1708c2ecf20Sopenharmony_ci int i; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) 1738c2ecf20Sopenharmony_ci if (chan == &mbox->chans[i]) 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (mbox->num_chans == i) { 1778c2ecf20Sopenharmony_ci dev_warn(mbox->dev, "Request to free non-existent channel\n"); 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Reset channel */ 1828c2ecf20Sopenharmony_ci mhu_db_mbox_clear_irq(chan); 1838c2ecf20Sopenharmony_ci devm_kfree(mbox->dev, chan->con_priv); 1848c2ecf20Sopenharmony_ci chan->con_priv = NULL; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct mbox_chan *mhu_db_mbox_xlate(struct mbox_controller *mbox, 1888c2ecf20Sopenharmony_ci const struct of_phandle_args *spec) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct arm_mhu *mhu = dev_get_drvdata(mbox->dev); 1918c2ecf20Sopenharmony_ci struct mhu_db_channel *chan_info; 1928c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1938c2ecf20Sopenharmony_ci unsigned int pchan = spec->args[0]; 1948c2ecf20Sopenharmony_ci unsigned int doorbell = spec->args[1]; 1958c2ecf20Sopenharmony_ci int i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Bounds checking */ 1988c2ecf20Sopenharmony_ci if (pchan >= MHU_CHANS || doorbell >= MHU_NUM_DOORBELLS) { 1998c2ecf20Sopenharmony_ci dev_err(mbox->dev, 2008c2ecf20Sopenharmony_ci "Invalid channel requested pchan: %d doorbell: %d\n", 2018c2ecf20Sopenharmony_ci pchan, doorbell); 2028c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Is requested channel free? */ 2068c2ecf20Sopenharmony_ci chan = mhu_db_mbox_to_channel(mbox, pchan, doorbell); 2078c2ecf20Sopenharmony_ci if (chan) { 2088c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Channel in use: pchan: %d doorbell: %d\n", 2098c2ecf20Sopenharmony_ci pchan, doorbell); 2108c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Find the first free slot */ 2148c2ecf20Sopenharmony_ci for (i = 0; i < mbox->num_chans; i++) 2158c2ecf20Sopenharmony_ci if (!mbox->chans[i].con_priv) 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (mbox->num_chans == i) { 2198c2ecf20Sopenharmony_ci dev_err(mbox->dev, "No free channels left\n"); 2208c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci chan = &mbox->chans[i]; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci chan_info = devm_kzalloc(mbox->dev, sizeof(*chan_info), GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!chan_info) 2278c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci chan_info->mhu = mhu; 2308c2ecf20Sopenharmony_ci chan_info->pchan = pchan; 2318c2ecf20Sopenharmony_ci chan_info->doorbell = doorbell; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci chan->con_priv = chan_info; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dev_dbg(mbox->dev, "mbox: created channel phys: %d doorbell: %d\n", 2368c2ecf20Sopenharmony_ci pchan, doorbell); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return chan; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops mhu_db_ops = { 2428c2ecf20Sopenharmony_ci .send_data = mhu_db_send_data, 2438c2ecf20Sopenharmony_ci .startup = mhu_db_startup, 2448c2ecf20Sopenharmony_ci .shutdown = mhu_db_shutdown, 2458c2ecf20Sopenharmony_ci .last_tx_done = mhu_db_last_tx_done, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int mhu_db_probe(struct amba_device *adev, const struct amba_id *id) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci u32 cell_count; 2518c2ecf20Sopenharmony_ci int i, err, max_chans; 2528c2ecf20Sopenharmony_ci struct arm_mhu *mhu; 2538c2ecf20Sopenharmony_ci struct mbox_chan *chans; 2548c2ecf20Sopenharmony_ci struct device *dev = &adev->dev; 2558c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2568c2ecf20Sopenharmony_ci int mhu_reg[MHU_CHANS] = { 2578c2ecf20Sopenharmony_ci MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET, 2588c2ecf20Sopenharmony_ci }; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "arm,mhu-doorbell")) 2618c2ecf20Sopenharmony_ci return -ENODEV; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "#mbox-cells", &cell_count); 2648c2ecf20Sopenharmony_ci if (err) { 2658c2ecf20Sopenharmony_ci dev_err(dev, "failed to read #mbox-cells in '%pOF'\n", np); 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (cell_count == 2) { 2708c2ecf20Sopenharmony_ci max_chans = MHU_CHAN_MAX; 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci dev_err(dev, "incorrect value of #mbox-cells in '%pOF'\n", np); 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); 2778c2ecf20Sopenharmony_ci if (!mhu) 2788c2ecf20Sopenharmony_ci return -ENOMEM; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mhu->base = devm_ioremap_resource(dev, &adev->res); 2818c2ecf20Sopenharmony_ci if (IS_ERR(mhu->base)) { 2828c2ecf20Sopenharmony_ci dev_err(dev, "ioremap failed\n"); 2838c2ecf20Sopenharmony_ci return PTR_ERR(mhu->base); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci chans = devm_kcalloc(dev, max_chans, sizeof(*chans), GFP_KERNEL); 2878c2ecf20Sopenharmony_ci if (!chans) 2888c2ecf20Sopenharmony_ci return -ENOMEM; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci mhu->dev = dev; 2918c2ecf20Sopenharmony_ci mhu->mbox.dev = dev; 2928c2ecf20Sopenharmony_ci mhu->mbox.chans = chans; 2938c2ecf20Sopenharmony_ci mhu->mbox.num_chans = max_chans; 2948c2ecf20Sopenharmony_ci mhu->mbox.txdone_irq = false; 2958c2ecf20Sopenharmony_ci mhu->mbox.txdone_poll = true; 2968c2ecf20Sopenharmony_ci mhu->mbox.txpoll_period = 1; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci mhu->mbox.of_xlate = mhu_db_mbox_xlate; 2998c2ecf20Sopenharmony_ci amba_set_drvdata(adev, mhu); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mhu->mbox.ops = &mhu_db_ops; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci err = devm_mbox_controller_register(dev, &mhu->mbox); 3048c2ecf20Sopenharmony_ci if (err) { 3058c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register mailboxes %d\n", err); 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci for (i = 0; i < MHU_CHANS; i++) { 3108c2ecf20Sopenharmony_ci int irq = mhu->mlink[i].irq = adev->irq[i]; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (irq <= 0) { 3138c2ecf20Sopenharmony_ci dev_dbg(dev, "No IRQ found for Channel %d\n", i); 3148c2ecf20Sopenharmony_ci continue; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i]; 3188c2ecf20Sopenharmony_ci mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(dev, irq, NULL, 3218c2ecf20Sopenharmony_ci mhu_db_mbox_rx_handler, 3228c2ecf20Sopenharmony_ci IRQF_ONESHOT, "mhu_db_link", mhu); 3238c2ecf20Sopenharmony_ci if (err) { 3248c2ecf20Sopenharmony_ci dev_err(dev, "Can't claim IRQ %d\n", irq); 3258c2ecf20Sopenharmony_ci mbox_controller_unregister(&mhu->mbox); 3268c2ecf20Sopenharmony_ci return err; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci dev_info(dev, "ARM MHU Doorbell mailbox registered\n"); 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic struct amba_id mhu_ids[] = { 3358c2ecf20Sopenharmony_ci { 3368c2ecf20Sopenharmony_ci .id = 0x1bb098, 3378c2ecf20Sopenharmony_ci .mask = 0xffffff, 3388c2ecf20Sopenharmony_ci }, 3398c2ecf20Sopenharmony_ci { 0, 0 }, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, mhu_ids); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic struct amba_driver arm_mhu_db_driver = { 3448c2ecf20Sopenharmony_ci .drv = { 3458c2ecf20Sopenharmony_ci .name = "mhu-doorbell", 3468c2ecf20Sopenharmony_ci }, 3478c2ecf20Sopenharmony_ci .id_table = mhu_ids, 3488c2ecf20Sopenharmony_ci .probe = mhu_db_probe, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_cimodule_amba_driver(arm_mhu_db_driver); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM MHU Doorbell Driver"); 3548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); 355