summaryrefslogtreecommitdiffstats
path: root/firmware/target/mips/ingenic_x1000/system-target.h
blob: ed077a3cce4532721e5b49a075b44d8777aa6619 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2021 Aidan MacDonald
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#ifndef __SYSTEM_TARGET_H__
#define __SYSTEM_TARGET_H__

#ifdef DEBUG
/* Define this to get CPU idle stats, visible in the debug menu. */
# define X1000_CPUIDLE_STATS
#endif

#include "cpu.h"
#include "mmu-mips.h"
#include "mipsregs.h"
#include "mipsr2-endian.h"
#include "system-mips.h"
#include <stdint.h>

/* Rockbox API */
#define enable_irq()        set_c0_status(ST0_IE)
#define disable_irq()       clear_c0_status(ST0_IE)
#define disable_irq_save()  set_irq_level(0)
#define restore_irq(arg)    write_c0_status(arg)
#define HIGHEST_IRQ_LEVEL   0

static inline int set_irq_level(int lev)
{
    unsigned reg, oldreg;
    reg = oldreg = read_c0_status();
    if(lev)
        reg |= ST0_IE;
    else
        reg &= ~ST0_IE;

    write_c0_status(reg);
    return oldreg;
}

#ifdef X1000_CPUIDLE_STATS
/* CPU idle stats, updated each kernel tick in kernel-x1000.c */
extern int __cpu_idle_avg;
extern int __cpu_idle_cur;
extern uint32_t __cpu_idle_ticks;
extern uint32_t __cpu_idle_reftick;
#endif

static inline uint32_t __ost_read32(void);
static inline void core_sleep(void)
{
#ifdef X1000_CPUIDLE_STATS
    uint32_t t1 = __ost_read32();
#endif

    __asm__ __volatile__(
        ".set push\n\t"
        ".set mips32r2\n\t"
        "mfc0 $8, $12\n\t"
        "move $9, $8\n\t"
        "la   $10, 0x8000000\n\t"
        "or   $8, $10\n\t"
        "mtc0 $8, $12\n\t"
        "wait\n\t"
        "mtc0 $9, $12\n\t"
        ".set pop\n\t"
        ::: "t0", "t1", "t2");

#ifdef X1000_CPUIDLE_STATS
    uint32_t t2 = __ost_read32();
    __cpu_idle_ticks += t2 - t1;
#endif

    enable_irq();
}

/* IRQ control */
typedef void(*irq_handler_t)(void);

extern irq_handler_t system_set_irq_handler(int irq, irq_handler_t handler);
extern void system_enable_irq(int irq);
extern void system_disable_irq(int irq);

extern void system_early_init(void) INIT_ATTR;

/* Simple delay API */
#define OST_FREQUENCY       (X1000_EXCLK_FREQ / 4)
#define OST_TICKS_PER_US    (OST_FREQUENCY / 1000000)
#define MAX_OST_DELAY_ARG   0x7fffffff
#define MAX_UDELAY_ARG      (MAX_OST_DELAY_ARG / OST_TICKS_PER_US)
#define MAX_MDELAY_ARG      (MAX_UDELAY_ARG / 1000)

/* Macros adapted from include/linux/delay.h,
 *   Copyright (C) 1993 Linus Torvalds
 *
 * These optimize away all calculations to compile time for the common case
 * of small constant arguments, reducing to a single __ost_delay() call.
 */

#define udelay(n) \
    ((__builtin_constant_p(n) && (n) <= MAX_UDELAY_ARG) ? \
      __ost_delay((n) * OST_TICKS_PER_US) : __udelay((n)))

#define mdelay(n) \
    ((__builtin_constant_p(n) && (n) <= MAX_MDELAY_ARG) ? \
      __ost_delay((n) * 1000 * OST_TICKS_PER_US) : __mdelay((n)))

/* Slow path implementations which handle their full argument range by
 * looping and calling __ost_delay() repeatedly.
 */
extern void __udelay(uint32_t us);
extern void __mdelay(uint32_t ms);

/* Read full 64-bit OST counter value; this requires disabling IRQs
 * to safely read the counter.
 */
extern uint64_t __ost_read64(void);

static inline uint32_t __ost_read32(void)
{
    /* Read OST_2CNTL using raw address to avoid exposing internal headers.
     * The 64-bit counter is read with IRQs disabled and since threads are
     * not pre-emptive in Rockbox we won't trash anybody's 64-bit read by
     * reading the low count without locking.
     */
    return *(const volatile uint32_t*)0xb2000020;
}

/* NOTE: it is required that count < MAX_OST_DELAY_ARG, this is to provide
 * some slack in the 32-bit counter so we can reliably detect the timeout.
 */
static inline void __ost_delay(uint32_t count)
{
    /* Add one to ensure we delay for at least the time given */
    count += 1;
    uint32_t start = __ost_read32();
    while(__ost_read32() - start < count);
}

#endif /* __SYSTEM_TARGET_H__ */