From 0c1382bd36ed3bc31b953028083619990e4dadf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 1 May 2015 19:11:03 +0200 Subject: [PATCH 003/203] mailbox: bcm2708: Add bcm2708-vcio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: popcornmix Copy the arch vcio.c driver to drivers/mailbox. This is done to make it available on ARCH_BCM2835. Signed-off-by: Noralf Trønnes mailbox: bcm2708-vcio: Allocation does not need to be atomic No need to do atomic allocation in a context that can sleep. Signed-off-by: Noralf Trønnes mailbox: bcm2708-vcio: Check the correct status register before writing With the VC reader blocked and the ARM writing, MAIL0_STA reads empty permanently while MAIL1_STA goes from empty (0x40000000) to non-empty (0x00000001-0x00000007) to full (0x80000008). Suggested-by: Phil Elwell Signed-off-by: Noralf Trønnes --- drivers/mailbox/Kconfig | 6 + drivers/mailbox/Makefile | 2 + drivers/mailbox/bcm2708-vcio.c | 427 ++++++++++++++++++++++++++ include/linux/platform_data/mailbox-bcm2708.h | 126 ++++++++ 4 files changed, 561 insertions(+) create mode 100644 drivers/mailbox/bcm2708-vcio.c create mode 100644 include/linux/platform_data/mailbox-bcm2708.h --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -7,6 +7,12 @@ menuconfig MAILBOX if MAILBOX +config BCM2708_MBOX + bool "Broadcom BCM2708 Mailbox (vcio)" + depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 + help + Broadcom BCM2708 Mailbox (vcio) + config ARM_MHU tristate "ARM MHU Mailbox" depends on ARM_AMBA --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_MAILBOX) += mailbox.o +obj-$(CONFIG_BCM2708_MBOX) += bcm2708-vcio.o + obj-$(CONFIG_ARM_MHU) += arm_mhu.o obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o --- /dev/null +++ b/drivers/mailbox/bcm2708-vcio.c @@ -0,0 +1,427 @@ +/* + * linux/arch/arm/mach-bcm2708/vcio.c + * + * Copyright (C) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This device provides a shared mechanism for writing to the mailboxes, + * semaphores, doorbells etc. that are shared between the ARM and the + * VideoCore processor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "bcm2708_vcio" +#define DEVICE_FILE_NAME "vcio" + +/* offsets from a mail box base address */ +#define MAIL0_RD 0x00 /* read - and next 4 words */ +#define MAIL0_POL 0x10 /* read without popping the fifo */ +#define MAIL0_SND 0x14 /* sender ID (bottom two bits) */ +#define MAIL0_STA 0x18 /* status */ +#define MAIL0_CNF 0x1C /* configuration */ +#define MAIL1_WRT 0x20 /* write - and next 4 words */ +#define MAIL1_STA 0x38 /* status */ + +/* On MACH_BCM270x these come through (arm_control.h ) */ +#ifndef ARM_MS_EMPTY +#define ARM_MS_EMPTY BIT(30) +#define ARM_MS_FULL BIT(31) + +#define ARM_MC_IHAVEDATAIRQEN BIT(0) +#endif + +#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) +#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) +#define MBOX_CHAN(msg) ((msg) & 0xf) +#define MBOX_DATA28(msg) ((msg) & ~0xf) +#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) + +#define MBOX_MAGIC 0xd0d0c0de + +#define MAJOR_NUM 100 +#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) + +static struct class *vcio_class; + +struct vc_mailbox { + void __iomem *regs; + uint32_t msg[MBOX_CHAN_COUNT]; + struct semaphore sema[MBOX_CHAN_COUNT]; + uint32_t magic; +}; + +static void mbox_init(struct vc_mailbox *mbox_out) +{ + int i; + + for (i = 0; i < MBOX_CHAN_COUNT; i++) { + mbox_out->msg[i] = 0; + sema_init(&mbox_out->sema[i], 0); + } + + /* Enable the interrupt on data reception */ + writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->regs + MAIL0_CNF); + + mbox_out->magic = MBOX_MAGIC; +} + +static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) +{ + if (mbox->magic != MBOX_MAGIC) + return -EINVAL; + + /* wait for the mailbox FIFO to have some space in it */ + while (0 != (readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL)) + cpu_relax(); + + writel(MBOX_MSG(chan, data28), mbox->regs + MAIL1_WRT); + + return 0; +} + +static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) +{ + if (mbox->magic != MBOX_MAGIC) + return -EINVAL; + + down(&mbox->sema[chan]); + *data28 = MBOX_DATA28(mbox->msg[chan]); + mbox->msg[chan] = 0; + + return 0; +} + +static irqreturn_t mbox_irq_handler(int irq, void *dev_id) +{ + /* wait for the mailbox FIFO to have some data in it */ + struct vc_mailbox *mbox = (struct vc_mailbox *)dev_id; + int status = readl(mbox->regs + MAIL0_STA); + int ret = IRQ_NONE; + + while (!(status & ARM_MS_EMPTY)) { + uint32_t msg = readl(mbox->regs + MAIL0_RD); + int chan = MBOX_CHAN(msg); + + if (chan < MBOX_CHAN_COUNT) { + if (mbox->msg[chan]) { + pr_err(DRIVER_NAME + ": mbox chan %d overflow - drop %08x\n", + chan, msg); + } else { + mbox->msg[chan] = (msg | 0xf); + up(&mbox->sema[chan]); + } + } else { + pr_err(DRIVER_NAME + ": invalid channel selector (msg %08x)\n", msg); + } + ret = IRQ_HANDLED; + status = readl(mbox->regs + MAIL0_STA); + } + return ret; +} + +/* Mailbox Methods */ + +static struct device *mbox_dev; /* we assume there's only one! */ + +static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) +{ + struct vc_mailbox *mailbox = dev_get_drvdata(dev); + int rc; + + device_lock(dev); + rc = mbox_write(mailbox, chan, data28); + device_unlock(dev); + + return rc; +} + +static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) +{ + struct vc_mailbox *mailbox = dev_get_drvdata(dev); + int rc; + + device_lock(dev); + rc = mbox_read(mailbox, chan, data28); + device_unlock(dev); + + return rc; +} + +extern int bcm_mailbox_write(unsigned chan, uint32_t data28) +{ + if (!mbox_dev) + return -ENODEV; + + return dev_mbox_write(mbox_dev, chan, data28); +} +EXPORT_SYMBOL_GPL(bcm_mailbox_write); + +extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) +{ + if (!mbox_dev) + return -ENODEV; + + return dev_mbox_read(mbox_dev, chan, data28); +} +EXPORT_SYMBOL_GPL(bcm_mailbox_read); + +static int mbox_copy_from_user(void *dst, const void *src, int size) +{ + if ((uint32_t)src < TASK_SIZE) + return copy_from_user(dst, src, size); + + memcpy(dst, src, size); + + return 0; +} + +static int mbox_copy_to_user(void *dst, const void *src, int size) +{ + if ((uint32_t)dst < TASK_SIZE) + return copy_to_user(dst, src, size); + + memcpy(dst, src, size); + + return 0; +} + +static DEFINE_MUTEX(mailbox_lock); +extern int bcm_mailbox_property(void *data, int size) +{ + uint32_t success; + dma_addr_t mem_bus; /* the memory address accessed from videocore */ + void *mem_kern; /* the memory address accessed from driver */ + int s = 0; + + mutex_lock(&mailbox_lock); + /* allocate some memory for the messages communicating with GPU */ + mem_kern = dma_alloc_coherent(NULL, PAGE_ALIGN(size), &mem_bus, + GFP_KERNEL); + if (mem_kern) { + /* create the message */ + mbox_copy_from_user(mem_kern, data, size); + + /* send the message */ + wmb(); + s = bcm_mailbox_write(MBOX_CHAN_PROPERTY, (uint32_t)mem_bus); + if (s == 0) + s = bcm_mailbox_read(MBOX_CHAN_PROPERTY, &success); + if (s == 0) { + /* copy the response */ + rmb(); + mbox_copy_to_user(data, mem_kern, size); + } + dma_free_coherent(NULL, PAGE_ALIGN(size), mem_kern, mem_bus); + } else { + s = -ENOMEM; + } + if (s != 0) + pr_err(DRIVER_NAME ": %s failed (%d)\n", __func__, s); + + mutex_unlock(&mailbox_lock); + return s; +} +EXPORT_SYMBOL_GPL(bcm_mailbox_property); + +/* Platform Device for Mailbox */ + +/* + * Is the device open right now? Used to prevent + * concurent access into the same device + */ +static bool device_is_open; + +/* This is called whenever a process attempts to open the device file */ +static int device_open(struct inode *inode, struct file *file) +{ + /* We don't want to talk to two processes at the same time */ + if (device_is_open) + return -EBUSY; + + device_is_open = true; + try_module_get(THIS_MODULE); + + return 0; +} + +static int device_release(struct inode *inode, struct file *file) +{ + /* We're now ready for our next caller */ + device_is_open = false; + + module_put(THIS_MODULE); + + return 0; +} + +/* + * This function is called whenever a process tries to do an ioctl on our + * device file. We get two extra parameters (additional to the inode and file + * structures, which all device functions get): the number of the ioctl called + * and the parameter given to the ioctl function. + * + * If the ioctl is write or read/write (meaning output is returned to the + * calling process), the ioctl call returns the output of this function. + * + */ +static long device_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + unsigned size; + + switch (ioctl_num) { + case IOCTL_MBOX_PROPERTY: + /* + * Receive a pointer to a message (in user space) and set that + * to be the device's message. Get the parameter given to + * ioctl by the process. + */ + mbox_copy_from_user(&size, (void *)ioctl_param, sizeof(size)); + return bcm_mailbox_property((void *)ioctl_param, size); + default: + pr_err(DRIVER_NAME "unknown ioctl: %d\n", ioctl_num); + return -EINVAL; + } + + return 0; +} + +/* Module Declarations */ + +/* + * This structure will hold the functions to be called + * when a process does something to the device we + * created. Since a pointer to this structure is kept in + * the devices table, it can't be local to + * init_module. NULL is for unimplemented functios. + */ +const struct file_operations fops = { + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_release, /* a.k.a. close */ +}; + +static int bcm_vcio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *vdev; + struct vc_mailbox *mailbox; + struct resource *res; + int irq, ret; + + mailbox = devm_kzalloc(dev, sizeof(*mailbox), GFP_KERNEL); + if (!mailbox) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mailbox->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(mailbox->regs)) + return PTR_ERR(mailbox->regs); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, mbox_irq_handler, + IRQF_IRQPOLL, + dev_name(dev), mailbox); + if (ret) { + dev_err(dev, "Interrupt request failed %d\n", ret); + return ret; + } + + ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops); + if (ret < 0) { + pr_err("Character device registration failed %d\n", ret); + return ret; + } + + vcio_class = class_create(THIS_MODULE, DRIVER_NAME); + if (IS_ERR(vcio_class)) { + ret = PTR_ERR(vcio_class); + pr_err("Class creation failed %d\n", ret); + goto err_class; + } + + vdev = device_create(vcio_class, NULL, MKDEV(MAJOR_NUM, 0), NULL, + "vcio"); + if (IS_ERR(vdev)) { + ret = PTR_ERR(vdev); + pr_err("Device creation failed %d\n", ret); + goto err_dev; + } + + mbox_init(mailbox); + platform_set_drvdata(pdev, mailbox); + mbox_dev = dev; + + dev_info(dev, "mailbox at %p\n", mailbox->regs); + + return 0; + +err_dev: + class_destroy(vcio_class); +err_class: + unregister_chrdev(MAJOR_NUM, DEVICE_FILE_NAME); + + return ret; +} + +static int bcm_vcio_remove(struct platform_device *pdev) +{ + mbox_dev = NULL; + platform_set_drvdata(pdev, NULL); + device_destroy(vcio_class, MKDEV(MAJOR_NUM, 0)); + class_destroy(vcio_class); + unregister_chrdev(MAJOR_NUM, DEVICE_FILE_NAME); + + return 0; +} + +static const struct of_device_id bcm_vcio_of_match_table[] = { + { .compatible = "brcm,bcm2708-vcio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_vcio_of_match_table); + +static struct platform_driver bcm_mbox_driver = { + .probe = bcm_vcio_probe, + .remove = bcm_vcio_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = bcm_vcio_of_match_table, + }, +}; + +static int __init bcm_mbox_init(void) +{ + return platform_driver_register(&bcm_mbox_driver); +} + +static void __exit bcm_mbox_exit(void) +{ + platform_driver_unregister(&bcm_mbox_driver); +} + +arch_initcall(bcm_mbox_init); /* Initialize early */ +module_exit(bcm_mbox_exit); + +MODULE_AUTHOR("Gray Girling"); +MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); +MODULE_LICENSE("GPL"); --- /dev/null +++ b/include/linux/platform_data/mailbox-bcm2708.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _PLAT_MAILBOX_BCM2708_H +#define _PLAT_MAILBOX_BCM2708_H + +/* Routines to handle I/O via the VideoCore "ARM control" registers + * (semaphores, doorbells, mailboxes) + */ + +/* Constants shared with the ARM identifying separate mailbox channels */ +#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ +#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ +#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ +#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ +#define MBOX_CHAN_COUNT 9 + +enum { + VCMSG_PROCESS_REQUEST = 0x00000000 +}; + +enum { + VCMSG_REQUEST_SUCCESSFUL = 0x80000000, + VCMSG_REQUEST_FAILED = 0x80000001 +}; + +/* Mailbox property tags */ +enum { + VCMSG_PROPERTY_END = 0x00000000, + VCMSG_GET_FIRMWARE_REVISION = 0x00000001, + VCMSG_GET_BOARD_MODEL = 0x00010001, + VCMSG_GET_BOARD_REVISION = 0x00010002, + VCMSG_GET_BOARD_MAC_ADDRESS = 0x00010003, + VCMSG_GET_BOARD_SERIAL = 0x00010004, + VCMSG_GET_ARM_MEMORY = 0x00010005, + VCMSG_GET_VC_MEMORY = 0x00010006, + VCMSG_GET_CLOCKS = 0x00010007, + VCMSG_GET_COMMAND_LINE = 0x00050001, + VCMSG_GET_DMA_CHANNELS = 0x00060001, + VCMSG_GET_POWER_STATE = 0x00020001, + VCMSG_GET_TIMING = 0x00020002, + VCMSG_SET_POWER_STATE = 0x00028001, + VCMSG_GET_CLOCK_STATE = 0x00030001, + VCMSG_SET_CLOCK_STATE = 0x00038001, + VCMSG_GET_CLOCK_RATE = 0x00030002, + VCMSG_SET_CLOCK_RATE = 0x00038002, + VCMSG_GET_VOLTAGE = 0x00030003, + VCMSG_SET_VOLTAGE = 0x00038003, + VCMSG_GET_MAX_CLOCK = 0x00030004, + VCMSG_GET_MAX_VOLTAGE = 0x00030005, + VCMSG_GET_TEMPERATURE = 0x00030006, + VCMSG_GET_MIN_CLOCK = 0x00030007, + VCMSG_GET_MIN_VOLTAGE = 0x00030008, + VCMSG_GET_TURBO = 0x00030009, + VCMSG_GET_MAX_TEMPERATURE = 0x0003000a, + VCMSG_GET_STC = 0x0003000b, + VCMSG_SET_TURBO = 0x00038009, + VCMSG_SET_ALLOCATE_MEM = 0x0003000c, + VCMSG_SET_LOCK_MEM = 0x0003000d, + VCMSG_SET_UNLOCK_MEM = 0x0003000e, + VCMSG_SET_RELEASE_MEM = 0x0003000f, + VCMSG_SET_EXECUTE_CODE = 0x00030010, + VCMSG_SET_EXECUTE_QPU = 0x00030011, + VCMSG_SET_ENABLE_QPU = 0x00030012, + VCMSG_GET_RESOURCE_HANDLE = 0x00030014, + VCMSG_GET_EDID_BLOCK = 0x00030020, + VCMSG_GET_CUSTOMER_OTP = 0x00030021, + VCMSG_SET_CUSTOMER_OTP = 0x00038021, + VCMSG_SET_ALLOCATE_BUFFER = 0x00040001, + VCMSG_SET_RELEASE_BUFFER = 0x00048001, + VCMSG_SET_BLANK_SCREEN = 0x00040002, + VCMSG_TST_BLANK_SCREEN = 0x00044002, + VCMSG_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + VCMSG_TST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + VCMSG_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + VCMSG_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + VCMSG_TST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + VCMSG_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + VCMSG_GET_DEPTH = 0x00040005, + VCMSG_TST_DEPTH = 0x00044005, + VCMSG_SET_DEPTH = 0x00048005, + VCMSG_GET_PIXEL_ORDER = 0x00040006, + VCMSG_TST_PIXEL_ORDER = 0x00044006, + VCMSG_SET_PIXEL_ORDER = 0x00048006, + VCMSG_GET_ALPHA_MODE = 0x00040007, + VCMSG_TST_ALPHA_MODE = 0x00044007, + VCMSG_SET_ALPHA_MODE = 0x00048007, + VCMSG_GET_PITCH = 0x00040008, + VCMSG_TST_PITCH = 0x00044008, + VCMSG_SET_PITCH = 0x00048008, + VCMSG_GET_VIRTUAL_OFFSET = 0x00040009, + VCMSG_TST_VIRTUAL_OFFSET = 0x00044009, + VCMSG_SET_VIRTUAL_OFFSET = 0x00048009, + VCMSG_GET_OVERSCAN = 0x0004000a, + VCMSG_TST_OVERSCAN = 0x0004400a, + VCMSG_SET_OVERSCAN = 0x0004800a, + VCMSG_GET_PALETTE = 0x0004000b, + VCMSG_TST_PALETTE = 0x0004400b, + VCMSG_SET_PALETTE = 0x0004800b, + VCMSG_GET_LAYER = 0x0004000c, + VCMSG_TST_LAYER = 0x0004400c, + VCMSG_SET_LAYER = 0x0004800c, + VCMSG_GET_TRANSFORM = 0x0004000d, + VCMSG_TST_TRANSFORM = 0x0004400d, + VCMSG_SET_TRANSFORM = 0x0004800d, + VCMSG_TST_VSYNC = 0x0004400e, + VCMSG_SET_VSYNC = 0x0004800e, + VCMSG_SET_CURSOR_INFO = 0x00008010, + VCMSG_SET_CURSOR_STATE = 0x00008011, +}; + +int bcm_mailbox_read(unsigned chan, uint32_t *data28); +int bcm_mailbox_write(unsigned chan, uint32_t data28); +int bcm_mailbox_property(void *data, int size); + +#endif