201 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2010,2015 Broadcom4 * Copyright (C) 2013-2014 Lubomir Rintel5 * Copyright (C) 2013 Craig McGeachie6 *7 * Parts of the driver are based on:8 * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was9 * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/10 * linux.git11 * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at12 * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/13 * mailbox/bcm2835-ipc.c14 * - documentation available on the following web site:15 * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface16 */17 18#include <linux/device.h>19#include <linux/dma-mapping.h>20#include <linux/err.h>21#include <linux/interrupt.h>22#include <linux/irq.h>23#include <linux/kernel.h>24#include <linux/mailbox_controller.h>25#include <linux/module.h>26#include <linux/of_address.h>27#include <linux/of_irq.h>28#include <linux/platform_device.h>29#include <linux/spinlock.h>30 31/* Mailboxes */32#define ARM_0_MAIL0 0x0033#define ARM_0_MAIL1 0x2034 35/*36 * Mailbox registers. We basically only support mailbox 0 & 1. We37 * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See38 * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about39 * the placement of memory barriers.40 */41#define MAIL0_RD (ARM_0_MAIL0 + 0x00)42#define MAIL0_POL (ARM_0_MAIL0 + 0x10)43#define MAIL0_STA (ARM_0_MAIL0 + 0x18)44#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C)45#define MAIL1_WRT (ARM_0_MAIL1 + 0x00)46#define MAIL1_STA (ARM_0_MAIL1 + 0x18)47 48/* Status register: FIFO state. */49#define ARM_MS_FULL BIT(31)50#define ARM_MS_EMPTY BIT(30)51 52/* Configuration register: Enable interrupts. */53#define ARM_MC_IHAVEDATAIRQEN BIT(0)54 55struct bcm2835_mbox {56 void __iomem *regs;57 spinlock_t lock;58 struct mbox_controller controller;59};60 61static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link)62{63 return container_of(link->mbox, struct bcm2835_mbox, controller);64}65 66static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id)67{68 struct bcm2835_mbox *mbox = dev_id;69 struct device *dev = mbox->controller.dev;70 struct mbox_chan *link = &mbox->controller.chans[0];71 72 while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) {73 u32 msg = readl(mbox->regs + MAIL0_RD);74 dev_dbg(dev, "Reply 0x%08X\n", msg);75 mbox_chan_received_data(link, &msg);76 }77 return IRQ_HANDLED;78}79 80static int bcm2835_send_data(struct mbox_chan *link, void *data)81{82 struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);83 u32 msg = *(u32 *)data;84 85 spin_lock(&mbox->lock);86 writel(msg, mbox->regs + MAIL1_WRT);87 dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg);88 spin_unlock(&mbox->lock);89 return 0;90}91 92static int bcm2835_startup(struct mbox_chan *link)93{94 struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);95 96 /* Enable the interrupt on data reception */97 writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF);98 99 return 0;100}101 102static void bcm2835_shutdown(struct mbox_chan *link)103{104 struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);105 106 writel(0, mbox->regs + MAIL0_CNF);107}108 109static bool bcm2835_last_tx_done(struct mbox_chan *link)110{111 struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);112 bool ret;113 114 spin_lock(&mbox->lock);115 ret = !(readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL);116 spin_unlock(&mbox->lock);117 return ret;118}119 120static const struct mbox_chan_ops bcm2835_mbox_chan_ops = {121 .send_data = bcm2835_send_data,122 .startup = bcm2835_startup,123 .shutdown = bcm2835_shutdown,124 .last_tx_done = bcm2835_last_tx_done125};126 127static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox,128 const struct of_phandle_args *sp)129{130 if (sp->args_count != 0)131 return ERR_PTR(-EINVAL);132 133 return &mbox->chans[0];134}135 136static int bcm2835_mbox_probe(struct platform_device *pdev)137{138 struct device *dev = &pdev->dev;139 int ret = 0;140 struct bcm2835_mbox *mbox;141 142 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);143 if (mbox == NULL)144 return -ENOMEM;145 spin_lock_init(&mbox->lock);146 147 ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),148 bcm2835_mbox_irq, IRQF_NO_SUSPEND, dev_name(dev),149 mbox);150 if (ret) {151 dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",152 ret);153 return -ENODEV;154 }155 156 mbox->regs = devm_platform_ioremap_resource(pdev, 0);157 if (IS_ERR(mbox->regs)) {158 ret = PTR_ERR(mbox->regs);159 return ret;160 }161 162 mbox->controller.txdone_poll = true;163 mbox->controller.txpoll_period = 5;164 mbox->controller.ops = &bcm2835_mbox_chan_ops;165 mbox->controller.of_xlate = &bcm2835_mbox_index_xlate;166 mbox->controller.dev = dev;167 mbox->controller.num_chans = 1;168 mbox->controller.chans = devm_kzalloc(dev,169 sizeof(*mbox->controller.chans), GFP_KERNEL);170 if (!mbox->controller.chans)171 return -ENOMEM;172 173 ret = devm_mbox_controller_register(dev, &mbox->controller);174 if (ret)175 return ret;176 177 platform_set_drvdata(pdev, mbox);178 dev_info(dev, "mailbox enabled\n");179 180 return ret;181}182 183static const struct of_device_id bcm2835_mbox_of_match[] = {184 { .compatible = "brcm,bcm2835-mbox", },185 {},186};187MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match);188 189static struct platform_driver bcm2835_mbox_driver = {190 .driver = {191 .name = "bcm2835-mbox",192 .of_match_table = bcm2835_mbox_of_match,193 },194 .probe = bcm2835_mbox_probe,195};196module_platform_driver(bcm2835_mbox_driver);197 198MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");199MODULE_DESCRIPTION("BCM2835 mailbox IPC driver");200MODULE_LICENSE("GPL v2");201