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