mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
BCM270x: Remove arch driver vcio.c
Remove the arch vcio.c driver and header file. Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
This commit is contained in:
committed by
popcornmix
parent
6ca78da96b
commit
ef87af464e
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* arch/arm/mach-bcm2708/include/mach/vcio.h
|
||||
*
|
||||
* 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 _MACH_BCM2708_VCIO_H
|
||||
#define _MACH_BCM2708_VCIO_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,
|
||||
};
|
||||
|
||||
extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28);
|
||||
extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28);
|
||||
extern int /*rc*/ bcm_mailbox_property(void *data, int size);
|
||||
|
||||
#endif
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* 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 <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <mach/vcio.h>
|
||||
|
||||
#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 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 + MAIL0_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_ATOMIC);
|
||||
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_DISABLED | 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");
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* arch/arm/mach-bcm2708/include/mach/vcio.h
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _MACH_BCM2708_VCIO_H
|
||||
#define _MACH_BCM2708_VCIO_H
|
||||
|
||||
/* Routines to handle I/O via the VideoCore "ARM control" registers
|
||||
* (semaphores, doorbells, mailboxes)
|
||||
*/
|
||||
|
||||
#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio"
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28);
|
||||
extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28);
|
||||
extern int /*rc*/ bcm_mailbox_property(void *data, int size);
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* The major device number. We can't rely on dynamic
|
||||
* registration any more, because ioctls need to know
|
||||
* it.
|
||||
*/
|
||||
#define MAJOR_NUM 100
|
||||
|
||||
/*
|
||||
* Set the message of the device driver
|
||||
*/
|
||||
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
|
||||
/*
|
||||
* _IOWR means that we're creating an ioctl command
|
||||
* number for passing information from a user process
|
||||
* to the kernel module and from the kernel module to user process
|
||||
*
|
||||
* The first arguments, MAJOR_NUM, is the major device
|
||||
* number we're using.
|
||||
*
|
||||
* The second argument is the number of the command
|
||||
* (there could be several with different meanings).
|
||||
*
|
||||
* The third argument is the type we want to get from
|
||||
* the process to the kernel.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The name of the device file
|
||||
*/
|
||||
#define DEVICE_FILE_NAME "vcio"
|
||||
|
||||
#endif
|
||||
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/vcio.h>
|
||||
#include <mach/platform.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
||||
#define DRIVER_NAME BCM_VCIO_DRIVER_NAME
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Mailbox
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
/* offsets from a mail box base address */
|
||||
#define MAIL_WRT 0x00 /* write - and next 4 words */
|
||||
#define MAIL_RD 0x00 /* read - and next 4 words */
|
||||
#define MAIL_POL 0x10 /* read without popping the fifo */
|
||||
#define MAIL_SND 0x14 /* sender ID (bottom two bits) */
|
||||
#define MAIL_STA 0x18 /* status */
|
||||
#define MAIL_CNF 0x1C /* configuration */
|
||||
|
||||
#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
|
||||
static struct class *vcio_class = NULL;
|
||||
struct vc_mailbox {
|
||||
struct device *dev; /* parent device */
|
||||
void __iomem *status;
|
||||
void __iomem *config;
|
||||
void __iomem *read;
|
||||
void __iomem *write;
|
||||
uint32_t msg[MBOX_CHAN_COUNT];
|
||||
struct semaphore sema[MBOX_CHAN_COUNT];
|
||||
uint32_t magic;
|
||||
};
|
||||
|
||||
static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev,
|
||||
uint32_t addr_mbox)
|
||||
{
|
||||
int i;
|
||||
|
||||
mbox_out->dev = dev;
|
||||
mbox_out->status = __io_address(addr_mbox + MAIL_STA);
|
||||
mbox_out->config = __io_address(addr_mbox + MAIL_CNF);
|
||||
mbox_out->read = __io_address(addr_mbox + MAIL_RD);
|
||||
/* Write to the other mailbox */
|
||||
mbox_out->write =
|
||||
__io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) +
|
||||
MAIL_WRT);
|
||||
|
||||
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->config);
|
||||
|
||||
mbox_out->magic = MBOX_MAGIC;
|
||||
}
|
||||
|
||||
static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (mbox->magic != MBOX_MAGIC)
|
||||
rc = -EINVAL;
|
||||
else {
|
||||
/* wait for the mailbox FIFO to have some space in it */
|
||||
while (0 != (readl(mbox->status) & ARM_MS_FULL))
|
||||
cpu_relax();
|
||||
|
||||
writel(MBOX_MSG(chan, data28), mbox->write);
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (mbox->magic != MBOX_MAGIC)
|
||||
rc = -EINVAL;
|
||||
else {
|
||||
down(&mbox->sema[chan]);
|
||||
*data28 = MBOX_DATA28(mbox->msg[chan]);
|
||||
mbox->msg[chan] = 0;
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static irqreturn_t mbox_irq(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->status);
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
while (!(status & ARM_MS_EMPTY)) {
|
||||
uint32_t msg = readl(mbox->read);
|
||||
int chan = MBOX_CHAN(msg);
|
||||
if (chan < MBOX_CHAN_COUNT) {
|
||||
if (mbox->msg[chan]) {
|
||||
/* Overflow */
|
||||
printk(KERN_ERR DRIVER_NAME
|
||||
": mbox chan %d overflow - drop %08x\n",
|
||||
chan, msg);
|
||||
} else {
|
||||
mbox->msg[chan] = (msg | 0xf);
|
||||
up(&mbox->sema[chan]);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR DRIVER_NAME
|
||||
": invalid channel selector (msg %08x)\n", msg);
|
||||
}
|
||||
ret = IRQ_HANDLED;
|
||||
status = readl(mbox->status);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct irqaction mbox_irqaction = {
|
||||
.name = "ARM Mailbox IRQ",
|
||||
.flags = IRQF_DISABLED | IRQF_IRQPOLL,
|
||||
.handler = mbox_irq,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* 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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct vc_mailbox *mailbox = dev_get_drvdata(dev);
|
||||
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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct vc_mailbox *mailbox = dev_get_drvdata(dev);
|
||||
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 dev_mbox_write(mbox_dev, chan, data28);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcm_mailbox_write);
|
||||
|
||||
extern int bcm_mailbox_read(unsigned chan, uint32_t *data28)
|
||||
{
|
||||
if (mbox_dev)
|
||||
return dev_mbox_read(mbox_dev, chan, data28);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcm_mailbox_read);
|
||||
|
||||
static void dev_mbox_register(const char *dev_name, struct device *dev)
|
||||
{
|
||||
mbox_dev = dev;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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_ATOMIC);
|
||||
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)
|
||||
printk(KERN_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 int Device_Open = 0;
|
||||
|
||||
/*
|
||||
* 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_Open)
|
||||
return -EBUSY;
|
||||
|
||||
Device_Open++;
|
||||
/*
|
||||
* Initialize the message
|
||||
*/
|
||||
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_Open--;
|
||||
|
||||
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, /* see include/linux/fs.h */
|
||||
unsigned int ioctl_num, /* number and param for ioctl */
|
||||
unsigned long ioctl_param)
|
||||
{
|
||||
unsigned size;
|
||||
/*
|
||||
* Switch according to the ioctl called
|
||||
*/
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_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.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
struct vc_mailbox *mailbox;
|
||||
|
||||
mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL);
|
||||
if (NULL == mailbox) {
|
||||
printk(KERN_ERR DRIVER_NAME ": failed to allocate "
|
||||
"mailbox memory\n");
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
printk(KERN_ERR DRIVER_NAME ": failed to obtain memory "
|
||||
"resource\n");
|
||||
ret = -ENODEV;
|
||||
kfree(mailbox);
|
||||
} else {
|
||||
/* should be based on the registers from res really */
|
||||
mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD);
|
||||
|
||||
platform_set_drvdata(pdev, mailbox);
|
||||
dev_mbox_register(DRIVER_NAME, &pdev->dev);
|
||||
|
||||
mbox_irqaction.dev_id = mailbox;
|
||||
setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction);
|
||||
printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n",
|
||||
__io_address(ARM_0_MAIL0_RD));
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* Register the character device
|
||||
*/
|
||||
ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops);
|
||||
|
||||
/*
|
||||
* Negative values signify an error
|
||||
*/
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR DRIVER_NAME
|
||||
"Failed registering the character device %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
vcio_class = class_create(THIS_MODULE, BCM_VCIO_DRIVER_NAME);
|
||||
if (IS_ERR(vcio_class)) {
|
||||
ret = PTR_ERR(vcio_class);
|
||||
return ret ;
|
||||
}
|
||||
device_create(vcio_class, NULL, MKDEV(MAJOR_NUM, 0), NULL,
|
||||
"vcio");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_vcio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vc_mailbox *mailbox = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(mailbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm_mbox_driver = {
|
||||
.probe = bcm_vcio_probe,
|
||||
.remove = bcm_vcio_remove,
|
||||
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init bcm_mbox_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n");
|
||||
|
||||
ret = platform_driver_register(&bcm_mbox_driver);
|
||||
if (ret != 0) {
|
||||
printk(KERN_ERR DRIVER_NAME ": failed to register "
|
||||
"on platform\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit bcm_mbox_exit(void)
|
||||
{
|
||||
device_destroy(vcio_class,MKDEV(MAJOR_NUM, 0));
|
||||
class_destroy(vcio_class);
|
||||
unregister_chrdev(MAJOR_NUM, DEVICE_FILE_NAME);
|
||||
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");
|
||||
MODULE_ALIAS("platform:bcm-mbox");
|
||||
Reference in New Issue
Block a user