18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010,2015 Broadcom 48c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Lubomir Rintel 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Craig McGeachie 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Parts of the driver are based on: 88c2ecf20Sopenharmony_ci * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was 98c2ecf20Sopenharmony_ci * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/ 108c2ecf20Sopenharmony_ci * linux.git 118c2ecf20Sopenharmony_ci * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at 128c2ecf20Sopenharmony_ci * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/ 138c2ecf20Sopenharmony_ci * mailbox/bcm2835-ipc.c 148c2ecf20Sopenharmony_ci * - documentation available on the following web site: 158c2ecf20Sopenharmony_ci * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/of_address.h> 278c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Mailboxes */ 328c2ecf20Sopenharmony_ci#define ARM_0_MAIL0 0x00 338c2ecf20Sopenharmony_ci#define ARM_0_MAIL1 0x20 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Mailbox registers. We basically only support mailbox 0 & 1. We 378c2ecf20Sopenharmony_ci * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See 388c2ecf20Sopenharmony_ci * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about 398c2ecf20Sopenharmony_ci * the placement of memory barriers. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define MAIL0_RD (ARM_0_MAIL0 + 0x00) 428c2ecf20Sopenharmony_ci#define MAIL0_POL (ARM_0_MAIL0 + 0x10) 438c2ecf20Sopenharmony_ci#define MAIL0_STA (ARM_0_MAIL0 + 0x18) 448c2ecf20Sopenharmony_ci#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) 458c2ecf20Sopenharmony_ci#define MAIL1_WRT (ARM_0_MAIL1 + 0x00) 468c2ecf20Sopenharmony_ci#define MAIL1_STA (ARM_0_MAIL1 + 0x18) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Status register: FIFO state. */ 498c2ecf20Sopenharmony_ci#define ARM_MS_FULL BIT(31) 508c2ecf20Sopenharmony_ci#define ARM_MS_EMPTY BIT(30) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Configuration register: Enable interrupts. */ 538c2ecf20Sopenharmony_ci#define ARM_MC_IHAVEDATAIRQEN BIT(0) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct bcm2835_mbox { 568c2ecf20Sopenharmony_ci void __iomem *regs; 578c2ecf20Sopenharmony_ci spinlock_t lock; 588c2ecf20Sopenharmony_ci struct mbox_controller controller; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return container_of(link->mbox, struct bcm2835_mbox, controller); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox = dev_id; 698c2ecf20Sopenharmony_ci struct device *dev = mbox->controller.dev; 708c2ecf20Sopenharmony_ci struct mbox_chan *link = &mbox->controller.chans[0]; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) { 738c2ecf20Sopenharmony_ci u32 msg = readl(mbox->regs + MAIL0_RD); 748c2ecf20Sopenharmony_ci dev_dbg(dev, "Reply 0x%08X\n", msg); 758c2ecf20Sopenharmony_ci mbox_chan_received_data(link, &msg); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci return IRQ_HANDLED; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int bcm2835_send_data(struct mbox_chan *link, void *data) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); 838c2ecf20Sopenharmony_ci u32 msg = *(u32 *)data; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci spin_lock(&mbox->lock); 868c2ecf20Sopenharmony_ci writel(msg, mbox->regs + MAIL1_WRT); 878c2ecf20Sopenharmony_ci dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg); 888c2ecf20Sopenharmony_ci spin_unlock(&mbox->lock); 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int bcm2835_startup(struct mbox_chan *link) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Enable the interrupt on data reception */ 978c2ecf20Sopenharmony_ci writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void bcm2835_shutdown(struct mbox_chan *link) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci writel(0, mbox->regs + MAIL0_CNF); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic bool bcm2835_last_tx_done(struct mbox_chan *link) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); 1128c2ecf20Sopenharmony_ci bool ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci spin_lock(&mbox->lock); 1158c2ecf20Sopenharmony_ci ret = !(readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL); 1168c2ecf20Sopenharmony_ci spin_unlock(&mbox->lock); 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops bcm2835_mbox_chan_ops = { 1218c2ecf20Sopenharmony_ci .send_data = bcm2835_send_data, 1228c2ecf20Sopenharmony_ci .startup = bcm2835_startup, 1238c2ecf20Sopenharmony_ci .shutdown = bcm2835_shutdown, 1248c2ecf20Sopenharmony_ci .last_tx_done = bcm2835_last_tx_done 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox, 1288c2ecf20Sopenharmony_ci const struct of_phandle_args *sp) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (sp->args_count != 0) 1318c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return &mbox->chans[0]; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int bcm2835_mbox_probe(struct platform_device *pdev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1398c2ecf20Sopenharmony_ci int ret = 0; 1408c2ecf20Sopenharmony_ci struct resource *iomem; 1418c2ecf20Sopenharmony_ci struct bcm2835_mbox *mbox; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 1448c2ecf20Sopenharmony_ci if (mbox == NULL) 1458c2ecf20Sopenharmony_ci return -ENOMEM; 1468c2ecf20Sopenharmony_ci spin_lock_init(&mbox->lock); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), 1498c2ecf20Sopenharmony_ci bcm2835_mbox_irq, 0, dev_name(dev), mbox); 1508c2ecf20Sopenharmony_ci if (ret) { 1518c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", 1528c2ecf20Sopenharmony_ci ret); 1538c2ecf20Sopenharmony_ci return -ENODEV; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1578c2ecf20Sopenharmony_ci mbox->regs = devm_ioremap_resource(&pdev->dev, iomem); 1588c2ecf20Sopenharmony_ci if (IS_ERR(mbox->regs)) { 1598c2ecf20Sopenharmony_ci ret = PTR_ERR(mbox->regs); 1608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mbox->controller.txdone_poll = true; 1658c2ecf20Sopenharmony_ci mbox->controller.txpoll_period = 5; 1668c2ecf20Sopenharmony_ci mbox->controller.ops = &bcm2835_mbox_chan_ops; 1678c2ecf20Sopenharmony_ci mbox->controller.of_xlate = &bcm2835_mbox_index_xlate; 1688c2ecf20Sopenharmony_ci mbox->controller.dev = dev; 1698c2ecf20Sopenharmony_ci mbox->controller.num_chans = 1; 1708c2ecf20Sopenharmony_ci mbox->controller.chans = devm_kzalloc(dev, 1718c2ecf20Sopenharmony_ci sizeof(*mbox->controller.chans), GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (!mbox->controller.chans) 1738c2ecf20Sopenharmony_ci return -ENOMEM; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(dev, &mbox->controller); 1768c2ecf20Sopenharmony_ci if (ret) 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mbox); 1808c2ecf20Sopenharmony_ci dev_info(dev, "mailbox enabled\n"); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct of_device_id bcm2835_mbox_of_match[] = { 1868c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2835-mbox", }, 1878c2ecf20Sopenharmony_ci {}, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic struct platform_driver bcm2835_mbox_driver = { 1928c2ecf20Sopenharmony_ci .driver = { 1938c2ecf20Sopenharmony_ci .name = "bcm2835-mbox", 1948c2ecf20Sopenharmony_ci .of_match_table = bcm2835_mbox_of_match, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci .probe = bcm2835_mbox_probe, 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_cimodule_platform_driver(bcm2835_mbox_driver); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 2018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); 2028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 203