1/*
2 * Copyright 2013, winocm. <winocm@icloud.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *   Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 *
11 *   Redistributions in binary form must reproduce the above copyright notice, this
12 *   list of conditions and the following disclaimer in the documentation and/or
13 *   other materials provided with the distribution.
14 *
15 *   If you are going to use this software in any form that does not involve
16 *   releasing the source to this project or improving it, let me know beforehand.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * Platform Expert for OMAP35xx/36xx and AM/DM37x.
31 */
32
33#if defined(BOARD_CONFIG_OMAP3530)
34
35#include <sys/types.h>
36#include <mach/vm_param.h>
37#include <machine/machine_routines.h>
38#include <pexpert/device_tree.h>
39#include <pexpert/protos.h>
40#include <pexpert/pexpert.h>
41#include <kern/debug.h>
42#include <kern/simple_lock.h>
43#include <machine/machine_routines.h>
44#include <vm/pmap.h>
45#include <arm/pmap.h>
46#include <kern/cpu_data.h>
47
48/* XXX: timer is so god awfully borked */
49void Omap3_timer_enabled(int enable);
50uint64_t Omap3_timer_value(void);
51uint64_t Omap3_get_timebase(void);
52
53#include "pe_omap3530.h"
54
55#define mmio_read(a)    (*(volatile uint32_t *)(a))
56#define mmio_write(a,v) (*(volatile uint32_t *)(a) = (v))
57#define mmio_set(a,v)   mmio_write((a), mmio_read((a)) | (v))
58#define mmio_clear(a,v) mmio_write((a), mmio_read((a)) & ~(v))
59
60#define HwReg(x) *((volatile unsigned long*)(x))
61
62#define KPRINTF_PREFIX  "PE_omap3530: "
63
64extern void rtclock_intr(arm_saved_state_t * regs);
65extern void rtc_configure(uint64_t hz);
66
67vm_offset_t gOmapSerialUartBase = 0x0;
68vm_offset_t gOmapInterruptControllerBase = 0x0;
69vm_offset_t gOmapTimerBase = 0x0;
70vm_offset_t gOmapDisplayControllerBase = 0x0;
71vm_offset_t gOmapPrcmBase = 0x0;
72
73static uint64_t clock_decrementer = 0;
74static boolean_t clock_initialized = FALSE;
75static boolean_t clock_had_irq = FALSE;
76static uint64_t clock_absolute_time = 0;
77
78#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
79#define FLD_MASK(start, end)    (((1 << ((start) - (end) + 1)) - 1) << (end))
80#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
81
82static void timer_configure(void)
83{
84    /*
85     * xxx hack for etimer since it does not know time yet
86     */
87    uint64_t hz = 3276800;
88    gPEClockFrequencyInfo.timebase_frequency_hz = hz;
89
90    clock_decrementer = 500;
91    kprintf(KPRINTF_PREFIX "decrementer frequency = %llu\n", clock_decrementer);
92
93    rtc_configure(hz);
94    return;
95}
96
97void Omap3_early_putc(int c)
98{
99    if (c == '\n')
100        Omap3_early_putc('\r');
101
102    while (!(HwReg(OMAP3_UART_BASE + LSR) & LSR_THRE))
103        barrier();
104
105    HwReg(OMAP3_UART_BASE + THR) = c;
106}
107
108void Omap3_putc(int c)
109{
110    if (!gOmapSerialUartBase)
111        return;
112
113    if (c == '\n')
114        Omap3_putc('\r');
115
116    while (!(HwReg(gOmapSerialUartBase + LSR) & LSR_THRE))
117        barrier();
118
119    HwReg(gOmapSerialUartBase + THR) = c;
120}
121
122int Omap3_getc(void)
123{
124    int i = 0x20000;
125    while (!(HwReg(gOmapSerialUartBase + LSR) & LSR_DR)) {
126        i--; if(!i) return -1;
127    }
128
129    return (HwReg(gOmapSerialUartBase + RBR));
130}
131
132void Omap3_uart_init(void)
133{
134    gOmapTimerBase = ml_io_map(OMAP3_TIMER0_BASE, PAGE_SIZE);
135    gOmapInterruptControllerBase = ml_io_map(OMAP3_GIC_BASE, PAGE_SIZE);
136    gOmapDisplayControllerBase = ml_io_map(OMAP3_DSS_BASE - 0x40, PAGE_SIZE);
137
138    /*
139     * XXX: God.
140     */
141    gOmapPrcmBase = ml_io_map(0x48004000, PAGE_SIZE);
142
143    int baudDivisor;
144    gOmapSerialUartBase = ml_io_map(OMAP3_UART_BASE, PAGE_SIZE);
145
146    assert(OMAP3_UART_BAUDRATE != 0);
147    baudDivisor = (OMAP3_UART_CLOCK / 16 / OMAP3_UART_BAUDRATE);
148
149    HwReg(gOmapSerialUartBase + IER) = 0x00;
150    HwReg(gOmapSerialUartBase + LCR) = LCR_BKSE | LCRVAL;
151    HwReg(gOmapSerialUartBase + DLL) = baudDivisor & 0xFF;
152    HwReg(gOmapSerialUartBase + DLM) = (baudDivisor >> 8) & 0xFF;
153    HwReg(gOmapSerialUartBase + LCR) = LCRVAL;
154    HwReg(gOmapSerialUartBase + MCR) = MCRVAL;
155    HwReg(gOmapSerialUartBase + FCR) = FCRVAL;
156}
157
158void Omap3_interrupt_init(void)
159{
160    int i;
161
162    /*
163     * Disable interrupts
164     */
165    ml_set_interrupts_enabled(FALSE);
166
167    /*
168     * Set MIR bits to enable all interrupts
169     */
170    HwReg(INTCPS_MIR(0)) = 0xffffffff;
171    HwReg(INTCPS_MIR(1)) = 0xffffffff;
172    HwReg(INTCPS_MIR(2)) = 0xffffffff;
173
174    /*
175     * Set the true bits
176     */
177    mmio_write(INTCPS_MIR_CLEAR(37 >> 5), 1 << (37 & 0x1f));
178
179    /*
180     * Set enable new IRQs/FIQs
181     */
182    HwReg(INTCPS_CONTROL) = (1 << 0);
183
184    barrier();
185    return;
186}
187
188void Omap3_timebase_init(void)
189{
190    /*
191     * Stop the timer.
192     */
193    Omap3_timer_enabled(FALSE);
194
195    /*
196     * Enable interrupts
197     */
198    ml_set_interrupts_enabled(TRUE);
199
200    /*
201     * Set rtclock stuff
202     */
203    timer_configure();
204
205    /*
206     * Set timer decrementer defaults
207     */
208    HwReg(gOmapTimerBase + TLDR) = 0xffffffe0;
209    HwReg(gOmapTimerBase + TCRR) = 0xffffffe0;
210
211    HwReg(gOmapTimerBase + TPIR) = 232000;
212    HwReg(gOmapTimerBase + TNIR) = -768000;
213
214    HwReg(gOmapTimerBase + TOCR) = 0;
215    HwReg(gOmapTimerBase + TOWR) = 100;
216
217    HwReg(gOmapTimerBase + TCLR) = (1 << 6);
218
219    /*
220     * !!! SET INTERRUPTS ENABLED ON OVERFLOW
221     */
222    HwReg(gOmapTimerBase + TISR) = 0x7;
223    HwReg(gOmapTimerBase + TIER) = 0x7;
224
225    /*
226     * Set to 32KHz
227     */
228    mmio_set(gOmapPrcmBase + 0xc40, 0x40);
229
230    /*
231     * Arm the timer
232     */
233    HwReg(gOmapTimerBase + TCLR) = (1 << 0) | (1 << 1) | (2 << 10);
234
235    /*
236     * Wait for it.
237     */
238    clock_initialized = TRUE;
239
240    while (!clock_had_irq)
241        barrier();
242
243    kprintf(KPRINTF_PREFIX "timer is now up, ticks %llu\n", Omap3_timer_value());
244
245    return;
246}
247
248void Omap3_handle_interrupt(void *context)
249{
250    uint32_t irq_number = (HwReg(INTCPS_SIR_IRQ)) & 0x7F;
251
252    if (irq_number == 37) {     /* GPTimer1 IRQ */
253        /*
254         * Stop the timer
255         */
256        Omap3_timer_enabled(FALSE);
257
258        /*
259         * Clear interrupt status
260         */
261        HwReg(gOmapTimerBase + TISR) = 0x7;
262
263        /*
264         * FFFFF
265         */
266        rtclock_intr((arm_saved_state_t *) context);
267
268        /*
269         * Set new IRQ generation
270         */
271        HwReg(INTCPS_CONTROL) = 0x1;
272
273        /*
274         * ARM IT.
275         */
276        Omap3_timer_enabled(TRUE);
277
278        /*
279         * Update absolute time
280         */
281        clock_absolute_time += (clock_decrementer - Omap3_timer_value());
282
283        clock_had_irq = 1;
284
285        return;
286    } else {
287        irq_iokit_dispatch(irq_number);
288    }
289
290    return;
291}
292
293uint64_t Omap3_get_timebase(void)
294{
295    uint32_t timestamp;
296
297    if (!clock_initialized)
298        return 0;
299
300    timestamp = Omap3_timer_value();
301
302    if (timestamp) {
303        uint64_t v = clock_absolute_time;
304        v += (uint64_t) (((uint64_t) clock_decrementer) - (uint64_t) (timestamp));
305        return v;
306    } else {
307        clock_absolute_time += clock_decrementer;
308        return clock_absolute_time;
309    }
310}
311
312uint64_t Omap3_timer_value(void)
313{
314    /*
315     * Return overflow value minus the counter
316     */
317    return 0xffffffff - (HwReg(gOmapTimerBase + TCRR));
318}
319
320void Omap3_timer_enabled(int enable)
321{
322    /*
323     * Clear the TCLR [ST] bit
324     */
325    if (enable)
326        HwReg(gOmapTimerBase + TCLR) |= (1 << 0);
327    else
328        HwReg(gOmapTimerBase + TCLR) &= ~(1 << 0);
329
330    return;
331}
332
333/*
334 * Stub for printing out to framebuffer.
335 */
336void vcputc(__unused int l, __unused int u, int c);
337
338static void _fb_putc(int c)
339{
340    if (c == '\n') {
341        vcputc(0, 0, '\r');
342    }
343    vcputc(0, 0, c);
344    Omap3_putc(c);
345}
346
347struct video_mode {
348    short width, height;
349    char *name;
350    uint32_t dispc_size;
351    uint32_t dispc_timing_h;
352    uint32_t dispc_timing_v;
353    uint32_t dispc_divisor;
354    uint32_t dss_divisor;
355};
356
357/* DISPC_TIMING_H bits and masks */
358#define DISPCB_HBP 20
359#define DISPCB_HFP 8
360#define DISPCB_HSW 0
361#define DISPCM_HBP 0xfff00000
362#define DISPCM_HFP 0x000fff00
363#define DISPCM_HSW 0x000000ff
364
365/* DISPC_TIMING_V bits and masks */
366#define DISPCB_VBP 20
367#define DISPCB_VFP 8
368#define DISPCB_VSW 0
369#define DISPCM_VBP 0x0ff00000
370#define DISPCM_VFP 0x0000ff00
371#define DISPCM_VSW 0x0000003f
372
373// Master clock is 864 Mhz, and changing it is a pita since it cascades to other
374// devices.
375// Pixel clock is  864 / cm_clksel_dss.dss1_alwan_fclk / dispc_divisor.divisor.pcd
376// So most of these modes are just approximate.
377// List must be in ascending order.
378struct video_mode modes[] = {
379    // 640x480@72    31.500        640    24    40    128      480    9    3    28
380    {
381        640, 480, "640x480-71",
382        ((480-1) << 16) | (640-1),
383        (128 << DISPCB_HBP) | (24 << DISPCB_HFP) | (40 << DISPCB_HSW),
384        (28 << DISPCB_VBP) | (9 << DISPCB_VFP) | (3 << DISPCB_VSW),
385        2, 14
386    },
387    //      800x600, 60Hz      40.000      800    40    128    88      600    1    4    23
388    {
389        800, 600, "800x600-59",
390        ((600-1) << 16) | (800-1),
391        (88 << DISPCB_HBP) | (40 << DISPCB_HFP) | (128 << DISPCB_HSW),
392        (23 << DISPCB_VBP) | (1 << DISPCB_VFP) | (4 << DISPCB_VSW),
393        2, 11
394    },
395    // 1024x768, 60Hz      65.000      1024    24    136    160      768    3    6    29
396    {
397        1024, 768, "1024x768-61",
398        ((768-1) << 16) | (1024-1),
399        (160 << DISPCB_HBP) | (24 << DISPCB_HFP) | (136 << DISPCB_HSW),
400        (29 << DISPCB_VBP) | (3 << DISPCB_VFP) | (6 << DISPCB_VSW),
401        1, 13
402    },
403    {
404        1280, 1024, "1280x1024-60",
405        ((1024-1) << 16) | (1280-1),
406        (248 << DISPCB_HBP) | (48 << DISPCB_HFP) | (112 << DISPCB_HSW),
407        (38 << DISPCB_VBP) | (1 << DISPCB_VFP) | (3 << DISPCB_VSW),
408        1, 8
409    },
410    /* 1280x800, dotclock 83.46MHz, approximated 86.4MHz */
411    {
412        1280, 800, "1280x800-60",
413        ((800 - 1) << 16) | (1280 - 1),
414        (200 << DISPCB_HBP) | (64 < DISPCB_HBP) | (136 << DISPCB_HSW),
415        (24 << DISPCB_VBP) | (1 << DISPCB_VFP) | (3 << DISPCB_HSW),
416        2, 5
417    },
418    /* 1366x768, dotclock 85.86MHz, approximated 86.4MHz */
419    {
420        1366, 768, "1366x768-60",
421        ((768 - 1) << 16) | (1366 - 1),
422        (216 << DISPCB_HBP) | (72 < DISPCB_HBP) | (144 << DISPCB_HSW),
423        (23 << DISPCB_VBP) | (1 << DISPCB_VFP) | (3 << DISPCB_HSW),
424        2, 5
425    },
426    /* 1440x900, dotclock 106.47MHz, approximated 108MHz */
427    {
428        1440, 900, "1440x900-60",
429        ((900 - 1) << 16) | (1440 - 1),
430        (232 << DISPCB_HBP) | (80 < DISPCB_HBP) | (152 << DISPCB_HSW),
431        (28 << DISPCB_VBP) | (1 << DISPCB_VFP) | (28 << DISPCB_HSW),
432        2, 4
433    },
434};
435
436void Omap3_framebuffer_init(void)
437{
438    int dont_mess_with_this = 0;
439    char tempbuf[16];
440
441    if (PE_parse_boot_argn("-dont-fuck-with-framebuffer", tempbuf, sizeof(tempbuf))) {
442        /* Do not fuck with the framebuffer whatsoever, rely on whatever u-boot set. */
443        dont_mess_with_this = 1;
444    }
445
446    /*
447     * This *must* be page aligned.
448     */
449    struct dispc_regs *OmapDispc = (struct dispc_regs *) (gOmapDisplayControllerBase + 0x440);
450
451    /*
452     * Set defaults
453     */
454    uint32_t timing_h, timing_v;
455    uint32_t hbp, hfp, hsw, vsw, vfp, vbp;
456
457    char tmpbuf[16];
458    struct video_mode *current_mode = &modes[0];
459
460    if (PE_parse_boot_argn("omapfbres", tmpbuf, sizeof(tmpbuf))) {
461        int cur = 0;
462        while (cur != (sizeof(modes) / sizeof(struct video_mode))) {
463            if (!strcmp(tmpbuf, modes[cur].name)) {
464                current_mode = &modes[cur];
465                break;
466            }
467            cur++;
468        }
469    }
470
471    uint32_t vs;
472    if(!dont_mess_with_this) {
473        OmapDispc->size_lcd = current_mode->dispc_size;
474        OmapDispc->timing_h = current_mode->dispc_timing_h;
475        OmapDispc->timing_v = current_mode->dispc_timing_v;
476        OmapDispc->pol_freq = 0x00007028;
477        OmapDispc->divisor = current_mode->dispc_divisor;
478        HwReg(gOmapPrcmBase + 0xE00 + 0x40) = current_mode->dss_divisor;
479        OmapDispc->config = (2 << 1);
480        OmapDispc->default_color0 = 0xffff0000;
481        OmapDispc->control = ((1 << 3) | (3 << 8));
482
483        /*
484         * Initialize display control
485         */
486        OmapDispc->control |= DISPC_ENABLE;
487        OmapDispc->default_color0 = 0xffff0000;
488
489        /*
490         * initialize lcd defaults
491         */
492        barrier();
493    }
494
495    vs = OmapDispc->size_lcd;
496    uint32_t lcd_width, lcd_height;
497
498    lcd_height = (vs >> 16) + 1;
499    lcd_width = (vs & 0xffff) + 1;
500    kprintf(KPRINTF_PREFIX "lcd size is %u x %u\n", lcd_width, lcd_height);
501
502    /*
503     * Allocate framebuffer
504     */
505    void *framebuffer = pmap_steal_memory(lcd_width * lcd_width * 4);
506    void *framebuffer_phys = pmap_extract(kernel_pmap, framebuffer);
507    bzero(framebuffer, lcd_width * lcd_height * 4);
508    kprintf(KPRINTF_PREFIX "software framebuffer at %p\n", framebuffer);
509
510    /*
511     * Set attributes in display controller
512     */
513    OmapDispc->gfx_ba0 = framebuffer_phys;
514    OmapDispc->gfx_ba1 = 0;
515    OmapDispc->gfx_position = 0;
516    OmapDispc->gfx_row_inc = 1;
517    OmapDispc->gfx_pixel_inc = 1;
518    OmapDispc->gfx_window_skip = 0;
519    OmapDispc->gfx_size = vs;
520    OmapDispc->gfx_attributes = 0x91;
521
522    /*
523     * Enable the display
524     */
525    *((volatile unsigned long *) (&OmapDispc->control)) |= 1 | (1 << 1) | (1 << 5) | (1 << 6) | (1 << 15) | (1 << 16);
526
527    /*
528     * Hook to our framebuffer
529     */
530    PE_state.video.v_baseAddr = (unsigned long) framebuffer_phys;
531    PE_state.video.v_rowBytes = lcd_width * 4;
532    PE_state.video.v_width = lcd_width;
533    PE_state.video.v_height = lcd_height;
534    PE_state.video.v_depth = 4 * (8);   // 32bpp
535
536    kprintf(KPRINTF_PREFIX "framebuffer initialized\n");
537
538    /*
539     * Enable early framebuffer.
540     */
541    if (PE_parse_boot_argn("-early-fb-debug", tempbuf, sizeof(tempbuf))) {
542        initialize_screen((void *) &PE_state.video, kPEAcquireScreen);
543    }
544
545    if (PE_parse_boot_argn("-graphics-mode", tempbuf, sizeof(tempbuf))) {
546        /*
547         * BootX like framebuffer.
548         */
549        memset(framebuffer, 0xb9, lcd_width * lcd_height * 4);
550        initialize_screen((void *) &PE_state.video, kPEGraphicsMode);
551    } else {
552        initialize_screen((void *) &PE_state.video, kPETextMode);
553    }
554
555    return;
556}
557
558void Omap3_InitCaches(void)
559{
560    kprintf(KPRINTF_PREFIX "initializing i+dcache\n");
561    cache_initialize();
562    kprintf(KPRINTF_PREFIX "done\n");
563}
564
565void PE_init_SocSupport_omap3(void)
566{
567    gPESocDispatch.uart_getc = Omap3_getc;
568    gPESocDispatch.uart_putc = Omap3_putc;
569    gPESocDispatch.uart_init = Omap3_uart_init;
570
571    gPESocDispatch.interrupt_init = Omap3_interrupt_init;
572    gPESocDispatch.timebase_init = Omap3_timebase_init;
573
574    gPESocDispatch.get_timebase = Omap3_get_timebase;
575
576    gPESocDispatch.handle_interrupt = Omap3_handle_interrupt;
577
578    gPESocDispatch.timer_value = Omap3_timer_value;
579    gPESocDispatch.timer_enabled = Omap3_timer_enabled;
580
581    gPESocDispatch.framebuffer_init = Omap3_framebuffer_init;
582
583    Omap3_uart_init();
584    PE_kputc = gPESocDispatch.uart_putc;
585
586    Omap3_framebuffer_init();
587}
588
589void PE_init_SocSupport_stub(void)
590{
591    PE_early_puts("PE_init_SocSupport: Initializing for OMAP3530\n");
592    PE_init_SocSupport_omap3();
593}
594
595#endif /* !BOARD_CONFIG_OMAP3530 */
596