mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-29 18:19:02 +00:00
c931b21161
SVN-Revision: 13619
588 lines
17 KiB
Diff
Executable File
588 lines
17 KiB
Diff
Executable File
From 6a2c7de90f47b7eb74f3cb2d181f950ece22b3fb Mon Sep 17 00:00:00 2001
|
|
From: mokopatches <mokopatches@openmoko.org>
|
|
Date: Fri, 25 Jul 2008 22:21:22 +0100
|
|
Subject: [PATCH] introduce-fiq-basis.patch
|
|
Adds a C-based FIQ ISR which is very convenient (and unusual --
|
|
normally you have to do FIQ ISR in assembler only).
|
|
Based on my article:
|
|
|
|
http://warmcat.com/_wp/2007/09/17/at91rm9200-fiq-faq-and-simple-example-code-patch/
|
|
|
|
Implemented as a platform device and driver.
|
|
|
|
Suspend / resume is tested and works.
|
|
|
|
Signed-off-by: Andy Green <andy@warmcat.com>
|
|
---
|
|
arch/arm/mach-s3c2440/Kconfig | 7 +
|
|
arch/arm/mach-s3c2440/Makefile | 1 +
|
|
arch/arm/mach-s3c2440/fiq_c_isr.c | 250 ++++++++++++++++++++++++++
|
|
arch/arm/mach-s3c2440/fiq_c_isr.h | 64 +++++++
|
|
arch/arm/mach-s3c2440/mach-gta02.c | 22 +++
|
|
arch/arm/plat-s3c24xx/irq.c | 32 +++-
|
|
include/asm-arm/arch-s3c2410/fiq_ipc_gta02.h | 28 +++
|
|
include/asm-arm/plat-s3c24xx/irq.h | 20 ++
|
|
8 files changed, 422 insertions(+), 2 deletions(-)
|
|
create mode 100644 arch/arm/mach-s3c2440/fiq_c_isr.c
|
|
create mode 100644 arch/arm/mach-s3c2440/fiq_c_isr.h
|
|
create mode 100644 include/asm-arm/arch-s3c2410/fiq_ipc_gta02.h
|
|
|
|
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
|
|
index 6b317d1..3015aaf 100644
|
|
--- a/arch/arm/mach-s3c2440/Kconfig
|
|
+++ b/arch/arm/mach-s3c2440/Kconfig
|
|
@@ -22,6 +22,13 @@ config S3C2440_DMA
|
|
help
|
|
Support for S3C2440 specific DMA code5A
|
|
|
|
+config S3C2440_C_FIQ
|
|
+ bool "FIQ ISR support in C"
|
|
+ depends on ARCH_S3C2410
|
|
+ select FIQ
|
|
+ help
|
|
+ Support for S3C2440 FIQ support in C -- see
|
|
+ ./arch/arm/macs3c2440/fiq_c_isr.c
|
|
|
|
menu "S3C2440 Machines"
|
|
|
|
diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile
|
|
index 1a4defd..4932232 100644
|
|
--- a/arch/arm/mach-s3c2440/Makefile
|
|
+++ b/arch/arm/mach-s3c2440/Makefile
|
|
@@ -13,6 +13,7 @@ obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o
|
|
obj-$(CONFIG_CPU_S3C2440) += irq.o
|
|
obj-$(CONFIG_CPU_S3C2440) += clock.o
|
|
obj-$(CONFIG_S3C2440_DMA) += dma.o
|
|
+obj-$(CONFIG_S3C2440_C_FIQ) += fiq_c_isr.o
|
|
|
|
# Machine support
|
|
|
|
diff --git a/arch/arm/mach-s3c2440/fiq_c_isr.c b/arch/arm/mach-s3c2440/fiq_c_isr.c
|
|
new file mode 100644
|
|
index 0000000..12f4527
|
|
--- /dev/null
|
|
+++ b/arch/arm/mach-s3c2440/fiq_c_isr.c
|
|
@@ -0,0 +1,250 @@
|
|
+/*
|
|
+ * Copyright 2007 Andy Green <andy@warmcat.com>
|
|
+ * S3C modfifications
|
|
+ * Copyright 2008 Andy Green <andy@openmoko.com>
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <asm/hardware.h>
|
|
+#include <asm/fiq.h>
|
|
+#include "fiq_c_isr.h"
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+#include <asm/io.h>
|
|
+
|
|
+#include <asm/plat-s3c24xx/cpu.h>
|
|
+#include <asm/plat-s3c24xx/irq.h>
|
|
+
|
|
+/*
|
|
+ * Major Caveats for using FIQ
|
|
+ * ---------------------------
|
|
+ *
|
|
+ * 1) it CANNOT touch any vmalloc()'d memory, only memory
|
|
+ * that was kmalloc()'d. Static allocations in the monolithic kernel
|
|
+ * are kmalloc()'d so they are okay. You can touch memory-mapped IO, but
|
|
+ * the pointer for it has to have been stored in kmalloc'd memory. The
|
|
+ * reason for this is simple: every now and then Linux turns off interrupts
|
|
+ * and reorders the paging tables. If a FIQ happens during this time, the
|
|
+ * virtual memory space can be partly or entirely disordered or missing.
|
|
+ *
|
|
+ * 2) Because vmalloc() is used when a module is inserted, THIS FIQ
|
|
+ * ISR HAS TO BE IN THE MONOLITHIC KERNEL, not a module. But the way
|
|
+ * it is set up, you can all to enable and disable it from your module
|
|
+ * and intercommunicate with it through struct fiq_ipc
|
|
+ * fiq_ipc which you can define in
|
|
+ * asm/archfiq_ipc_type.h. The reason is the same as above, a
|
|
+ * FIQ could happen while even the ISR is not present in virtual memory
|
|
+ * space due to pagetables being changed at the time.
|
|
+ *
|
|
+ * 3) You can't call any Linux API code except simple macros
|
|
+ * - understand that FIQ can come in at any time, no matter what
|
|
+ * state of undress the kernel may privately be in, thinking it
|
|
+ * locked the door by turning off interrupts... FIQ is an
|
|
+ * unstoppable monster force (which is its value)
|
|
+ * - they are not vmalloc()'d memory safe
|
|
+ * - they might do crazy stuff like sleep: FIQ pisses fire and
|
|
+ * is not interested in 'sleep' that the weak seem to need
|
|
+ * - calling APIs from FIQ can re-enter un-renterable things
|
|
+ * - summary: you cannot interoperate with linux APIs directly in the FIQ ISR
|
|
+ *
|
|
+ * If you follow these rules, it is fantastic, an extremely powerful, solid,
|
|
+ * genuine hard realtime feature.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/* more than enough to cover our jump instruction to the isr */
|
|
+#define SIZEOF_FIQ_JUMP 8
|
|
+/* more than enough to cover s3c2440_fiq_isr() in 4K blocks */
|
|
+#define SIZEOF_FIQ_ISR 0x2000
|
|
+/* increase the size of the stack that is active during FIQ as needed */
|
|
+static u8 u8aFiqStack[4096];
|
|
+
|
|
+/* only one FIQ ISR possible, okay to do these here */
|
|
+u32 _fiq_ack_mask; /* used by isr exit define */
|
|
+unsigned long _fiq_count_fiqs; /* used by isr exit define */
|
|
+static int _fiq_irq; /* private ; irq index we were started with, or 0 */
|
|
+
|
|
+/* this function must live in the monolithic kernel somewhere! A module is
|
|
+ * NOT good enough!
|
|
+ */
|
|
+extern void __attribute__ ((naked)) s3c2440_fiq_isr(void);
|
|
+
|
|
+/* this is copied into the hard FIQ vector during init */
|
|
+
|
|
+static void __attribute__ ((naked)) s3c2440_FIQ_Branch(void)
|
|
+{
|
|
+ asm __volatile__ (
|
|
+ "mov pc, r8 ; "
|
|
+ );
|
|
+}
|
|
+
|
|
+/* sysfs */
|
|
+
|
|
+static ssize_t show_count(struct device *dev, struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sprintf(buf, "%ld\n", _fiq_count_fiqs);
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(count, 0444, show_count, NULL);
|
|
+
|
|
+static struct attribute *s3c2440_fiq_sysfs_entries[] = {
|
|
+ &dev_attr_count.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group s3c2440_fiq_attr_group = {
|
|
+ .name = "fiq",
|
|
+ .attrs = s3c2440_fiq_sysfs_entries,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * call this from your kernel module to set up the FIQ ISR to service FIQs,
|
|
+ * You need to have configured your FIQ input pin before anything will happen
|
|
+ *
|
|
+ * call it with, eg, IRQ_TIMER3 from asm-arm/arch-s3c2410/irqs.h
|
|
+ *
|
|
+ * you still need to clear the source interrupt in S3C2410_INTMSK to get
|
|
+ * anything good happening
|
|
+ */
|
|
+static void fiq_init_irq_source(int irq_index_fiq)
|
|
+{
|
|
+ if (!irq_index_fiq) /* no interrupt */
|
|
+ return;
|
|
+
|
|
+ printk(KERN_INFO"Enabling FIQ using int idx %d\n",
|
|
+ irq_index_fiq - S3C2410_CPUIRQ_OFFSET);
|
|
+ local_fiq_disable();
|
|
+
|
|
+ _fiq_irq = irq_index_fiq;
|
|
+ _fiq_ack_mask = 1 << (irq_index_fiq - S3C2410_CPUIRQ_OFFSET);
|
|
+
|
|
+ /* let our selected interrupt be a magic FIQ interrupt */
|
|
+ __raw_writel(_fiq_ack_mask, S3C2410_INTMOD);
|
|
+
|
|
+ /* it's ready to go as soon as we unmask the source in S3C2410_INTMSK */
|
|
+ local_fiq_enable();
|
|
+}
|
|
+
|
|
+
|
|
+/* call this from your kernel module to disable generation of FIQ actions */
|
|
+static void fiq_disable_irq_source(void)
|
|
+{
|
|
+ /* nothing makes FIQ any more */
|
|
+ __raw_writel(0, S3C2410_INTMOD);
|
|
+ local_fiq_disable();
|
|
+ _fiq_irq = 0; /* no active source interrupt now either */
|
|
+}
|
|
+
|
|
+/* this starts FIQ timer events... they continue until the FIQ ISR sees that
|
|
+ * its work is done and it turns off the timer. After setting up the fiq_ipc
|
|
+ * struct with new work, you call this to start FIQ timer actions up again.
|
|
+ * Only the FIQ ISR decides when it is done and controls turning off the
|
|
+ * timer events.
|
|
+ */
|
|
+void fiq_kick(void)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* we have to take care about FIQ because this modification is
|
|
+ * non-atomic, FIQ could come in after the read and before the
|
|
+ * writeback and its changes to the register would be lost
|
|
+ * (platform INTMSK mod code is taken care of already)
|
|
+ */
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+ __raw_writel(__raw_readl(S3C2410_INTMSK) &
|
|
+ ~(1 << (_fiq_irq - S3C2410_CPUIRQ_OFFSET)),
|
|
+ S3C2410_INTMSK);
|
|
+ local_irq_restore(flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fiq_kick);
|
|
+
|
|
+
|
|
+
|
|
+static int __init sc32440_fiq_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct resource *r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
+
|
|
+ if (!r)
|
|
+ return -EIO;
|
|
+ /* configure for the interrupt we are meant to use */
|
|
+ fiq_init_irq_source(r->start);
|
|
+
|
|
+ return sysfs_create_group(&pdev->dev.kobj, &s3c2440_fiq_attr_group);
|
|
+}
|
|
+
|
|
+static int sc32440_fiq_remove(struct platform_device *pdev)
|
|
+{
|
|
+ fiq_disable_irq_source();
|
|
+ sysfs_remove_group(&pdev->dev.kobj, &s3c2440_fiq_attr_group);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void fiq_set_vector_and_regs(void)
|
|
+{
|
|
+ struct pt_regs regs;
|
|
+
|
|
+ /* prep the special FIQ mode regs */
|
|
+ memset(®s, 0, sizeof(regs));
|
|
+ regs.ARM_r8 = (unsigned long)s3c2440_fiq_isr;
|
|
+ regs.ARM_sp = (unsigned long)u8aFiqStack + sizeof(u8aFiqStack) - 4;
|
|
+ /* set up the special FIQ-mode-only registers from our regs */
|
|
+ set_fiq_regs(®s);
|
|
+ /* copy our jump to the real ISR into the hard vector address */
|
|
+ set_fiq_handler(s3c2440_FIQ_Branch, SIZEOF_FIQ_JUMP);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int sc32440_fiq_suspend(struct platform_device *pdev, pm_message_t state)
|
|
+{
|
|
+ /* nothing makes FIQ any more */
|
|
+ __raw_writel(0, S3C2410_INTMOD);
|
|
+ local_fiq_disable();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sc32440_fiq_resume(struct platform_device *pdev)
|
|
+{
|
|
+ fiq_set_vector_and_regs();
|
|
+ fiq_init_irq_source(_fiq_irq);
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+#define sc32440_fiq_suspend NULL
|
|
+#define sc32440_fiq_resume NULL
|
|
+#endif
|
|
+
|
|
+static struct platform_driver sc32440_fiq_driver = {
|
|
+ .driver = {
|
|
+ .name = "sc32440_fiq",
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+
|
|
+ .probe = sc32440_fiq_probe,
|
|
+ .remove = __devexit_p(sc32440_fiq_remove),
|
|
+ .suspend = sc32440_fiq_suspend,
|
|
+ .resume = sc32440_fiq_resume,
|
|
+};
|
|
+
|
|
+static int __init sc32440_fiq_init(void)
|
|
+{
|
|
+ fiq_set_vector_and_regs();
|
|
+
|
|
+ return platform_driver_register(&sc32440_fiq_driver);
|
|
+}
|
|
+
|
|
+static void __exit sc32440_fiq_exit(void)
|
|
+{
|
|
+ fiq_disable_irq_source();
|
|
+}
|
|
+
|
|
+MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+module_init(sc32440_fiq_init);
|
|
+module_exit(sc32440_fiq_exit);
|
|
diff --git a/arch/arm/mach-s3c2440/fiq_c_isr.h b/arch/arm/mach-s3c2440/fiq_c_isr.h
|
|
new file mode 100644
|
|
index 0000000..f08740e
|
|
--- /dev/null
|
|
+++ b/arch/arm/mach-s3c2440/fiq_c_isr.h
|
|
@@ -0,0 +1,64 @@
|
|
+#ifndef _LINUX_FIQ_C_ISR_H
|
|
+#define _LINUX_FIQ_C_ISR_H
|
|
+
|
|
+#include <asm/arch-s3c2410/regs-irq.h>
|
|
+
|
|
+extern unsigned long _fiq_count_fiqs;
|
|
+extern u32 _fiq_ack_mask;
|
|
+
|
|
+/* This CANNOT be implemented in a module -- it has to be used in code
|
|
+ * included in the monolithic kernel
|
|
+ */
|
|
+
|
|
+#define FIQ_HANDLER_START() \
|
|
+void __attribute__ ((naked)) s3c2440_fiq_isr(void) \
|
|
+{\
|
|
+ /*\
|
|
+ * you can declare local vars here, take care to set the frame size\
|
|
+ * below accordingly if there are more than a few dozen bytes of them\
|
|
+ */\
|
|
+
|
|
+/* stick your locals here :-)
|
|
+ * Do NOT initialize them here! define them and initialize them after
|
|
+ * FIQ_HANDLER_ENTRY() is done.
|
|
+ */
|
|
+
|
|
+#define FIQ_HANDLER_ENTRY(LOCALS, FRAME) \
|
|
+ const int _FIQ_FRAME_SIZE = FRAME; \
|
|
+ /* entry takes care to store registers we will be treading on here */\
|
|
+ asm __volatile__ (\
|
|
+ "mov ip, sp ;"\
|
|
+ /* stash FIQ and r0-r8 normal regs */\
|
|
+ "stmdb sp!, {r0-r12, lr};"\
|
|
+ /* allow SP to get some space */\
|
|
+ "sub sp, sp, %1 ;"\
|
|
+ /* !! THIS SETS THE FRAME, adjust to > sizeof locals */\
|
|
+ "sub fp, sp, %0 ;"\
|
|
+ :\
|
|
+ : "rI" (LOCALS), "rI" (FRAME)\
|
|
+ :"r9"\
|
|
+ );
|
|
+
|
|
+/* stick your ISR code here and then end with... */
|
|
+
|
|
+#define FIQ_HANDLER_END() \
|
|
+ _fiq_count_fiqs++;\
|
|
+ __raw_writel(_fiq_ack_mask, S3C2410_SRCPND);\
|
|
+\
|
|
+ /* exit back to normal mode restoring everything */\
|
|
+ asm __volatile__ (\
|
|
+ /* pop our allocation */\
|
|
+ "add sp, sp, %0 ;"\
|
|
+ /* return FIQ regs back to pristine state\
|
|
+ * and get normal regs back\
|
|
+ */\
|
|
+ "ldmia sp!, {r0-r12, lr};"\
|
|
+\
|
|
+ /* return */\
|
|
+ "subs pc, lr, #4;"\
|
|
+ : \
|
|
+ : "rI" (_FIQ_FRAME_SIZE) \
|
|
+ );\
|
|
+}
|
|
+
|
|
+#endif /* _LINUX_FIQ_C_ISR_H */
|
|
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
|
|
index 46acede..0bdd0e0 100644
|
|
--- a/arch/arm/mach-s3c2440/mach-gta02.c
|
|
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
|
|
@@ -78,9 +78,31 @@
|
|
|
|
#include <linux/glamofb.h>
|
|
|
|
+#include <asm/arch/fiq_ipc_gta02.h>
|
|
+#include "fiq_c_isr.h"
|
|
+
|
|
/* arbitrates which sensor IRQ owns the shared SPI bus */
|
|
static spinlock_t motion_irq_lock;
|
|
|
|
+/* define FIQ IPC struct */
|
|
+/*
|
|
+ * contains stuff FIQ ISR modifies and normal kernel code can see and use
|
|
+ * this is defined in <asm/arch/fiq_ipc_gta02.h>, you should customize
|
|
+ * the definition in there and include the same definition in your kernel
|
|
+ * module that wants to interoperate with your FIQ code.
|
|
+ */
|
|
+struct fiq_ipc fiq_ipc;
|
|
+EXPORT_SYMBOL(fiq_ipc);
|
|
+
|
|
+/* define FIQ ISR */
|
|
+
|
|
+FIQ_HANDLER_START()
|
|
+/* define your locals here -- no initializers though */
|
|
+FIQ_HANDLER_ENTRY(256, 512)
|
|
+/* Your ISR here :-) */
|
|
+FIQ_HANDLER_END()
|
|
+
|
|
+
|
|
static struct map_desc gta02_iodesc[] __initdata = {
|
|
{
|
|
.virtual = 0xe0000000,
|
|
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
|
|
index ae2c5d7..9887db1 100644
|
|
--- a/arch/arm/plat-s3c24xx/irq.c
|
|
+++ b/arch/arm/plat-s3c24xx/irq.c
|
|
@@ -133,12 +133,20 @@ static void
|
|
s3c_irq_mask(unsigned int irqno)
|
|
{
|
|
unsigned long mask;
|
|
-
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ unsigned long flags;
|
|
+#endif
|
|
irqno -= IRQ_EINT0;
|
|
-
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+#endif
|
|
mask = __raw_readl(S3C2410_INTMSK);
|
|
mask |= 1UL << irqno;
|
|
__raw_writel(mask, S3C2410_INTMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_irq_restore(flags);
|
|
+#endif
|
|
}
|
|
|
|
static inline void
|
|
@@ -155,9 +163,19 @@ s3c_irq_maskack(unsigned int irqno)
|
|
{
|
|
unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
|
|
unsigned long mask;
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ unsigned long flags;
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+#endif
|
|
mask = __raw_readl(S3C2410_INTMSK);
|
|
__raw_writel(mask|bitval, S3C2410_INTMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_irq_restore(flags);
|
|
+#endif
|
|
|
|
__raw_writel(bitval, S3C2410_SRCPND);
|
|
__raw_writel(bitval, S3C2410_INTPND);
|
|
@@ -168,15 +186,25 @@ static void
|
|
s3c_irq_unmask(unsigned int irqno)
|
|
{
|
|
unsigned long mask;
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ unsigned long flags;
|
|
+#endif
|
|
|
|
if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23)
|
|
irqdbf2("s3c_irq_unmask %d\n", irqno);
|
|
|
|
irqno -= IRQ_EINT0;
|
|
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+#endif
|
|
mask = __raw_readl(S3C2410_INTMSK);
|
|
mask &= ~(1UL << irqno);
|
|
__raw_writel(mask, S3C2410_INTMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_irq_restore(flags);
|
|
+#endif
|
|
}
|
|
|
|
struct irq_chip s3c_irq_level_chip = {
|
|
diff --git a/include/asm-arm/arch-s3c2410/fiq_ipc_gta02.h b/include/asm-arm/arch-s3c2410/fiq_ipc_gta02.h
|
|
new file mode 100644
|
|
index 0000000..341f2bb
|
|
--- /dev/null
|
|
+++ b/include/asm-arm/arch-s3c2410/fiq_ipc_gta02.h
|
|
@@ -0,0 +1,28 @@
|
|
+#ifndef _LINUX_FIQ_IPC_H
|
|
+#define _LINUX_FIQ_IPC_H
|
|
+
|
|
+/*
|
|
+ * this defines the struct which is used to communicate between the FIQ
|
|
+ * world and the normal linux kernel world. One of these structs is
|
|
+ * statically defined for you in the monolithic kernel so the FIQ ISR code
|
|
+ * can safely touch it any any time.
|
|
+ *
|
|
+ * You also want to include this file in your kernel module that wants to
|
|
+ * communicate with your FIQ code. Add any kinds of vars that are used by
|
|
+ * the FIQ ISR and the module in here.
|
|
+ *
|
|
+ * To get you started there is just an int that is incremented every FIQ
|
|
+ * you can remove this when you are ready to customize, but it is useful
|
|
+ * for testing
|
|
+ */
|
|
+
|
|
+struct fiq_ipc {
|
|
+ u8 u8a[0]; /* placeholder */
|
|
+};
|
|
+
|
|
+/* actual definition lives in arch/arm/mach-s3c2440/fiq_c_isr.c */
|
|
+extern struct fiq_ipc fiq_ipc;
|
|
+
|
|
+extern void fiq_kick(void); /* provoke a FIQ "immediately" */
|
|
+
|
|
+#endif /* _LINUX_FIQ_IPC_H */
|
|
diff --git a/include/asm-arm/plat-s3c24xx/irq.h b/include/asm-arm/plat-s3c24xx/irq.h
|
|
index 45746a9..bf15e1c 100644
|
|
--- a/include/asm-arm/plat-s3c24xx/irq.h
|
|
+++ b/include/asm-arm/plat-s3c24xx/irq.h
|
|
@@ -25,8 +25,15 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit,
|
|
{
|
|
unsigned long mask;
|
|
unsigned long submask;
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ unsigned long flags;
|
|
+#endif
|
|
|
|
submask = __raw_readl(S3C2410_INTSUBMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+#endif
|
|
mask = __raw_readl(S3C2410_INTMSK);
|
|
|
|
submask |= (1UL << (irqno - IRQ_S3CUART_RX0));
|
|
@@ -39,6 +46,9 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit,
|
|
|
|
/* write back masks */
|
|
__raw_writel(submask, S3C2410_INTSUBMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_irq_restore(flags);
|
|
+#endif
|
|
|
|
}
|
|
|
|
@@ -47,8 +57,15 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit)
|
|
{
|
|
unsigned long mask;
|
|
unsigned long submask;
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ unsigned long flags;
|
|
+#endif
|
|
|
|
submask = __raw_readl(S3C2410_INTSUBMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_save_flags(flags);
|
|
+ local_fiq_disable();
|
|
+#endif
|
|
mask = __raw_readl(S3C2410_INTMSK);
|
|
|
|
submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0));
|
|
@@ -57,6 +74,9 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit)
|
|
/* write back masks */
|
|
__raw_writel(submask, S3C2410_INTSUBMSK);
|
|
__raw_writel(mask, S3C2410_INTMSK);
|
|
+#ifdef CONFIG_S3C2440_C_FIQ
|
|
+ local_irq_restore(flags);
|
|
+#endif
|
|
}
|
|
|
|
|
|
--
|
|
1.5.6.3
|
|
|