From 3db6ef4d2bf156826a3c9ee7bd6be334d6696662 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Mon, 5 Oct 2009 16:53:09 +0200
Subject: [PATCH 28/69] gta02: Add fiq handler

---
 arch/arm/mach-s3c2440/Kconfig                  |    1 +
 arch/arm/mach-s3c2440/Makefile                 |    1 +
 arch/arm/mach-s3c2440/gta02-fiq.c              |  128 ++++++++++++++++++++++++
 arch/arm/mach-s3c2440/include/mach/gta02-fiq.h |    9 ++
 arch/arm/mach-s3c2440/mach-gta02.c             |    3 +
 5 files changed, 142 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-s3c2440/gta02-fiq.c
 create mode 100644 arch/arm/mach-s3c2440/include/mach/gta02-fiq.h

diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
index 034c1cb..5122b3e 100644
--- a/arch/arm/mach-s3c2440/Kconfig
+++ b/arch/arm/mach-s3c2440/Kconfig
@@ -102,6 +102,7 @@ config MACH_NEO1973_GTA02
 	select S3C2410_PWM
 	select S3C_DEV_USB_HOST
 	select SPARSE_IRQ
+	select FIQ
 	help
 	   Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone
 
diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile
index e4e2ef2..0701323 100644
--- a/arch/arm/mach-s3c2440/Makefile
+++ b/arch/arm/mach-s3c2440/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \
 	gta02-pm-gps.o \
 	gta02-pm-gsm.o \
 	gta02-pm-wlan.o \
+	gta02-fiq.o \
 
 # extra machine support
 
diff --git a/arch/arm/mach-s3c2440/gta02-fiq.c b/arch/arm/mach-s3c2440/gta02-fiq.c
new file mode 100644
index 0000000..9f7a0f8
--- /dev/null
+++ b/arch/arm/mach-s3c2440/gta02-fiq.c
@@ -0,0 +1,128 @@
+#include <linux/kernel.h>
+
+#include <asm/fiq.h>
+#include <mach/regs-irq.h>
+#include <plat/regs-timer.h>
+#include <mach/irqs.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/err.h>
+
+/*
+ * GTA02 FIQ related
+ *
+ * Calls into vibrator and hdq and based on the return values
+ * determines if we the FIQ source be kept alive
+ */
+
+#define DIVISOR_FROM_US(x) ((x) << 3)
+
+/* Global data related to our fiq source */
+static uint32_t gta02_fiq_ack_mask;
+static const int gta02_gta02_fiq_timer_id = 2;
+
+struct pwm_device *gta02_fiq_timer;
+
+void gta02_fiq_handler(void)
+{
+	unsigned long intmask;
+	int keep_running = 0;
+	/* disable further timer interrupts if nobody has any work
+	 * or adjust rate according to who still has work
+	 *
+	 * CAUTION: it means forground code must disable FIQ around
+	 * its own non-atomic S3C2410_INTMSK changes... not common
+	 * thankfully and taken care of by the fiq-basis patch
+	 */
+
+	if (!keep_running) {
+		/* Disable irq */
+		intmask = __raw_readl(S3C2410_INTMSK);
+		intmask |= (gta02_fiq_ack_mask);
+		__raw_writel(intmask, S3C2410_INTMSK);
+	}
+
+	__raw_writel(gta02_fiq_ack_mask, S3C2410_SRCPND);
+}
+
+void gta02_fiq_kick(void)
+{
+	unsigned long flags;
+	unsigned long intmask;
+	/* 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();
+
+	/* allow FIQs to resume */
+	intmask = __raw_readl(S3C2410_INTMSK);
+	intmask &= ~(gta02_fiq_ack_mask);
+	__raw_writel(intmask, S3C2410_INTMSK);
+
+	local_irq_restore(flags);
+
+}
+
+int gta02_fiq_enable(void)
+{
+	int ret = 0;
+
+	local_fiq_disable();
+
+	gta02_fiq_timer = pwm_request(gta02_gta02_fiq_timer_id, "fiq timer");
+
+	if (IS_ERR(gta02_fiq_timer)) {
+		ret = PTR_ERR(gta02_fiq_timer);
+		printk(KERN_ERR "GTA02 FIQ: Could not request fiq timer: %d\n",
+			ret);
+		return ret;
+	}
+
+	gta02_fiq_ack_mask = 1 << (IRQ_TIMER0 + gta02_gta02_fiq_timer_id
+					- S3C2410_CPUIRQ_OFFSET);
+
+
+	ret = pwm_config(gta02_fiq_timer, HDQ_SAMPLE_PERIOD_US * 1000,
+					HDQ_SAMPLE_PERIOD_US * 1000);
+	if (ret) {
+		printk(KERN_ERR "GTA02 FIQ: Could not configure fiq timer: %d\n",
+			ret);
+		goto err;
+	}
+
+	set_fiq_c_handler(gta02_fiq_handler);
+
+	__raw_writel(gta02_fiq_ack_mask, S3C2410_INTMOD);
+
+	pwm_enable(gta02_fiq_timer);
+
+	local_fiq_enable();
+
+	return 0;
+
+err:
+	pwm_free(gta02_fiq_timer);
+
+	return ret;
+}
+
+void gta02_fiq_disable(void)
+{
+	local_fiq_disable();
+
+	if (!gta02_fiq_timer)
+		return;
+
+	__raw_writel(0, S3C2410_INTMOD);
+	set_fiq_c_handler(NULL);
+
+	pwm_disable(gta02_fiq_timer);
+
+	pwm_free(gta02_fiq_timer);
+
+	gta02_fiq_timer = NULL;
+}
+/* -------------------- /GTA02 FIQ Handler ------------------------------------- */
diff --git a/arch/arm/mach-s3c2440/include/mach/gta02-fiq.h b/arch/arm/mach-s3c2440/include/mach/gta02-fiq.h
new file mode 100644
index 0000000..90de353
--- /dev/null
+++ b/arch/arm/mach-s3c2440/include/mach/gta02-fiq.h
@@ -0,0 +1,9 @@
+#ifndef __GTA02_FIQ_H
+#define __GTA02_FIQ_H
+
+extern void gta02_fiq_handler(void);
+extern void gta02_fiq_kick(void);
+extern int gta02_fiq_enable(void);
+extern void gta02_fiq_disable(void);
+
+#endif
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index 0e7df72..d599a8c 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -100,6 +100,8 @@
 #include <mach/gta02-pm-gps.h>
 #include <mach/gta02-pm-wlan.h>
 
+#include <mach/gta02-fiq.h>
+
 #include <linux/jbt6k74.h>
 #include <linux/glamofb.h>
 #include <linux/mfd/glamo.h>
@@ -842,6 +844,7 @@ static struct platform_device *gta02_devices[] __initdata = {
 	&gta02_nor_flash,
 	&s3c_device_timer[0],
 	&s3c_device_timer[1],
+	&s3c_device_timer[2],
 	&s3c_device_timer[3],
 	&s3c_device_iis,
 	&samsung_asoc_dma,
-- 
1.7.2.5

