brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · ea12fb8 Raw
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