paul@68 | 1 | /* |
paul@68 | 2 | * Interrupt handling. |
paul@68 | 3 | * |
paul@121 | 4 | * Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> |
paul@68 | 5 | * |
paul@68 | 6 | * This program is free software: you can redistribute it and/or modify |
paul@68 | 7 | * it under the terms of the GNU General Public License as published by |
paul@68 | 8 | * the Free Software Foundation, either version 3 of the License, or |
paul@68 | 9 | * (at your option) any later version. |
paul@68 | 10 | * |
paul@68 | 11 | * This program is distributed in the hope that it will be useful, |
paul@68 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@68 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@68 | 14 | * GNU General Public License for more details. |
paul@68 | 15 | * |
paul@68 | 16 | * You should have received a copy of the GNU General Public License |
paul@68 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
paul@68 | 18 | */ |
paul@68 | 19 | |
paul@108 | 20 | #ifdef CONFIG_CPU_JZ4730_MINIPC |
paul@108 | 21 | #include "minipc.h" |
paul@108 | 22 | #else |
paul@108 | 23 | #include "nanonote.h" |
paul@108 | 24 | #endif |
paul@108 | 25 | |
paul@68 | 26 | #include "board-specific.h" |
paul@68 | 27 | #include "board.h" |
paul@68 | 28 | #include "lcd.h" |
paul@68 | 29 | #include "jzlcd.h" |
paul@68 | 30 | #include "cpu.h" |
paul@121 | 31 | #include "irq.h" |
paul@68 | 32 | |
paul@68 | 33 | extern vidinfo_t panel_info; |
paul@68 | 34 | |
paul@121 | 35 | void next_pixel(unsigned short *x, unsigned short *y) |
paul@68 | 36 | { |
paul@121 | 37 | (*x)++; |
paul@121 | 38 | if (*x >= panel_info.vl_col) { |
paul@121 | 39 | *x = 0; |
paul@121 | 40 | (*y)++; |
paul@121 | 41 | if (*y >= panel_info.vl_row) |
paul@121 | 42 | *y = 0; |
paul@68 | 43 | } |
paul@68 | 44 | } |
paul@68 | 45 | |
paul@132 | 46 | /* Task management. */ |
paul@132 | 47 | |
paul@133 | 48 | enum { max_tasks = 3 }; |
paul@133 | 49 | static u32 stack_pointers[max_tasks]; |
paul@133 | 50 | u8 current_task = 0; |
paul@133 | 51 | u32 current_stack_pointer; |
paul@133 | 52 | extern u32 _got_copy_start; |
paul@133 | 53 | |
paul@143 | 54 | const u32 stack_start = 0x80080000; |
paul@132 | 55 | const u32 stack_size = 0x00002000; |
paul@132 | 56 | const u32 pagesize = 4 * 1024; |
paul@132 | 57 | const u32 framesize = 120; /* see the handlers */ |
paul@132 | 58 | |
paul@68 | 59 | /* Tasks. */ |
paul@68 | 60 | |
paul@121 | 61 | void plot_pattern(unsigned short pixel_type, unsigned short x, unsigned short y) |
paul@68 | 62 | { |
paul@68 | 63 | while (1) { |
paul@68 | 64 | if (pixel_type) |
paul@138 | 65 | test_pixel(x, y, pixel_type); |
paul@68 | 66 | else |
paul@68 | 67 | clear_pixel(x, y); |
paul@121 | 68 | next_pixel(&x, &y); |
paul@68 | 69 | udelay(100); |
paul@68 | 70 | } |
paul@68 | 71 | } |
paul@68 | 72 | |
paul@68 | 73 | /* Initialisation and handling. */ |
paul@68 | 74 | |
paul@68 | 75 | void irq_init() |
paul@68 | 76 | { |
paul@136 | 77 | handle_error_level(); |
paul@68 | 78 | timer_init_irq(); |
paul@84 | 79 | init_interrupts(); |
paul@68 | 80 | } |
paul@68 | 81 | |
paul@68 | 82 | void irq_handle() |
paul@68 | 83 | { |
paul@68 | 84 | unsigned short i; |
paul@68 | 85 | |
paul@68 | 86 | /* Check interrupt identity. */ |
paul@68 | 87 | |
paul@68 | 88 | if (REG_INTC_IPR & (1 << TIMER_CHAN_IRQ)) { |
paul@68 | 89 | |
paul@133 | 90 | /* Switch task. */ |
paul@68 | 91 | |
paul@143 | 92 | switch_task(); |
paul@68 | 93 | |
paul@68 | 94 | /* Clear interrupt status. */ |
paul@68 | 95 | |
paul@68 | 96 | __intc_ack_irq(TIMER_CHAN_IRQ); |
paul@68 | 97 | __tcu_clear_full_match_flag(TIMER_CHAN); |
paul@68 | 98 | |
paul@68 | 99 | /* Handle other interrupts, anyway. */ |
paul@68 | 100 | |
paul@68 | 101 | } else { |
paul@68 | 102 | for (i = 0; i < 32; i++) { |
paul@68 | 103 | if (REG_INTC_IPR & (1 << i)) |
paul@68 | 104 | __intc_ack_irq(i); |
paul@68 | 105 | } |
paul@68 | 106 | } |
paul@68 | 107 | } |
paul@68 | 108 | |
paul@133 | 109 | void start_task(unsigned short task) |
paul@133 | 110 | { |
paul@138 | 111 | u32 args[] = {task, 0, (task - 1) * 60}; |
paul@136 | 112 | u32 virtual, physical; |
paul@133 | 113 | |
paul@133 | 114 | /* |
paul@133 | 115 | Each task employs a stack at a multiple of the given start address in |
paul@133 | 116 | physical memory, but at the same address in virtual memory. |
paul@133 | 117 | */ |
paul@133 | 118 | |
paul@142 | 119 | physical = stack_start + stack_size * task; |
paul@143 | 120 | virtual = physical; |
paul@136 | 121 | |
paul@142 | 122 | init_page_table(page_table_start, virtual - pagesize * 2, physical - pagesize * 2, pagesize, 0x1e, task); |
paul@136 | 123 | |
paul@133 | 124 | /* |
paul@133 | 125 | Set the stack for the new task, initialising the global pointer and |
paul@133 | 126 | return address. |
paul@133 | 127 | */ |
paul@133 | 128 | |
paul@142 | 129 | init_stack(physical, _got_copy_start, (void (*)()) plot_pattern, args, 3); |
paul@133 | 130 | |
paul@133 | 131 | /* Advance the stack pointer so that the adopted frame will be found. */ |
paul@133 | 132 | |
paul@142 | 133 | stack_pointers[task] = virtual - framesize; |
paul@131 | 134 | } |
paul@133 | 135 | |
paul@133 | 136 | void switch_task() |
paul@133 | 137 | { |
paul@133 | 138 | /* |
paul@133 | 139 | Get the current stack pointer. This is obtained just after saving the |
paul@133 | 140 | task's state. |
paul@133 | 141 | */ |
paul@133 | 142 | |
paul@133 | 143 | stack_pointers[current_task] = current_stack_pointer; |
paul@133 | 144 | |
paul@133 | 145 | /* Switch the current task. */ |
paul@133 | 146 | |
paul@133 | 147 | current_task++; |
paul@133 | 148 | if (current_task == max_tasks) current_task = 1; |
paul@133 | 149 | |
paul@133 | 150 | /* |
paul@133 | 151 | Set the current stack pointer. This is actually set just before |
paul@133 | 152 | restoring the task's state. |
paul@133 | 153 | */ |
paul@133 | 154 | |
paul@133 | 155 | current_stack_pointer = stack_pointers[current_task]; |
paul@133 | 156 | } |
paul@134 | 157 | |
paul@134 | 158 | void invoke_task(unsigned short task) |
paul@134 | 159 | { |
paul@134 | 160 | current_task = task; |
paul@134 | 161 | current_stack_pointer = stack_pointers[current_task]; |
paul@134 | 162 | set_task(current_task); |
paul@134 | 163 | |
paul@134 | 164 | asm volatile( |
paul@134 | 165 | ".set noat\n" |
paul@134 | 166 | "move $sp, %0\n" |
paul@134 | 167 | "addi $sp, $sp, 120\n" |
paul@134 | 168 | "lw $at, -4($sp)\n" |
paul@134 | 169 | "lw $v0, -8($sp)\n" |
paul@134 | 170 | "lw $v1, -12($sp)\n" |
paul@134 | 171 | "lw $a0, -16($sp)\n" |
paul@134 | 172 | "lw $a1, -20($sp)\n" |
paul@134 | 173 | "lw $a2, -24($sp)\n" |
paul@134 | 174 | "lw $a3, -28($sp)\n" |
paul@134 | 175 | "lw $t0, -32($sp)\n" |
paul@134 | 176 | "lw $t1, -36($sp)\n" |
paul@134 | 177 | "lw $t2, -40($sp)\n" |
paul@134 | 178 | "lw $t3, -44($sp)\n" |
paul@134 | 179 | "lw $t4, -48($sp)\n" |
paul@134 | 180 | "lw $t5, -52($sp)\n" |
paul@134 | 181 | "lw $t6, -56($sp)\n" |
paul@134 | 182 | "lw $t7, -60($sp)\n" |
paul@134 | 183 | "lw $s0, -64($sp)\n" |
paul@134 | 184 | "lw $s1, -68($sp)\n" |
paul@134 | 185 | "lw $s2, -72($sp)\n" |
paul@134 | 186 | "lw $s3, -76($sp)\n" |
paul@134 | 187 | "lw $s4, -80($sp)\n" |
paul@134 | 188 | "lw $s5, -84($sp)\n" |
paul@134 | 189 | "lw $s6, -88($sp)\n" |
paul@134 | 190 | "lw $s7, -92($sp)\n" |
paul@134 | 191 | "lw $t8, -96($sp)\n" |
paul@134 | 192 | "lw $t9, -100($sp)\n" |
paul@134 | 193 | "lw $gp, -104($sp)\n" |
paul@134 | 194 | "lw $fp, -108($sp)\n" |
paul@134 | 195 | "lw $ra, -112($sp)\n" |
paul@134 | 196 | "lw $k0, -116($sp)\n" |
paul@142 | 197 | "mtc0 $k0, $14\n" /* CP0_EPC */ |
paul@134 | 198 | "nop\n" |
paul@134 | 199 | "jr $ra\n" |
paul@134 | 200 | "nop" |
paul@134 | 201 | : |
paul@134 | 202 | : "r" (current_stack_pointer) |
paul@134 | 203 | ); |
paul@134 | 204 | } |