Created
April 25, 2015 12:31
-
-
Save notro/dbb63a021d8ea575ddb5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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