18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2017-2018 Hisilicon Limited. 38c2ecf20Sopenharmony_ci// Copyright (c) 2017-2018 Linaro Limited. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/bitops.h> 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 128c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "mailbox.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define MBOX_CHAN_MAX 32 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MBOX_RX 0x0 228c2ecf20Sopenharmony_ci#define MBOX_TX 0x1 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40)) 258c2ecf20Sopenharmony_ci#define MBOX_SRC_REG 0x00 268c2ecf20Sopenharmony_ci#define MBOX_DST_REG 0x04 278c2ecf20Sopenharmony_ci#define MBOX_DCLR_REG 0x08 288c2ecf20Sopenharmony_ci#define MBOX_DSTAT_REG 0x0c 298c2ecf20Sopenharmony_ci#define MBOX_MODE_REG 0x10 308c2ecf20Sopenharmony_ci#define MBOX_IMASK_REG 0x14 318c2ecf20Sopenharmony_ci#define MBOX_ICLR_REG 0x18 328c2ecf20Sopenharmony_ci#define MBOX_SEND_REG 0x1c 338c2ecf20Sopenharmony_ci#define MBOX_DATA_REG 0x20 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MBOX_IPC_LOCK_REG 0xa00 368c2ecf20Sopenharmony_ci#define MBOX_IPC_UNLOCK 0x1acce551 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MBOX_AUTOMATIC_ACK 1 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MBOX_STATE_IDLE BIT(4) 418c2ecf20Sopenharmony_ci#define MBOX_STATE_READY BIT(5) 428c2ecf20Sopenharmony_ci#define MBOX_STATE_ACK BIT(7) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define MBOX_MSG_LEN 8 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * Hi3660 mailbox channel information 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * A channel can be used for TX or RX, it can trigger remote 508c2ecf20Sopenharmony_ci * processor interrupt to notify remote processor and can receive 518c2ecf20Sopenharmony_ci * interrupt if has incoming message. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * @dst_irq: Interrupt vector for remote processor 548c2ecf20Sopenharmony_ci * @ack_irq: Interrupt vector for local processor 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistruct hi3660_chan_info { 578c2ecf20Sopenharmony_ci unsigned int dst_irq; 588c2ecf20Sopenharmony_ci unsigned int ack_irq; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * Hi3660 mailbox controller data 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Mailbox controller includes 32 channels and can allocate 658c2ecf20Sopenharmony_ci * channel for message transferring. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * @dev: Device to which it is attached 688c2ecf20Sopenharmony_ci * @base: Base address of the register mapping region 698c2ecf20Sopenharmony_ci * @chan: Representation of channels in mailbox controller 708c2ecf20Sopenharmony_ci * @mchan: Representation of channel info 718c2ecf20Sopenharmony_ci * @controller: Representation of a communication channel controller 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistruct hi3660_mbox { 748c2ecf20Sopenharmony_ci struct device *dev; 758c2ecf20Sopenharmony_ci void __iomem *base; 768c2ecf20Sopenharmony_ci struct mbox_chan chan[MBOX_CHAN_MAX]; 778c2ecf20Sopenharmony_ci struct hi3660_chan_info mchan[MBOX_CHAN_MAX]; 788c2ecf20Sopenharmony_ci struct mbox_controller controller; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return container_of(mbox, struct hi3660_mbox, controller); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int hi3660_mbox_check_state(struct mbox_chan *chan) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned long ch = (unsigned long)chan->con_priv; 898c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 908c2ecf20Sopenharmony_ci struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 918c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mbox, ch); 928c2ecf20Sopenharmony_ci unsigned long val; 938c2ecf20Sopenharmony_ci unsigned int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Mailbox is ready to use */ 968c2ecf20Sopenharmony_ci if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY) 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Wait for acknowledge from remote */ 1008c2ecf20Sopenharmony_ci ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG, 1018c2ecf20Sopenharmony_ci val, (val & MBOX_STATE_ACK), 1000, 300000); 1028c2ecf20Sopenharmony_ci if (ret) { 1038c2ecf20Sopenharmony_ci dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__); 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* clear ack state, mailbox will get back to ready state */ 1088c2ecf20Sopenharmony_ci writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int hi3660_mbox_unlock(struct mbox_chan *chan) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 1168c2ecf20Sopenharmony_ci unsigned int val, retry = 3; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci do { 1198c2ecf20Sopenharmony_ci writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci val = readl(mbox->base + MBOX_IPC_LOCK_REG); 1228c2ecf20Sopenharmony_ci if (!val) 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci udelay(10); 1268c2ecf20Sopenharmony_ci } while (retry--); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (val) 1298c2ecf20Sopenharmony_ci dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return (!val) ? 0 : -ETIMEDOUT; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int hi3660_mbox_acquire_channel(struct mbox_chan *chan) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci unsigned long ch = (unsigned long)chan->con_priv; 1378c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 1388c2ecf20Sopenharmony_ci struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 1398c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mbox, ch); 1408c2ecf20Sopenharmony_ci unsigned int val, retry; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (retry = 10; retry; retry--) { 1438c2ecf20Sopenharmony_ci /* Check if channel is in idle state */ 1448c2ecf20Sopenharmony_ci if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) { 1458c2ecf20Sopenharmony_ci writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Check ack bit has been set successfully */ 1488c2ecf20Sopenharmony_ci val = readl(base + MBOX_SRC_REG); 1498c2ecf20Sopenharmony_ci if (val & BIT(mchan->ack_irq)) 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!retry) 1558c2ecf20Sopenharmony_ci dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return retry ? 0 : -ETIMEDOUT; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int hi3660_mbox_startup(struct mbox_chan *chan) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = hi3660_mbox_unlock(chan); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = hi3660_mbox_acquire_channel(chan); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci unsigned long ch = (unsigned long)chan->con_priv; 1788c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); 1798c2ecf20Sopenharmony_ci struct hi3660_chan_info *mchan = &mbox->mchan[ch]; 1808c2ecf20Sopenharmony_ci void __iomem *base = MBOX_BASE(mbox, ch); 1818c2ecf20Sopenharmony_ci u32 *buf = msg; 1828c2ecf20Sopenharmony_ci unsigned int i; 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = hi3660_mbox_check_state(chan); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Clear mask for destination interrupt */ 1908c2ecf20Sopenharmony_ci writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Config destination for interrupt vector */ 1938c2ecf20Sopenharmony_ci writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Automatic acknowledge mode */ 1968c2ecf20Sopenharmony_ci writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Fill message data */ 1998c2ecf20Sopenharmony_ci for (i = 0; i < MBOX_MSG_LEN; i++) 2008c2ecf20Sopenharmony_ci writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Trigger data transferring */ 2038c2ecf20Sopenharmony_ci writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops hi3660_mbox_ops = { 2088c2ecf20Sopenharmony_ci .startup = hi3660_mbox_startup, 2098c2ecf20Sopenharmony_ci .send_data = hi3660_mbox_send_data, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, 2138c2ecf20Sopenharmony_ci const struct of_phandle_args *spec) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox = to_hi3660_mbox(controller); 2168c2ecf20Sopenharmony_ci struct hi3660_chan_info *mchan; 2178c2ecf20Sopenharmony_ci unsigned int ch = spec->args[0]; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (ch >= MBOX_CHAN_MAX) { 2208c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Invalid channel idx %d\n", ch); 2218c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mchan = &mbox->mchan[ch]; 2258c2ecf20Sopenharmony_ci mchan->dst_irq = spec->args[1]; 2268c2ecf20Sopenharmony_ci mchan->ack_irq = spec->args[2]; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return &mbox->chan[ch]; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic const struct of_device_id hi3660_mbox_of_match[] = { 2328c2ecf20Sopenharmony_ci { .compatible = "hisilicon,hi3660-mbox", }, 2338c2ecf20Sopenharmony_ci {}, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int hi3660_mbox_probe(struct platform_device *pdev) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2418c2ecf20Sopenharmony_ci struct hi3660_mbox *mbox; 2428c2ecf20Sopenharmony_ci struct mbox_chan *chan; 2438c2ecf20Sopenharmony_ci struct resource *res; 2448c2ecf20Sopenharmony_ci unsigned long ch; 2458c2ecf20Sopenharmony_ci int err; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 2488c2ecf20Sopenharmony_ci if (!mbox) 2498c2ecf20Sopenharmony_ci return -ENOMEM; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2528c2ecf20Sopenharmony_ci mbox->base = devm_ioremap_resource(dev, res); 2538c2ecf20Sopenharmony_ci if (IS_ERR(mbox->base)) 2548c2ecf20Sopenharmony_ci return PTR_ERR(mbox->base); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci mbox->dev = dev; 2578c2ecf20Sopenharmony_ci mbox->controller.dev = dev; 2588c2ecf20Sopenharmony_ci mbox->controller.chans = mbox->chan; 2598c2ecf20Sopenharmony_ci mbox->controller.num_chans = MBOX_CHAN_MAX; 2608c2ecf20Sopenharmony_ci mbox->controller.ops = &hi3660_mbox_ops; 2618c2ecf20Sopenharmony_ci mbox->controller.of_xlate = hi3660_mbox_xlate; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Initialize mailbox channel data */ 2648c2ecf20Sopenharmony_ci chan = mbox->chan; 2658c2ecf20Sopenharmony_ci for (ch = 0; ch < MBOX_CHAN_MAX; ch++) 2668c2ecf20Sopenharmony_ci chan[ch].con_priv = (void *)ch; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci err = devm_mbox_controller_register(dev, &mbox->controller); 2698c2ecf20Sopenharmony_ci if (err) { 2708c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register mailbox %d\n", err); 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mbox); 2758c2ecf20Sopenharmony_ci dev_info(dev, "Mailbox enabled\n"); 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic struct platform_driver hi3660_mbox_driver = { 2808c2ecf20Sopenharmony_ci .probe = hi3660_mbox_probe, 2818c2ecf20Sopenharmony_ci .driver = { 2828c2ecf20Sopenharmony_ci .name = "hi3660-mbox", 2838c2ecf20Sopenharmony_ci .of_match_table = hi3660_mbox_of_match, 2848c2ecf20Sopenharmony_ci }, 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int __init hi3660_mbox_init(void) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci return platform_driver_register(&hi3660_mbox_driver); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_cicore_initcall(hi3660_mbox_init); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void __exit hi3660_mbox_exit(void) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci platform_driver_unregister(&hi3660_mbox_driver); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_cimodule_exit(hi3660_mbox_exit); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); 3018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); 302