Skip to content

Instantly share code, notes, and snippets.

@notro
Created April 25, 2015 12:31
Show Gist options
  • Save notro/dbb63a021d8ea575ddb5 to your computer and use it in GitHub Desktop.
Save notro/dbb63a021d8ea575ddb5 to your computer and use it in GitHub Desktop.
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index c706448..9f4ed2f 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -45,7 +45,7 @@
clock-frequency = <100000>;
};
-&sdhci {
+&mmc {
status = "okay";
bus-width = <4>;
};
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 3342cb1..46dc89b 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -22,8 +22,9 @@
clock-frequency = <1000000>;
};
- dma: dma@7e007000 {
- compatible = "brcm,bcm2835-dma";
+ /* Legacy DMA driver */
+ dma@7e007000 {
+ compatible = "brcm,bcm2708-dma";
reg = <0x7e007000 0xf00>;
interrupts = <1 16>,
<1 17>,
@@ -38,7 +39,10 @@
<1 26>,
<1 27>,
<1 28>;
+ };
+ dma: dmaengine {
+ compatible = "brcm,bcm2835-dma";
#dma-cells = <1>;
brcm,dma-channel-mask = <0x7f35>;
};
@@ -60,6 +64,12 @@
reg = <0x7e104000 0x10>;
};
+ mailbox@7e00b800 {
+ compatible = "brcm,bcm2708-mbox";
+ reg = <0x7e00b880 0x40>;
+ interrupts = <0 1>;
+ };
+
gpio: gpio@7e200000 {
compatible = "brcm,bcm2835-gpio";
reg = <0x7e200000 0xb4>;
@@ -130,6 +140,17 @@
status = "disabled";
};
+ mmc: mmc@7e300000 {
+ compatible = "brcm,bcm2835-mmc";
+ reg = <0x7e300000 0x100>;
+ interrupts = <2 30>;
+ clocks = <&clk_mmc>;
+ dmas = <&dma 11>,
+ <&dma 11>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
@@ -140,15 +161,69 @@
status = "disabled";
};
- usb@7e980000 {
- compatible = "brcm,bcm2835-usb";
- reg = <0x7e980000 0x10000>;
- interrupts = <1 9>;
+// usb@7e980000 {
+// compatible = "brcm,bcm2835-usb";
+// reg = <0x7e980000 0x10000>;
+// interrupts = <1 9>;
+// };
+
+ usb: usb@7e980000 {
+ compatible = "brcm,bcm2708-usb";
+ reg = <0x7e980000 0x10000>,
+ <0x7e006000 0x1000>;
+ interrupts = <2 0>,
+ <1 9>;
};
arm-pmu {
compatible = "arm,arm1176-pmu";
};
+
+ thermal {
+ compatible = "brcm,bcm2835-thermal";
+ };
+
+ fb: fb {
+ compatible = "brcm,bcm2708-fb";
+ };
+
+ vchiq: vchiq {
+ compatible = "brcm,bcm2835-vchiq";
+ reg = <0x7e00b800 0x50>;
+ interrupts = <0 2>;
+ };
+
+ audio@0 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@1 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@2 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@3 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@4 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@5 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@6 {
+ compatible = "brcm,bcm2835-audio";
+ };
+
+ audio@7 {
+ compatible = "brcm,bcm2835-audio";
+ };
};
clocks {
@@ -161,7 +236,7 @@
reg = <0>;
#clock-cells = <0>;
clock-output-names = "mmc";
- clock-frequency = <100000000>;
+ clock-frequency = <250000000>;
};
clk_i2c: clock@1 {
diff --git a/drivers/char/broadcom/Kconfig b/drivers/char/broadcom/Kconfig
index fd23e00..c352616 100644
--- a/drivers/char/broadcom/Kconfig
+++ b/drivers/char/broadcom/Kconfig
@@ -20,3 +20,10 @@ config BCM_VC_SM
help
Support for the VC shared memory on the Broadcom reference
design. Uses the VCHIQ stack.
+
+config BCM2708_VCMEM_2
+ bool "Videocore Memory"
+ depends on BRCM_CHAR_DRIVERS && BCM2708_MBOX
+# default y if (MACH_BCM2708 || MACH_BCM2709)
+ help
+ Helper for videocore memory access and total size allocation.
diff --git a/drivers/char/broadcom/Makefile b/drivers/char/broadcom/Makefile
index 0bf7fdf..c43f1e6 100644
--- a/drivers/char/broadcom/Makefile
+++ b/drivers/char/broadcom/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_BCM_VC_CMA) += vc_cma/
obj-$(CONFIG_BCM_VC_SM) += vc_sm/
+obj-$(CONFIG_BCM2708_VCMEM_2) += vc_mem.o
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 2395f63..e0140fa 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -339,9 +339,13 @@ config DMA_BCM2835
config DMA_BCM2708
tristate "BCM2708 DMA engine support"
- depends on MACH_BCM2708 || MACH_BCM2709
+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
+ select DMA_BCM2708_LEGACY if ARCH_BCM2835
+
+config DMA_BCM2708_LEGACY
+ bool
config TI_CPPI41
tristate "AM33xx CPPI41 DMA support"
@@ -381,7 +385,7 @@ config MOXART_DMA
select DMA_VIRTUAL_CHANNELS
help
Enable support for the MOXA ART SoC DMA controller.
-
+
config FSL_EDMA
tristate "Freescale eDMA engine support"
depends on OF
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index e2c0cb1..f930022 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
obj-$(CONFIG_DMA_BCM2708) += bcm2708-dmaengine.o
+obj-$(CONFIG_DMA_BCM2708_LEGACY) += bcm2708-dma.o
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
obj-$(CONFIG_TI_CPPI41) += cppi41.o
diff --git a/drivers/dma/bcm2708-dmaengine.c b/drivers/dma/bcm2708-dmaengine.c
index 8182b16..3c85bbd 100644
--- a/drivers/dma/bcm2708-dmaengine.c
+++ b/drivers/dma/bcm2708-dmaengine.c
@@ -42,8 +42,9 @@
#include <linux/io.h>
#include <linux/spinlock.h>
-#ifndef CONFIG_ARCH_BCM2835
-
+#ifdef CONFIG_ARCH_BCM2835
+#include <linux/platform_data/dma-bcm2708.h>
+#else
/* dma manager */
#include <mach/dma.h>
@@ -709,7 +710,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan)
return 0;
}
-#ifdef CONFIG_ARCH_BCM2835
+#if 0 /* #ifdef CONFIG_ARCH_BCM2835 */
static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq)
{
struct bcm2835_chan *c;
@@ -787,7 +788,7 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
static int bcm2835_dma_probe(struct platform_device *pdev)
{
struct bcm2835_dmadev *od;
-#ifdef CONFIG_ARCH_BCM2835
+#if 0 /* #ifdef CONFIG_ARCH_BCM2835 */
struct resource *res;
void __iomem *base;
uint32_t chans_available;
@@ -803,7 +804,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
/* If CONFIG_ARCH_BCM2835 is selected, device tree is used */
/* hence the difference between probing */
-#ifndef CONFIG_ARCH_BCM2835
+#if 1 /* #ifndef CONFIG_ARCH_BCM2835 */
rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (rc)
@@ -853,6 +854,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq);
if (rc)
goto err_no_dma;
+dev_info(&pdev->dev, "Channel: %d\n", chan_id);
}
if (pdev->dev.of_node) {
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 84325f2..fdcb206 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -6,6 +6,12 @@ menuconfig MAILBOX
signals. Say Y if your platform supports hardware mailboxes.
if MAILBOX
+config BCM2708_MBOX
+ bool "Broadcom BCM2708 Mailbox (vcio)"
+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835
+ help
+ Broadcom BCM2708 Mailbox (vcio)
+
config PL320_MBOX
bool "ARM PL320 Mailbox"
depends on ARM_AMBA
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 2e79231..c2d2bed 100644
--- 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_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig
index b94e6cd..b4198c2 100644
--- a/drivers/misc/vc04_services/Kconfig
+++ b/drivers/misc/vc04_services/Kconfig
@@ -1,6 +1,6 @@
config BCM2708_VCHIQ
tristate "Videocore VCHIQ"
- depends on MACH_BCM2708 || MACH_BCM2709
+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835
default y
help
Kernel to VideoCore communication interface for the
diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 7e7b09f..5789434 100644
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -43,14 +43,34 @@
#include <linux/uaccess.h>
#include <asm/pgtable.h>
+#ifdef CONFIG_ARCH_BCM2835
+
+#include <linux/platform_data/mailbox-bcm2708.h>
+
+static void __iomem *regs;
+static unsigned int irq;
+
+#define __io_address(a) (regs + a)
+#define TO_VC_PHYS(a) (0x40000000 | (a))
+
+#define ARM_0_BELL2 0x48
+#define ARM_0_BELL0 0x40
+
+#else /* CONFIG_ARCH_BCM2835 */
+
#include <mach/irqs.h>
#include <mach/platform.h>
#include <mach/vcio.h>
+static unsigned int irq = IRQ_ARM_DOORBELL_0;
+
+#define TO_VC_PHYS(a) (a)
+
+#endif /* CONFIG_ARCH_BCM2835 */
+
#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32)
-#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0
#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x))
#include "vchiq_arm.h"
@@ -87,19 +107,25 @@ static void
free_pagelist(PAGELIST_T *pagelist, int actual);
int __init
-vchiq_platform_init(VCHIQ_STATE_T *state)
+vchiq_platform_init(VCHIQ_STATE_T *state, void __iomem *a_regs, unsigned int a_irq)
{
VCHIQ_SLOT_ZERO_T *vchiq_slot_zero;
int frag_mem_size;
int err;
int i;
+#ifdef CONFIG_ARCH_BCM2835
+ regs = a_regs;
+ irq = a_irq;
+#endif
+
/* Allocate space for the channels in coherent memory */
g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE);
frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS);
+printk("g_slot_mem_size = %d, frag_mem_size = %d\n", g_slot_mem_size, frag_mem_size);
g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size,
- &g_slot_phys, GFP_ATOMIC);
+ &g_slot_phys, GFP_KERNEL);
if (!g_slot_mem) {
vchiq_log_error(vchiq_arm_log_level,
@@ -138,13 +164,13 @@ vchiq_platform_init(VCHIQ_STATE_T *state)
goto failed_vchiq_init;
}
- err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq,
+ err = request_irq(irq, vchiq_doorbell_irq,
IRQF_IRQPOLL, "VCHIQ doorbell",
state);
if (err < 0) {
vchiq_log_error(vchiq_arm_log_level, "%s: failed to register "
"irq=%d err=%d", __func__,
- VCHIQ_DOORBELL_IRQ, err);
+ irq, err);
goto failed_request_irq;
}
@@ -152,7 +178,7 @@ vchiq_platform_init(VCHIQ_STATE_T *state)
dsb(); /* Ensure all writes have completed */
- bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys);
+ bcm_mailbox_write(MBOX_CHAN_VCHIQ, TO_VC_PHYS((unsigned int)g_slot_phys));
vchiq_log_info(vchiq_arm_log_level,
"vchiq_init - done (slots %x, phys %x)",
@@ -174,7 +200,7 @@ failed_alloc:
void __exit
vchiq_platform_exit(VCHIQ_STATE_T *state)
{
- free_irq(VCHIQ_DOORBELL_IRQ, state);
+ free_irq(irq, state);
dma_free_coherent(NULL, g_slot_mem_size,
g_slot_mem, g_slot_phys);
}
diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
index 0ad9656..373ef80 100644
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -42,6 +42,7 @@
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
+#include <linux/platform_device.h>
#include <linux/bug.h>
#include <linux/semaphore.h>
#include <linux/list.h>
@@ -2798,7 +2799,7 @@ void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state,
***************************************************************************/
static int __init
-vchiq_init(void)
+vchiq_init_common(void __iomem *regs, unsigned int irq)
{
int err;
void *ptr_err;
@@ -2835,7 +2836,7 @@ vchiq_init(void)
if (IS_ERR(ptr_err))
goto failed_device_create;
- err = vchiq_platform_init(&g_state);
+ err = vchiq_platform_init(&g_state, regs, irq);
if (err != 0)
goto failed_platform_init;
@@ -2878,7 +2879,66 @@ vchiq_exit(void)
unregister_chrdev_region(vchiq_devid, 1);
}
+#ifdef CONFIG_ARCH_BCM2835
+
+static int
+vchiq_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ void __iomem *regs;
+ unsigned int irq;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "failed to get IRQ\n");
+ return -ENODEV;
+ }
+
+ return vchiq_init_common(regs, irq);
+}
+
+static int
+vchiq_remove(struct platform_device *pdev)
+{
+ vchiq_exit();
+
+ return 0;
+}
+
+static const struct of_device_id vchiq_of_match[] = {
+ { .compatible = "brcm,bcm2835-vchiq", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vchiq_of_match);
+
+static struct platform_driver vchiq_driver = {
+ .driver = {
+ .name = "vchiq",
+ .owner = THIS_MODULE,
+ .of_match_table = vchiq_of_match,
+ },
+ .probe = vchiq_probe,
+ .remove = vchiq_remove,
+};
+module_platform_driver(vchiq_driver);
+
+#else /* CONFIG_ARCH_BCM2835 */
+
+static int __init
+vchiq_init(void)
+{
+ vchiq_init_common(NULL, 0);
+}
+
module_init(vchiq_init);
module_exit(vchiq_exit);
+
+#endif /* CONFIG_ARCH_BCM2835 */
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h
index d1e2741..e2d3a19 100644
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h
@@ -129,7 +129,7 @@ extern int vchiq_arm_log_level;
extern int vchiq_susp_log_level;
extern int __init
-vchiq_platform_init(VCHIQ_STATE_T *state);
+vchiq_platform_init(VCHIQ_STATE_T *state, void __iomem *base, unsigned int irq);
extern void __exit
vchiq_platform_exit(VCHIQ_STATE_T *state);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index b4928ec..bd7ecf9 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -6,7 +6,7 @@ comment "MMC/SD/SDIO Host Controller Drivers"
config MMC_BCM2835
tristate "MMC support on BCM2835"
- depends on MACH_BCM2708
+ depends on MACH_BCM2708 || ARCH_BCM2835
help
This selects the MMC Interface on BCM2835.
diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c
index 85fceb5..092b6db 100644
--- a/drivers/thermal/bcm2835-thermal.c
+++ b/drivers/thermal/bcm2835-thermal.c
@@ -1,3 +1,4 @@
+#define THERMAL_DEBUG_ENABLE
/*****************************************************************************
* Copyright 2011 Broadcom Corporation. All rights reserved.
*
@@ -18,9 +19,14 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
-#include <mach/vcio.h>
#include <linux/thermal.h>
+#ifdef CONFIG_ARCH_BCM2835
+#include <linux/platform_data/mailbox-bcm2708.h>
+#else
+#include <mach/vcio.h>
+#endif
+
/* --- DEFINITIONS --- */
#define MODULE_NAME "bcm2835_thermal"
@@ -167,12 +173,18 @@ static struct thermal_zone_device_ops ops = {
.get_mode = bcm2835_get_mode,
};
-/* Thermal Driver */
+static const struct of_device_id bcm2835_thermal_of_match_table[] = {
+ { .compatible = "brcm,bcm2835-thermal", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
+
static struct platform_driver bcm2835_thermal_driver = {
.probe = bcm2835_thermal_probe,
.remove = bcm2835_thermal_remove,
.driver = {
.name = "bcm2835_thermal",
+ .of_match_table = bcm2835_thermal_of_match_table,
.owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
index dc7cd32..d0a3bf8 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
@@ -240,10 +240,15 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = {
.adp_enable = -1,
};
+#ifndef CONFIG_ARCH_BCM2835
//Global variable to switch the fiq fix on or off
bool fiq_enable = 1;
// Global variable to enable the split transaction fix
bool fiq_fsm_enable = true;
+#else
+bool fiq_enable = 0;
+bool fiq_fsm_enable = false;
+#endif
//Bulk split-transaction NAK holdoff in microframes
uint16_t nak_holdoff = 8;
@@ -1043,9 +1048,16 @@ static struct platform_device_id platform_ids[] = {
};
MODULE_DEVICE_TABLE(platform, platform_ids);
+static const struct of_device_id dwc_otg_of_match_table[] = {
+ { .compatible = "brcm,bcm2708-usb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dwc_otg_of_match_table);
+
static struct platform_driver dwc_otg_driver = {
.driver = {
.name = (char *)dwc_driver_name,
+ .of_match_table = dwc_otg_of_match_table,
},
.id_table = platform_ids,
@@ -1095,9 +1107,12 @@ static int __init dwc_otg_driver_init(void)
printk(KERN_ERR "%s retval=%d\n", __func__, retval);
return retval;
}
- printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
- printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
- printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
+// printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
+// printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
+// printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
+ printk("dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
+ printk("dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
+ printk("dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
error = driver_create_file(drv, &driver_attr_version);
#ifdef DEBUG
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
index 8a31562..66ba1ae 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
@@ -36,9 +36,11 @@
#include "dwc_otg_regs.h"
#include <linux/jiffies.h>
+
+#ifndef CONFIG_ARCH_BCM2835
#include <mach/hardware.h>
#include <asm/fiq.h>
-
+#endif
extern bool microframe_schedule;
@@ -2601,7 +2603,7 @@ void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
break;
default:
- DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x",
+ DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x",
hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num);
qtd->error_count++;
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
index 1d28459..344272a 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
@@ -393,6 +393,10 @@ static struct dwc_otg_hcd_function_ops hcd_fops = {
.get_b_hnp_enable = _get_b_hnp_enable,
};
+#ifdef CONFIG_ARCH_BCM2835
+static void hcd_init_fiq(void *cookie) { }
+#else
+
static struct fiq_handler fh = {
.name = "usb_fiq",
};
@@ -448,6 +452,7 @@ static void hcd_init_fiq(void *cookie)
enable_fiq(INTERRUPT_VC_USB);
local_fiq_enable();
}
+#endif
/**
* Initializes the HCD. This function allocates memory for and initializes the
diff --git a/drivers/video/fbdev/bcm2708_fb.c b/drivers/video/fbdev/bcm2708_fb.c
index f632750..b247950 100644
--- a/drivers/video/fbdev/bcm2708_fb.c
+++ b/drivers/video/fbdev/bcm2708_fb.c
@@ -30,19 +30,24 @@
#include <linux/console.h>
#include <linux/debugfs.h>
+#ifdef CONFIG_ARCH_BCM2835
+#include <linux/platform_data/dma-bcm2708.h>
+#include <linux/platform_data/mailbox-bcm2708.h>
+#else
#include <mach/dma.h>
#include <mach/platform.h>
#include <mach/vcio.h>
+#endif
#include <asm/sizes.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
-//#define BCM2708_FB_DEBUG
+#define BCM2708_FB_DEBUG
#define MODULE_NAME "bcm2708_fb"
#ifdef BCM2708_FB_DEBUG
-#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
+#define print_debug(fmt,...) pr_info("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
#else
#define print_debug(fmt,...)
#endif
@@ -295,7 +300,12 @@ static int bcm2708_fb_set_par(struct fb_info *info)
wmb();
/* inform vc about new framebuffer */
+#ifdef CONFIG_ARCH_BCM2835
+#define TO_VC_PHYS(a) (0x40000000 | (a))
+ bcm_mailbox_write(MBOX_CHAN_FB, TO_VC_PHYS(fb->dma));
+#else
bcm_mailbox_write(MBOX_CHAN_FB, fb->dma);
+#endif
/* TODO: replace fb driver with vchiq version */
/* wait for response */
@@ -313,6 +323,21 @@ static int bcm2708_fb_set_par(struct fb_info *info)
fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
fb->fb_bus_address = fbinfo->base;
+#ifdef CONFIG_ARCH_BCM2835
+#define FROM_VC_PHYS(a) (0x3fffffff & (a))
+ fb->fb.fix.smem_start = FROM_VC_PHYS(fbinfo->base);
+ fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual;
+ fb->fb.screen_size = fbinfo->screen_size;
+pr_info("fbinfo->base = %p\n", (void *)fbinfo->base);
+pr_info("fb->fb.fix.smem_start= %p\n", (void *)fb->fb.fix.smem_start);
+pr_info("fb->fb.fix.smem_len = %d\n", fb->fb.fix.smem_len);
+pr_info("fb->fb.screen_size = %lu\n", fb->fb.screen_size);
+ if (fb->fb.screen_base)
+ iounmap(fb->fb.screen_base);
+ fb->fb.screen_base = (void *)ioremap_wc(
+ fb->fb.fix.smem_start,
+ fb->fb.screen_size);
+#else
fbinfo->base &= ~0xc0000000;
fb->fb.fix.smem_start = fbinfo->base;
fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual;
@@ -321,12 +346,12 @@ static int bcm2708_fb_set_par(struct fb_info *info)
iounmap(fb->fb.screen_base);
fb->fb.screen_base =
(void *)ioremap_wc(fbinfo->base, fb->fb.screen_size);
+#endif
if (!fb->fb.screen_base) {
/* the console may currently be locked */
console_trylock();
console_unlock();
-
- BUG(); /* what can we do here */
+ return -EIO;
}
}
print_debug
@@ -677,9 +702,11 @@ static int bcm2708_fb_register(struct bcm2708_fb *fb)
*/
fb_set_var(&fb->fb, &fb->fb.var);
- bcm2708_fb_set_par(&fb->fb);
+ ret = bcm2708_fb_set_par(&fb->fb);
+ if (ret)
+ return ret;
- print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n", fbwidth
+ print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n", fbwidth,
fbheight, fbdepth, fbswap);
ret = register_framebuffer(&fb->fb);
@@ -781,11 +808,18 @@ static int bcm2708_fb_remove(struct platform_device *dev)
return 0;
}
+static const struct of_device_id bcm2708_fb_of_match_table[] = {
+ { .compatible = "brcm,bcm2708-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_fb_of_match_table);
+
static struct platform_driver bcm2708_fb_driver = {
.probe = bcm2708_fb_probe,
.remove = bcm2708_fb_remove,
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = bcm2708_fb_of_match_table,
.owner = THIS_MODULE,
},
};
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index ada7ba2..3fb42e2 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -41,7 +41,7 @@ config SND_PXA2XX_AC97
config SND_BCM2835
tristate "BCM2835 ALSA driver"
- depends on (ARCH_BCM2708 || ARCH_BCM2709) && BCM2708_VCHIQ && SND
+ depends on (ARCH_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835) && BCM2708_VCHIQ && SND
select SND_PCM
help
Say Y or M if you want to support BCM2835 Alsa pcm card driver
diff --git a/sound/arm/bcm2835.c b/sound/arm/bcm2835.c
old mode 100755
new mode 100644
index 7ed5079..95abab6
--- a/sound/arm/bcm2835.c
+++ b/sound/arm/bcm2835.c
@@ -1,3 +1,5 @@
+#define AUDIO_DEBUG_ENABLE
+#define AUDIO_VERBOSE_DEBUG_ENABLE
/*****************************************************************************
* Copyright 2011 Broadcom Corporation. All rights reserved.
*
@@ -224,6 +226,12 @@ static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
#endif
+static const struct of_device_id snd_bcm2835_of_match_table[] = {
+ { .compatible = "brcm,bcm2835-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
+
static struct platform_driver bcm2835_alsa0_driver = {
.probe = snd_bcm2835_alsa_probe,
.remove = snd_bcm2835_alsa_remove,
@@ -233,6 +241,7 @@ static struct platform_driver bcm2835_alsa0_driver = {
#endif
.driver = {
.name = "bcm2835_AUD0",
+ .of_match_table = snd_bcm2835_of_match_table,
.owner = THIS_MODULE,
},
};
diff --git a/include/linux/platform_data/dma-bcm2708.h b/include/linux/platform_data/dma-bcm2708.h
new file mode 100644
index 0000000..ff3d388
--- /dev/null
+++ b/include/linux/platform_data/dma-bcm2708.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef _PLAT_BCM2708_DMA_H
+#define _PLAT_BCM2708_DMA_H
+
+/* DMA CS Control and Status bits */
+#define BCM2708_DMA_ACTIVE (1 << 0)
+#define BCM2708_DMA_INT (1 << 2)
+#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
+#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
+#define BCM2708_DMA_ERR (1 << 8)
+#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
+#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
+
+/* DMA control block "info" field bits */
+#define BCM2708_DMA_INT_EN (1 << 0)
+#define BCM2708_DMA_TDMODE (1 << 1)
+#define BCM2708_DMA_WAIT_RESP (1 << 3)
+#define BCM2708_DMA_D_INC (1 << 4)
+#define BCM2708_DMA_D_WIDTH (1 << 5)
+#define BCM2708_DMA_D_DREQ (1 << 6)
+#define BCM2708_DMA_S_INC (1 << 8)
+#define BCM2708_DMA_S_WIDTH (1 << 9)
+#define BCM2708_DMA_S_DREQ (1 << 10)
+
+#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12)
+#define BCM2708_DMA_PER_MAP(x) ((x) << 16)
+#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21)
+
+#define BCM2708_DMA_DREQ_EMMC 11
+#define BCM2708_DMA_DREQ_SDHOST 13
+
+#define BCM2708_DMA_CS 0x00 /* Control and Status */
+#define BCM2708_DMA_ADDR 0x04
+/* the current control block appears in the following registers - read only */
+#define BCM2708_DMA_INFO 0x08
+#define BCM2708_DMA_SOURCE_AD 0x0c
+#define BCM2708_DMA_DEST_AD 0x10
+#define BCM2708_DMA_NEXTCB 0x1C
+#define BCM2708_DMA_DEBUG 0x20
+
+#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS)
+#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR)
+
+#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w))
+
+struct bcm2708_dma_cb {
+ unsigned long info;
+ unsigned long src;
+ unsigned long dst;
+ unsigned long length;
+ unsigned long stride;
+ unsigned long next;
+ unsigned long pad[2];
+};
+
+struct scatterlist;
+
+extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
+extern void bcm_dma_start(void __iomem *dma_chan_base,
+ dma_addr_t control_block);
+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base);
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base);
+extern int bcm_dma_abort(void __iomem *dma_chan_base);
+
+/* When listing features we can ask for when allocating DMA channels give
+ those with higher priority smaller ordinal numbers */
+#define BCM_DMA_FEATURE_FAST_ORD 0
+#define BCM_DMA_FEATURE_BULK_ORD 1
+#define BCM_DMA_FEATURE_NORMAL_ORD 2
+#define BCM_DMA_FEATURE_LITE_ORD 3
+#define BCM_DMA_FEATURE_FAST (1 << BCM_DMA_FEATURE_FAST_ORD)
+#define BCM_DMA_FEATURE_BULK (1 << BCM_DMA_FEATURE_BULK_ORD)
+#define BCM_DMA_FEATURE_NORMAL (1 << BCM_DMA_FEATURE_NORMAL_ORD)
+#define BCM_DMA_FEATURE_LITE (1 << BCM_DMA_FEATURE_LITE_ORD)
+#define BCM_DMA_FEATURE_COUNT 4
+
+/* return channel no or -ve error */
+extern int bcm_dma_chan_alloc(unsigned preferred_feature_set,
+ void __iomem **out_dma_base, int *out_dma_irq);
+extern int bcm_dma_chan_free(int channel);
+
+
+#endif /* _PLAT_BCM2708_DMA_H */
diff --git a/drivers/dma/bcm2708-dma.c b/drivers/dma/bcm2708-dma.c
new file mode 100644
index 0000000..89113c4
--- /dev/null
+++ b/drivers/dma/bcm2708-dma.c
@@ -0,0 +1,488 @@
+/*
+ * linux/arch/arm/mach-bcm2708/dma.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.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+
+
+#include <linux/platform_data/dma-bcm2708.h>
+
+//#include <mach/dma.h>
+//#include <mach/irqs.h>
+
+/*****************************************************************************\
+ * *
+ * Configuration *
+ * *
+\*****************************************************************************/
+
+#define CACHE_LINE_MASK 31
+#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME
+#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */
+
+/* valid only for channels 0 - 14, 15 has its own base address */
+#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */
+#define BCM2708_DMA_CHANIO(dma_base, n) \
+ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n)))
+
+
+/*****************************************************************************\
+ * *
+ * dma.h *
+ * *
+\*****************************************************************************/
+
+#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma"
+
+
+/*****************************************************************************\
+ * *
+ * irq.h *
+ * *
+\*****************************************************************************/
+
+// This should be passed using device resources. Not hardcoded in the driver
+
+#define ARM_IRQ1_BASE 0
+#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16)
+#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17)
+
+#ifndef CONFIG_ARCH_BCM2835
+#define INTERRUPT_DMA2 (ARM_IRQ1_BASE + 18)
+#define INTERRUPT_DMA3 (ARM_IRQ1_BASE + 19)
+#else
+#define ARM_IRQ0_BASE 64
+#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13)
+#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14)
+#endif
+
+#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20)
+#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21)
+#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22)
+#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23)
+#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24)
+#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25)
+#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26)
+#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27)
+#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28)
+
+#define IRQ_ARMCTRL_START 0
+#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0)
+#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1)
+#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2)
+#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3)
+#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4)
+#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5)
+#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6)
+#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7)
+#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8)
+#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9)
+#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10)
+#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11)
+#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12)
+
+/*****************************************************************************\
+ * *
+ * DMA Auxilliary Functions *
+ * *
+\*****************************************************************************/
+
+/* A DMA buffer on an arbitrary boundary may separate a cache line into a
+ section inside the DMA buffer and another section outside it.
+ Even if we flush DMA buffers from the cache there is always the chance that
+ during a DMA someone will access the part of a cache line that is outside
+ the DMA buffer - which will then bring in unwelcome data.
+ Without being able to dictate our own buffer pools we must insist that
+ DMA buffers consist of a whole number of cache lines.
+*/
+
+extern int
+bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len)
+{
+ int i;
+
+ for (i = 0; i < sg_len; i++) {
+ if (sg_ptr[i].offset & CACHE_LINE_MASK ||
+ sg_ptr[i].length & CACHE_LINE_MASK)
+ return 0;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma);
+
+extern void
+bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block)
+{
+ dsb(); /* ARM data synchronization (push) operation */
+
+ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR);
+ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS);
+}
+
+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base)
+{
+ dsb();
+
+ /* ugly busy wait only option for now */
+ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE)
+ cpu_relax();
+}
+
+EXPORT_SYMBOL_GPL(bcm_dma_start);
+
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
+{
+ dsb();
+
+ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
+
+/* Complete an ongoing DMA (assuming its results are to be ignored)
+ Does nothing if there is no DMA in progress.
+ This routine waits for the current AXI transfer to complete before
+ terminating the current DMA. If the current transfer is hung on a DREQ used
+ by an uncooperative peripheral the AXI transfer may never complete. In this
+ case the routine times out and return a non-zero error code.
+ Use of this routine doesn't guarantee that the ongoing or aborted DMA
+ does not produce an interrupt.
+*/
+extern int
+bcm_dma_abort(void __iomem *dma_chan_base)
+{
+ unsigned long int cs;
+ int rc = 0;
+
+ cs = readl(dma_chan_base + BCM2708_DMA_CS);
+
+ if (BCM2708_DMA_ACTIVE & cs) {
+ long int timeout = 10000;
+
+ /* write 0 to the active bit - pause the DMA */
+ writel(0, dma_chan_base + BCM2708_DMA_CS);
+
+ /* wait for any current AXI transfer to complete */
+ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0)
+ cs = readl(dma_chan_base + BCM2708_DMA_CS);
+
+ if (0 != (cs & BCM2708_DMA_ISPAUSED)) {
+ /* we'll un-pause when we set of our next DMA */
+ rc = -ETIMEDOUT;
+
+ } else if (BCM2708_DMA_ACTIVE & cs) {
+ /* terminate the control block chain */
+ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB);
+
+ /* abort the whole DMA */
+ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE,
+ dma_chan_base + BCM2708_DMA_CS);
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_abort);
+
+
+/***************************************************************************** \
+ * *
+ * DMA Manager Device Methods *
+ * *
+\*****************************************************************************/
+
+struct vc_dmaman {
+ void __iomem *dma_base;
+ u32 chan_available; /* bitmap of available channels */
+ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */
+};
+
+static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base,
+ u32 chans_available)
+{
+ dmaman->dma_base = dma_base;
+ dmaman->chan_available = chans_available;
+ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */
+ dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */
+ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */
+ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */
+}
+
+static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman,
+ unsigned preferred_feature_set)
+{
+ u32 chans;
+ int feature;
+
+ chans = dmaman->chan_available;
+ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++)
+ /* select the subset of available channels with the desired
+ feature so long as some of the candidate channels have that
+ feature */
+ if ((preferred_feature_set & (1 << feature)) &&
+ (chans & dmaman->has_feature[feature]))
+ chans &= dmaman->has_feature[feature];
+
+ if (chans) {
+ int chan = 0;
+ /* return the ordinal of the first channel in the bitmap */
+ while (chans != 0 && (chans & 1) == 0) {
+ chans >>= 1;
+ chan++;
+ }
+ /* claim the channel */
+ dmaman->chan_available &= ~(1 << chan);
+ return chan;
+ } else
+ return -ENOMEM;
+}
+
+static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan)
+{
+ if (chan < 0)
+ return -EINVAL;
+ else if ((1 << chan) & dmaman->chan_available)
+ return -EIDRM;
+ else {
+ dmaman->chan_available |= (1 << chan);
+ return 0;
+ }
+}
+
+/*****************************************************************************\
+ * *
+ * DMA IRQs *
+ * *
+\*****************************************************************************/
+
+static unsigned char bcm_dma_irqs[] = {
+ IRQ_DMA0,
+ IRQ_DMA1,
+ IRQ_DMA2,
+ IRQ_DMA3,
+ IRQ_DMA4,
+ IRQ_DMA5,
+ IRQ_DMA6,
+ IRQ_DMA7,
+ IRQ_DMA8,
+ IRQ_DMA9,
+ IRQ_DMA10,
+ IRQ_DMA11,
+ IRQ_DMA12
+};
+
+
+/***************************************************************************** \
+ * *
+ * DMA Manager Monitor *
+ * *
+\*****************************************************************************/
+
+static struct device *dmaman_dev; /* we assume there's only one! */
+
+extern int bcm_dma_chan_alloc(unsigned preferred_feature_set,
+ void __iomem **out_dma_base, int *out_dma_irq)
+{
+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev);
+ struct platform_device *pdev = to_platform_device(dmaman_dev);
+ struct resource *r;
+ int rc;
+
+ if (!dmaman_dev)
+ return -ENODEV;
+
+ device_lock(dmaman_dev);
+ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set);
+ if (rc < 0)
+ goto out;
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, (unsigned int) rc);
+ if (!r) {
+ dev_err(dmaman_dev, "failed to get irq for DMA channel %d\n",
+ rc);
+ vc_dmaman_chan_free(dmaman, rc);
+ rc = -ENOENT;
+ goto out;
+ }
+
+ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, rc);
+ *out_dma_irq = r->start;
+ dev_info(dmaman_dev, "%s: channel=%d, base=%p, irq=%i, bcm_dma_irqs[]=%d\n", __func__, rc, *out_dma_base, *out_dma_irq, bcm_dma_irqs[rc]);
+
+out:
+ device_unlock(dmaman_dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc);
+
+extern int bcm_dma_chan_free(int channel)
+{
+ if (dmaman_dev) {
+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev);
+ int rc;
+
+ device_lock(dmaman_dev);
+ rc = vc_dmaman_chan_free(dmaman, channel);
+ device_unlock(dmaman_dev);
+
+ return rc;
+ } else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_chan_free);
+
+static int dev_dmaman_register(const char *dev_name, struct device *dev)
+{
+ int rc = dmaman_dev ? -EINVAL : 0;
+ dmaman_dev = dev;
+ return rc;
+}
+
+static void dev_dmaman_deregister(const char *dev_name, struct device *dev)
+{
+ dmaman_dev = NULL;
+}
+
+/*****************************************************************************\
+ * *
+ * DMA Device *
+ * *
+\*****************************************************************************/
+
+//static int dmachans = -1; /* module parameter */
+static int dmachans = 0x7f35;
+
+static int bcm_dmaman_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct vc_dmaman *dmaman;
+ struct resource *dma_res = NULL;
+ void __iomem *dma_base = NULL;
+ int have_dma_region = 0;
+
+ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL);
+ if (NULL == dmaman) {
+ printk(KERN_ERR DRIVER_NAME ": failed to allocate "
+ "DMA management memory\n");
+ ret = -ENOMEM;
+ } else {
+
+ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (dma_res == NULL) {
+ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory "
+ "resource\n");
+ ret = -ENODEV;
+ } else if (!request_mem_region(dma_res->start,
+ resource_size(dma_res),
+ DRIVER_NAME)) {
+ dev_err(&pdev->dev, "cannot obtain DMA region\n");
+ ret = -EBUSY;
+ } else {
+ have_dma_region = 1;
+ dma_base = ioremap(dma_res->start,
+ resource_size(dma_res));
+ if (!dma_base) {
+ dev_err(&pdev->dev, "cannot map DMA region\n");
+ ret = -ENOMEM;
+ } else {
+ /* use module parameter if one was provided */
+ if (dmachans > 0)
+ vc_dmaman_init(dmaman, dma_base,
+ dmachans);
+ else
+ vc_dmaman_init(dmaman, dma_base,
+ DEFAULT_DMACHAN_BITMAP);
+
+ platform_set_drvdata(pdev, dmaman);
+ dev_dmaman_register(DRIVER_NAME, &pdev->dev);
+
+ printk(KERN_INFO DRIVER_NAME ": DMA manager "
+ "at %p\n", dma_base);
+ }
+ }
+ }
+ if (ret != 0) {
+ if (dma_base)
+ iounmap(dma_base);
+ if (dma_res && have_dma_region)
+ release_mem_region(dma_res->start,
+ resource_size(dma_res));
+ if (dmaman)
+ kfree(dmaman);
+ }
+ return ret;
+}
+
+static int bcm_dmaman_remove(struct platform_device *pdev)
+{
+ struct vc_dmaman *dmaman = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev);
+ kfree(dmaman);
+
+ return 0;
+}
+
+static const struct of_device_id bcm2708_dma_of_match[] = {
+ { .compatible = "brcm,bcm2708-dma", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_dma_of_match);
+
+static struct platform_driver bcm_dmaman_driver = {
+ .probe = bcm_dmaman_probe,
+ .remove = bcm_dmaman_remove,
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2708_dma_of_match,
+ },
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init bcm_dmaman_drv_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&bcm_dmaman_driver);
+ if (ret != 0) {
+ printk(KERN_ERR DRIVER_NAME ": failed to register "
+ "on platform\n");
+ }
+
+ return ret;
+}
+
+static void __exit bcm_dmaman_drv_exit(void)
+{
+ platform_driver_unregister(&bcm_dmaman_driver);
+}
+
+arch_initcall(bcm_dmaman_drv_init);
+module_exit(bcm_dmaman_drv_exit);
+
+module_param(dmachans, int, 0644);
+
+MODULE_AUTHOR("Gray Girling <grayg@broadcom.com>");
+MODULE_DESCRIPTION("DMA channel manager driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM");
diff --git a/include/linux/platform_data/mailbox-bcm2708.h b/include/linux/platform_data/mailbox-bcm2708.h
new file mode 100644
index 0000000..90459fe
--- /dev/null
+++ b/include/linux/platform_data/mailbox-bcm2708.h
@@ -0,0 +1,124 @@
+/*
+ * 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 _PLAT_MAILBOX_BCM2708_H
+#define _PLAT_MAILBOX_BCM2708_H
+
+/* 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 /* _PLAT_MAILBOX_BCM2708_H */
diff --git a/drivers/mailbox/bcm2708-vcio.c b/drivers/mailbox/bcm2708-vcio.c
new file mode 100644
index 0000000..6ab3d89
--- /dev/null
+++ b/drivers/mailbox/bcm2708-vcio.c
@@ -0,0 +1,519 @@
+/*
+ * 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/ioctl.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_data/mailbox-bcm2708.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 "bcm2708_vcio"
+#define DEVICE_FILE_NAME "vcio"
+
+#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.
+ */
+
+
+/* ----------------------------------------------------------------------
+ * Mailbox
+ * -------------------------------------------------------------------- */
+
+/* offsets from a mail box base address */
+#define MAIL_WRT 0x20 /* 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 */
+
+/* Mailbox config register */
+#define ARM_MC_IHAVEDATAIRQEN BIT(0)
+
+/* Mailbox status register */
+#define ARM_MS_FULL BIT(31)
+#define ARM_MS_EMPTY BIT(30)
+
+#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,
+ void __iomem *addr_mbox)
+{
+ int i;
+
+ mbox_out->dev = dev;
+// mbox_out->status = mbox_out->regs + MAIL_STA;
+// mbox_out->config = mbox_out->regs + MAIL_CNF;
+// mbox_out->read = mbox_out->regs + MAIL_RD;
+// mbox_out->write = mbox_out->regs + MAIL_WRT;
+
+ mbox_out->status = addr_mbox + MAIL_STA;
+ mbox_out->config = addr_mbox + MAIL_CNF;
+ mbox_out->read = addr_mbox + MAIL_RD;
+ /* Write to the other mailbox */
+ mbox_out->write = addr_mbox + MAIL_WRT;
+
+pr_info("%s: status=%p, config=%p, read=%p, write=%p\n", __func__, mbox_out->status, mbox_out->config, mbox_out->read, mbox_out->write);
+
+ 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;
+ }
+pr_info("%s(chan=%u, data28=0x%04x): return %d\n", __func__, chan, data28, rc);
+ 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;
+ }
+pr_info("%s(chan=%u, *data28=0x%04x): return %d\n", __func__, chan, *data28, rc);
+ 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;
+
+pr_info("%s\n", __func__);
+ 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);
+pr_info(" chan=%d, status=0x%x\n", chan, 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;
+
+pr_info("%s(size=%d)\n", __func__, size);
+ 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)
+{
+ struct vc_mailbox *mailbox;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret;
+
+ mailbox = devm_kzalloc(&pdev->dev, sizeof(*mailbox), GFP_KERNEL);
+ if (!mailbox)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ\n");
+ return -EINVAL;
+ }
+
+ mbox_init(mailbox, &pdev->dev, regs);
+ dev_mbox_register(DRIVER_NAME, &pdev->dev);
+
+ mbox_irqaction.dev_id = mailbox;
+ setup_irq(irq, &mbox_irqaction);
+
+ /*
+ * Register the character device
+ */
+ ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Failed registering the character device %d\n", ret);
+ return ret;
+ }
+
+ vcio_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(vcio_class))
+ return PTR_ERR(vcio_class);
+
+ device_create(vcio_class, NULL, MKDEV(MAJOR_NUM, 0), NULL, "vcio");
+ platform_set_drvdata(pdev, mailbox);
+ dev_info(&pdev->dev, "mailbox at %p\n", regs);
+
+ return 0;
+}
+
+static int bcm_vcio_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id bcm_vcio_of_match_table[] = {
+ { .compatible = "brcm,bcm2708-mbox", },
+ {},
+};
+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)
+{
+ 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");
diff --git a/include/linux/broadcom/vc_mem.h b/include/linux/broadcom/vc_mem.h
new file mode 100644
index 0000000..4a4a338
--- /dev/null
+++ b/include/linux/broadcom/vc_mem.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#if !defined( VC_MEM_H )
+#define VC_MEM_H
+
+#include <linux/ioctl.h>
+
+#define VC_MEM_IOC_MAGIC 'v'
+
+#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long )
+#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int )
+#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int )
+#define VC_MEM_IOC_MEM_LOAD _IOR( VC_MEM_IOC_MAGIC, 3, unsigned int )
+
+#if defined( __KERNEL__ )
+#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF
+
+extern unsigned long mm_vc_mem_phys_addr;
+extern unsigned int mm_vc_mem_size;
+extern int vc_mem_get_current_size( void );
+#endif
+
+#endif /* VC_MEM_H */
diff --git a/drivers/char/broadcom/vc_mem.c b/drivers/char/broadcom/vc_mem.c
new file mode 100644
index 0000000..d7880ee
--- /dev/null
+++ b/drivers/char/broadcom/vc_mem.c
@@ -0,0 +1,425 @@
+/*****************************************************************************
+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/broadcom/vc_mem.h>
+//#include <mach/vcio.h>
+
+#define DRIVER_NAME "vc-mem"
+
+// Device (/dev) related variables
+static dev_t vc_mem_devnum = 0;
+static struct class *vc_mem_class = NULL;
+static struct cdev vc_mem_cdev;
+static int vc_mem_inited = 0;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vc_mem_debugfs_entry;
+#endif
+
+/*
+ * Videocore memory addresses and size
+ *
+ * Drivers that wish to know the videocore memory addresses and sizes should
+ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in
+ * headers. This allows the other drivers to not be tied down to a a certain
+ * address/size at compile time.
+ *
+ * In the future, the goal is to have the videocore memory virtual address and
+ * size be calculated at boot time rather than at compile time. The decision of
+ * where the videocore memory resides and its size would be in the hands of the
+ * bootloader (and/or kernel). When that happens, the values of these variables
+ * would be calculated and assigned in the init function.
+ */
+// in the 2835 VC in mapped above ARM, but ARM has full access to VC space
+unsigned long mm_vc_mem_phys_addr = 0x00000000;
+unsigned int mm_vc_mem_size = 0;
+unsigned int mm_vc_mem_base = 0;
+
+EXPORT_SYMBOL(mm_vc_mem_phys_addr);
+EXPORT_SYMBOL(mm_vc_mem_size);
+EXPORT_SYMBOL(mm_vc_mem_base);
+
+static uint phys_addr = 0;
+//static uint mem_size = 0;
+//static uint mem_base = 0;
+static uint mem_size = 0x20000000;
+static uint mem_base = 0x1ec00000;
+
+/****************************************************************************
+*
+* vc_mem_open
+*
+***************************************************************************/
+
+static int
+vc_mem_open(struct inode *inode, struct file *file)
+{
+ (void) inode;
+ (void) file;
+
+ pr_debug("%s: called file = 0x%p\n", __func__, file);
+
+ return 0;
+}
+
+/****************************************************************************
+*
+* vc_mem_release
+*
+***************************************************************************/
+
+static int
+vc_mem_release(struct inode *inode, struct file *file)
+{
+ (void) inode;
+ (void) file;
+
+ pr_debug("%s: called file = 0x%p\n", __func__, file);
+
+ return 0;
+}
+
+/****************************************************************************
+*
+* vc_mem_get_size
+*
+***************************************************************************/
+
+static void
+vc_mem_get_size(void)
+{
+}
+
+/****************************************************************************
+*
+* vc_mem_get_base
+*
+***************************************************************************/
+
+static void
+vc_mem_get_base(void)
+{
+}
+
+/****************************************************************************
+*
+* vc_mem_get_current_size
+*
+***************************************************************************/
+
+int
+vc_mem_get_current_size(void)
+{
+ return mm_vc_mem_size;
+}
+
+EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
+
+/****************************************************************************
+*
+* vc_mem_ioctl
+*
+***************************************************************************/
+
+static long
+vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+
+ (void) cmd;
+ (void) arg;
+
+ pr_debug("%s: called file = 0x%p\n", __func__, file);
+
+ switch (cmd) {
+ case VC_MEM_IOC_MEM_PHYS_ADDR:
+ {
+ pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n",
+ __func__, (void *) mm_vc_mem_phys_addr);
+
+ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr,
+ sizeof (mm_vc_mem_phys_addr)) != 0) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case VC_MEM_IOC_MEM_SIZE:
+ {
+ // Get the videocore memory size first
+ vc_mem_get_size();
+
+ pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%u\n", __func__,
+ mm_vc_mem_size);
+
+ if (copy_to_user((void *) arg, &mm_vc_mem_size,
+ sizeof (mm_vc_mem_size)) != 0) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case VC_MEM_IOC_MEM_BASE:
+ {
+ // Get the videocore memory base
+ vc_mem_get_base();
+
+ pr_debug("%s: VC_MEM_IOC_MEM_BASE=%u\n", __func__,
+ mm_vc_mem_base);
+
+ if (copy_to_user((void *) arg, &mm_vc_mem_base,
+ sizeof (mm_vc_mem_base)) != 0) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case VC_MEM_IOC_MEM_LOAD:
+ {
+ // Get the videocore memory base
+ vc_mem_get_base();
+
+ pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%u\n", __func__,
+ mm_vc_mem_base);
+
+ if (copy_to_user((void *) arg, &mm_vc_mem_base,
+ sizeof (mm_vc_mem_base)) != 0) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ {
+ return -ENOTTY;
+ }
+ }
+ pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc);
+
+ return rc;
+}
+
+/****************************************************************************
+*
+* vc_mem_mmap
+*
+***************************************************************************/
+
+static int
+vc_mem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int rc = 0;
+ unsigned long length = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n",
+ __func__, (long) vma->vm_start, (long) vma->vm_end,
+ (long) vma->vm_pgoff);
+
+ if (offset + length > mm_vc_mem_size) {
+ pr_err("%s: length %ld is too big\n", __func__, length);
+ return -EINVAL;
+ }
+ // Do not cache the memory map
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ rc = remap_pfn_range(vma, vma->vm_start,
+ (mm_vc_mem_phys_addr >> PAGE_SHIFT) +
+ vma->vm_pgoff, length, vma->vm_page_prot);
+ if (rc != 0) {
+ pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc);
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+*
+* File Operations for the driver.
+*
+***************************************************************************/
+
+static const struct file_operations vc_mem_fops = {
+ .owner = THIS_MODULE,
+ .open = vc_mem_open,
+ .release = vc_mem_release,
+ .unlocked_ioctl = vc_mem_ioctl,
+ .mmap = vc_mem_mmap,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static void vc_mem_debugfs_deinit(void)
+{
+ debugfs_remove_recursive(vc_mem_debugfs_entry);
+ vc_mem_debugfs_entry = NULL;
+}
+
+
+static int vc_mem_debugfs_init(
+ struct device *dev)
+{
+ vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL);
+ if (!vc_mem_debugfs_entry) {
+ dev_warn(dev, "could not create debugfs entry\n");
+ return -EFAULT;
+ }
+
+ if (!debugfs_create_x32("vc_mem_phys_addr",
+ 0444,
+ vc_mem_debugfs_entry,
+ (u32 *)&mm_vc_mem_phys_addr)) {
+ dev_warn(dev, "%s:could not create vc_mem_phys entry\n",
+ __func__);
+ goto fail;
+ }
+
+ if (!debugfs_create_x32("vc_mem_size",
+ 0444,
+ vc_mem_debugfs_entry,
+ (u32 *)&mm_vc_mem_size)) {
+ dev_warn(dev, "%s:could not create vc_mem_size entry\n",
+ __func__);
+ goto fail;
+ }
+
+ if (!debugfs_create_x32("vc_mem_base",
+ 0444,
+ vc_mem_debugfs_entry,
+ (u32 *)&mm_vc_mem_base)) {
+ dev_warn(dev, "%s:could not create vc_mem_base entry\n",
+ __func__);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ vc_mem_debugfs_deinit();
+ return -EFAULT;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+
+/****************************************************************************
+*
+* vc_mem_init
+*
+***************************************************************************/
+
+static int __init
+vc_mem_init(void)
+{
+ int rc = -EFAULT;
+ struct device *dev;
+
+ pr_debug("%s: called\n", __func__);
+
+ mm_vc_mem_phys_addr = phys_addr;
+ mm_vc_mem_size = mem_size;
+ mm_vc_mem_base = mem_base;
+
+ vc_mem_get_size();
+
+ pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n",
+ mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024));
+
+ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) {
+ pr_err("%s: alloc_chrdev_region failed (rc=%d)\n",
+ __func__, rc);
+ goto out_err;
+ }
+
+ cdev_init(&vc_mem_cdev, &vc_mem_fops);
+ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) {
+ pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc);
+ goto out_unregister;
+ }
+
+ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(vc_mem_class)) {
+ rc = PTR_ERR(vc_mem_class);
+ pr_err("%s: class_create failed (rc=%d)\n", __func__, rc);
+ goto out_cdev_del;
+ }
+
+ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL,
+ DRIVER_NAME);
+ if (IS_ERR(dev)) {
+ rc = PTR_ERR(dev);
+ pr_err("%s: device_create failed (rc=%d)\n", __func__, rc);
+ goto out_class_destroy;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ /* don't fail if the debug entries cannot be created */
+ vc_mem_debugfs_init(dev);
+#endif
+
+ vc_mem_inited = 1;
+ return 0;
+
+ device_destroy(vc_mem_class, vc_mem_devnum);
+
+ out_class_destroy:
+ class_destroy(vc_mem_class);
+ vc_mem_class = NULL;
+
+ out_cdev_del:
+ cdev_del(&vc_mem_cdev);
+
+ out_unregister:
+ unregister_chrdev_region(vc_mem_devnum, 1);
+
+ out_err:
+ return -1;
+}
+
+/****************************************************************************
+*
+* vc_mem_exit
+*
+***************************************************************************/
+
+static void __exit
+vc_mem_exit(void)
+{
+ pr_debug("%s: called\n", __func__);
+
+ if (vc_mem_inited) {
+#if CONFIG_DEBUG_FS
+ vc_mem_debugfs_deinit();
+#endif
+ device_destroy(vc_mem_class, vc_mem_devnum);
+ class_destroy(vc_mem_class);
+ cdev_del(&vc_mem_cdev);
+ unregister_chrdev_region(vc_mem_devnum, 1);
+ }
+}
+
+module_init(vc_mem_init);
+module_exit(vc_mem_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Broadcom Corporation");
+
+module_param(phys_addr, uint, 0644);
+module_param(mem_size, uint, 0644);
+module_param(mem_base, uint, 0644);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment