diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-12-12 00:24:06 +0000 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-03-08 12:23:51 +0000 |
commit | 6419711a164ba1304fa8fbb75ae9485455e04dcd (patch) | |
tree | 15fe79b0ffff33d253258b849f1b69ac4c2c3bbe /arch | |
parent | a3f66351b3cc89011e8a34068c245b413ce696d6 (diff) | |
download | blackbird-op-linux-6419711a164ba1304fa8fbb75ae9485455e04dcd.tar.gz blackbird-op-linux-6419711a164ba1304fa8fbb75ae9485455e04dcd.zip |
[ARM] S3C: Move PM support functions to common location
Start moving the PM code by moving all the common support functions
to a common location in arch/arm/plat-s3c. With the move we rename
the functions from s3cxxx_ to s3c_ to fit the new location.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-s3c2410/pm.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-s3c2412/pm.c | 4 | ||||
-rw-r--r-- | arch/arm/plat-s3c/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 36 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm.c | 97 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/irq.c | 4 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/pm.c | 113 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c244x.c | 4 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/sleep.S | 8 |
9 files changed, 171 insertions, 110 deletions
diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c index a6970f613192..72f96869aa94 100644 --- a/arch/arm/mach-s3c2410/pm.c +++ b/arch/arm/mach-s3c2410/pm.c @@ -37,21 +37,14 @@ #include <plat/cpu.h> #include <plat/pm.h> -#ifdef CONFIG_S3C2410_PM_DEBUG -extern void pm_dbg(const char *fmt, ...); -#define DBG(fmt...) pm_dbg(fmt) -#else -#define DBG(fmt...) printk(KERN_DEBUG fmt) -#endif - static void s3c2410_pm_prepare(void) { /* ensure at least GSTATUS3 has the resume address */ __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3); - DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); - DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); + S3C_PMDBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); + S3C_PMDBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); if (machine_is_h1940()) { void *base = phys_to_virt(H1940_SUSPEND_CHECK); diff --git a/arch/arm/mach-s3c2412/pm.c b/arch/arm/mach-s3c2412/pm.c index 217e9e4ed45f..c9cfe40e21f6 100644 --- a/arch/arm/mach-s3c2412/pm.c +++ b/arch/arm/mach-s3c2412/pm.c @@ -85,7 +85,7 @@ static struct sleep_save s3c2412_sleep[] = { static int s3c2412_pm_suspend(struct sys_device *dev, pm_message_t state) { - s3c2410_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); + s3c_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); return 0; } @@ -98,7 +98,7 @@ static int s3c2412_pm_resume(struct sys_device *dev) tmp |= S3C2412_PWRCFG_STANDBYWFI_IDLE; __raw_writel(tmp, S3C2412_PWRCFG); - s3c2410_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); + s3c_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); return 0; } diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 39195f972d5e..afd8d089ba16 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -18,6 +18,10 @@ obj-y += pwm-clock.o obj-y += gpio.o obj-y += gpio-config.o +# PM support + +obj-$(CONFIG_PM) += pm.o + # devices obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index cc623667e48a..a1520997ab82 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h @@ -1,6 +1,7 @@ /* linux/include/asm-arm/plat-s3c24xx/pm.h * * Copyright (c) 2004 Simtec Electronics + * http://armlinux.simtec.co.uk/ * Written by Ben Dooks, <ben@simtec.co.uk> * * This program is free software; you can redistribute it and/or modify @@ -49,10 +50,18 @@ extern int s3c2410_cpu_save(unsigned long *saveblk); extern void s3c2410_cpu_suspend(void); extern void s3c2410_cpu_resume(void); -extern unsigned long s3c2410_sleep_save_phys; +extern unsigned long s3c_sleep_save_phys; /* sleep save info */ +/** + * struct sleep_save - save information for shared peripherals. + * @reg: Pointer to the register to save. + * @val: Holder for the value saved from reg. + * + * This describes a list of registers which is used by the pm core and + * other subsystem to save and restore register values over suspend. + */ struct sleep_save { void __iomem *reg; unsigned long val; @@ -61,8 +70,11 @@ struct sleep_save { #define SAVE_ITEM(x) \ { .reg = (x) } -extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count); -extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count); +/* helper functions to save/restore lists of registers. */ + +extern void s3c_pm_do_save(struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore(struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore_core(struct sleep_save *ptr, int count); #ifdef CONFIG_PM extern int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state); @@ -71,3 +83,21 @@ extern int s3c24xx_irq_resume(struct sys_device *dev); #define s3c24xx_irq_suspend NULL #define s3c24xx_irq_resume NULL #endif + +/* PM debug functions */ + +#ifdef CONFIG_S3C2410_PM_DEBUG +/** + * s3c_pm_dbg() - low level debug function for use in suspend/resume. + * @msg: The message to print. + * + * This function is used mainly to debug the resume process before the system + * can rely on printk/console output. It uses the low-level debugging output + * routine printascii() to do its work. + */ +extern void s3c_pm_dbg(const char *msg, ...); + +#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt) +#else +#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) +#endif diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c new file mode 100644 index 000000000000..122e9b91a7f4 --- /dev/null +++ b/arch/arm/plat-s3c/pm.c @@ -0,0 +1,97 @@ +/* linux/arch/arm/plat-s3c/pm.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2004,2006,2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C common power management (suspend to ram) support. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/io.h> + +#include <plat/pm.h> + +/* for external use */ + +unsigned long s3c_pm_flags; + +#ifdef CONFIG_S3C2410_PM_DEBUG +extern void printascii(const char *); + +void s3c_pm_dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + printascii(buff); +} +#endif /* CONFIG_S3C2410_PM_DEBUG */ + + +/* helper functions to save and restore register state */ + +/** + * s3c_pm_do_save() - save a set of registers for restoration on resume. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Run through the list of registers given, saving their contents in the + * array for later restoration when we wakeup. + */ +void s3c_pm_do_save(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + ptr->val = __raw_readl(ptr->reg); + S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); + } +} + +/** + * s3c_pm_do_restore() - restore register values from the save list. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Restore the register values saved from s3c_pm_do_save(). + * + * Note, we do not use S3C_PMDBG() in here, as the system may not have + * restore the UARTs state yet +*/ + +void s3c_pm_do_restore(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", + ptr->reg, ptr->val, __raw_readl(ptr->reg)); + + __raw_writel(ptr->val, ptr->reg); + } +} + +/** + * s3c_pm_do_restore_core() - early restore register values from save list. + * + * This is similar to s3c_pm_do_restore() except we try and minimise the + * side effects of the function in case registers that hardware might need + * to work has been restored. + * + * WARNING: Do not put any debug in here that may effect memory or use + * peripherals, as things may be changing! +*/ + +void s3c_pm_do_restore_core(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) + __raw_writel(ptr->val, ptr->reg); +} diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c index 0192ecdc1442..2fa8c9f4b354 100644 --- a/arch/arm/plat-s3c24xx/irq.c +++ b/arch/arm/plat-s3c24xx/irq.c @@ -616,7 +616,7 @@ int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state) for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4)); - s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); + s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); save_eintmask = __raw_readl(S3C24XX_EINTMASK); return 0; @@ -632,7 +632,7 @@ int s3c24xx_irq_resume(struct sys_device *dev) for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) __raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4)); - s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); + s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); __raw_writel(save_eintmask, S3C24XX_EINTMASK); return 0; diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c index 34ef18e5b2a1..c161f9c9a1d4 100644 --- a/arch/arm/plat-s3c24xx/pm.c +++ b/arch/arm/plat-s3c24xx/pm.c @@ -49,9 +49,6 @@ #include <plat/pm.h> -/* for external use */ - -unsigned long s3c_pm_flags; #define PFX "s3c24xx-pm: " @@ -143,20 +140,6 @@ static struct sleep_save uart_save[] = { * system never wakes up from the sleep */ -extern void printascii(const char *); - -void pm_dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsprintf(buff, fmt, va); - va_end(va); - - printascii(buff); -} - static void s3c2410_pm_debug_init(void) { unsigned long tmp = __raw_readl(S3C2410_CLKCON); @@ -170,10 +153,7 @@ static void s3c2410_pm_debug_init(void) udelay(10); } -#define DBG(fmt...) pm_dbg(fmt) #else -#define DBG(fmt...) printk(KERN_DEBUG fmt) - #define s3c2410_pm_debug_init() do { } while(0) static struct sleep_save uart_save[] = {}; @@ -211,7 +191,7 @@ static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) if ((ptr->flags & IORESOURCE_MEM) && strcmp(ptr->name, "System RAM") == 0) { - DBG("Found system RAM at %08lx..%08lx\n", + S3C_PMDBG("Found system RAM at %08lx..%08lx\n", ptr->start, ptr->end); arg = (fn)(ptr, arg); } @@ -232,7 +212,7 @@ static u32 *s3c2410_pm_countram(struct resource *res, u32 *val) size += CHECK_CHUNKSIZE-1; size /= CHECK_CHUNKSIZE; - DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); + S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); *val += size * sizeof(u32); return val; @@ -252,7 +232,7 @@ static void s3c2410_pm_check_prepare(void) s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size); - DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); + S3C_PMDBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); crcs = kmalloc(crc_size+4, GFP_KERNEL); if (crcs == NULL) @@ -308,7 +288,7 @@ static inline int in_region(void *ptr, int size, void *what, size_t whatsz) static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) { - void *save_at = phys_to_virt(s3c2410_sleep_save_phys); + void *save_at = phys_to_virt(s3c_sleep_save_phys); unsigned long addr; unsigned long left; void *ptr; @@ -324,12 +304,12 @@ static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) ptr = phys_to_virt(addr); if (in_region(ptr, left, crcs, crc_size)) { - DBG("skipping %08lx, has crc block in\n", addr); + S3C_PMDBG("skipping %08lx, has crc block in\n", addr); goto skip_check; } if (in_region(ptr, left, save_at, 32*4 )) { - DBG("skipping %08lx, has save block in\n", addr); + S3C_PMDBG("skipping %08lx, has save block in\n", addr); goto skip_check; } @@ -340,7 +320,7 @@ static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) printk(KERN_ERR PFX "Restore CRC error at " "%08lx (%08x vs %08x)\n", addr, calc, *val); - DBG("Restore CRC error at %08lx (%08x vs %08x)\n", + S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", addr, calc, *val); } @@ -373,49 +353,6 @@ static void s3c2410_pm_check_restore(void) #define s3c2410_pm_check_store() do { } while(0) #endif -/* helper functions to save and restore register state */ - -void s3c2410_pm_do_save(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - ptr->val = __raw_readl(ptr->reg); - DBG("saved %p value %08lx\n", ptr->reg, ptr->val); - } -} - -/* s3c2410_pm_do_restore - * - * restore the system from the given list of saved registers - * - * Note, we do not use DBG() in here, as the system may not have - * restore the UARTs state yet -*/ - -void s3c2410_pm_do_restore(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", - ptr->reg, ptr->val, __raw_readl(ptr->reg)); - - __raw_writel(ptr->val, ptr->reg); - } -} - -/* s3c2410_pm_do_restore_core - * - * similar to s3c2410_pm_do_restore_core - * - * WARNING: Do not put any debug in here that may effect memory or use - * peripherals, as things may be changing! -*/ - -static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - __raw_writel(ptr->val, ptr->reg); - } -} - /* s3c2410_pm_show_resume_irqs * * print any IRQs asserted at resume time (ie, we woke from) @@ -430,7 +367,7 @@ static void s3c2410_pm_show_resume_irqs(int start, unsigned long which, for (i = 0; i <= 31; i++) { if ((which) & (1L<<i)) { - DBG("IRQ %d asserted at resume\n", start+i); + S3C_PMDBG("IRQ %d asserted at resume\n", start+i); } } } @@ -456,10 +393,10 @@ static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) if (!irqstate) { if (pinstate == S3C2410_GPIO_IRQ) - DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); + S3C_PMDBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); } else { if (pinstate == S3C2410_GPIO_IRQ) { - DBG("Disabling IRQ %d (pin %d)\n", irq, pin); + S3C_PMDBG("Disabling IRQ %d (pin %d)\n", irq, pin); s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT); } } @@ -646,8 +583,8 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps) __raw_writel(gps->gpup, base + OFFS_UP); } - DBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n", - index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); + S3C_PMDBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n", + index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); } @@ -684,7 +621,7 @@ static int s3c2410_pm_enter(suspend_state_t state) s3c2410_pm_debug_init(); - DBG("s3c2410_pm_enter(%d)\n", state); + S3C_PMDBG("s3c2410_pm_enter(%d)\n", state); if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); @@ -709,22 +646,22 @@ static int s3c2410_pm_enter(suspend_state_t state) /* store the physical address of the register recovery block */ - s3c2410_sleep_save_phys = virt_to_phys(regs_save); + s3c_sleep_save_phys = virt_to_phys(regs_save); - DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys); + S3C_PMDBG("s3c_sleep_save_phys=0x%08lx\n", s3c_sleep_save_phys); /* save all necessary core registers not covered by the drivers */ s3c2410_pm_save_gpios(); - s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); - s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); - s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); + s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); + s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); + s3c_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); /* set the irq configuration for wake */ s3c2410_pm_configure_extint(); - DBG("sleep: irq wakeup masks: %08lx,%08lx\n", + S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", s3c_irqwake_intmask, s3c_irqwake_eintmask); __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); @@ -765,16 +702,16 @@ static int s3c2410_pm_enter(suspend_state_t state) /* restore the system state */ - s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); - s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); - s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); + s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); + s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); + s3c_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); s3c2410_pm_restore_gpios(); s3c2410_pm_debug_init(); /* check what irq (if any) restored the system */ - DBG("post sleep: IRQs 0x%08x, 0x%08x\n", + S3C_PMDBG("post sleep: IRQs 0x%08x, 0x%08x\n", __raw_readl(S3C2410_SRCPND), __raw_readl(S3C2410_EINTPEND)); @@ -784,13 +721,13 @@ static int s3c2410_pm_enter(suspend_state_t state) s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), s3c_irqwake_eintmask); - DBG("post sleep, preparing to return\n"); + S3C_PMDBG("post sleep, preparing to return\n"); s3c2410_pm_check_restore(); /* ok, let's return from sleep */ - DBG("S3C2410 PM Resume (post-restore)\n"); + S3C_PMDBG("S3C2410 PM Resume (post-restore)\n"); return 0; } diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index c1de6bb0101b..1364317d421e 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -145,13 +145,13 @@ static struct sleep_save s3c244x_sleep[] = { static int s3c244x_suspend(struct sys_device *dev, pm_message_t state) { - s3c2410_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); + s3c_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); return 0; } static int s3c244x_resume(struct sys_device *dev) { - s3c2410_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); + s3c_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); return 0; } diff --git a/arch/arm/plat-s3c24xx/sleep.S b/arch/arm/plat-s3c24xx/sleep.S index 76594b212802..7c1955ff3171 100644 --- a/arch/arm/plat-s3c24xx/sleep.S +++ b/arch/arm/plat-s3c24xx/sleep.S @@ -84,7 +84,7 @@ resume_with_mmu: .ltorg @@ the next bits sit in the .data segment, even though they - @@ happen to be code... the s3c2410_sleep_save_phys needs to be + @@ happen to be code... the s3c_sleep_save_phys needs to be @@ accessed by the resume code before it can restore the MMU. @@ This means that the variable has to be close enough for the @@ code to read it... since the .text segment needs to be RO, @@ -92,8 +92,8 @@ resume_with_mmu: .data - .global s3c2410_sleep_save_phys -s3c2410_sleep_save_phys: + .global s3c_sleep_save_phys +s3c_sleep_save_phys: .word 0 @@ -145,7 +145,7 @@ ENTRY(s3c2410_cpu_resume) mcr p15, 0, r1, c8, c7, 0 @@ invalidate I & D TLBs mcr p15, 0, r1, c7, c7, 0 @@ invalidate I & D caches - ldr r0, s3c2410_sleep_save_phys @ address of restore block + ldr r0, s3c_sleep_save_phys @ address of restore block ldmia r0, { r4 - r13 } mcr p15, 0, r4, c13, c0, 0 @ PID |