Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mailbox: Add Altera mailbox driver

The Altera mailbox allows for interprocessor communication. It supports
only one channel and work as either sender or receiver.

Signed-off-by: Ley Foon Tan <lftan@altera.com>

authored by

Ley Foon Tan and committed by
Jassi Brar
f62092f6 01340df8

+451
+49
Documentation/devicetree/bindings/mailbox/altera-mailbox.txt
··· 1 + Altera Mailbox Driver 2 + ===================== 3 + 4 + Required properties: 5 + - compatible : "altr,mailbox-1.0". 6 + - reg : physical base address of the mailbox and length of 7 + memory mapped region. 8 + - #mbox-cells: Common mailbox binding property to identify the number 9 + of cells required for the mailbox specifier. Should be 1. 10 + 11 + Optional properties: 12 + - interrupt-parent : interrupt source phandle. 13 + - interrupts : interrupt number. The interrupt specifier format 14 + depends on the interrupt controller parent. 15 + 16 + Example: 17 + mbox_tx: mailbox@0x100 { 18 + compatible = "altr,mailbox-1.0"; 19 + reg = <0x100 0x8>; 20 + interrupt-parent = < &gic_0 >; 21 + interrupts = <5>; 22 + #mbox-cells = <1>; 23 + }; 24 + 25 + mbox_rx: mailbox@0x200 { 26 + compatible = "altr,mailbox-1.0"; 27 + reg = <0x200 0x8>; 28 + interrupt-parent = < &gic_0 >; 29 + interrupts = <6>; 30 + #mbox-cells = <1>; 31 + }; 32 + 33 + Mailbox client 34 + =============== 35 + "mboxes" and the optional "mbox-names" (please see 36 + Documentation/devicetree/bindings/mailbox/mailbox.txt for details). Each value 37 + of the mboxes property should contain a phandle to the mailbox controller 38 + device node and second argument is the channel index. It must be 0 (hardware 39 + support only one channel).The equivalent "mbox-names" property value can be 40 + used to give a name to the communication channel to be used by the client user. 41 + 42 + Example: 43 + mclient0: mclient0@0x400 { 44 + compatible = "client-1.0"; 45 + reg = <0x400 0x10>; 46 + mbox-names = "mbox-tx", "mbox-rx"; 47 + mboxes = <&mbox_tx 0>, 48 + <&mbox_rx 0>; 49 + };
+6
MAINTAINERS
··· 563 563 L: linux-alpha@vger.kernel.org 564 564 F: arch/alpha/ 565 565 566 + ALTERA MAILBOX DRIVER 567 + M: Ley Foon Tan <lftan@altera.com> 568 + L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) 569 + S: Maintained 570 + F: drivers/mailbox/mailbox-altera.c 571 + 566 572 ALTERA TRIPLE SPEED ETHERNET DRIVER 567 573 M: Vince Bridgers <vbridger@opensource.altera.com> 568 574 L: netdev@vger.kernel.org
+6
drivers/mailbox/Kconfig
··· 45 45 states). Select this driver if your platform implements the 46 46 PCC clients mentioned above. 47 47 48 + config ALTERA_MBOX 49 + tristate "Altera Mailbox" 50 + help 51 + An implementation of the Altera Mailbox soft core. It is used 52 + to send message between processors. Say Y here if you want to use the 53 + Altera mailbox support. 48 54 endif
+2
drivers/mailbox/Makefile
··· 7 7 obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o 8 8 9 9 obj-$(CONFIG_PCC) += pcc.o 10 + 11 + obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o
+388
drivers/mailbox/mailbox-altera.c
··· 1 + /* 2 + * Copyright Altera Corporation (C) 2013-2014. All rights reserved 3 + * 4 + * This program is free software; you can redistribute it and/or modify it 5 + * under the terms and conditions of the GNU General Public License, 6 + * version 2, as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope it will be useful, but WITHOUT 9 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 + * more details. 12 + * 13 + * You should have received a copy of the GNU General Public License along with 14 + * this program. If not, see <http://www.gnu.org/licenses/>. 15 + */ 16 + 17 + #include <linux/device.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/io.h> 20 + #include <linux/kernel.h> 21 + #include <linux/mailbox_controller.h> 22 + #include <linux/module.h> 23 + #include <linux/of.h> 24 + #include <linux/platform_device.h> 25 + 26 + #define DRIVER_NAME "altera-mailbox" 27 + 28 + #define MAILBOX_CMD_REG 0x00 29 + #define MAILBOX_PTR_REG 0x04 30 + #define MAILBOX_STS_REG 0x08 31 + #define MAILBOX_INTMASK_REG 0x0C 32 + 33 + #define INT_PENDING_MSK 0x1 34 + #define INT_SPACE_MSK 0x2 35 + 36 + #define STS_PENDING_MSK 0x1 37 + #define STS_FULL_MSK 0x2 38 + #define STS_FULL_OFT 0x1 39 + 40 + #define MBOX_PENDING(status) (((status) & STS_PENDING_MSK)) 41 + #define MBOX_FULL(status) (((status) & STS_FULL_MSK) >> STS_FULL_OFT) 42 + 43 + enum altera_mbox_msg { 44 + MBOX_CMD = 0, 45 + MBOX_PTR, 46 + }; 47 + 48 + #define MBOX_POLLING_MS 5 /* polling interval 5ms */ 49 + 50 + struct altera_mbox { 51 + bool is_sender; /* 1-sender, 0-receiver */ 52 + bool intr_mode; 53 + int irq; 54 + void __iomem *mbox_base; 55 + struct device *dev; 56 + struct mbox_controller controller; 57 + 58 + /* If the controller supports only RX polling mode */ 59 + struct timer_list rxpoll_timer; 60 + }; 61 + 62 + static struct altera_mbox *mbox_chan_to_altera_mbox(struct mbox_chan *chan) 63 + { 64 + if (!chan || !chan->con_priv) 65 + return NULL; 66 + 67 + return (struct altera_mbox *)chan->con_priv; 68 + } 69 + 70 + static inline int altera_mbox_full(struct altera_mbox *mbox) 71 + { 72 + u32 status; 73 + 74 + status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG); 75 + return MBOX_FULL(status); 76 + } 77 + 78 + static inline int altera_mbox_pending(struct altera_mbox *mbox) 79 + { 80 + u32 status; 81 + 82 + status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG); 83 + return MBOX_PENDING(status); 84 + } 85 + 86 + static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable) 87 + { 88 + u32 mask; 89 + 90 + mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG); 91 + if (enable) 92 + mask |= INT_PENDING_MSK; 93 + else 94 + mask &= ~INT_PENDING_MSK; 95 + writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); 96 + } 97 + 98 + static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable) 99 + { 100 + u32 mask; 101 + 102 + mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG); 103 + if (enable) 104 + mask |= INT_SPACE_MSK; 105 + else 106 + mask &= ~INT_SPACE_MSK; 107 + writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); 108 + } 109 + 110 + static bool altera_mbox_is_sender(struct altera_mbox *mbox) 111 + { 112 + u32 reg; 113 + /* Write a magic number to PTR register and read back this register. 114 + * This register is read-write if it is a sender. 115 + */ 116 + #define MBOX_MAGIC 0xA5A5AA55 117 + writel_relaxed(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG); 118 + reg = readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG); 119 + if (reg == MBOX_MAGIC) { 120 + /* Clear to 0 */ 121 + writel_relaxed(0, mbox->mbox_base + MAILBOX_PTR_REG); 122 + return true; 123 + } 124 + return false; 125 + } 126 + 127 + static void altera_mbox_rx_data(struct mbox_chan *chan) 128 + { 129 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 130 + u32 data[2]; 131 + 132 + if (altera_mbox_pending(mbox)) { 133 + data[MBOX_PTR] = 134 + readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG); 135 + data[MBOX_CMD] = 136 + readl_relaxed(mbox->mbox_base + MAILBOX_CMD_REG); 137 + mbox_chan_received_data(chan, (void *)data); 138 + } 139 + } 140 + 141 + static void altera_mbox_poll_rx(unsigned long data) 142 + { 143 + struct mbox_chan *chan = (struct mbox_chan *)data; 144 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 145 + 146 + altera_mbox_rx_data(chan); 147 + 148 + mod_timer(&mbox->rxpoll_timer, 149 + jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); 150 + } 151 + 152 + static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p) 153 + { 154 + struct mbox_chan *chan = (struct mbox_chan *)p; 155 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 156 + 157 + altera_mbox_tx_intmask(mbox, false); 158 + mbox_chan_txdone(chan, 0); 159 + 160 + return IRQ_HANDLED; 161 + } 162 + 163 + static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p) 164 + { 165 + struct mbox_chan *chan = (struct mbox_chan *)p; 166 + 167 + altera_mbox_rx_data(chan); 168 + return IRQ_HANDLED; 169 + } 170 + 171 + static int altera_mbox_startup_sender(struct mbox_chan *chan) 172 + { 173 + int ret; 174 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 175 + 176 + if (mbox->intr_mode) { 177 + ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0, 178 + DRIVER_NAME, chan); 179 + if (unlikely(ret)) { 180 + dev_err(mbox->dev, 181 + "failed to register mailbox interrupt:%d\n", 182 + ret); 183 + return ret; 184 + } 185 + } 186 + 187 + return 0; 188 + } 189 + 190 + static int altera_mbox_startup_receiver(struct mbox_chan *chan) 191 + { 192 + int ret; 193 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 194 + 195 + if (mbox->intr_mode) { 196 + ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0, 197 + DRIVER_NAME, chan); 198 + if (unlikely(ret)) { 199 + mbox->intr_mode = false; 200 + goto polling; /* use polling if failed */ 201 + } 202 + 203 + altera_mbox_rx_intmask(mbox, true); 204 + return 0; 205 + } 206 + 207 + polling: 208 + /* Setup polling timer */ 209 + setup_timer(&mbox->rxpoll_timer, altera_mbox_poll_rx, 210 + (unsigned long)chan); 211 + mod_timer(&mbox->rxpoll_timer, 212 + jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); 213 + 214 + return 0; 215 + } 216 + 217 + static int altera_mbox_send_data(struct mbox_chan *chan, void *data) 218 + { 219 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 220 + u32 *udata = (u32 *)data; 221 + 222 + if (!mbox || !data) 223 + return -EINVAL; 224 + if (!mbox->is_sender) { 225 + dev_warn(mbox->dev, 226 + "failed to send. This is receiver mailbox.\n"); 227 + return -EINVAL; 228 + } 229 + 230 + if (altera_mbox_full(mbox)) 231 + return -EBUSY; 232 + 233 + /* Enable interrupt before send */ 234 + if (mbox->intr_mode) 235 + altera_mbox_tx_intmask(mbox, true); 236 + 237 + /* Pointer register must write before command register */ 238 + writel_relaxed(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG); 239 + writel_relaxed(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG); 240 + 241 + return 0; 242 + } 243 + 244 + static bool altera_mbox_last_tx_done(struct mbox_chan *chan) 245 + { 246 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 247 + 248 + /* Return false if mailbox is full */ 249 + return altera_mbox_full(mbox) ? false : true; 250 + } 251 + 252 + static bool altera_mbox_peek_data(struct mbox_chan *chan) 253 + { 254 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 255 + 256 + return altera_mbox_pending(mbox) ? true : false; 257 + } 258 + 259 + static int altera_mbox_startup(struct mbox_chan *chan) 260 + { 261 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 262 + int ret = 0; 263 + 264 + if (!mbox) 265 + return -EINVAL; 266 + 267 + if (mbox->is_sender) 268 + ret = altera_mbox_startup_sender(chan); 269 + else 270 + ret = altera_mbox_startup_receiver(chan); 271 + 272 + return ret; 273 + } 274 + 275 + static void altera_mbox_shutdown(struct mbox_chan *chan) 276 + { 277 + struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan); 278 + 279 + if (mbox->intr_mode) { 280 + /* Unmask all interrupt masks */ 281 + writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG); 282 + free_irq(mbox->irq, chan); 283 + } else if (!mbox->is_sender) { 284 + del_timer_sync(&mbox->rxpoll_timer); 285 + } 286 + } 287 + 288 + static struct mbox_chan_ops altera_mbox_ops = { 289 + .send_data = altera_mbox_send_data, 290 + .startup = altera_mbox_startup, 291 + .shutdown = altera_mbox_shutdown, 292 + .last_tx_done = altera_mbox_last_tx_done, 293 + .peek_data = altera_mbox_peek_data, 294 + }; 295 + 296 + static int altera_mbox_probe(struct platform_device *pdev) 297 + { 298 + struct altera_mbox *mbox; 299 + struct resource *regs; 300 + struct mbox_chan *chans; 301 + int ret; 302 + 303 + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), 304 + GFP_KERNEL); 305 + if (!mbox) 306 + return -ENOMEM; 307 + 308 + /* Allocated one channel */ 309 + chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL); 310 + if (!chans) 311 + return -ENOMEM; 312 + 313 + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 314 + 315 + mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs); 316 + if (IS_ERR(mbox->mbox_base)) 317 + return PTR_ERR(mbox->mbox_base); 318 + 319 + /* Check is it a sender or receiver? */ 320 + mbox->is_sender = altera_mbox_is_sender(mbox); 321 + 322 + mbox->irq = platform_get_irq(pdev, 0); 323 + if (mbox->irq >= 0) 324 + mbox->intr_mode = true; 325 + 326 + mbox->dev = &pdev->dev; 327 + 328 + /* Hardware supports only one channel. */ 329 + chans[0].con_priv = mbox; 330 + mbox->controller.dev = mbox->dev; 331 + mbox->controller.num_chans = 1; 332 + mbox->controller.chans = chans; 333 + mbox->controller.ops = &altera_mbox_ops; 334 + 335 + if (mbox->is_sender) { 336 + if (mbox->intr_mode) { 337 + mbox->controller.txdone_irq = true; 338 + } else { 339 + mbox->controller.txdone_poll = true; 340 + mbox->controller.txpoll_period = MBOX_POLLING_MS; 341 + } 342 + } 343 + 344 + ret = mbox_controller_register(&mbox->controller); 345 + if (ret) { 346 + dev_err(&pdev->dev, "Register mailbox failed\n"); 347 + goto err; 348 + } 349 + 350 + platform_set_drvdata(pdev, mbox); 351 + err: 352 + return ret; 353 + } 354 + 355 + static int altera_mbox_remove(struct platform_device *pdev) 356 + { 357 + struct altera_mbox *mbox = platform_get_drvdata(pdev); 358 + 359 + if (!mbox) 360 + return -EINVAL; 361 + 362 + mbox_controller_unregister(&mbox->controller); 363 + 364 + return 0; 365 + } 366 + 367 + static const struct of_device_id altera_mbox_match[] = { 368 + { .compatible = "altr,mailbox-1.0" }, 369 + { /* Sentinel */ } 370 + }; 371 + 372 + MODULE_DEVICE_TABLE(of, altera_mbox_match); 373 + 374 + static struct platform_driver altera_mbox_driver = { 375 + .probe = altera_mbox_probe, 376 + .remove = altera_mbox_remove, 377 + .driver = { 378 + .name = DRIVER_NAME, 379 + .of_match_table = altera_mbox_match, 380 + }, 381 + }; 382 + 383 + module_platform_driver(altera_mbox_driver); 384 + 385 + MODULE_LICENSE("GPL v2"); 386 + MODULE_DESCRIPTION("Altera mailbox specific functions"); 387 + MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); 388 + MODULE_ALIAS("platform:altera-mailbox");