xref: /netbsd-src/sys/arch/amigappc/amigappc/machdep.c (revision f8762b53a5d1c7fd5d29aedfa8f25c6e17701608)
1 /* $NetBSD: machdep.c,v 1.55 2024/09/08 10:17:54 andvar Exp $ */
2 
3 /*
4  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5  * Copyright (C) 1995, 1996 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.55 2024/09/08 10:17:54 andvar Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/mount.h>
40 #include <sys/msgbuf.h>
41 #include <sys/kernel.h>
42 #include <sys/reboot.h>
43 #include <sys/ksyms.h>
44 
45 #include <uvm/uvm_extern.h>
46 
47 #include <dev/cons.h>
48 
49 #include <machine/autoconf.h>
50 #include <machine/powerpc.h>
51 
52 #include <powerpc/oea/bat.h>
53 #include <powerpc/pic/picvar.h>
54 
55 #include <amiga/amiga/cc.h>
56 #include <amiga/amiga/cia.h>
57 #include <amiga/amiga/custom.h>
58 #include <amiga/amiga/device.h>
59 #include <amiga/amiga/isr.h>
60 #include <amiga/amiga/memlist.h>
61 #include <amigappc/amigappc/p5reg.h>
62 
63 #include "opt_ddb.h"
64 
65 #include "fd.h"
66 #include "ser.h"
67 
68 extern void setup_amiga_intr(void);
69 #if NSER > 0
70 extern void ser_outintr(void);
71 extern void ser_fastint(void);
72 #endif
73 #if NFD > 0
74 extern void fdintr(int);
75 #endif
76 
77 #define AMIGAMEMREGIONS 8
78 static struct mem_region physmemr[AMIGAMEMREGIONS], availmemr[AMIGAMEMREGIONS];
79 static char model[80];
80 
81 /*
82  * patched by some devices at attach time (currently, only the coms)
83  */
84 int amiga_serialspl = 4;
85 
86 /*
87  * current open serial device speed;  used by some SCSI drivers to reduce
88  * DMA transfer lengths.
89  */
90 int ser_open_speed;
91 
92 /* interrupt handler chains for level2 and level6 interrupt */
93 struct isr *isr_ports;
94 struct isr *isr_exter;
95 
96 extern void *startsym, *endsym;
97 
98 void
99 add_isr(struct isr *isr)
100 {
101 	struct isr **p, *q;
102 
103 	p = isr->isr_ipl == 2 ? &isr_ports : &isr_exter;
104 
105 	while ((q = *p) != NULL)
106 		p = &q->isr_forw;
107 	isr->isr_forw = NULL;
108 	*p = isr;
109 
110 	/* enable interrupt */
111 	custom.intena = isr->isr_ipl == 2 ?
112 	    INTF_SETCLR | INTF_PORTS :
113 	    INTF_SETCLR | INTF_EXTER;
114 }
115 
116 void
117 remove_isr(struct isr *isr)
118 {
119 	struct isr **p, *q;
120 
121 	p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports;
122 
123 	while ((q = *p) != NULL && q != isr)
124 		p = &q->isr_forw;
125 	if (q)
126 		*p = q->isr_forw;
127 	else
128 		panic("remove_isr: handler not registered");
129 
130 	/* disable interrupt if no more handlers */
131 	p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports;
132 	if (*p == NULL) {
133 		custom.intena = isr->isr_ipl == 6 ?
134 		    INTF_EXTER : INTF_PORTS;
135 	}
136 }
137 
138 static int
139 ports_intr(void *arg)
140 {
141 	struct isr **p;
142 	struct isr *q;
143 
144 	p = (struct isr **)arg;
145 	while ((q = *p) != NULL) {
146 		if ((q->isr_intr)(q->isr_arg))
147 			break;
148 		p = &q->isr_forw;
149 	}
150 	if (q == NULL)
151 		ciaa_intr();  /* ciaa handles keyboard and parallel port */
152 
153 	custom.intreq = INTF_PORTS;
154 	return 0;
155 }
156 
157 static int
158 exter_intr(void *arg)
159 {
160 	struct isr **p;
161 	struct isr *q;
162 
163 	p = (struct isr **)arg;
164 	while ((q = *p) != NULL) {
165 		if ((q->isr_intr)(q->isr_arg))
166 			break;
167 		p = &q->isr_forw;
168 	}
169 	if (q == NULL)
170 		ciab_intr();  /* clear ciab icr */
171 
172 	custom.intreq = INTF_EXTER;
173 	return 0;
174 }
175 
176 static int
177 lev1_intr(void *arg)
178 {
179 	unsigned short ireq;
180 
181 	ireq = custom.intreqr;
182 	if (ireq & INTF_TBE) {
183 #if NSER > 0
184 		ser_outintr();
185 #else
186 		custom.intreq = INTF_TBE;
187 #endif
188 	}
189 	if (ireq & INTF_DSKBLK) {
190 #if NFD > 0
191 		fdintr(0);
192 #endif
193 		custom.intreq = INTF_DSKBLK;
194 	}
195 	if (ireq & INTF_SOFTINT) {
196 #ifdef DEBUG
197 		printf("intrhand: SOFTINT ignored\n");
198 #endif
199 		custom.intreq = INTF_SOFTINT;
200 	}
201 	return 0;
202 }
203 
204 static int
205 lev3_intr(void *arg)
206 {
207 	unsigned short ireq;
208 
209 	ireq = custom.intreqr;
210 	if (ireq & INTF_BLIT)
211 		blitter_handler();
212 	if (ireq & INTF_COPER)
213 		copper_handler();
214 	if (ireq & INTF_VERTB)
215 		vbl_handler();
216 	return 0;
217 }
218 
219 static int
220 lev4_intr(void *arg)
221 {
222 
223 	audio_handler();
224 	return 0;
225 }
226 
227 static int
228 lev5_intr(void *arg)
229 {
230 
231 	ser_fastint();
232 	if (custom.intreqr & INTF_DSKSYNC)
233 		custom.intreq = INTF_DSKSYNC;
234 	return 0;
235 }
236 
237 static void
238 amigappc_install_handlers(void)
239 {
240 
241 	/* handlers for all 6 Amiga interrupt levels */
242 	intr_establish(1, IST_LEVEL, IPL_BIO, lev1_intr, NULL);
243 	intr_establish(2, IST_LEVEL, IPL_BIO, ports_intr, &isr_ports);
244 	intr_establish(3, IST_LEVEL, IPL_TTY, lev3_intr, NULL);
245 	intr_establish(4, IST_LEVEL, IPL_AUDIO, lev4_intr, NULL);
246 	intr_establish(5, IST_LEVEL, IPL_SERIAL, lev5_intr, NULL);
247 	intr_establish(6, IST_LEVEL, IPL_SERIAL, exter_intr, &isr_exter);
248 }
249 
250 static void
251 amigappc_identify(void)
252 {
253 	extern u_long ns_per_tick, ticks_per_sec;
254 	static const unsigned char pll[] = {
255 		10, 10, 70, 10, 20, 65, 25, 45,
256 		30, 55, 40, 50, 15, 60, 35, 00
257 	};
258 	const char *cpuname, *mach, *p5type_p, *pup;
259 	u_long busclock, cpuclock;
260 	register int pvr, hid1;
261 
262 	/* PowerUp ROM id location */
263 	p5type_p = (const char *)0xf00010;
264 
265 	/*
266 	 * PVR holds the CPU-type and version while
267          * HID1 holds the PLL configuration
268 	 */
269 	__asm ("mfpvr %0; mfspr %1,1009" : "=r"(pvr), "=r"(hid1));
270 
271 	/* Amiga types which can run a PPC board */
272 	if (is_a4000())
273 		mach = "Amiga 4000";
274 	else if (is_a3000())
275 		mach = "Amiga 3000";
276 	else
277 		mach = "Amiga 1200";
278 
279 	/* find CPU type - BlizzardPPC has 603e/603ev, CyberstormPPC has 604e */
280 	switch (pvr >> 16) {
281 	case 3:
282 		cpuname = "603";
283 		break;
284 	case 4:
285 		cpuname = "604";
286 		break;
287 	case 6:
288 		cpuname = "603e";
289 		break;
290 	case 7:
291 		cpuname = "603ev";
292 		break;
293 	case 9:
294 	case 10:
295 		cpuname = "604e";
296 		break;
297 	default:
298 		cpuname = "unknown";
299 		break;
300 	}
301 
302 	switch (p5type_p[0]) {
303 	case 'D':
304 		pup = "[PowerUP]";
305 		break;
306 	case 'E':
307 		pup = "[CSPPC]";
308 		break;
309 	case 'F':
310 		pup = "[CS Mk.III]";
311 		break;
312 	case 'H':
313 	case 'I':
314 		pup = "[BlizzardPPC]";
315 		break;
316 	default:
317 		pup = "";
318 		break;
319 	}
320 
321 	/* XXX busclock can be measured with CIA-timers */
322 	switch (p5type_p[1]) {
323 	case 'A':
324 		busclock = 60000000;
325 		break;
326 	/* case B, C, D */
327 	default:
328 		busclock = 66666667;
329 		break;
330 	}
331 
332 	/* compute cpuclock based on PLL configuration */
333 	cpuclock = busclock * pll[hid1>>28 & 0xf] / 10;
334 
335 	snprintf(model, sizeof(model),
336 	    "%s %s (%s v%d.%d %lu MHz, busclk %lu MHz)",
337 	    mach, pup, cpuname, (pvr>>8) & 0xf, (pvr >> 0) & 0xf,
338 	    cpuclock / 1000000, busclock / 1000000);
339 
340 	/* set timebase */
341 	ticks_per_sec = busclock / 4;
342 	ns_per_tick = 1000000000 / ticks_per_sec;
343 }
344 
345 static void
346 amigappc_reboot(void)
347 {
348 
349 	/* reboot CSPPC/BPPC */
350 	if (!is_a1200()) {
351 		P5write(P5_REG_LOCK,0x60);
352 		P5write(P5_REG_LOCK,0x50);
353 		P5write(P5_REG_LOCK,0x30);
354 		P5write(P5_REG_SHADOW,P5_SET_CLEAR|P5_SHADOW);
355 		P5write(P5_REG_LOCK,0x00);
356 	} else
357 		P5write(P5_BPPC_MAGIC,0x00);
358 
359 	P5write(P5_REG_LOCK,0x60);
360 	P5write(P5_REG_LOCK,0x50);
361 	P5write(P5_REG_LOCK,0x30);
362 	P5write(P5_REG_SHADOW,P5_SELF_RESET);
363 	P5write(P5_REG_RESET,P5_AMIGA_RESET);
364 }
365 
366 static void
367 amigappc_bat_add(paddr_t pa, register_t len, register_t prot)
368 {
369 	static int nd = 0, ni = 0;
370 	const uint32_t i = pa >> 28;
371 
372 	battable[i].batl = BATL(pa, prot, BAT_PP_RW);
373 	battable[i].batu = BATU(pa, len, BAT_Vs);
374 
375 	/*
376 	 * Let's start loading the BAT registers.
377 	 */
378 	if (!(prot & (BAT_I | BAT_G))) {
379 		switch (ni) {
380 		case 0:
381 			__asm volatile ("isync");
382 			__asm volatile ("mtibatl 0,%0; mtibatu 0,%1;"
383 			    ::	"r"(battable[i].batl),
384 				"r"(battable[i].batu));
385 			__asm volatile ("isync");
386 			ni = 1;
387 			break;
388 		case 1:
389 			__asm volatile ("isync");
390 			__asm volatile ("mtibatl 1,%0; mtibatu 1,%1;"
391 			    ::	"r"(battable[i].batl),
392 				"r"(battable[i].batu));
393 			__asm volatile ("isync");
394 			ni = 2;
395 			break;
396 		case 2:
397 			__asm volatile ("isync");
398 			__asm volatile ("mtibatl 2,%0; mtibatu 2,%1;"
399 			    ::	"r"(battable[i].batl),
400 				"r"(battable[i].batu));
401 			__asm volatile ("isync");
402 			ni = 3;
403 			break;
404 		case 3:
405 			__asm volatile ("isync");
406 			__asm volatile ("mtibatl 3,%0; mtibatu 3,%1;"
407 			    ::	"r"(battable[i].batl),
408 				"r"(battable[i].batu));
409 			__asm volatile ("isync");
410 			ni = 4;
411 			break;
412 		default:
413 			break;
414 		}
415 	}
416 	switch (nd) {
417 	case 0:
418 		__asm volatile ("isync");
419 		__asm volatile ("mtdbatl 0,%0; mtdbatu 0,%1;"
420 		    ::	"r"(battable[i].batl),
421 			"r"(battable[i].batu));
422 		__asm volatile ("isync");
423 		nd = 1;
424 		break;
425 	case 1:
426 		__asm volatile ("isync");
427 		__asm volatile ("mtdbatl 1,%0; mtdbatu 1,%1;"
428 		    ::	"r"(battable[i].batl),
429 			"r"(battable[i].batu));
430 		__asm volatile ("isync");
431 		nd = 2;
432 		break;
433 	case 2:
434 		__asm volatile ("isync");
435 		__asm volatile ("mtdbatl 2,%0; mtdbatu 2,%1;"
436 		    ::	"r"(battable[i].batl),
437 			"r"(battable[i].batu));
438 		__asm volatile ("isync");
439 		nd = 3;
440 		break;
441 	case 3:
442 		__asm volatile ("isync");
443 		__asm volatile ("mtdbatl 3,%0; mtdbatu 3,%1;"
444 		    ::	"r"(battable[i].batl),
445 			"r"(battable[i].batu));
446 		__asm volatile ("isync");
447 		nd = 4;
448 		break;
449 	default:
450 		break;
451 	}
452 }
453 
454 static void
455 amigappc_batinit(paddr_t pa, ...)
456 {
457 	va_list ap;
458 	register_t msr;
459 
460 	msr = mfmsr();
461 
462 	/*
463 	 * Set BAT registers to unmapped to avoid overlapping mappings below.
464 	 */
465 	if ((msr & (PSL_IR|PSL_DR)) == 0) {
466 		__asm volatile ("isync");
467 		__asm volatile ("mtibatu 0,%0" :: "r"(0));
468 		__asm volatile ("mtibatu 1,%0" :: "r"(0));
469 		__asm volatile ("mtibatu 2,%0" :: "r"(0));
470 		__asm volatile ("mtibatu 3,%0" :: "r"(0));
471 		__asm volatile ("mtdbatu 0,%0" :: "r"(0));
472 		__asm volatile ("mtdbatu 1,%0" :: "r"(0));
473 		__asm volatile ("mtdbatu 2,%0" :: "r"(0));
474 		__asm volatile ("mtdbatu 3,%0" :: "r"(0));
475 		__asm volatile ("isync");
476 	}
477 
478 	/*
479 	 * Setup BATs
480 	 */
481 	va_start(ap, pa);
482 	while (pa != ~0) {
483 		register_t len, prot;
484 
485 		len = va_arg(ap, register_t);
486 		prot = va_arg(ap, register_t);
487 		amigappc_bat_add(pa, len, prot);
488 		pa = va_arg(ap, paddr_t);
489 	}
490 	va_end(ap);
491 }
492 
493 void
494 initppc(u_int startkernel, u_int endkernel)
495 {
496 	struct boot_memseg *ms;
497 	u_int i, r;
498 
499 	/*
500 	 * amigappc memory region set
501 	 */
502 	ms = &memlist->m_seg[0];
503 	for (i = 0, r = 0; i < memlist->m_nseg; i++, ms++) {
504 		if (ms->ms_attrib & MEMF_FAST) {
505 			/*
506 			 * XXX Only recognize the memory segment in which
507 			 * the kernel resides, for now
508 			 */
509 			if (ms->ms_start <= startkernel &&
510 			    ms->ms_start + ms->ms_size > endkernel) {
511 				physmemr[r].start = (paddr_t)ms->ms_start;
512 				physmemr[r].size = (psize_t)ms->ms_size & ~PGOFSET;
513 				availmemr[r].start = (endkernel + PGOFSET) & ~PGOFSET;
514 				availmemr[r].size  = (physmemr[0].start +
515 						      physmemr[0].size)
516 						     - availmemr[0].start;
517 			}
518 			r++;
519 		}
520 	}
521 	physmemr[r].start = 0;
522 	physmemr[r].size = 0;
523 	availmemr[r].start = 0;
524 	availmemr[r].size = 0;
525 	if (r == 0)
526 		panic("initppc: no suitable memory segment found");
527 
528 	/*
529 	 * Initialize BAT tables.
530 	 * The CSPPC RAM (A3000/A4000) always starts at 0x08000000 and is
531 	 * up to 128MB big.
532 	 * The BPPC RAM (A1200) can be up to 256MB and may start at nearly
533 	 * any address between 0x40000000 and 0x80000000 depending on which
534 	 * RAM module of which size was inserted into which bank:
535 	 * The RAM module in bank 1 is located from 0x?8000000 downwards.
536 	 * The RAM module in bank 2 is located from 0x?8000000 upwards.
537 	 * Whether '?' is 4, 5, 6 or 7 probably depends on the size.
538 	 * So we have to use the 'startkernel' symbol for BAT-mapping
539 	 * our RAM.
540 	 */
541 	if (is_a1200()) {
542 		amigappc_batinit(0x00000000, BAT_BL_16M, BAT_I|BAT_G,
543 		    (startkernel & 0xf0000000), BAT_BL_256M, 0,
544 		    0xfff00000, BAT_BL_512K, 0,
545 		    ~0);
546 	} else {
547 		/* A3000 or A4000 */
548 		amigappc_batinit(0x00000000, BAT_BL_16M, BAT_I|BAT_G,
549 		    (startkernel & 0xf8000000), BAT_BL_128M, 0,
550 		    0xfff00000, BAT_BL_512K, 0,
551 		    0x40000000, BAT_BL_256M, BAT_I|BAT_G,
552 		    ~0);
553 	}
554 
555 	/*
556 	 * Set up trap vectors and interrupt handler
557 	 */
558 	oea_init(NULL);
559 
560 	/* XXX bus_space_init() not needed here */
561 
562 	uvm_md_init();
563 
564 	/*
565 	 * Initialize pmap module
566 	 */
567 	pmap_bootstrap(startkernel, endkernel);
568 
569 #if NKSYMS || defined(DDB) || defined(MODULAR)
570 	ksyms_addsyms_elf((int)((u_int)endsym - (u_int)startsym), startsym, endsym);
571 #endif
572 
573 	/*
574 	 * CPU model, bus clock, timebase
575 	 */
576 	amigappc_identify();
577 
578 #ifdef DDB
579 	if (boothowto & RB_KDB)
580 		Debugger();
581 #endif
582 }
583 
584 /*
585  * This is called during initppc, before the system is really initialized.
586  * It shall provide the total and the available regions of RAM.
587  * Both lists must have a zero-size entry as terminator.
588  * The available regions need not take the kernel into account, but needs
589  * to provide space for two additional entry beyond the terminating one.
590  */
591 void
592 mem_regions(struct mem_region **memp, struct mem_region **availp)
593 {
594 
595 	*memp = physmemr;
596 	*availp = availmemr;
597 }
598 
599 /*
600  * Machine dependent startup code
601  */
602 void
603 cpu_startup(void)
604 {
605 	int msr;
606 
607 	/*
608 	 * hello world
609 	 */
610 	oea_startup(model);
611 
612 	/* Setup interrupts */
613 	pic_init();
614 	setup_amiga_intr();
615 	amigappc_install_handlers();
616 
617 	oea_install_extint(pic_ext_intr);
618 
619 	/*
620 	 * Now allow hardware interrupts.
621 	 */
622 	splhigh();
623 	__asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0"
624 	    :	"=r"(msr)
625 	    :	"K"(PSL_EE));
626 
627 #if 0 /* XXX Not in amiga bus.h - fix it? */
628 	/*
629 	 * Now that we have VM, malloc's are OK in bus_space.
630 	 */
631 	bus_space_mallocok();
632 #endif
633 }
634 
635 /*
636  * consinit
637  * Initialize system console.
638  */
639 void
640 consinit(void)
641 {
642 
643 	/* preconfigure graphics cards */
644 	custom_chips_init();
645 	config_console();
646 
647 	/* Initialize the console before we print anything out. */
648 	cninit();
649 }
650 
651 /*
652  * Halt or reboot the machine after syncing/dumping according to howto
653  */
654 void
655 cpu_reboot(int howto, char *what)
656 {
657 	static int syncing;
658 
659 	if (cold) {
660 		howto |= RB_HALT;
661 		goto halt_sys;
662 	}
663 
664 	boothowto = howto;
665 	if ((howto & RB_NOSYNC) == 0 && syncing == 0) {
666 		syncing = 1;
667 		vfs_shutdown();		/* sync */
668 	}
669 
670 	/* Disable intr */
671 	splhigh();
672 
673 	/* Do dump if requested */
674 	if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) {
675 		oea_dumpsys();
676 #ifdef DDB
677 		/* XXX dumpsys doesn't work, so give a chance to debug */
678 		Debugger();
679 #endif
680 	}
681 
682 halt_sys:
683 	doshutdownhooks();
684 
685 	if (howto & RB_HALT) {
686 		printf("\n");
687 		printf("The operating system has halted.\n");
688 		printf("Please press any key to reboot.\n\n");
689 		cnpollc(1);	/* for proper keyboard command handling */
690 		cngetc();
691 		cnpollc(0);
692 	}
693 
694 	printf("rebooting...\n\n");
695 	delay(1000000);
696 	amigappc_reboot();
697 
698 	for (;;);
699 	/* NOTREACHED */
700 }
701 
702 /*
703  * Try to emulate the functionality from m68k/m68k/sys_machdep.c
704  * used by several amiga scsi drivers.
705  */
706 int
707 dma_cachectl(void *addr, int len)
708 {
709 	paddr_t pa, end;
710 	int inc;
711 
712 	if (addr == NULL || len == 0)
713 		return 0;
714 
715 	pa = kvtop(addr);
716 	inc = curcpu()->ci_ci.dcache_line_size;
717 
718 	for (end = pa + len; pa < end; pa += inc)
719 		__asm volatile("dcbf 0,%0" :: "r"(pa));
720 	__asm volatile("sync");
721 
722 #if 0 /* XXX not needed, we don't have instructions in DMA buffers */
723 	pa = kvtop(addr);
724 	for (end = pa + len; pa < end; pa += inc)
725 		__asm volatile("icbi 0,%0" :: "r"(pa));
726 	__asm volatile("isync");
727 #endif
728 
729 	return 0;
730 }
731