1/*
2 * Copyright 2013, winocm. <winocm@icloud.com>
3 * Copyright 2013, furkanmustafa.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without modification,
7 * are permitted provided that the following conditions are met:
8 *
9 *   Redistributions of source code must retain the above copyright notice, this
10 *   list of conditions and the following disclaimer.
11 *
12 *   Redistributions in binary form must reproduce the above copyright notice, this
13 *   list of conditions and the following disclaimer in the documentation and/or
14 *   other materials provided with the distribution.
15 *
16 *   If you are going to use this software in any form that does not involve
17 *   releasing the source to this project or improving it, let me know beforehand.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30/*
31 * Platform Expert for OMAP335X
32 */
33
34#if defined(BOARD_CONFIG_OMAP335X)
35
36#include <sys/types.h>
37#include <mach/vm_param.h>
38#include <machine/machine_routines.h>
39#include <pexpert/device_tree.h>
40#include <pexpert/protos.h>
41#include <pexpert/pexpert.h>
42#include <kern/debug.h>
43#include <kern/simple_lock.h>
44#include <machine/machine_routines.h>
45#include <vm/pmap.h>
46#include <arm/pmap.h>
47
48extern int disableConsoleOutput, serialmode;
49
50void Omap3_timer_enabled(int enable);
51uint64_t Omap3_timer_value(void);
52uint64_t Omap3_get_timebase(void);
53
54#include "pe_omap335x.h"
55
56#define mmio_read(a)    (*(volatile uint32_t *)(a))
57#define mmio_write(a,v) (*(volatile uint32_t *)(a) = (v))
58#define mmio_set(a,v)   mmio_write((a), mmio_read((a)) | (v))
59#define mmio_clear(a,v) mmio_write((a), mmio_read((a)) & ~(v))
60
61#define HwReg8(x) *((volatile uint8_t*)(x))
62#define HwReg16(x) *((volatile uint16_t*)(x))
63#define HwReg32(x) *((volatile uint32_t*)(x))
64
65#define KPRINTF_PREFIX  "PE_omap335x: "
66
67extern void rtclock_intr(arm_saved_state_t * regs);
68extern void rtc_configure(uint64_t hz);
69
70vm_offset_t gOmapSerialUartBase = 0x0;
71vm_offset_t gOmapInterruptControllerBase = 0x0;
72vm_offset_t gOmapTimerBase = 0x0;
73vm_offset_t gOmapDisplayControllerBase = 0x0;
74vm_offset_t gOmapPrcmBase = 0x0;
75
76static uint64_t clock_decrementer = 0;
77static boolean_t clock_initialized = FALSE;
78static boolean_t clock_had_irq = FALSE;
79static uint64_t clock_absolute_time = 0;
80
81#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
82#define FLD_MASK(start, end)    (((1 << ((start) - (end) + 1)) - 1) << (end))
83#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
84
85static void timer_configure(void)
86{
87    /*
88     * xxx hack for etimer since it does not know time yet
89     */
90    uint64_t hz = 3276800;
91    gPEClockFrequencyInfo.timebase_frequency_hz = hz;
92
93    clock_decrementer = 1000;
94    kprintf(KPRINTF_PREFIX "decrementer frequency = %llu\n", clock_decrementer);
95
96    rtc_configure(hz);
97    return;
98}
99
100void Omap3_early_putc(int c)
101{
102    if (c == '\n')
103        Omap3_early_putc('\r');
104
105    while ((HwReg32(OMAP3_UART_BASE + SSR) & SSR_TXFIFOFULL))
106        barrier();
107
108    HwReg32(OMAP3_UART_BASE + THR) = c;
109}
110
111void Omap3_putc(int c)
112{
113    if (!gOmapSerialUartBase)
114        return;
115
116    if (c == '\n')
117        Omap3_putc('\r');
118
119    while ((HwReg32(gOmapSerialUartBase + SSR) & SSR_TXFIFOFULL))
120        barrier();
121
122    HwReg32(gOmapSerialUartBase + THR) = c;
123}
124
125int Omap3_getc(void)
126{
127    int i = 0x80000;
128    while (!(HwReg32(gOmapSerialUartBase + LSR) & LSR_DR)) {
129        i--; if(!i) return -1;
130    }
131
132    return (HwReg32(gOmapSerialUartBase + RBR));
133}
134
135void Omap3_uart_init(void)
136{
137    int baudDivisor;
138
139    assert(OMAP3_UART_BAUDRATE != 0);
140    baudDivisor = (OMAP3_UART_CLOCK / 16 / OMAP3_UART_BAUDRATE);
141
142    HwReg32(gOmapSerialUartBase + IER) = 0x00;
143    HwReg32(gOmapSerialUartBase + LCR) = LCR_BKSE | LCRVAL;
144    HwReg32(gOmapSerialUartBase + DLL) = baudDivisor & 0xFF;
145    HwReg32(gOmapSerialUartBase + DLM) = (baudDivisor >> 8) & 0xFF;
146    HwReg32(gOmapSerialUartBase + LCR) = LCRVAL;
147    HwReg32(gOmapSerialUartBase + MCR) = MCRVAL;
148    HwReg32(gOmapSerialUartBase + FCR) = FCRVAL;
149}
150
151void Omap3_interrupt_init(void)
152{
153    int i;
154
155    /*
156     * Disable interrupts
157     */
158    ml_set_interrupts_enabled(FALSE);
159
160    /*
161     * Set MIR bits to enable all interrupts
162     */
163    HwReg32(INTCPS_MIR(0)) = 0xffffffff;
164    HwReg32(INTCPS_MIR(1)) = 0xffffffff;
165    HwReg32(INTCPS_MIR(2)) = 0xffffffff;
166
167    /*
168     * Set the true bits (for interrupts we're interested in)
169     */
170    mmio_write(INTCPS_MIR_CLEAR(OMAP335X_SCH_TIMER_IRQ >> 5), 1 << (OMAP335X_SCH_TIMER_IRQ & 0x1f));
171
172    /*
173     * Set enable new IRQs/FIQs
174     */
175    HwReg32(INTCPS_CONTROL) = (1 << 0);
176
177    barrier();
178    return;
179}
180
181void Omap3_timebase_init(void)
182{
183    /*
184     * Stop the timer.
185     */
186    Omap3_timer_enabled(FALSE);
187
188    /*
189     * Enable interrupts
190     */
191    ml_set_interrupts_enabled(TRUE);
192
193    /*
194     * Set rtclock stuff
195     */
196    timer_configure();
197
198    /*
199     * Set timer decrementer defaults
200     */
201    HwReg32(gOmapTimerBase + TLDR) = 0xffffffe0;
202    HwReg32(gOmapTimerBase + TCRR) = 0xffffffe0;
203
204    HwReg32(gOmapTimerBase + TPIR) = 232000;
205    HwReg32(gOmapTimerBase + TNIR) = -768000;
206
207    HwReg32(gOmapTimerBase + TOCR) = 0;
208    HwReg32(gOmapTimerBase + TOWR) = 100;
209
210    HwReg32(gOmapTimerBase + TCLR) = (1 << 6);
211
212    /*
213     * !!! SET INTERRUPTS ENABLED ON OVERFLOW
214     */
215    HwReg32(gOmapTimerBase + TISR) = 0x7;   // 0x7; //0x2;
216    HwReg32(gOmapTimerBase + TIER) = 0x7;   // 0x7; //0x2;
217
218    kprintf(KPRINTF_PREFIX "starting timer..\n");
219
220    // /*
221    //  * Set to 32KHz
222    //  */
223    // mmio_set(gOmapPrcmBase + 0xc40, 0x40);           // CLKSEL_TIMER2_CLK = 32K_FCLK ????
224    // HwReg32(gOmapPrcmBase + 0x08) &= 0x02;
225    // mmio_set(gOmapPrcmBase + 0x400 + 0xc4, 0x02);            // CLKSEL_TIMER2_CLK = 32K_FCLK
226
227    /*
228     * Arm the timer
229     */
230    HwReg32(gOmapTimerBase + TCLR) = (1 << 0) | (1 << 1) | (2 << 10);
231
232    /*
233     * Wait for it.
234     */
235    clock_initialized = TRUE;
236
237    while (!clock_had_irq)
238        barrier();
239
240    kprintf(KPRINTF_PREFIX "timer is now up, ticks %llu\n", Omap3_timer_value());
241
242    return;
243}
244
245void Omap3_handle_interrupt(void *context)
246{
247    uint32_t irq_number = (HwReg32(INTCPS_SIR_IRQ)) & 0x7F;
248
249    if (irq_number == OMAP335X_SCH_TIMER_IRQ) {
250        /*
251         * Stop the timer
252         */
253        Omap3_timer_enabled(FALSE);
254
255        /*
256         * Clear interrupt status
257         */
258        HwReg32(gOmapTimerBase + TISR) = 0x7;   // 0x2; wrong?
259
260        /*
261         * FFFFF
262         */
263        rtclock_intr((arm_saved_state_t *) context);
264
265        /*
266         * Set new IRQ generation
267         */
268        HwReg32(INTCPS_CONTROL) = 0x1;
269
270        /*
271         * ARM IT.
272         */
273        Omap3_timer_enabled(TRUE);
274
275        /*
276         * Update absolute time
277         */
278        clock_absolute_time += (clock_decrementer - Omap3_timer_value());
279
280        clock_had_irq = 1;
281
282        return;
283    } else {
284        irq_iokit_dispatch(irq_number);
285    }
286
287    return;
288}
289
290uint64_t Omap3_get_timebase(void)
291{
292    uint32_t timestamp;
293
294    if (!clock_initialized)
295        return 0;
296
297    timestamp = Omap3_timer_value();
298
299    if (timestamp) {
300        uint64_t v = clock_absolute_time;
301        v += (uint64_t) (((uint64_t) clock_decrementer) - (uint64_t) (timestamp));
302        return v;
303    } else {
304        clock_absolute_time += clock_decrementer;
305        return clock_absolute_time;
306    }
307}
308
309uint64_t Omap3_timer_value(void)
310{
311    /*
312     * Return overflow value minus the counter
313     */
314    return 0xffffffff - (HwReg32(gOmapTimerBase + TCRR));
315}
316
317void Omap3_timer_enabled(int enable)
318{
319
320    /*
321     * Clear the TCLR [ST] bit
322     */
323    if (enable)
324        mmio_set(gOmapTimerBase + TCLR, 0x1);
325    else
326        mmio_clear(gOmapTimerBase + TCLR, 0x1);
327
328    return;
329}
330
331/*
332 * Stub for printing out to framebuffer.
333 */
334void vcputc(__unused int l, __unused int u, int c);
335
336static void _fb_putc(int c)
337{
338    if (c == '\n') {
339        vcputc(0, 0, '\r');
340    }
341    vcputc(0, 0, c);
342    Omap3_putc(c);
343}
344
345extern int serialmode;
346
347void Omap3_framebuffer_init(void)
348{
349    /*
350     * This is an emulated framebuffer.
351     */
352    void *framebuffer = pmap_steal_memory(1024 * 768 * 4);
353    void *framebuffer_phys = pmap_extract(kernel_pmap, framebuffer);
354
355    uint32_t depth = 4;
356    uint32_t width = 1024;
357    uint32_t height = 768;
358
359    uint32_t pitch = (width * depth);
360    uint32_t fb_length = (pitch * width);
361
362    PE_state.video.v_baseAddr = (unsigned long) framebuffer_phys;
363    PE_state.video.v_rowBytes = width * 2;
364    PE_state.video.v_width = width;
365    PE_state.video.v_height = height;
366    PE_state.video.v_depth = 2 * (8);   // 16bpp
367
368    kprintf(KPRINTF_PREFIX "fake framebuffer initialized\n");
369    bzero(framebuffer, (pitch * height));
370
371    char tempbuf[16];
372    initialize_screen((void *) &PE_state.video, kPETextMode);
373
374    // TEMPORARY, because beaglebone (usually) doesn't have a display
375    serialmode = 3;
376    switch_to_serial_console();
377    disableConsoleOutput = FALSE;
378
379    return;
380}
381
382void Omap3_InitCaches(void)
383{
384    kprintf(KPRINTF_PREFIX "initializing i+dcache\n");
385    cache_initialize();
386    kprintf(KPRINTF_PREFIX "done\n");
387}
388
389void PE_init_SocSupport_omap3(void)
390{
391    gPESocDispatch.uart_getc = Omap3_getc;
392    gPESocDispatch.uart_putc = Omap3_putc;
393    gPESocDispatch.uart_init = Omap3_uart_init;
394
395    gPESocDispatch.interrupt_init = Omap3_interrupt_init;
396    gPESocDispatch.timebase_init = Omap3_timebase_init;
397
398    gPESocDispatch.get_timebase = Omap3_get_timebase;
399
400    gPESocDispatch.handle_interrupt = Omap3_handle_interrupt;
401
402    gPESocDispatch.timer_value = Omap3_timer_value;
403    gPESocDispatch.timer_enabled = Omap3_timer_enabled;
404
405    gPESocDispatch.framebuffer_init = Omap3_framebuffer_init;
406
407    // init device base addresses
408    gOmapSerialUartBase = ml_io_map(OMAP3_UART_BASE, PAGE_SIZE);
409    gOmapTimerBase = ml_io_map(OMAP335X_SCH_TIMER_BASE, PAGE_SIZE);
410    gOmapInterruptControllerBase = ml_io_map(OMAP3_GIC_BASE, PAGE_SIZE);
411    // gOmapDisplayControllerBase = ml_io_map(OMAP3_DSS_BASE - 0x40, PAGE_SIZE); // doesn't apply for omap335x yet
412
413    gOmapPrcmBase = ml_io_map(0x44E00000, PAGE_SIZE);   // 0x48004000 (L4 Core / Clock Manager)
414
415    Omap3_uart_init();
416    PE_kputc = gPESocDispatch.uart_putc;
417
418    Omap3_framebuffer_init();
419
420    mmio_set(gOmapPrcmBase + 0x404, 0x2);   // Enable Timer1 Clock
421    mmio_set(gOmapPrcmBase + 0x4C4, 0x2);   // Enable Timer1 Clock
422}
423
424void PE_init_SocSupport_stub(void)
425{
426    PE_early_puts("PE_init_SocSupport: Initializing for OMAP335x\n");
427    PE_init_SocSupport_omap3();
428}
429
430#endif /* !BOARD_CONFIG_OMAP335X */
431