aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-s3c2440/gta02-fiq.c
blob: d91c52341d537c8898ea50111441d5d5516d7f6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#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>
#include <linux/hdq.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)

#ifdef CONFIG_HDQ_GPIO_BITBANG
#define FIQ_DIVISOR_HDQ DIVISOR_FROM_US(HDQ_SAMPLE_PERIOD_US)
extern int hdq_fiq_handler(void);
#endif

/* 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
	 */

#ifdef CONFIG_HDQ_GPIO_BITBANG
	keep_running = hdq_fiq_handler();
#endif
	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("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("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 ------------------------------------- */