1#include <vm/vm.h>
2
3#include "soc_pxa.h"
4
5/* PXA Interrupt Controller Registers */
6#define PXA_ICIP	0x00
7#define PXA_ICMR	0x01
8#define PXA_ICFP	0x03
9#define PXA_ICMR2	0x28
10
11void
12PXAInterruptController::EnableInterrupt(int32 irq)
13{
14	if (irq <= 31) {
15		fRegBase[PXA_ICMR] |= 1 << irq;
16		return;
17	}
18
19	fRegBase[PXA_ICMR2] |= 1 << (irq - 32);
20}
21
22
23void
24PXAInterruptController::DisableInterrupt(int32 irq)
25{
26	if (irq <= 31) {
27		fRegBase[PXA_ICMR] &= ~(1 << irq);
28		return;
29	}
30
31	fRegBase[PXA_ICMR2] &= ~(1 << (irq - 32));
32}
33
34
35void
36PXAInterruptController::HandleInterrupt()
37{
38	for (int i=0; i < 32; i++) {
39		if (fRegBase[PXA_ICIP] & (1 << i))
40			int_io_interrupt_handler(i, true);
41	}
42}
43
44
45PXAInterruptController::PXAInterruptController(uint32_t reg_base)
46{
47	fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "intc-pxa", (void**)&fRegBase,
48		B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
49		reg_base, false);
50	if (fRegArea < 0)
51		panic("PXAInterruptController: cannot map registers!");
52
53	fRegBase[PXA_ICMR] = 0;
54	fRegBase[PXA_ICMR2] = 0;
55}
56
57
58#define PXA_TIMERS_INTERRUPT	7 /* OST_4_11 */
59
60#define PXA_OSSR	0x05
61#define PXA_OIER	0x07
62#define PXA_OSCR4	0x10
63#define PXA_OSCR5	0x11
64#define PXA_OSMR4	0x20
65#define PXA_OSMR5	0x21
66#define PXA_OMCR4	0x30
67#define PXA_OMCR5	0x31
68
69#define PXA_RES_S	(3 << 0)
70#define PXA_RES_MS	(1 << 1)
71#define PXA_RES_US	(1 << 2)
72
73#define US2S(bt)	((bt) / 1000000ULL)
74#define US2MS(bt)	((bt) / 1000ULL)
75
76void
77PXATimer::SetTimeout(bigtime_t timeout)
78{
79	uint32 val = timeout & UINT_MAX;
80	uint32 res = PXA_RES_US;
81
82	if (timeout & ~UINT_MAX) {
83		// Does not fit, so scale resolution down to milliseconds
84		if (US2MS(timeout) & ~UINT_MAX) {
85			// Still does not fit, scale down to seconds as last ditch attempt
86			val = US2S(timeout) & UINT_MAX;
87			res = PXA_RES_S;
88		} else {
89			// Fits in millisecond resolution
90			val = US2MS(timeout) & UINT_MAX;
91			res = PXA_RES_MS;
92		}
93	}
94
95	dprintf("arch_timer_set_hardware_timer(val=%" B_PRIu32 ", res=%" B_PRIu32 ")\n", val, res);
96	fRegBase[PXA_OIER] |= (1 << 4);
97	fRegBase[PXA_OMCR4] = res;
98	fRegBase[PXA_OSMR4] = val;
99	fRegBase[PXA_OSCR4] = 0; // start counting from 0 again
100}
101
102void
103PXATimer::Clear()
104{
105	fRegBase[PXA_OMCR4] = 0; // disable our timer
106	fRegBase[PXA_OIER] &= ~(1 << 4);
107}
108
109
110bigtime_t
111PXATimer::Time()
112{
113	if (fRegArea < 0)
114		return 0;
115
116	return (fRegBase != NULL) ?
117		fSystemTime + fRegBase[PXA_OSCR5] :
118		0ULL;
119}
120
121
122int32
123PXATimer::_InterruptWrapper(void *data)
124{
125	return ((PXATimer*)data)->HandleInterrupt();
126}
127
128
129int32
130PXATimer::HandleInterrupt()
131{
132	if (fRegBase[PXA_OSSR] & (1 << 4)) {
133		fRegBase[PXA_OSSR] |= (1 << 4);
134		return timer_interrupt();
135	}
136
137	if (fRegBase[PXA_OSSR]  & (1 << 5)) {
138		fRegBase[PXA_OSSR] |= (1 << 5);
139		fSystemTime += UINT_MAX + 1ULL;
140	}
141
142	return B_HANDLED_INTERRUPT;
143}
144
145
146PXATimer::PXATimer(uint32_t reg_base)
147{
148	fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "pxa-timer", (void**)&fRegBase,
149		B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
150		reg_base, false);
151	if (fRegArea < 0)
152		panic("Cannot map PXATimer registers!");
153
154	fRegBase[PXA_OIER] |= (1 << 5); // enable timekeeping timer
155	fRegBase[PXA_OMCR5] = PXA_RES_US | (1 << 7);
156	fRegBase[PXA_OSMR5] = UINT_MAX;
157	fRegBase[PXA_OSCR5] = 0;
158
159	install_io_interrupt_handler(PXA_TIMERS_INTERRUPT, &PXATimer::_InterruptWrapper, NULL, 0);
160}
161