Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

C6X: early boot code

Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>

This patch provides the early boot code for C6X architecture. There is a
16 entry vector table which is used to direct reset and interrupt events. The
vector table entries contain a small amount of code (maximum of 8 opcodes)
which simply branches to the actual event handling code.

The head.S code simply clears BSS, setups up a few control registers, and calls
machine_init followed by start_kernel. The machine_init code in setup.c does
the early flat tree parsing (memory, commandline, etc). At setup_arch time, the
code does the usual memory setup and minimally scans the devicetree for any
needed information.

Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

authored by

Aurelien Jacquiot and committed by
Mark Salter
c1a144d7 c278400c

+663
+84
arch/c6x/kernel/head.S
··· 1 + ; 2 + ; Port on Texas Instruments TMS320C6x architecture 3 + ; 4 + ; Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated 5 + ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) 6 + ; 7 + ; This program is free software; you can redistribute it and/or modify 8 + ; it under the terms of the GNU General Public License version 2 as 9 + ; published by the Free Software Foundation. 10 + ; 11 + #include <linux/linkage.h> 12 + #include <linux/of_fdt.h> 13 + #include <asm/asm-offsets.h> 14 + 15 + __HEAD 16 + ENTRY(_c_int00) 17 + ;; Save magic and pointer 18 + MV .S1 A4,A10 19 + MV .S2 B4,B10 20 + MVKL .S2 __bss_start,B5 21 + MVKH .S2 __bss_start,B5 22 + MVKL .S2 __bss_stop,B6 23 + MVKH .S2 __bss_stop,B6 24 + SUB .L2 B6,B5,B6 ; bss size 25 + 26 + ;; Set the stack pointer 27 + MVKL .S2 current_ksp,B0 28 + MVKH .S2 current_ksp,B0 29 + LDW .D2T2 *B0,B15 30 + 31 + ;; clear bss 32 + SHR .S2 B6,3,B0 ; number of dwords to clear 33 + ZERO .L2 B13 34 + ZERO .L2 B12 35 + bss_loop: 36 + BDEC .S2 bss_loop,B0 37 + NOP 3 38 + CMPLT .L2 B0,0,B1 39 + [!B1] STDW .D2T2 B13:B12,*B5++[1] 40 + 41 + NOP 4 42 + AND .D2 ~7,B15,B15 43 + 44 + ;; Clear GIE and PGIE 45 + MVC .S2 CSR,B2 46 + CLR .S2 B2,0,1,B2 47 + MVC .S2 B2,CSR 48 + MVC .S2 TSR,B2 49 + CLR .S2 B2,0,1,B2 50 + MVC .S2 B2,TSR 51 + MVC .S2 ITSR,B2 52 + CLR .S2 B2,0,1,B2 53 + MVC .S2 B2,ITSR 54 + MVC .S2 NTSR,B2 55 + CLR .S2 B2,0,1,B2 56 + MVC .S2 B2,NTSR 57 + 58 + ;; pass DTB pointer to machine_init (or zero if none) 59 + MVKL .S1 OF_DT_HEADER,A0 60 + MVKH .S1 OF_DT_HEADER,A0 61 + CMPEQ .L1 A10,A0,A0 62 + [A0] MV .S1X B10,A4 63 + [!A0] MVK .S1 0,A4 64 + 65 + #ifdef CONFIG_C6X_BIG_KERNEL 66 + MVKL .S1 machine_init,A0 67 + MVKH .S1 machine_init,A0 68 + B .S2X A0 69 + ADDKPC .S2 0f,B3,4 70 + 0: 71 + #else 72 + CALLP .S2 machine_init,B3 73 + #endif 74 + 75 + ;; Jump to Linux init 76 + #ifdef CONFIG_C6X_BIG_KERNEL 77 + MVKL .S1 start_kernel,A0 78 + MVKH .S1 start_kernel,A0 79 + B .S2X A0 80 + #else 81 + B .S2 start_kernel 82 + #endif 83 + NOP 5 84 + L1: BNOP .S2 L1,5
+498
arch/c6x/kernel/setup.c
··· 1 + /* 2 + * Port on Texas Instruments TMS320C6x architecture 3 + * 4 + * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated 5 + * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + #include <linux/dma-mapping.h> 12 + #include <linux/memblock.h> 13 + #include <linux/seq_file.h> 14 + #include <linux/bootmem.h> 15 + #include <linux/clkdev.h> 16 + #include <linux/initrd.h> 17 + #include <linux/kernel.h> 18 + #include <linux/module.h> 19 + #include <linux/of_fdt.h> 20 + #include <linux/string.h> 21 + #include <linux/errno.h> 22 + #include <linux/cache.h> 23 + #include <linux/delay.h> 24 + #include <linux/sched.h> 25 + #include <linux/clk.h> 26 + #include <linux/fs.h> 27 + #include <linux/of.h> 28 + 29 + 30 + #include <asm/sections.h> 31 + #include <asm/div64.h> 32 + #include <asm/setup.h> 33 + #include <asm/dscr.h> 34 + #include <asm/clock.h> 35 + #include <asm/soc.h> 36 + 37 + static const char *c6x_soc_name; 38 + 39 + int c6x_num_cores; 40 + EXPORT_SYMBOL_GPL(c6x_num_cores); 41 + 42 + unsigned int c6x_silicon_rev; 43 + EXPORT_SYMBOL_GPL(c6x_silicon_rev); 44 + 45 + /* 46 + * Device status register. This holds information 47 + * about device configuration needed by some drivers. 48 + */ 49 + unsigned int c6x_devstat; 50 + EXPORT_SYMBOL_GPL(c6x_devstat); 51 + 52 + /* 53 + * Some SoCs have fuse registers holding a unique MAC 54 + * address. This is parsed out of the device tree with 55 + * the resulting MAC being held here. 56 + */ 57 + unsigned char c6x_fuse_mac[6]; 58 + 59 + unsigned long memory_start; 60 + unsigned long memory_end; 61 + 62 + unsigned long ram_start; 63 + unsigned long ram_end; 64 + 65 + /* Uncached memory for DMA consistent use (memdma=) */ 66 + static unsigned long dma_start __initdata; 67 + static unsigned long dma_size __initdata; 68 + 69 + char c6x_command_line[COMMAND_LINE_SIZE]; 70 + 71 + #if defined(CONFIG_CMDLINE_BOOL) 72 + static const char default_command_line[COMMAND_LINE_SIZE] __section(.cmdline) = 73 + CONFIG_CMDLINE; 74 + #endif 75 + 76 + struct cpuinfo_c6x { 77 + const char *cpu_name; 78 + const char *cpu_voltage; 79 + const char *mmu; 80 + const char *fpu; 81 + char *cpu_rev; 82 + unsigned int core_id; 83 + char __cpu_rev[5]; 84 + }; 85 + 86 + static DEFINE_PER_CPU(struct cpuinfo_c6x, cpu_data); 87 + 88 + unsigned int ticks_per_ns_scaled; 89 + EXPORT_SYMBOL(ticks_per_ns_scaled); 90 + 91 + unsigned int c6x_core_freq; 92 + 93 + static void __init get_cpuinfo(void) 94 + { 95 + unsigned cpu_id, rev_id, csr; 96 + struct clk *coreclk = clk_get_sys(NULL, "core"); 97 + unsigned long core_khz; 98 + u64 tmp; 99 + struct cpuinfo_c6x *p; 100 + struct device_node *node, *np; 101 + 102 + p = &per_cpu(cpu_data, smp_processor_id()); 103 + 104 + if (!IS_ERR(coreclk)) 105 + c6x_core_freq = clk_get_rate(coreclk); 106 + else { 107 + printk(KERN_WARNING 108 + "Cannot find core clock frequency. Using 700MHz\n"); 109 + c6x_core_freq = 700000000; 110 + } 111 + 112 + core_khz = c6x_core_freq / 1000; 113 + 114 + tmp = (uint64_t)core_khz << C6X_NDELAY_SCALE; 115 + do_div(tmp, 1000000); 116 + ticks_per_ns_scaled = tmp; 117 + 118 + csr = get_creg(CSR); 119 + cpu_id = csr >> 24; 120 + rev_id = (csr >> 16) & 0xff; 121 + 122 + p->mmu = "none"; 123 + p->fpu = "none"; 124 + p->cpu_voltage = "unknown"; 125 + 126 + switch (cpu_id) { 127 + case 0: 128 + p->cpu_name = "C67x"; 129 + p->fpu = "yes"; 130 + break; 131 + case 2: 132 + p->cpu_name = "C62x"; 133 + break; 134 + case 8: 135 + p->cpu_name = "C64x"; 136 + break; 137 + case 12: 138 + p->cpu_name = "C64x"; 139 + break; 140 + case 16: 141 + p->cpu_name = "C64x+"; 142 + p->cpu_voltage = "1.2"; 143 + break; 144 + default: 145 + p->cpu_name = "unknown"; 146 + break; 147 + } 148 + 149 + if (cpu_id < 16) { 150 + switch (rev_id) { 151 + case 0x1: 152 + if (cpu_id > 8) { 153 + p->cpu_rev = "DM640/DM641/DM642/DM643"; 154 + p->cpu_voltage = "1.2 - 1.4"; 155 + } else { 156 + p->cpu_rev = "C6201"; 157 + p->cpu_voltage = "2.5"; 158 + } 159 + break; 160 + case 0x2: 161 + p->cpu_rev = "C6201B/C6202/C6211"; 162 + p->cpu_voltage = "1.8"; 163 + break; 164 + case 0x3: 165 + p->cpu_rev = "C6202B/C6203/C6204/C6205"; 166 + p->cpu_voltage = "1.5"; 167 + break; 168 + case 0x201: 169 + p->cpu_rev = "C6701 revision 0 (early CPU)"; 170 + p->cpu_voltage = "1.8"; 171 + break; 172 + case 0x202: 173 + p->cpu_rev = "C6701/C6711/C6712"; 174 + p->cpu_voltage = "1.8"; 175 + break; 176 + case 0x801: 177 + p->cpu_rev = "C64x"; 178 + p->cpu_voltage = "1.5"; 179 + break; 180 + default: 181 + p->cpu_rev = "unknown"; 182 + } 183 + } else { 184 + p->cpu_rev = p->__cpu_rev; 185 + snprintf(p->__cpu_rev, sizeof(p->__cpu_rev), "0x%x", cpu_id); 186 + } 187 + 188 + p->core_id = get_coreid(); 189 + 190 + node = of_find_node_by_name(NULL, "cpus"); 191 + if (node) { 192 + for_each_child_of_node(node, np) 193 + if (!strcmp("cpu", np->name)) 194 + ++c6x_num_cores; 195 + of_node_put(node); 196 + } 197 + 198 + node = of_find_node_by_name(NULL, "soc"); 199 + if (node) { 200 + if (of_property_read_string(node, "model", &c6x_soc_name)) 201 + c6x_soc_name = "unknown"; 202 + of_node_put(node); 203 + } else 204 + c6x_soc_name = "unknown"; 205 + 206 + printk(KERN_INFO "CPU%d: %s rev %s, %s volts, %uMHz\n", 207 + p->core_id, p->cpu_name, p->cpu_rev, 208 + p->cpu_voltage, c6x_core_freq / 1000000); 209 + } 210 + 211 + /* 212 + * Early parsing of the command line 213 + */ 214 + static u32 mem_size __initdata; 215 + 216 + /* "mem=" parsing. */ 217 + static int __init early_mem(char *p) 218 + { 219 + if (!p) 220 + return -EINVAL; 221 + 222 + mem_size = memparse(p, &p); 223 + /* don't remove all of memory when handling "mem={invalid}" */ 224 + if (mem_size == 0) 225 + return -EINVAL; 226 + 227 + return 0; 228 + } 229 + early_param("mem", early_mem); 230 + 231 + /* "memdma=<size>[@<address>]" parsing. */ 232 + static int __init early_memdma(char *p) 233 + { 234 + if (!p) 235 + return -EINVAL; 236 + 237 + dma_size = memparse(p, &p); 238 + if (*p == '@') 239 + dma_start = memparse(p, &p); 240 + 241 + return 0; 242 + } 243 + early_param("memdma", early_memdma); 244 + 245 + int __init c6x_add_memory(phys_addr_t start, unsigned long size) 246 + { 247 + static int ram_found __initdata; 248 + 249 + /* We only handle one bank (the one with PAGE_OFFSET) for now */ 250 + if (ram_found) 251 + return -EINVAL; 252 + 253 + if (start > PAGE_OFFSET || PAGE_OFFSET >= (start + size)) 254 + return 0; 255 + 256 + ram_start = start; 257 + ram_end = start + size; 258 + 259 + ram_found = 1; 260 + return 0; 261 + } 262 + 263 + /* 264 + * Do early machine setup and device tree parsing. This is called very 265 + * early on the boot process. 266 + */ 267 + notrace void __init machine_init(unsigned long dt_ptr) 268 + { 269 + struct boot_param_header *dtb = __va(dt_ptr); 270 + struct boot_param_header *fdt = (struct boot_param_header *)_fdt_start; 271 + 272 + /* interrupts must be masked */ 273 + set_creg(IER, 2); 274 + 275 + /* 276 + * Set the Interrupt Service Table (IST) to the beginning of the 277 + * vector table. 278 + */ 279 + set_ist(_vectors_start); 280 + 281 + lockdep_init(); 282 + 283 + /* 284 + * dtb is passed in from bootloader. 285 + * fdt is linked in blob. 286 + */ 287 + if (dtb && dtb != fdt) 288 + fdt = dtb; 289 + 290 + /* Do some early initialization based on the flat device tree */ 291 + early_init_devtree(fdt); 292 + 293 + /* parse_early_param needs a boot_command_line */ 294 + strlcpy(boot_command_line, c6x_command_line, COMMAND_LINE_SIZE); 295 + parse_early_param(); 296 + } 297 + 298 + void __init setup_arch(char **cmdline_p) 299 + { 300 + int bootmap_size; 301 + struct memblock_region *reg; 302 + 303 + printk(KERN_INFO "Initializing kernel\n"); 304 + 305 + /* Initialize command line */ 306 + *cmdline_p = c6x_command_line; 307 + 308 + memblock_init(); 309 + 310 + memory_end = ram_end; 311 + memory_end &= ~(PAGE_SIZE - 1); 312 + 313 + if (mem_size && (PAGE_OFFSET + PAGE_ALIGN(mem_size)) < memory_end) 314 + memory_end = PAGE_OFFSET + PAGE_ALIGN(mem_size); 315 + 316 + /* add block that this kernel can use */ 317 + memblock_add(PAGE_OFFSET, memory_end - PAGE_OFFSET); 318 + 319 + /* reserve kernel text/data/bss */ 320 + memblock_reserve(PAGE_OFFSET, 321 + PAGE_ALIGN((unsigned long)&_end - PAGE_OFFSET)); 322 + 323 + if (dma_size) { 324 + /* align to cacheability granularity */ 325 + dma_size = CACHE_REGION_END(dma_size); 326 + 327 + if (!dma_start) 328 + dma_start = memory_end - dma_size; 329 + 330 + /* align to cacheability granularity */ 331 + dma_start = CACHE_REGION_START(dma_start); 332 + 333 + /* reserve DMA memory taken from kernel memory */ 334 + if (memblock_is_region_memory(dma_start, dma_size)) 335 + memblock_reserve(dma_start, dma_size); 336 + } 337 + 338 + memory_start = PAGE_ALIGN((unsigned int) &_end); 339 + 340 + printk(KERN_INFO "Memory Start=%08lx, Memory End=%08lx\n", 341 + memory_start, memory_end); 342 + 343 + #ifdef CONFIG_BLK_DEV_INITRD 344 + /* 345 + * Reserve initrd memory if in kernel memory. 346 + */ 347 + if (initrd_start < initrd_end) 348 + if (memblock_is_region_memory(initrd_start, 349 + initrd_end - initrd_start)) 350 + memblock_reserve(initrd_start, 351 + initrd_end - initrd_start); 352 + #endif 353 + 354 + init_mm.start_code = (unsigned long) &_stext; 355 + init_mm.end_code = (unsigned long) &_etext; 356 + init_mm.end_data = memory_start; 357 + init_mm.brk = memory_start; 358 + 359 + /* 360 + * Give all the memory to the bootmap allocator, tell it to put the 361 + * boot mem_map at the start of memory 362 + */ 363 + bootmap_size = init_bootmem_node(NODE_DATA(0), 364 + memory_start >> PAGE_SHIFT, 365 + PAGE_OFFSET >> PAGE_SHIFT, 366 + memory_end >> PAGE_SHIFT); 367 + memblock_reserve(memory_start, bootmap_size); 368 + 369 + memblock_analyze(); 370 + unflatten_device_tree(); 371 + 372 + c6x_cache_init(); 373 + 374 + /* Set the whole external memory as non-cacheable */ 375 + disable_caching(ram_start, ram_end - 1); 376 + 377 + /* Set caching of external RAM used by Linux */ 378 + for_each_memblock(memory, reg) 379 + enable_caching(CACHE_REGION_START(reg->base), 380 + CACHE_REGION_START(reg->base + reg->size - 1)); 381 + 382 + #ifdef CONFIG_BLK_DEV_INITRD 383 + /* 384 + * Enable caching for initrd which falls outside kernel memory. 385 + */ 386 + if (initrd_start < initrd_end) { 387 + if (!memblock_is_region_memory(initrd_start, 388 + initrd_end - initrd_start)) 389 + enable_caching(CACHE_REGION_START(initrd_start), 390 + CACHE_REGION_START(initrd_end - 1)); 391 + } 392 + #endif 393 + 394 + /* 395 + * Disable caching for dma coherent memory taken from kernel memory. 396 + */ 397 + if (dma_size && memblock_is_region_memory(dma_start, dma_size)) 398 + disable_caching(dma_start, 399 + CACHE_REGION_START(dma_start + dma_size - 1)); 400 + 401 + /* Initialize the coherent memory allocator */ 402 + coherent_mem_init(dma_start, dma_size); 403 + 404 + /* 405 + * Free all memory as a starting point. 406 + */ 407 + free_bootmem(PAGE_OFFSET, memory_end - PAGE_OFFSET); 408 + 409 + /* 410 + * Then reserve memory which is already being used. 411 + */ 412 + for_each_memblock(reserved, reg) { 413 + pr_debug("reserved - 0x%08x-0x%08x\n", 414 + (u32) reg->base, (u32) reg->size); 415 + reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT); 416 + } 417 + 418 + max_low_pfn = PFN_DOWN(memory_end); 419 + min_low_pfn = PFN_UP(memory_start); 420 + max_mapnr = max_low_pfn - min_low_pfn; 421 + 422 + /* Get kmalloc into gear */ 423 + paging_init(); 424 + 425 + /* 426 + * Probe for Device State Configuration Registers. 427 + * We have to do this early in case timer needs to be enabled 428 + * through DSCR. 429 + */ 430 + dscr_probe(); 431 + 432 + /* We do this early for timer and core clock frequency */ 433 + c64x_setup_clocks(); 434 + 435 + /* Get CPU info */ 436 + get_cpuinfo(); 437 + 438 + #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) 439 + conswitchp = &dummy_con; 440 + #endif 441 + } 442 + 443 + #define cpu_to_ptr(n) ((void *)((long)(n)+1)) 444 + #define ptr_to_cpu(p) ((long)(p) - 1) 445 + 446 + static int show_cpuinfo(struct seq_file *m, void *v) 447 + { 448 + int n = ptr_to_cpu(v); 449 + struct cpuinfo_c6x *p = &per_cpu(cpu_data, n); 450 + 451 + if (n == 0) { 452 + seq_printf(m, 453 + "soc\t\t: %s\n" 454 + "soc revision\t: 0x%x\n" 455 + "soc cores\t: %d\n", 456 + c6x_soc_name, c6x_silicon_rev, c6x_num_cores); 457 + } 458 + 459 + seq_printf(m, 460 + "\n" 461 + "processor\t: %d\n" 462 + "cpu\t\t: %s\n" 463 + "core revision\t: %s\n" 464 + "core voltage\t: %s\n" 465 + "core id\t\t: %d\n" 466 + "mmu\t\t: %s\n" 467 + "fpu\t\t: %s\n" 468 + "cpu MHz\t\t: %u\n" 469 + "bogomips\t: %lu.%02lu\n\n", 470 + n, 471 + p->cpu_name, p->cpu_rev, p->cpu_voltage, 472 + p->core_id, p->mmu, p->fpu, 473 + (c6x_core_freq + 500000) / 1000000, 474 + (loops_per_jiffy/(500000/HZ)), 475 + (loops_per_jiffy/(5000/HZ))%100); 476 + 477 + return 0; 478 + } 479 + 480 + static void *c_start(struct seq_file *m, loff_t *pos) 481 + { 482 + return *pos < nr_cpu_ids ? cpu_to_ptr(*pos) : NULL; 483 + } 484 + static void *c_next(struct seq_file *m, void *v, loff_t *pos) 485 + { 486 + ++*pos; 487 + return NULL; 488 + } 489 + static void c_stop(struct seq_file *m, void *v) 490 + { 491 + } 492 + 493 + const struct seq_operations cpuinfo_op = { 494 + c_start, 495 + c_stop, 496 + c_next, 497 + show_cpuinfo 498 + };
+81
arch/c6x/kernel/vectors.S
··· 1 + ; 2 + ; Port on Texas Instruments TMS320C6x architecture 3 + ; 4 + ; Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated 5 + ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) 6 + ; 7 + ; This program is free software; you can redistribute it and/or modify 8 + ; it under the terms of the GNU General Public License version 2 as 9 + ; published by the Free Software Foundation. 10 + ; 11 + ; This section handles all the interrupt vector routines. 12 + ; At RESET the processor sets up the DRAM timing parameters and 13 + ; branches to the label _c_int00 which handles initialization for the C code. 14 + ; 15 + 16 + #define ALIGNMENT 5 17 + 18 + .macro IRQVEC name, handler 19 + .align ALIGNMENT 20 + .hidden \name 21 + .global \name 22 + \name: 23 + #ifdef CONFIG_C6X_BIG_KERNEL 24 + STW .D2T1 A0,*B15--[2] 25 + || MVKL .S1 \handler,A0 26 + MVKH .S1 \handler,A0 27 + B .S2X A0 28 + LDW .D2T1 *++B15[2],A0 29 + NOP 4 30 + NOP 31 + NOP 32 + .endm 33 + #else /* CONFIG_C6X_BIG_KERNEL */ 34 + B .S2 \handler 35 + NOP 36 + NOP 37 + NOP 38 + NOP 39 + NOP 40 + NOP 41 + NOP 42 + .endm 43 + #endif /* CONFIG_C6X_BIG_KERNEL */ 44 + 45 + .sect ".vectors","ax" 46 + .align ALIGNMENT 47 + .global RESET 48 + .hidden RESET 49 + RESET: 50 + #ifdef CONFIG_C6X_BIG_KERNEL 51 + MVKL .S1 _c_int00,A0 ; branch to _c_int00 52 + MVKH .S1 _c_int00,A0 53 + B .S2X A0 54 + #else 55 + B .S2 _c_int00 56 + NOP 57 + NOP 58 + #endif 59 + NOP 60 + NOP 61 + NOP 62 + NOP 63 + NOP 64 + 65 + 66 + IRQVEC NMI,_nmi_handler ; NMI interrupt 67 + IRQVEC AINT,_bad_interrupt ; reserved 68 + IRQVEC MSGINT,_bad_interrupt ; reserved 69 + 70 + IRQVEC INT4,_int4_handler 71 + IRQVEC INT5,_int5_handler 72 + IRQVEC INT6,_int6_handler 73 + IRQVEC INT7,_int7_handler 74 + IRQVEC INT8,_int8_handler 75 + IRQVEC INT9,_int9_handler 76 + IRQVEC INT10,_int10_handler 77 + IRQVEC INT11,_int11_handler 78 + IRQVEC INT12,_int12_handler 79 + IRQVEC INT13,_int13_handler 80 + IRQVEC INT14,_int14_handler 81 + IRQVEC INT15,_int15_handler