diff options
Diffstat (limited to 'arch')
118 files changed, 5481 insertions, 917 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b27f8abf163c..19792a9192b7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -19,6 +19,8 @@ config ARM select HAVE_KPROBES if (!XIP_KERNEL) select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) + select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) + select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) select HAVE_GENERIC_DMA_COHERENT select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZO @@ -26,6 +28,7 @@ config ARM select HAVE_PERF_EVENTS select PERF_USE_VMALLOC select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V7)) help The ARM series is a line of low-power-consumption RISC chip designs licensed by ARM Ltd and targeted at embedded applications and @@ -145,6 +148,9 @@ config ARCH_HAS_CPUFREQ and that the relevant menu configurations are displayed for it. +config ARCH_HAS_CPU_IDLE_WAIT + def_bool y + config GENERIC_HWEIGHT bool default y @@ -1016,7 +1022,7 @@ endif config ARM_ERRATA_411920 bool "ARM errata: Invalidation of the Instruction Cache operation can fail" - depends on CPU_V6 && !SMP + depends on CPU_V6 help Invalidation of the Instruction Cache operation can fail. This erratum is present in 1136 (before r1p4), 1156 and 1176. @@ -1195,13 +1201,13 @@ source "kernel/time/Kconfig" config SMP bool "Symmetric Multi-Processing (EXPERIMENTAL)" - depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\ - MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\ - ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4) + depends on EXPERIMENTAL depends on GENERIC_CLOCKEVENTS + depends on REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP || \ + MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\ + ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 select USE_GENERIC_SMP_HELPERS - select HAVE_ARM_SCU if ARCH_REALVIEW || ARCH_OMAP4 || ARCH_S5PV310 ||\ - ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 + select HAVE_ARM_SCU help This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If @@ -1219,6 +1225,19 @@ config SMP If you don't know what to do here, say N. +config SMP_ON_UP + bool "Allow booting SMP kernel on uniprocessor systems (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on SMP && !XIP && !THUMB2_KERNEL + default y + help + SMP kernels contain instructions which fail on non-SMP processors. + Enabling this option allows the kernel to modify itself to make + these instructions safe. Disabling it allows about 1K of space + savings. + + If you don't know what to do here, say Y. + config HAVE_ARM_SCU bool depends on SMP @@ -1269,12 +1288,9 @@ config HOTPLUG_CPU config LOCAL_TIMERS bool "Use local timer interrupts" - depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \ - REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \ - ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4) + depends on SMP default y - select HAVE_ARM_TWD if ARCH_REALVIEW || ARCH_OMAP4 || ARCH_S5PV310 || \ - ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS + select HAVE_ARM_TWD help Enable support for local timers on SMP platforms, rather then the legacy IPI broadcast method. Local timers allows the system diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index c29fb382aeee..2fd0b99afc4b 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -41,6 +41,11 @@ config ARM_UNWIND the performance is not affected. Currently, this feature only works with EABI compilers. If unsure say Y. +config OLD_MCOUNT + bool + depends on FUNCTION_TRACER && FRAME_POINTER + default y + config DEBUG_USER bool "Verbose user fault messages" help diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index 5ebbab6242a7..8f0f86db3602 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c @@ -146,8 +146,7 @@ #define DESIGNER 0x41 #define REVISION 0x0 #define INTEG_CFG 0x0 -#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12) \ - | (REVISION << 20) | (INTEG_CFG << 24)) +#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) #define PCELL_ID_VAL 0xb105f00d @@ -1859,10 +1858,10 @@ int pl330_add(struct pl330_info *pi) regs = pi->base; /* Check if we can handle this DMAC */ - if (get_id(pi, PERIPH_ID) != PERIPH_ID_VAL + if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", - readl(regs + PERIPH_ID), readl(regs + PCELL_ID)); + get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID)); return -EINVAL; } diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index 517d50ddbeb3..c0258a8c103b 100644 --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -678,7 +678,7 @@ out: * %-EBUSY physical address already marked in-use. * %0 successful. */ -static int +static int __devinit __sa1111_probe(struct device *me, struct resource *mem, int irq) { struct sa1111 *sachip; diff --git a/arch/arm/configs/realview-smp_defconfig b/arch/arm/configs/realview-smp_defconfig index 9312ef9f9bf4..5ca7a61f7c01 100644 --- a/arch/arm/configs/realview-smp_defconfig +++ b/arch/arm/configs/realview-smp_defconfig @@ -39,6 +39,7 @@ CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_ARM_INTEGRATOR=y +CONFIG_ARM_CHARLCD=y CONFIG_NETDEVICES=y CONFIG_SMSC_PHY=y CONFIG_NET_ETHERNET=y @@ -52,10 +53,13 @@ CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_LEGACY_PTY_COUNT=16 # CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_VERSATILE=y +CONFIG_SPI=y +CONFIG_GPIOLIB=y # CONFIG_HWMON is not set CONFIG_FB=y CONFIG_FB_ARMCLCD=y -# CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set @@ -70,7 +74,13 @@ CONFIG_SND_ARMAACI=y # CONFIG_USB_SUPPORT is not set CONFIG_MMC=y CONFIG_MMC_ARMMMCI=y -CONFIG_INOTIFY=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_PL031=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_CRAMFS=y @@ -80,6 +90,7 @@ CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y # CONFIG_SCHED_DEBUG is not set # CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/arm/configs/realview_defconfig b/arch/arm/configs/realview_defconfig index fb75192ee7e5..fcaa60328051 100644 --- a/arch/arm/configs/realview_defconfig +++ b/arch/arm/configs/realview_defconfig @@ -38,6 +38,7 @@ CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_ARM_INTEGRATOR=y +CONFIG_ARM_CHARLCD=y CONFIG_NETDEVICES=y CONFIG_SMSC_PHY=y CONFIG_NET_ETHERNET=y @@ -51,10 +52,13 @@ CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_LEGACY_PTY_COUNT=16 # CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_VERSATILE=y +CONFIG_SPI=y +CONFIG_GPIOLIB=y # CONFIG_HWMON is not set CONFIG_FB=y CONFIG_FB_ARMCLCD=y -# CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set @@ -69,7 +73,13 @@ CONFIG_SND_ARMAACI=y # CONFIG_USB_SUPPORT is not set CONFIG_MMC=y CONFIG_MMC_ARMMMCI=y -CONFIG_INOTIFY=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_PL031=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_CRAMFS=y @@ -79,6 +89,7 @@ CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y # CONFIG_SCHED_DEBUG is not set # CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/arm/configs/u300_defconfig b/arch/arm/configs/u300_defconfig index 46e5e0747269..c1c252cdca60 100644 --- a/arch/arm/configs/u300_defconfig +++ b/arch/arm/configs/u300_defconfig @@ -28,26 +28,9 @@ CONFIG_CPU_IDLE=y CONFIG_FPE_NWFPE=y CONFIG_PM=y # CONFIG_SUSPEND is not set -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set -# CONFIG_INET_DIAG is not set -# CONFIG_IPV6 is not set -# CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" # CONFIG_PREVENT_FIRMWARE_BUILD is not set -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_ECC_SMC=y +# CONFIG_MISC_DEVICES is not set # CONFIG_INPUT_MOUSEDEV is not set CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set @@ -58,7 +41,6 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_LEGACY_PTY_COUNT=16 # CONFIG_HW_RANDOM is not set CONFIG_I2C=y -CONFIG_POWER_SUPPLY=y # CONFIG_HWMON is not set CONFIG_WATCHDOG=y CONFIG_REGULATOR=y @@ -66,24 +48,10 @@ CONFIG_FB=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y -# CONFIG_VGA_CONSOLE is not set -CONFIG_SOUND=y -CONFIG_SND=y -# CONFIG_SND_SUPPORT_OLD_API is not set -# CONFIG_SND_VERBOSE_PROCFS is not set -# CONFIG_SND_DRIVERS is not set -# CONFIG_SND_ARM is not set -# CONFIG_SND_SPI is not set -CONFIG_SND_SOC=y # CONFIG_HID_SUPPORT is not set # CONFIG_USB_SUPPORT is not set CONFIG_MMC=y -CONFIG_MMC_DEBUG=y CONFIG_MMC_ARMMMCI=y -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y -CONFIG_LEDS_TRIGGERS=y -CONFIG_LEDS_TRIGGER_BACKLIGHT=y CONFIG_RTC_CLASS=y # CONFIG_RTC_HCTOSYS is not set CONFIG_RTC_DRV_COH901331=y @@ -93,12 +61,11 @@ CONFIG_COH901318=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y -# CONFIG_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y -# CONFIG_DETECT_SOFTLOCKUP is not set # CONFIG_SCHED_DEBUG is not set CONFIG_TIMER_STATS=y # CONFIG_DEBUG_PREEMPT is not set diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 6e8f05c8a1c8..062b58c029ab 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -154,16 +154,39 @@ .long 9999b,9001f; \ .popsection +#ifdef CONFIG_SMP +#define ALT_SMP(instr...) \ +9998: instr +#define ALT_UP(instr...) \ + .pushsection ".alt.smp.init", "a" ;\ + .long 9998b ;\ + instr ;\ + .popsection +#define ALT_UP_B(label) \ + .equ up_b_offset, label - 9998b ;\ + .pushsection ".alt.smp.init", "a" ;\ + .long 9998b ;\ + b . + up_b_offset ;\ + .popsection +#else +#define ALT_SMP(instr...) +#define ALT_UP(instr...) instr +#define ALT_UP_B(label) b label +#endif + /* * SMP data memory barrier */ .macro smp_dmb #ifdef CONFIG_SMP #if __LINUX_ARM_ARCH__ >= 7 - dmb + ALT_SMP(dmb) #elif __LINUX_ARM_ARCH__ == 6 - mcr p15, 0, r0, c7, c10, 5 @ dmb + ALT_SMP(mcr p15, 0, r0, c7, c10, 5) @ dmb +#else +#error Incompatible SMP platform #endif + ALT_UP(nop) #endif .endm diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 4656a24058d2..3acd8fa25e34 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -137,10 +137,10 @@ #endif /* - * This flag is used to indicate that the page pointed to by a pte - * is dirty and requires cleaning before returning it to the user. + * This flag is used to indicate that the page pointed to by a pte is clean + * and does not require cleaning before returning it to the user. */ -#define PG_dcache_dirty PG_arch_1 +#define PG_dcache_clean PG_arch_1 /* * MM Cache Management @@ -156,6 +156,12 @@ * Please note that the implementation of these, and the required * effects are cache-type (VIVT/VIPT/PIPT) specific. * + * flush_icache_all() + * + * Unconditionally clean and invalidate the entire icache. + * Currently only needed for cache-v6.S and cache-v7.S, see + * __flush_icache_all for the generic implementation. + * * flush_kern_all() * * Unconditionally clean and invalidate the entire cache. @@ -206,6 +212,7 @@ */ struct cpu_cache_fns { + void (*flush_icache_all)(void); void (*flush_kern_all)(void); void (*flush_user_all)(void); void (*flush_user_range)(unsigned long, unsigned long, unsigned int); @@ -227,6 +234,7 @@ struct cpu_cache_fns { extern struct cpu_cache_fns cpu_cache; +#define __cpuc_flush_icache_all cpu_cache.flush_icache_all #define __cpuc_flush_kern_all cpu_cache.flush_kern_all #define __cpuc_flush_user_all cpu_cache.flush_user_all #define __cpuc_flush_user_range cpu_cache.flush_user_range @@ -246,6 +254,7 @@ extern struct cpu_cache_fns cpu_cache; #else +#define __cpuc_flush_icache_all __glue(_CACHE,_flush_icache_all) #define __cpuc_flush_kern_all __glue(_CACHE,_flush_kern_cache_all) #define __cpuc_flush_user_all __glue(_CACHE,_flush_user_cache_all) #define __cpuc_flush_user_range __glue(_CACHE,_flush_user_cache_range) @@ -253,6 +262,7 @@ extern struct cpu_cache_fns cpu_cache; #define __cpuc_coherent_user_range __glue(_CACHE,_coherent_user_range) #define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) +extern void __cpuc_flush_icache_all(void); extern void __cpuc_flush_kern_all(void); extern void __cpuc_flush_user_all(void); extern void __cpuc_flush_user_range(unsigned long, unsigned long, unsigned int); @@ -291,6 +301,37 @@ extern void copy_to_user_page(struct vm_area_struct *, struct page *, /* * Convert calls to our calling convention. */ + +/* Invalidate I-cache */ +#define __flush_icache_all_generic() \ + asm("mcr p15, 0, %0, c7, c5, 0" \ + : : "r" (0)); + +/* Invalidate I-cache inner shareable */ +#define __flush_icache_all_v7_smp() \ + asm("mcr p15, 0, %0, c7, c1, 0" \ + : : "r" (0)); + +/* + * Optimized __flush_icache_all for the common cases. Note that UP ARMv7 + * will fall through to use __flush_icache_all_generic. + */ +#if (defined(CONFIG_CPU_V7) && defined(CONFIG_CPU_V6)) || \ + defined(CONFIG_SMP_ON_UP) +#define __flush_icache_preferred __cpuc_flush_icache_all +#elif __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) +#define __flush_icache_preferred __flush_icache_all_v7_smp +#elif __LINUX_ARM_ARCH__ == 6 && defined(CONFIG_ARM_ERRATA_411920) +#define __flush_icache_preferred __cpuc_flush_icache_all +#else +#define __flush_icache_preferred __flush_icache_all_generic +#endif + +static inline void __flush_icache_all(void) +{ + __flush_icache_preferred(); +} + #define flush_cache_all() __cpuc_flush_kern_all() static inline void vivt_flush_cache_mm(struct mm_struct *mm) @@ -366,21 +407,6 @@ extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 extern void flush_dcache_page(struct page *); -static inline void __flush_icache_all(void) -{ -#ifdef CONFIG_ARM_ERRATA_411920 - extern void v6_icache_inval_all(void); - v6_icache_inval_all(); -#elif defined(CONFIG_SMP) && __LINUX_ARM_ARCH__ >= 7 - asm("mcr p15, 0, %0, c7, c1, 0 @ invalidate I-cache inner shareable\n" - : - : "r" (0)); -#else - asm("mcr p15, 0, %0, c7, c5, 0 @ invalidate I-cache\n" - : - : "r" (0)); -#endif -} static inline void flush_kernel_vmap_range(void *addr, int size) { if ((cache_is_vivt() || cache_is_vipt_aliasing())) @@ -405,9 +431,6 @@ static inline void flush_anon_page(struct vm_area_struct *vma, #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE static inline void flush_kernel_dcache_page(struct page *page) { - /* highmem pages are always flushed upon kunmap already */ - if ((cache_is_vivt() || cache_is_vipt_aliasing()) && !PageHighMem(page)) - __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); } #define flush_dcache_mmap_lock(mapping) \ diff --git a/arch/arm/include/asm/cachetype.h b/arch/arm/include/asm/cachetype.h index d3a4c2cb9f2f..c023db09fcc1 100644 --- a/arch/arm/include/asm/cachetype.h +++ b/arch/arm/include/asm/cachetype.h @@ -6,6 +6,7 @@ #define CACHEID_VIPT_ALIASING (1 << 2) #define CACHEID_VIPT (CACHEID_VIPT_ALIASING|CACHEID_VIPT_NONALIASING) #define CACHEID_ASID_TAGGED (1 << 3) +#define CACHEID_VIPT_I_ALIASING (1 << 4) extern unsigned int cacheid; @@ -14,15 +15,18 @@ extern unsigned int cacheid; #define cache_is_vipt_nonaliasing() cacheid_is(CACHEID_VIPT_NONALIASING) #define cache_is_vipt_aliasing() cacheid_is(CACHEID_VIPT_ALIASING) #define icache_is_vivt_asid_tagged() cacheid_is(CACHEID_ASID_TAGGED) +#define icache_is_vipt_aliasing() cacheid_is(CACHEID_VIPT_I_ALIASING) /* * __LINUX_ARM_ARCH__ is the minimum supported CPU architecture * Mask out support which will never be present on newer CPUs. * - v6+ is never VIVT - * - v7+ VIPT never aliases + * - v7+ VIPT never aliases on D-side */ #if __LINUX_ARM_ARCH__ >= 7 -#define __CACHEID_ARCH_MIN (CACHEID_VIPT_NONALIASING | CACHEID_ASID_TAGGED) +#define __CACHEID_ARCH_MIN (CACHEID_VIPT_NONALIASING |\ + CACHEID_ASID_TAGGED |\ + CACHEID_VIPT_I_ALIASING) #elif __LINUX_ARM_ARCH__ >= 6 #define __CACHEID_ARCH_MIN (~CACHEID_VIVT) #else diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index 103f7ee97313..f89515adac60 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -2,12 +2,30 @@ #define _ASM_ARM_FTRACE #ifdef CONFIG_FUNCTION_TRACER -#define MCOUNT_ADDR ((long)(mcount)) +#define MCOUNT_ADDR ((unsigned long)(__gnu_mcount_nc)) #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ #ifndef __ASSEMBLY__ extern void mcount(void); extern void __gnu_mcount_nc(void); + +#ifdef CONFIG_DYNAMIC_FTRACE +struct dyn_arch_ftrace { +#ifdef CONFIG_OLD_MCOUNT + bool old_mcount; +#endif +}; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* With Thumb-2, the recorded addresses have the lsb set */ + return addr & ~1; +} + +extern void ftrace_caller_old(void); +extern void ftrace_call_old(void); +#endif + #endif #endif diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 212e47828c79..7ecd793b8f5a 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -21,18 +21,6 @@ #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) -struct tracectx { - unsigned int etb_bufsz; - void __iomem *etb_regs; - void __iomem *etm_regs; - unsigned long flags; - int ncmppairs; - int etm_portsz; - struct device *dev; - struct clk *emu_clk; - struct mutex mutex; -}; - #define TRACER_TIMEOUT 10000 #define etm_writel(t, v, x) \ @@ -112,10 +100,10 @@ struct tracectx { /* ETM status register, "ETM Architecture", 3.3.2 */ #define ETMR_STATUS (0x10) -#define ETMST_OVERFLOW (1 << 0) -#define ETMST_PROGBIT (1 << 1) -#define ETMST_STARTSTOP (1 << 2) -#define ETMST_TRIGGER (1 << 3) +#define ETMST_OVERFLOW BIT(0) +#define ETMST_PROGBIT BIT(1) +#define ETMST_STARTSTOP BIT(2) +#define ETMST_TRIGGER BIT(3) #define etm_progbit(t) (etm_readl((t), ETMR_STATUS) & ETMST_PROGBIT) #define etm_started(t) (etm_readl((t), ETMR_STATUS) & ETMST_STARTSTOP) @@ -123,7 +111,7 @@ struct tracectx { #define ETMR_TRACEENCTRL2 0x1c #define ETMR_TRACEENCTRL 0x24 -#define ETMTE_INCLEXCL (1 << 24) +#define ETMTE_INCLEXCL BIT(24) #define ETMR_TRACEENEVT 0x20 #define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ ETMCTRL_DATA_DO_ADDR | \ @@ -146,12 +134,12 @@ struct tracectx { #define ETBR_CTRL 0x20 #define ETBR_FORMATTERCTRL 0x304 #define ETBFF_ENFTC 1 -#define ETBFF_ENFCONT (1 << 1) -#define ETBFF_FONFLIN (1 << 4) -#define ETBFF_MANUAL_FLUSH (1 << 6) -#define ETBFF_TRIGIN (1 << 8) -#define ETBFF_TRIGEVT (1 << 9) -#define ETBFF_TRIGFL (1 << 10) +#define ETBFF_ENFCONT BIT(1) +#define ETBFF_FONFLIN BIT(4) +#define ETBFF_MANUAL_FLUSH BIT(6) +#define ETBFF_TRIGIN BIT(8) +#define ETBFF_TRIGEVT BIT(9) +#define ETBFF_TRIGFL BIT(10) #define etb_writel(t, v, x) \ (__raw_writel((v), (t)->etb_regs + (x))) diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h new file mode 100644 index 000000000000..4d8ae9d67abe --- /dev/null +++ b/arch/arm/include/asm/hw_breakpoint.h @@ -0,0 +1,133 @@ +#ifndef _ARM_HW_BREAKPOINT_H +#define _ARM_HW_BREAKPOINT_H + +#ifdef __KERNEL__ + +struct task_struct; + +#ifdef CONFIG_HAVE_HW_BREAKPOINT + +struct arch_hw_breakpoint_ctrl { + u32 __reserved : 9, + mismatch : 1, + : 9, + len : 8, + type : 2, + privilege : 2, + enabled : 1; +}; + +struct arch_hw_breakpoint { + u32 address; + u32 trigger; + struct perf_event *suspended_wp; + struct arch_hw_breakpoint_ctrl ctrl; +}; + +static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl) +{ + return (ctrl.mismatch << 22) | (ctrl.len << 5) | (ctrl.type << 3) | + (ctrl.privilege << 1) | ctrl.enabled; +} + +static inline void decode_ctrl_reg(u32 reg, + struct arch_hw_breakpoint_ctrl *ctrl) +{ + ctrl->enabled = reg & 0x1; + reg >>= 1; + ctrl->privilege = reg & 0x3; + reg >>= 2; + ctrl->type = reg & 0x3; + reg >>= 2; + ctrl->len = reg & 0xff; + reg >>= 17; + ctrl->mismatch = reg & 0x1; +} + +/* Debug architecture numbers. */ +#define ARM_DEBUG_ARCH_RESERVED 0 /* In case of ptrace ABI updates. */ +#define ARM_DEBUG_ARCH_V6 1 +#define ARM_DEBUG_ARCH_V6_1 2 +#define ARM_DEBUG_ARCH_V7_ECP14 3 +#define ARM_DEBUG_ARCH_V7_MM 4 + +/* Breakpoint */ +#define ARM_BREAKPOINT_EXECUTE 0 + +/* Watchpoints */ +#define ARM_BREAKPOINT_LOAD 1 +#define ARM_BREAKPOINT_STORE 2 + +/* Privilege Levels */ +#define ARM_BREAKPOINT_PRIV 1 +#define ARM_BREAKPOINT_USER 2 + +/* Lengths */ +#define ARM_BREAKPOINT_LEN_1 0x1 +#define ARM_BREAKPOINT_LEN_2 0x3 +#define ARM_BREAKPOINT_LEN_4 0xf +#define ARM_BREAKPOINT_LEN_8 0xff + +/* Limits */ +#define ARM_MAX_BRP 16 +#define ARM_MAX_WRP 16 +#define ARM_MAX_HBP_SLOTS (ARM_MAX_BRP + ARM_MAX_WRP) + +/* DSCR method of entry bits. */ +#define ARM_DSCR_MOE(x) ((x >> 2) & 0xf) +#define ARM_ENTRY_BREAKPOINT 0x1 +#define ARM_ENTRY_ASYNC_WATCHPOINT 0x2 +#define ARM_ENTRY_SYNC_WATCHPOINT 0xa + +/* DSCR monitor/halting bits. */ +#define ARM_DSCR_HDBGEN (1 << 14) +#define ARM_DSCR_MDBGEN (1 << 15) + +/* opcode2 numbers for the co-processor instructions. */ +#define ARM_OP2_BVR 4 +#define ARM_OP2_BCR 5 +#define ARM_OP2_WVR 6 +#define ARM_OP2_WCR 7 + +/* Base register numbers for the debug registers. */ +#define ARM_BASE_BVR 64 +#define ARM_BASE_BCR 80 +#define ARM_BASE_WVR 96 +#define ARM_BASE_WCR 112 + +/* Accessor macros for the debug registers. */ +#define ARM_DBG_READ(M, OP2, VAL) do {\ + asm volatile("mrc p14, 0, %0, c0," #M ", " #OP2 : "=r" (VAL));\ +} while (0) + +#define ARM_DBG_WRITE(M, OP2, VAL) do {\ + asm volatile("mcr p14, 0, %0, c0," #M ", " #OP2 : : "r" (VAL));\ +} while (0) + +struct notifier_block; +struct perf_event; +struct pmu; + +extern struct pmu perf_ops_bp; +extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, + int *gen_len, int *gen_type); +extern int arch_check_bp_in_kernelspace(struct perf_event *bp); +extern int arch_validate_hwbkpt_settings(struct perf_event *bp); +extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, + unsigned long val, void *data); + +extern u8 arch_get_debug_arch(void); +extern u8 arch_get_max_wp_len(void); +extern void clear_ptrace_hw_breakpoint(struct task_struct *tsk); + +int arch_install_hw_breakpoint(struct perf_event *bp); +void arch_uninstall_hw_breakpoint(struct perf_event *bp); +void hw_breakpoint_pmu_read(struct perf_event *bp); +int hw_breakpoint_slots(int type); + +#else +static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk) {} + +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ +#endif /* __KERNEL__ */ +#endif /* _ARM_HW_BREAKPOINT_H */ diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index e4dfa69abb68..cbb0bc295d2b 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -7,20 +7,27 @@ struct unwind_table; -struct mod_arch_specific -{ #ifdef CONFIG_ARM_UNWIND - Elf_Shdr *unw_sec_init; - Elf_Shdr *unw_sec_devinit; - Elf_Shdr *unw_sec_core; - Elf_Shdr *sec_init_text; - Elf_Shdr *sec_devinit_text; - Elf_Shdr *sec_core_text; - struct unwind_table *unwind_init; - struct unwind_table *unwind_devinit; - struct unwind_table *unwind_core; -#endif +struct arm_unwind_mapping { + Elf_Shdr *unw_sec; + Elf_Shdr *sec_text; + struct unwind_table *unwind; +}; +enum { + ARM_SEC_INIT, + ARM_SEC_DEVINIT, + ARM_SEC_CORE, + ARM_SEC_EXIT, + ARM_SEC_DEVEXIT, + ARM_SEC_MAX, +}; +struct mod_arch_specific { + struct arm_unwind_mapping map[ARM_SEC_MAX]; }; +#else +struct mod_arch_specific { +}; +#endif /* * Include the ARM architecture version. diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index e90b167ea848..a9672e8406a3 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -278,9 +278,24 @@ extern struct page *empty_zero_page; #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext) -#define set_pte_at(mm,addr,ptep,pteval) do { \ - set_pte_ext(ptep, pteval, (addr) >= TASK_SIZE ? 0 : PTE_EXT_NG); \ - } while (0) +#if __LINUX_ARM_ARCH__ < 6 +static inline void __sync_icache_dcache(pte_t pteval) +{ +} +#else +extern void __sync_icache_dcache(pte_t pteval); +#endif + +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + if (addr >= TASK_SIZE) + set_pte_ext(ptep, pteval, 0); + else { + __sync_icache_dcache(pteval); + set_pte_ext(ptep, pteval, PTE_EXT_NG); + } +} /* * The following only work if pte_present() is true. @@ -290,8 +305,13 @@ extern struct page *empty_zero_page; #define pte_write(pte) (pte_val(pte) & L_PTE_WRITE) #define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY) #define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG) +#define pte_exec(pte) (pte_val(pte) & L_PTE_EXEC) #define pte_special(pte) (0) +#define pte_present_user(pte) \ + ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \ + (L_PTE_PRESENT | L_PTE_USER)) + #define PTE_BIT_FUNC(fn,op) \ static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index 7bed3daf83b8..67357baaeeeb 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -19,6 +19,7 @@ #ifdef __KERNEL__ +#include <asm/hw_breakpoint.h> #include <asm/ptrace.h> #include <asm/types.h> @@ -41,6 +42,9 @@ struct debug_entry { struct debug_info { int nsaved; struct debug_entry bp[2]; +#ifdef CONFIG_HAVE_HW_BREAKPOINT + struct perf_event *hbp[ARM_MAX_HBP_SLOTS]; +#endif }; struct thread_struct { diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 7ce15eb15f72..783d50f32618 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -29,6 +29,8 @@ #define PTRACE_SETCRUNCHREGS 26 #define PTRACE_GETVFPREGS 27 #define PTRACE_SETVFPREGS 28 +#define PTRACE_GETHBPREGS 29 +#define PTRACE_SETHBPREGS 30 /* * PSR bits diff --git a/arch/arm/include/asm/smp_mpidr.h b/arch/arm/include/asm/smp_mpidr.h new file mode 100644 index 000000000000..6a9307d64900 --- /dev/null +++ b/arch/arm/include/asm/smp_mpidr.h @@ -0,0 +1,17 @@ +#ifndef ASMARM_SMP_MIDR_H +#define ASMARM_SMP_MIDR_H + +#define hard_smp_processor_id() \ + ({ \ + unsigned int cpunum; \ + __asm__("\n" \ + "1: mrc p15, 0, %0, c0, c0, 5\n" \ + " .pushsection \".alt.smp.init\", \"a\"\n"\ + " .long 1b\n" \ + " mov %0, #0\n" \ + " .popsection" \ + : "=r" (cpunum)); \ + cpunum &= 0x0F; \ + }) + +#endif diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index e6215305544a..f24c1b9e211d 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -7,15 +7,40 @@ #include <asm/cputype.h> +/* + * Return true if we are running on a SMP platform + */ +static inline bool is_smp(void) +{ +#ifndef CONFIG_SMP + return false; +#elif defined(CONFIG_SMP_ON_UP) + extern unsigned int smp_on_up; + return !!smp_on_up; +#else + return true; +#endif +} + /* all SMP configurations have the extended CPUID registers */ static inline int tlb_ops_need_broadcast(void) { + if (!is_smp()) + return 0; + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; } +#if !defined(CONFIG_SMP) || __LINUX_ARM_ARCH__ >= 7 +#define cache_ops_need_broadcast() 0 +#else static inline int cache_ops_need_broadcast(void) { + if (!is_smp()) + return 0; + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 1; } +#endif #endif diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 8ba1ccf82a02..1120f18a6b17 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -85,6 +85,10 @@ void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), int sig, int code, const char *name); +void hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, + struct pt_regs *), + int sig, int code, const char *name); + #define xchg(ptr,x) \ ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) @@ -325,6 +329,8 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size extern void disable_hlt(void); extern void enable_hlt(void); +void cpu_idle_wait(void); + #include <asm-generic/cmpxchg-local.h> #if __LINUX_ARM_ARCH__ < 6 diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index 33b546ae72d4..ce7378ea15a2 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -70,6 +70,10 @@ #undef _TLB #undef MULTI_TLB +#ifdef CONFIG_SMP_ON_UP +#define MULTI_TLB 1 +#endif + #define v3_tlb_flags (TLB_V3_FULL | TLB_V3_PAGE) #ifdef CONFIG_CPU_TLB_V3 @@ -185,17 +189,23 @@ # define v6wbi_always_flags (-1UL) #endif -#ifdef CONFIG_SMP -#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_V7_IS_BTB | \ +#define v7wbi_tlb_flags_smp (TLB_WB | TLB_DCLEAN | TLB_V7_IS_BTB | \ TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID) -#else -#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_BTB | \ +#define v7wbi_tlb_flags_up (TLB_WB | TLB_DCLEAN | TLB_BTB | \ TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID) -#endif #ifdef CONFIG_CPU_TLB_V7 -# define v7wbi_possible_flags v7wbi_tlb_flags -# define v7wbi_always_flags v7wbi_tlb_flags + +# ifdef CONFIG_SMP_ON_UP +# define v7wbi_possible_flags (v7wbi_tlb_flags_smp | v7wbi_tlb_flags_up) +# define v7wbi_always_flags (v7wbi_tlb_flags_smp & v7wbi_tlb_flags_up) +# elif defined(CONFIG_SMP) +# define v7wbi_possible_flags v7wbi_tlb_flags_smp +# define v7wbi_always_flags v7wbi_tlb_flags_smp +# else +# define v7wbi_possible_flags v7wbi_tlb_flags_up +# define v7wbi_always_flags v7wbi_tlb_flags_up +# endif # ifdef _TLB # define MULTI_TLB 1 # else @@ -560,12 +570,20 @@ extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); #endif /* - * if PG_dcache_dirty is set for the page, we need to ensure that any + * If PG_dcache_clean is not set for the page, we need to ensure that any * cache entries for the kernels virtual memory range are written - * back to the page. + * back to the page. On ARMv6 and later, the cache coherency is handled via + * the set_pte_at() function. */ +#if __LINUX_ARM_ARCH__ < 6 extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep); +#else +static inline void update_mmu_cache(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ +} +#endif #endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 980b78e31328..5b9b268f4fbb 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_HAVE_TCM) += tcm.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 8214bfebfaca..e5e1e5387678 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -165,6 +165,8 @@ EXPORT_SYMBOL(_find_next_bit_be); #endif #ifdef CONFIG_FUNCTION_TRACER +#ifdef CONFIG_OLD_MCOUNT EXPORT_SYMBOL(mcount); +#endif EXPORT_SYMBOL(__gnu_mcount_nc); #endif diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index bb8e93a76407..c09e3573c5de 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -46,7 +46,8 @@ * this macro assumes that irqstat (r6) and base (r5) are * preserved from get_irqnr_and_base above */ - test_for_ipi r0, r6, r5, lr + ALT_SMP(test_for_ipi r0, r6, r5, lr) + ALT_UP_B(9997f) movne r0, sp adrne lr, BSYM(1b) bne do_IPI @@ -57,6 +58,7 @@ adrne lr, BSYM(1b) bne do_local_timer #endif +9997: #endif .endm @@ -965,11 +967,8 @@ kuser_cmpxchg_fixup: beq 1b rsbs r0, r3, #0 /* beware -- each __kuser slot must be 8 instructions max */ -#ifdef CONFIG_SMP - b __kuser_memory_barrier -#else - usr_ret lr -#endif + ALT_SMP(b __kuser_memory_barrier) + ALT_UP(usr_ret lr) #endif diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 0385a8207b67..8bfa98757cd2 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -129,30 +129,58 @@ ENDPROC(ret_from_fork) * clobber the ip register. This is OK because the ARM calling convention * allows it to be clobbered in subroutines and doesn't use it to hold * parameters.) + * + * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0" + * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see + * arch/arm/kernel/ftrace.c). */ + +#ifndef CONFIG_OLD_MCOUNT +#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) +#error Ftrace requires CONFIG_FRAME_POINTER=y with GCC older than 4.4.0. +#endif +#endif + #ifdef CONFIG_DYNAMIC_FTRACE -ENTRY(mcount) +ENTRY(__gnu_mcount_nc) + mov ip, lr + ldmia sp!, {lr} + mov pc, ip +ENDPROC(__gnu_mcount_nc) + +ENTRY(ftrace_caller) stmdb sp!, {r0-r3, lr} mov r0, lr sub r0, r0, #MCOUNT_INSN_SIZE + ldr r1, [sp, #20] - .globl mcount_call -mcount_call: + .global ftrace_call +ftrace_call: bl ftrace_stub - ldr lr, [fp, #-4] @ restore lr - ldmia sp!, {r0-r3, pc} + ldmia sp!, {r0-r3, ip, lr} + mov pc, ip +ENDPROC(ftrace_caller) -ENTRY(ftrace_caller) +#ifdef CONFIG_OLD_MCOUNT +ENTRY(mcount) + stmdb sp!, {lr} + ldr lr, [fp, #-4] + ldmia sp!, {pc} +ENDPROC(mcount) + +ENTRY(ftrace_caller_old) stmdb sp!, {r0-r3, lr} ldr r1, [fp, #-4] mov r0, lr sub r0, r0, #MCOUNT_INSN_SIZE - .globl ftrace_call -ftrace_call: + .globl ftrace_call_old +ftrace_call_old: bl ftrace_stub ldr lr, [fp, #-4] @ restore lr ldmia sp!, {r0-r3, pc} +ENDPROC(ftrace_caller_old) +#endif #else @@ -160,7 +188,7 @@ ENTRY(__gnu_mcount_nc) stmdb sp!, {r0-r3, lr} ldr r0, =ftrace_trace_function ldr r2, [r0] - adr r0, ftrace_stub + adr r0, .Lftrace_stub cmp r0, r2 bne gnu_trace ldmia sp!, {r0-r3, ip, lr} @@ -170,11 +198,19 @@ gnu_trace: ldr r1, [sp, #20] @ lr of instrumented routine mov r0, lr sub r0, r0, #MCOUNT_INSN_SIZE - mov lr, pc + adr lr, BSYM(1f) mov pc, r2 +1: ldmia sp!, {r0-r3, ip, lr} mov pc, ip +ENDPROC(__gnu_mcount_nc) +#ifdef CONFIG_OLD_MCOUNT +/* + * This is under an ifdef in order to force link-time errors for people trying + * to build with !FRAME_POINTER with a GCC which doesn't use the new-style + * mcount. + */ ENTRY(mcount) stmdb sp!, {r0-r3, lr} ldr r0, =ftrace_trace_function @@ -193,12 +229,15 @@ trace: mov pc, r2 ldr lr, [fp, #-4] @ restore lr ldmia sp!, {r0-r3, pc} +ENDPROC(mcount) +#endif #endif /* CONFIG_DYNAMIC_FTRACE */ - .globl ftrace_stub -ftrace_stub: +ENTRY(ftrace_stub) +.Lftrace_stub: mov pc, lr +ENDPROC(ftrace_stub) #endif /* CONFIG_FUNCTION_TRACER */ diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 33c7077174db..a48d51257988 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -30,6 +30,21 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shishkin"); +/* + * ETM tracer state + */ +struct tracectx { + unsigned int etb_bufsz; + void __iomem *etb_regs; + void __iomem *etm_regs; + unsigned long flags; + int ncmppairs; + int etm_portsz; + struct device *dev; + struct clk *emu_clk; + struct mutex mutex; +}; + static struct tracectx tracer; static inline bool trace_isrunning(struct tracectx *t) diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 0298286ad4ad..971ac8c36ea7 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -2,102 +2,194 @@ * Dynamic function tracing support. * * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com> + * Copyright (C) 2010 Rabin Vincent <rabin@rab.in> * * For licencing details, see COPYING. * * Defines low-level handling of mcount calls when the kernel * is compiled with the -pg flag. When using dynamic ftrace, the - * mcount call-sites get patched lazily with NOP till they are - * enabled. All code mutation routines here take effect atomically. + * mcount call-sites get patched with NOP till they are enabled. + * All code mutation routines here are called under stop_machine(). */ #include <linux/ftrace.h> +#include <linux/uaccess.h> #include <asm/cacheflush.h> #include <asm/ftrace.h> -#define PC_OFFSET 8 -#define BL_OPCODE 0xeb000000 -#define BL_OFFSET_MASK 0x00ffffff +#ifdef CONFIG_THUMB2_KERNEL +#define NOP 0xeb04f85d /* pop.w {lr} */ +#else +#define NOP 0xe8bd4000 /* pop {lr} */ +#endif -static unsigned long bl_insn; -static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */ +#ifdef CONFIG_OLD_MCOUNT +#define OLD_MCOUNT_ADDR ((unsigned long) mcount) +#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old) -unsigned char *ftrace_nop_replace(void) +#define OLD_NOP 0xe1a00000 /* mov r0, r0 */ + +static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) { - return (char *)&NOP; + return rec->arch.old_mcount ? OLD_NOP : NOP; } +static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) +{ + if (!rec->arch.old_mcount) + return addr; + + if (addr == MCOUNT_ADDR) + addr = OLD_MCOUNT_ADDR; + else if (addr == FTRACE_ADDR) + addr = OLD_FTRACE_ADDR; + + return addr; +} +#else +static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) +{ + return NOP; +} + +static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) +{ + return addr; +} +#endif + /* construct a branch (BL) instruction to addr */ -unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) +#ifdef CONFIG_THUMB2_KERNEL +static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) { + unsigned long s, j1, j2, i1, i2, imm10, imm11; + unsigned long first, second; long offset; - offset = (long)addr - (long)(pc + PC_OFFSET); + offset = (long)addr - (long)(pc + 4); + if (offset < -16777216 || offset > 16777214) { + WARN_ON_ONCE(1); + return 0; + } + + s = (offset >> 24) & 0x1; + i1 = (offset >> 23) & 0x1; + i2 = (offset >> 22) & 0x1; + imm10 = (offset >> 12) & 0x3ff; + imm11 = (offset >> 1) & 0x7ff; + + j1 = (!i1) ^ s; + j2 = (!i2) ^ s; + + first = 0xf000 | (s << 10) | imm10; + second = 0xd000 | (j1 << 13) | (j2 << 11) | imm11; + + return (second << 16) | first; +} +#else +static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) +{ + long offset; + + offset = (long)addr - (long)(pc + 8); if (unlikely(offset < -33554432 || offset > 33554428)) { /* Can't generate branches that far (from ARM ARM). Ftrace * doesn't generate branches outside of kernel text. */ WARN_ON_ONCE(1); - return NULL; + return 0; } - offset = (offset >> 2) & BL_OFFSET_MASK; - bl_insn = BL_OPCODE | offset; - return (unsigned char *)&bl_insn; -} -int ftrace_modify_code(unsigned long pc, unsigned char *old_code, - unsigned char *new_code) -{ - unsigned long err = 0, replaced = 0, old, new; + offset = (offset >> 2) & 0x00ffffff; - old = *(unsigned long *)old_code; - new = *(unsigned long *)new_code; + return 0xeb000000 | offset; +} +#endif - __asm__ __volatile__ ( - "1: ldr %1, [%2] \n" - " cmp %1, %4 \n" - "2: streq %3, [%2] \n" - " cmpne %1, %3 \n" - " movne %0, #2 \n" - "3:\n" +static int ftrace_modify_code(unsigned long pc, unsigned long old, + unsigned long new) +{ + unsigned long replaced; - ".pushsection .fixup, \"ax\"\n" - "4: mov %0, #1 \n" - " b 3b \n" - ".popsection\n" + if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE)) + return -EFAULT; - ".pushsection __ex_table, \"a\"\n" - " .long 1b, 4b \n" - " .long 2b, 4b \n" - ".popsection\n" + if (replaced != old) + return -EINVAL; - : "=r"(err), "=r"(replaced) - : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced) - : "memory"); + if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) + return -EPERM; - if (!err && (replaced == old)) - flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); + flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); - return err; + return 0; } int ftrace_update_ftrace_func(ftrace_func_t func) { - int ret; unsigned long pc, old; - unsigned char *new; + unsigned long new; + int ret; pc = (unsigned long)&ftrace_call; memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE); new = ftrace_call_replace(pc, (unsigned long)func); - ret = ftrace_modify_code(pc, (unsigned char *)&old, new); + + ret = ftrace_modify_code(pc, old, new); + +#ifdef CONFIG_OLD_MCOUNT + if (!ret) { + pc = (unsigned long)&ftrace_call_old; + memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE); + new = ftrace_call_replace(pc, (unsigned long)func); + + ret = ftrace_modify_code(pc, old, new); + } +#endif + + return ret; +} + +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long new, old; + unsigned long ip = rec->ip; + + old = ftrace_nop_replace(rec); + new = ftrace_call_replace(ip, adjust_address(rec, addr)); + + return ftrace_modify_code(rec->ip, old, new); +} + +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long ip = rec->ip; + unsigned long old; + unsigned long new; + int ret; + + old = ftrace_call_replace(ip, adjust_address(rec, addr)); + new = ftrace_nop_replace(rec); + ret = ftrace_modify_code(ip, old, new); + +#ifdef CONFIG_OLD_MCOUNT + if (ret == -EINVAL && addr == MCOUNT_ADDR) { + rec->arch.old_mcount = true; + + old = ftrace_call_replace(ip, adjust_address(rec, addr)); + new = ftrace_nop_replace(rec); + ret = ftrace_modify_code(ip, old, new); + } +#endif + return ret; } -/* run from ftrace_init with irqs disabled */ int __init ftrace_dyn_arch_init(void *data) { - ftrace_mcount_set(data); + *(unsigned long *)data = 0; + return 0; } diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index b9505aa267c0..bbecaac1e013 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -15,55 +15,6 @@ #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2) - .align 2 - .type __switch_data, %object -__switch_data: - .long __mmap_switched - .long __data_loc @ r4 - .long _data @ r5 - .long __bss_start @ r6 - .long _end @ r7 - .long processor_id @ r4 - .long __machine_arch_type @ r5 - .long __atags_pointer @ r6 - .long cr_alignment @ r7 - .long init_thread_union + THREAD_START_SP @ sp - -/* - * The following fragment of code is executed with the MMU on in MMU mode, - * and uses absolute addresses; this is not position independent. - * - * r0 = cp#15 control register - * r1 = machine ID - * r2 = atags pointer - * r9 = processor ID - */ -__mmap_switched: - adr r3, __switch_data + 4 - - ldmia r3!, {r4, r5, r6, r7} - cmp r4, r5 @ Copy data segment if needed -1: cmpne r5, r6 - ldrne fp, [r4], #4 - strne fp, [r5], #4 - bne 1b - - mov fp, #0 @ Clear BSS (and zero fp) -1: cmp r6, r7 - strcc fp, [r6],#4 - bcc 1b - - ARM( ldmia r3, {r4, r5, r6, r7, sp}) - THUMB( ldmia r3, {r4, r5, r6, r7} ) - THUMB( ldr sp, [r3, #16] ) - str r9, [r4] @ Save processor ID - str r1, [r5] @ Save machine type - str r2, [r6] @ Save atags pointer - bic r4, r0, #CR_A @ Clear 'A' bit - stmia r7, {r0, r4} @ Save control register values - b start_kernel -ENDPROC(__mmap_switched) - /* * Exception handling. Something went wrong and we can't proceed. We * ought to tell the user, but since we don't have any guarantee that @@ -73,21 +24,7 @@ ENDPROC(__mmap_switched) * and hope for the best (useful if bootloader fails to pass a proper * machine ID for example). */ -__error_p: -#ifdef CONFIG_DEBUG_LL - adr r0, str_p1 - bl printascii - mov r0, r9 - bl printhex8 - adr r0, str_p2 - bl printascii - b __error -str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x" -str_p2: .asciz ").\n" - .align -#endif -ENDPROC(__error_p) - + __HEAD __error_a: #ifdef CONFIG_DEBUG_LL mov r4, r1 @ preserve machine ID @@ -97,7 +34,7 @@ __error_a: bl printhex8 adr r0, str_a2 bl printascii - adr r3, 4f + adr r3, __lookup_machine_type_data ldmia r3, {r4, r5, r6} @ get machine desc list sub r4, r3, r4 @ get offset between virt&phys add r5, r5, r4 @ convert virt addresses to @@ -125,78 +62,6 @@ str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n" .align #endif -__error: -#ifdef CONFIG_ARCH_RPC -/* - * Turn the screen red on a error - RiscPC only. - */ - mov r0, #0x02000000 - mov r3, #0x11 - orr r3, r3, r3, lsl #8 - orr r3, r3, r3, lsl #16 - str r3, [r0], #4 - str r3, [r0], #4 - str r3, [r0], #4 - str r3, [r0], #4 -#endif -1: mov r0, r0 - b 1b -ENDPROC(__error) - - -/* - * Read processor ID register (CP#15, CR0), and look up in the linker-built - * supported processor list. Note that we can't use the absolute addresses - * for the __proc_info lists since we aren't running with the MMU on - * (and therefore, we are not in the correct address space). We have to - * calculate the offset. - * - * r9 = cpuid - * Returns: - * r3, r4, r6 corrupted - * r5 = proc_info pointer in physical address space - * r9 = cpuid (preserved) - */ -__lookup_processor_type: - adr r3, 3f - ldmia r3, {r5 - r7} - add r3, r3, #8 - sub r3, r3, r7 @ get offset between virt&phys - add r5, r5, r3 @ convert virt addresses to - add r6, r6, r3 @ physical address space -1: ldmia r5, {r3, r4} @ value, mask - and r4, r4, r9 @ mask wanted bits - teq r3, r4 - beq 2f - add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) - cmp r5, r6 - blo 1b - mov r5, #0 @ unknown processor -2: mov pc, lr -ENDPROC(__lookup_processor_type) - -/* - * This provides a C-API version of the above function. - */ -ENTRY(lookup_processor_type) - stmfd sp!, {r4 - r7, r9, lr} - mov r9, r0 - bl __lookup_processor_type - mov r0, r5 - ldmfd sp!, {r4 - r7, r9, pc} -ENDPROC(lookup_processor_type) - -/* - * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for - * more information about the __proc_info and __arch_info structures. - */ - .align 2 -3: .long __proc_info_begin - .long __proc_info_end -4: .long . - .long __arch_info_begin - .long __arch_info_end - /* * Lookup machine architecture in the linker-build list of architectures. * Note that we can't use the absolute addresses for the __arch_info @@ -209,7 +74,7 @@ ENDPROC(lookup_processor_type) * r5 = mach_info pointer in physical address space */ __lookup_machine_type: - adr r3, 4b + adr r3, __lookup_machine_type_data ldmia r3, {r4, r5, r6} sub r3, r3, r4 @ get offset between virt&phys add r5, r5, r3 @ convert virt addresses to @@ -225,15 +90,16 @@ __lookup_machine_type: ENDPROC(__lookup_machine_type) /* - * This provides a C-API version of the above function. + * Look in arch/arm/kernel/arch.[ch] for information about the + * __arch_info structures. */ -ENTRY(lookup_machine_type) - stmfd sp!, {r4 - r6, lr} - mov r1, r0 - bl __lookup_machine_type - mov r0, r5 - ldmfd sp!, {r4 - r6, pc} -ENDPROC(lookup_machine_type) + .align 2 + .type __lookup_machine_type_data, %object +__lookup_machine_type_data: + .long . + .long __arch_info_begin + .long __arch_info_end + .size __lookup_machine_type_data, . - __lookup_machine_type_data /* Determine validity of the r2 atags pointer. The heuristic requires * that the pointer be aligned, in the first 16k of physical RAM and @@ -265,3 +131,150 @@ __vet_atags: 1: mov r2, #0 mov pc, lr ENDPROC(__vet_atags) + +/* + * The following fragment of code is executed with the MMU on in MMU mode, + * and uses absolute addresses; this is not position independent. + * + * r0 = cp#15 control register + * r1 = machine ID + * r2 = atags pointer + * r9 = processor ID + */ + __INIT +__mmap_switched: + adr r3, __mmap_switched_data + + ldmia r3!, {r4, r5, r6, r7} + cmp r4, r5 @ Copy data segment if needed +1: cmpne r5, r6 + ldrne fp, [r4], #4 + strne fp, [r5], #4 + bne 1b + + mov fp, #0 @ Clear BSS (and zero fp) +1: cmp r6, r7 + strcc fp, [r6],#4 + bcc 1b + + ARM( ldmia r3, {r4, r5, r6, r7, sp}) + THUMB( ldmia r3, {r4, r5, r6, r7} ) + THUMB( ldr sp, [r3, #16] ) + str r9, [r4] @ Save processor ID + str r1, [r5] @ Save machine type + str r2, [r6] @ Save atags pointer + bic r4, r0, #CR_A @ Clear 'A' bit + stmia r7, {r0, r4} @ Save control register values + b start_kernel +ENDPROC(__mmap_switched) + + .align 2 + .type __mmap_switched_data, %object +__mmap_switched_data: + .long __data_loc @ r4 + .long _sdata @ r5 + .long __bss_start @ r6 + .long _end @ r7 + .long processor_id @ r4 + .long __machine_arch_type @ r5 + .long __atags_pointer @ r6 + .long cr_alignment @ r7 + .long init_thread_union + THREAD_START_SP @ sp + .size __mmap_switched_data, . - __mmap_switched_data + +/* + * This provides a C-API version of __lookup_machine_type + */ +ENTRY(lookup_machine_type) + stmfd sp!, {r4 - r6, lr} + mov r1, r0 + bl __lookup_machine_type + mov r0, r5 + ldmfd sp!, {r4 - r6, pc} +ENDPROC(lookup_machine_type) + +/* + * This provides a C-API version of __lookup_processor_type + */ +ENTRY(lookup_processor_type) + stmfd sp!, {r4 - r6, r9, lr} + mov r9, r0 + bl __lookup_processor_type + mov r0, r5 + ldmfd sp!, {r4 - r6, r9, pc} +ENDPROC(lookup_processor_type) + +/* + * Read processor ID register (CP#15, CR0), and look up in the linker-built + * supported processor list. Note that we can't use the absolute addresses + * for the __proc_info lists since we aren't running with the MMU on + * (and therefore, we are not in the correct address space). We have to + * calculate the offset. + * + * r9 = cpuid + * Returns: + * r3, r4, r6 corrupted + * r5 = proc_info pointer in physical address space + * r9 = cpuid (preserved) + */ + __CPUINIT +__lookup_processor_type: + adr r3, __lookup_processor_type_data + ldmia r3, {r4 - r6} + sub r3, r3, r4 @ get offset between virt&phys + add r5, r5, r3 @ convert virt addresses to + add r6, r6, r3 @ physical address space +1: ldmia r5, {r3, r4} @ value, mask + and r4, r4, r9 @ mask wanted bits + teq r3, r4 + beq 2f + add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) + cmp r5, r6 + blo 1b + mov r5, #0 @ unknown processor +2: mov pc, lr +ENDPROC(__lookup_processor_type) + +/* + * Look in <asm/procinfo.h> for information about the __proc_info structure. + */ + .align 2 + .type __lookup_processor_type_data, %object +__lookup_processor_type_data: + .long . + .long __proc_info_begin + .long __proc_info_end + .size __lookup_processor_type_data, . - __lookup_processor_type_data + +__error_p: +#ifdef CONFIG_DEBUG_LL + adr r0, str_p1 + bl printascii + mov r0, r9 + bl printhex8 + adr r0, str_p2 + bl printascii + b __error +str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x" +str_p2: .asciz ").\n" + .align +#endif +ENDPROC(__error_p) + +__error: +#ifdef CONFIG_ARCH_RPC +/* + * Turn the screen red on a error - RiscPC only. + */ + mov r0, #0x02000000 + mov r3, #0x11 + orr r3, r3, r3, lsl #8 + orr r3, r3, r3, lsl #16 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 +#endif +1: mov r0, r0 + b 1b +ENDPROC(__error) diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 573b803dc6bf..814ce1a73270 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -48,8 +48,6 @@ ENTRY(stext) movs r8, r5 @ invalid machine (r5=0)? beq __error_a @ yes, error 'a' - ldr r13, __switch_data @ address to jump to after - @ the initialization is done adr lr, BSYM(__after_proc_init) @ return (PIC) address ARM( add pc, r10, #PROCINFO_INITFUNC ) THUMB( add r12, r10, #PROCINFO_INITFUNC ) @@ -87,8 +85,7 @@ __after_proc_init: mcr p15, 0, r0, c1, c0, 0 @ write control reg #endif /* CONFIG_CPU_CP15 */ - mov r3, r13 - mov pc, r3 @ clear the BSS and jump + b __mmap_switched @ clear the BSS and jump @ to start_kernel ENDPROC(__after_proc_init) .ltorg diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index eb62bf947212..767390449e0a 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -86,6 +86,9 @@ ENTRY(stext) movs r8, r5 @ invalid machine (r5=0)? beq __error_a @ yes, error 'a' bl __vet_atags +#ifdef CONFIG_SMP_ON_UP + bl __fixup_smp +#endif bl __create_page_tables /* @@ -95,113 +98,15 @@ ENTRY(stext) * above. On return, the CPU will be ready for the MMU to be * turned on, and r0 will hold the CPU control register value. */ - ldr r13, __switch_data @ address to jump to after + ldr r13, =__mmap_switched @ address to jump to after @ mmu has been enabled - adr lr, BSYM(__enable_mmu) @ return (PIC) address + adr lr, BSYM(1f) @ return (PIC) address ARM( add pc, r10, #PROCINFO_INITFUNC ) THUMB( add r12, r10, #PROCINFO_INITFUNC ) THUMB( mov pc, r12 ) +1: b __enable_mmu ENDPROC(stext) - -#if defined(CONFIG_SMP) -ENTRY(secondary_startup) - /* - * Common entry point for secondary CPUs. - * - * Ensure that we're in SVC mode, and IRQs are disabled. Lookup - * the processor type - there is no need to check the machine type - * as it has already been validated by the primary processor. - */ - setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 - mrc p15, 0, r9, c0, c0 @ get processor id - bl __lookup_processor_type - movs r10, r5 @ invalid processor? - moveq r0, #'p' @ yes, error 'p' - beq __error - - /* - * Use the page tables supplied from __cpu_up. - */ - adr r4, __secondary_data - ldmia r4, {r5, r7, r12} @ address to jump to after - sub r4, r4, r5 @ mmu has been enabled - ldr r4, [r7, r4] @ get secondary_data.pgdir - adr lr, BSYM(__enable_mmu) @ return address - mov r13, r12 @ __secondary_switched address - ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor - @ (return control reg) - THUMB( add r12, r10, #PROCINFO_INITFUNC ) - THUMB( mov pc, r12 ) -ENDPROC(secondary_startup) - - /* - * r6 = &secondary_data - */ -ENTRY(__secondary_switched) - ldr sp, [r7, #4] @ get secondary_data.stack - mov fp, #0 - b secondary_start_kernel -ENDPROC(__secondary_switched) - - .type __secondary_data, %object -__secondary_data: - .long . - .long secondary_data - .long __secondary_switched -#endif /* defined(CONFIG_SMP) */ - - - -/* - * Setup common bits before finally enabling the MMU. Essentially - * this is just loading the page table pointer and domain access - * registers. - */ -__enable_mmu: -#ifdef CONFIG_ALIGNMENT_TRAP - orr r0, r0, #CR_A -#else - bic r0, r0, #CR_A -#endif -#ifdef CONFIG_CPU_DCACHE_DISABLE - bic r0, r0, #CR_C -#endif -#ifdef CONFIG_CPU_BPREDICT_DISABLE - bic r0, r0, #CR_Z -#endif -#ifdef CONFIG_CPU_ICACHE_DISABLE - bic r0, r0, #CR_I -#endif - mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_IO, DOMAIN_CLIENT)) - mcr p15, 0, r5, c3, c0, 0 @ load domain access register - mcr p15, 0, r4, c2, c0, 0 @ load page table pointer - b __turn_mmu_on -ENDPROC(__enable_mmu) - -/* - * Enable the MMU. This completely changes the structure of the visible - * memory space. You will not be able to trace execution through this. - * If you have an enquiry about this, *please* check the linux-arm-kernel - * mailing list archives BEFORE sending another post to the list. - * - * r0 = cp#15 control register - * r13 = *virtual* address to jump to upon completion - * - * other registers depend on the function called upon completion - */ - .align 5 -__turn_mmu_on: - mov r0, r0 - mcr p15, 0, r0, c1, c0, 0 @ write control reg - mrc p15, 0, r3, c0, c0, 0 @ read id reg - mov r3, r3 - mov r3, r13 - mov pc, r3 -ENDPROC(__turn_mmu_on) - + .ltorg /* * Setup the initial page tables. We only setup the barest @@ -213,7 +118,7 @@ ENDPROC(__turn_mmu_on) * r10 = procinfo * * Returns: - * r0, r3, r6, r7 corrupted + * r0, r3, r5-r7 corrupted * r4 = physical page table address */ __create_page_tables: @@ -235,20 +140,30 @@ __create_page_tables: ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags /* - * Create identity mapping for first MB of kernel to - * cater for the MMU enable. This identity mapping - * will be removed by paging_init(). We use our current program - * counter to determine corresponding section base address. + * Create identity mapping to cater for __enable_mmu. + * This identity mapping will be removed by paging_init(). */ - mov r6, pc - mov r6, r6, lsr #20 @ start of kernel section - orr r3, r7, r6, lsl #20 @ flags + kernel base - str r3, [r4, r6, lsl #2] @ identity mapping + adr r0, __enable_mmu_loc + ldmia r0, {r3, r5, r6} + sub r0, r0, r3 @ virt->phys offset + add r5, r5, r0 @ phys __enable_mmu + add r6, r6, r0 @ phys __enable_mmu_end + mov r5, r5, lsr #20 + mov r6, r6, lsr #20 + +1: orr r3, r7, r5, lsl #20 @ flags + kernel base + str r3, [r4, r5, lsl #2] @ identity mapping + teq r5, r6 + addne r5, r5, #1 @ next section + bne 1b /* * Now setup the pagetables for our kernel direct * mapped region. */ + mov r3, pc + mov r3, r3, lsr #20 + orr r3, r7, r3, lsl #20 add r0, r4, #(KERNEL_START & 0xff000000) >> 18 str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! ldr r6, =(KERNEL_END - 1) @@ -332,5 +247,168 @@ __create_page_tables: mov pc, lr ENDPROC(__create_page_tables) .ltorg +__enable_mmu_loc: + .long . + .long __enable_mmu + .long __enable_mmu_end + +#if defined(CONFIG_SMP) + __CPUINIT +ENTRY(secondary_startup) + /* + * Common entry point for secondary CPUs. + * + * Ensure that we're in SVC mode, and IRQs are disabled. Lookup + * the processor type - there is no need to check the machine type + * as it has already been validated by the primary processor. + */ + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 + mrc p15, 0, r9, c0, c0 @ get processor id + bl __lookup_processor_type + movs r10, r5 @ invalid processor? + moveq r0, #'p' @ yes, error 'p' + beq __error_p + + /* + * Use the page tables supplied from __cpu_up. + */ + adr r4, __secondary_data + ldmia r4, {r5, r7, r12} @ address to jump to after + sub r4, r4, r5 @ mmu has been enabled + ldr r4, [r7, r4] @ get secondary_data.pgdir + adr lr, BSYM(__enable_mmu) @ return address + mov r13, r12 @ __secondary_switched address + ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor + @ (return control reg) + THUMB( add r12, r10, #PROCINFO_INITFUNC ) + THUMB( mov pc, r12 ) +ENDPROC(secondary_startup) + + /* + * r6 = &secondary_data + */ +ENTRY(__secondary_switched) + ldr sp, [r7, #4] @ get secondary_data.stack + mov fp, #0 + b secondary_start_kernel +ENDPROC(__secondary_switched) + + .type __secondary_data, %object +__secondary_data: + .long . + .long secondary_data + .long __secondary_switched +#endif /* defined(CONFIG_SMP) */ + + + +/* + * Setup common bits before finally enabling the MMU. Essentially + * this is just loading the page table pointer and domain access + * registers. + * + * r0 = cp#15 control register + * r1 = machine ID + * r2 = atags pointer + * r4 = page table pointer + * r9 = processor ID + * r13 = *virtual* address to jump to upon completion + */ +__enable_mmu: +#ifdef CONFIG_ALIGNMENT_TRAP + orr r0, r0, #CR_A +#else + bic r0, r0, #CR_A +#endif +#ifdef CONFIG_CPU_DCACHE_DISABLE + bic r0, r0, #CR_C +#endif +#ifdef CONFIG_CPU_BPREDICT_DISABLE + bic r0, r0, #CR_Z +#endif +#ifdef CONFIG_CPU_ICACHE_DISABLE + bic r0, r0, #CR_I +#endif + mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ + domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \ + domain_val(DOMAIN_IO, DOMAIN_CLIENT)) + mcr p15, 0, r5, c3, c0, 0 @ load domain access register + mcr p15, 0, r4, c2, c0, 0 @ load page table pointer + b __turn_mmu_on +ENDPROC(__enable_mmu) + +/* + * Enable the MMU. This completely changes the structure of the visible + * memory space. You will not be able to trace execution through this. + * If you have an enquiry about this, *please* check the linux-arm-kernel + * mailing list archives BEFORE sending another post to the list. + * + * r0 = cp#15 control register + * r1 = machine ID + * r2 = atags pointer + * r9 = processor ID + * r13 = *virtual* address to jump to upon completion + * + * other registers depend on the function called upon completion + */ + .align 5 +__turn_mmu_on: + mov r0, r0 + mcr p15, 0, r0, c1, c0, 0 @ write control reg + mrc p15, 0, r3, c0, c0, 0 @ read id reg + mov r3, r3 + mov r3, r13 + mov pc, r3 +__enable_mmu_end: +ENDPROC(__turn_mmu_on) + + +#ifdef CONFIG_SMP_ON_UP +__fixup_smp: + mov r7, #0x00070000 + orr r6, r7, #0xff000000 @ mask 0xff070000 + orr r7, r7, #0x41000000 @ val 0x41070000 + and r0, r9, r6 + teq r0, r7 @ ARM CPU and ARMv6/v7? + bne __fixup_smp_on_up @ no, assume UP + + orr r6, r6, #0x0000ff00 + orr r6, r6, #0x000000f0 @ mask 0xff07fff0 + orr r7, r7, #0x0000b000 + orr r7, r7, #0x00000020 @ val 0x4107b020 + and r0, r9, r6 + teq r0, r7 @ ARM 11MPCore? + moveq pc, lr @ yes, assume SMP + + mrc p15, 0, r0, c0, c0, 5 @ read MPIDR + tst r0, #1 << 31 + movne pc, lr @ bit 31 => SMP + +__fixup_smp_on_up: + adr r0, 1f + ldmia r0, {r3, r6, r7} + sub r3, r0, r3 + add r6, r6, r3 + add r7, r7, r3 +2: cmp r6, r7 + ldmia r6!, {r0, r4} + strlo r4, [r0, r3] + blo 2b + mov pc, lr +ENDPROC(__fixup_smp) + +1: .word . + .word __smpalt_begin + .word __smpalt_end + + .pushsection .data + .globl smp_on_up +smp_on_up: + ALT_SMP(.long 1) + ALT_UP(.long 0) + .popsection + +#endif #include "head-common.S" diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c new file mode 100644 index 000000000000..54593b0c241b --- /dev/null +++ b/arch/arm/kernel/hw_breakpoint.c @@ -0,0 +1,849 @@ +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2009, 2010 ARM Limited + * + * Author: Will Deacon <will.deacon@arm.com> + */ + +/* + * HW_breakpoint: a unified kernel/user-space hardware breakpoint facility, + * using the CPU's debug registers. + */ +#define pr_fmt(fmt) "hw-breakpoint: " fmt + +#include <linux/errno.h> +#include <linux/perf_event.h> +#include <linux/hw_breakpoint.h> +#include <linux/smp.h> + +#include <asm/cacheflush.h> +#include <asm/cputype.h> +#include <asm/current.h> +#include <asm/hw_breakpoint.h> +#include <asm/kdebug.h> +#include <asm/system.h> +#include <asm/traps.h> + +/* Breakpoint currently in use for each BRP. */ +static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]); + +/* Watchpoint currently in use for each WRP. */ +static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[ARM_MAX_WRP]); + +/* Number of BRP/WRP registers on this CPU. */ +static int core_num_brps; +static int core_num_wrps; + +/* Debug architecture version. */ +static u8 debug_arch; + +/* Maximum supported watchpoint length. */ +static u8 max_watchpoint_len; + +/* Determine number of BRP registers available. */ +static int get_num_brps(void) +{ + u32 didr; + ARM_DBG_READ(c0, 0, didr); + return ((didr >> 24) & 0xf) + 1; +} + +/* Determine number of WRP registers available. */ +static int get_num_wrps(void) +{ + /* + * FIXME: When a watchpoint fires, the only way to work out which + * watchpoint it was is by disassembling the faulting instruction + * and working out the address of the memory access. + * + * Furthermore, we can only do this if the watchpoint was precise + * since imprecise watchpoints prevent us from calculating register + * based addresses. + * + * For the time being, we only report 1 watchpoint register so we + * always know which watchpoint fired. In the future we can either + * add a disassembler and address generation emulator, or we can + * insert a check to see if the DFAR is set on watchpoint exception + * entry [the ARM ARM states that the DFAR is UNKNOWN, but + * experience shows that it is set on some implementations]. + */ + +#if 0 + u32 didr, wrps; + ARM_DBG_READ(c0, 0, didr); + return ((didr >> 28) & 0xf) + 1; +#endif + + return 1; +} + +int hw_breakpoint_slots(int type) +{ + /* + * We can be called early, so don't rely on + * our static variables being initialised. + */ + switch (type) { + case TYPE_INST: + return get_num_brps(); + case TYPE_DATA: + return get_num_wrps(); + default: + pr_warning("unknown slot type: %d\n", type); + return 0; + } +} + +/* Determine debug architecture. */ +static u8 get_debug_arch(void) +{ + u32 didr; + + /* Do we implement the extended CPUID interface? */ + if (((read_cpuid_id() >> 16) & 0xf) != 0xf) { + pr_warning("CPUID feature registers not supported. " + "Assuming v6 debug is present.\n"); + return ARM_DEBUG_ARCH_V6; + } + + ARM_DBG_READ(c0, 0, didr); + return (didr >> 16) & 0xf; +} + +/* Does this core support mismatch breakpoints? */ +static int core_has_mismatch_bps(void) +{ + return debug_arch >= ARM_DEBUG_ARCH_V7_ECP14 && core_num_brps > 1; +} + +u8 arch_get_debug_arch(void) +{ + return debug_arch; +} + +#define READ_WB_REG_CASE(OP2, M, VAL) \ + case ((OP2 << 4) + M): \ + ARM_DBG_READ(c ## M, OP2, VAL); \ + break + +#define WRITE_WB_REG_CASE(OP2, M, VAL) \ + case ((OP2 << 4) + M): \ + ARM_DBG_WRITE(c ## M, OP2, VAL);\ + break + +#define GEN_READ_WB_REG_CASES(OP2, VAL) \ + READ_WB_REG_CASE(OP2, 0, VAL); \ + READ_WB_REG_CASE(OP2, 1, VAL); \ + READ_WB_REG_CASE(OP2, 2, VAL); \ + READ_WB_REG_CASE(OP2, 3, VAL); \ + READ_WB_REG_CASE(OP2, 4, VAL); \ + READ_WB_REG_CASE(OP2, 5, VAL); \ + READ_WB_REG_CASE(OP2, 6, VAL); \ + READ_WB_REG_CASE(OP2, 7, VAL); \ + READ_WB_REG_CASE(OP2, 8, VAL); \ + READ_WB_REG_CASE(OP2, 9, VAL); \ + READ_WB_REG_CASE(OP2, 10, VAL); \ + READ_WB_REG_CASE(OP2, 11, VAL); \ + READ_WB_REG_CASE(OP2, 12, VAL); \ + READ_WB_REG_CASE(OP2, 13, VAL); \ + READ_WB_REG_CASE(OP2, 14, VAL); \ + READ_WB_REG_CASE(OP2, 15, VAL) + +#define GEN_WRITE_WB_REG_CASES(OP2, VAL) \ + WRITE_WB_REG_CASE(OP2, 0, VAL); \ + WRITE_WB_REG_CASE(OP2, 1, VAL); \ + WRITE_WB_REG_CASE(OP2, 2, VAL); \ + WRITE_WB_REG_CASE(OP2, 3, VAL); \ + WRITE_WB_REG_CASE(OP2, 4, VAL); \ + WRITE_WB_REG_CASE(OP2, 5, VAL); \ + WRITE_WB_REG_CASE(OP2, 6, VAL); \ + WRITE_WB_REG_CASE(OP2, 7, VAL); \ + WRITE_WB_REG_CASE(OP2, 8, VAL); \ + WRITE_WB_REG_CASE(OP2, 9, VAL); \ + WRITE_WB_REG_CASE(OP2, 10, VAL); \ + WRITE_WB_REG_CASE(OP2, 11, VAL); \ + WRITE_WB_REG_CASE(OP2, 12, VAL); \ + WRITE_WB_REG_CASE(OP2, 13, VAL); \ + WRITE_WB_REG_CASE(OP2, 14, VAL); \ + WRITE_WB_REG_CASE(OP2, 15, VAL) + +static u32 read_wb_reg(int n) +{ + u32 val = 0; + + switch (n) { + GEN_READ_WB_REG_CASES(ARM_OP2_BVR, val); + GEN_READ_WB_REG_CASES(ARM_OP2_BCR, val); + GEN_READ_WB_REG_CASES(ARM_OP2_WVR, val); + GEN_READ_WB_REG_CASES(ARM_OP2_WCR, val); + default: + pr_warning("attempt to read from unknown breakpoint " + "register %d\n", n); + } + + return val; +} + +static void write_wb_reg(int n, u32 val) +{ + switch (n) { + GEN_WRITE_WB_REG_CASES(ARM_OP2_BVR, val); + GEN_WRITE_WB_REG_CASES(ARM_OP2_BCR, val); + GEN_WRITE_WB_REG_CASES(ARM_OP2_WVR, val); + GEN_WRITE_WB_REG_CASES(ARM_OP2_WCR, val); + default: + pr_warning("attempt to write to unknown breakpoint " + "register %d\n", n); + } + isb(); +} + +/* + * In order to access the breakpoint/watchpoint control registers, + * we must be running in debug monitor mode. Unfortunately, we can + * be put into halting debug mode at any time by an external debugger + * but there is nothing we can do to prevent that. + */ +static int enable_monitor_mode(void) +{ + u32 dscr; + int ret = 0; + + ARM_DBG_READ(c1, 0, dscr); + + /* Ensure that halting mode is disabled. */ + if (WARN_ONCE(dscr & ARM_DSCR_HDBGEN, "halting debug mode enabled." + "Unable to access hardware resources.")) { + ret = -EPERM; + goto out; + } + + /* Write to the corresponding DSCR. */ + switch (debug_arch) { + case ARM_DEBUG_ARCH_V6: + case ARM_DEBUG_ARCH_V6_1: + ARM_DBG_WRITE(c1, 0, (dscr | ARM_DSCR_MDBGEN)); + break; + case ARM_DEBUG_ARCH_V7_ECP14: + ARM_DBG_WRITE(c2, 2, (dscr | ARM_DSCR_MDBGEN)); + break; + default: + ret = -ENODEV; + goto out; + } + + /* Check that the write made it through. */ + ARM_DBG_READ(c1, 0, dscr); + if (WARN_ONCE(!(dscr & ARM_DSCR_MDBGEN), + "failed to enable monitor mode.")) { + ret = -EPERM; + } + +out: + return ret; +} + +/* + * Check if 8-bit byte-address select is available. + * This clobbers WRP 0. + */ +static u8 get_max_wp_len(void) +{ + u32 ctrl_reg; + struct arch_hw_breakpoint_ctrl ctrl; + u8 size = 4; + + if (debug_arch < ARM_DEBUG_ARCH_V7_ECP14) + goto out; + + if (enable_monitor_mode()) + goto out; + + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.len = ARM_BREAKPOINT_LEN_8; + ctrl_reg = encode_ctrl_reg(ctrl); + + write_wb_reg(ARM_BASE_WVR, 0); + write_wb_reg(ARM_BASE_WCR, ctrl_reg); + if ((read_wb_reg(ARM_BASE_WCR) & ctrl_reg) == ctrl_reg) + size = 8; + +out: + return size; +} + +u8 arch_get_max_wp_len(void) +{ + return max_watchpoint_len; +} + +/* + * Handler for reactivating a suspended watchpoint when the single + * step `mismatch' breakpoint is triggered. + */ +static void wp_single_step_handler(struct perf_event *bp, int unused, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + perf_event_enable(counter_arch_bp(bp)->suspended_wp); + unregister_hw_breakpoint(bp); +} + +static int bp_is_single_step(struct perf_event *bp) +{ + return bp->overflow_handler == wp_single_step_handler; +} + +/* + * Install a perf counter breakpoint. + */ +int arch_install_hw_breakpoint(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + struct perf_event **slot, **slots; + int i, max_slots, ctrl_base, val_base, ret = 0; + + /* Ensure that we are in monitor mode and halting mode is disabled. */ + ret = enable_monitor_mode(); + if (ret) + goto out; + + if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { + /* Breakpoint */ + ctrl_base = ARM_BASE_BCR; + val_base = ARM_BASE_BVR; + slots = __get_cpu_var(bp_on_reg); + max_slots = core_num_brps - 1; + + if (bp_is_single_step(bp)) { + info->ctrl.mismatch = 1; + i = max_slots; + slots[i] = bp; + goto setup; + } + } else { + /* Watchpoint */ + ctrl_base = ARM_BASE_WCR; + val_base = ARM_BASE_WVR; + slots = __get_cpu_var(wp_on_reg); + max_slots = core_num_wrps; + } + + for (i = 0; i < max_slots; ++i) { + slot = &slots[i]; + + if (!*slot) { + *slot = bp; + break; + } + } + + if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) { + ret = -EBUSY; + goto out; + } + +setup: + /* Setup the address register. */ + write_wb_reg(val_base + i, info->address); + + /* Setup the control register. */ + write_wb_reg(ctrl_base + i, encode_ctrl_reg(info->ctrl) | 0x1); + +out: + return ret; +} + +void arch_uninstall_hw_breakpoint(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + struct perf_event **slot, **slots; + int i, max_slots, base; + + if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { + /* Breakpoint */ + base = ARM_BASE_BCR; + slots = __get_cpu_var(bp_on_reg); + max_slots = core_num_brps - 1; + + if (bp_is_single_step(bp)) { + i = max_slots; + slots[i] = NULL; + goto reset; + } + } else { + /* Watchpoint */ + base = ARM_BASE_WCR; + slots = __get_cpu_var(wp_on_reg); + max_slots = core_num_wrps; + } + + /* Remove the breakpoint. */ + for (i = 0; i < max_slots; ++i) { + slot = &slots[i]; + + if (*slot == bp) { + *slot = NULL; + break; + } + } + + if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) + return; + +reset: + /* Reset the control register. */ + write_wb_reg(base + i, 0); +} + +static int get_hbp_len(u8 hbp_len) +{ + unsigned int len_in_bytes = 0; + + switch (hbp_len) { + case ARM_BREAKPOINT_LEN_1: + len_in_bytes = 1; + break; + case ARM_BREAKPOINT_LEN_2: + len_in_bytes = 2; + break; + case ARM_BREAKPOINT_LEN_4: + len_in_bytes = 4; + break; + case ARM_BREAKPOINT_LEN_8: + len_in_bytes = 8; + break; + } + + return len_in_bytes; +} + +/* + * Check whether bp virtual address is in kernel space. + */ +int arch_check_bp_in_kernelspace(struct perf_event *bp) +{ + unsigned int len; + unsigned long va; + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + + va = info->address; + len = get_hbp_len(info->ctrl.len); + + return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); +} + +/* + * Extract generic type and length encodings from an arch_hw_breakpoint_ctrl. + * Hopefully this will disappear when ptrace can bypass the conversion + * to generic breakpoint descriptions. + */ +int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, + int *gen_len, int *gen_type) +{ + /* Type */ + switch (ctrl.type) { + case ARM_BREAKPOINT_EXECUTE: + *gen_type = HW_BREAKPOINT_X; + break; + case ARM_BREAKPOINT_LOAD: + *gen_type = HW_BREAKPOINT_R; + break; + case ARM_BREAKPOINT_STORE: + *gen_type = HW_BREAKPOINT_W; + break; + case ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE: + *gen_type = HW_BREAKPOINT_RW; + break; + default: + return -EINVAL; + } + + /* Len */ + switch (ctrl.len) { + case ARM_BREAKPOINT_LEN_1: + *gen_len = HW_BREAKPOINT_LEN_1; + break; + case ARM_BREAKPOINT_LEN_2: + *gen_len = HW_BREAKPOINT_LEN_2; + break; + case ARM_BREAKPOINT_LEN_4: + *gen_len = HW_BREAKPOINT_LEN_4; + break; + case ARM_BREAKPOINT_LEN_8: + *gen_len = HW_BREAKPOINT_LEN_8; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Construct an arch_hw_breakpoint from a perf_event. + */ +static int arch_build_bp_info(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + + /* Type */ + switch (bp->attr.bp_type) { + case HW_BREAKPOINT_X: + info->ctrl.type = ARM_BREAKPOINT_EXECUTE; + break; + case HW_BREAKPOINT_R: + info->ctrl.type = ARM_BREAKPOINT_LOAD; + break; + case HW_BREAKPOINT_W: + info->ctrl.type = ARM_BREAKPOINT_STORE; + break; + case HW_BREAKPOINT_RW: + info->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE; + break; + default: + return -EINVAL; + } + + /* Len */ + switch (bp->attr.bp_len) { + case HW_BREAKPOINT_LEN_1: + info->ctrl.len = ARM_BREAKPOINT_LEN_1; + break; + case HW_BREAKPOINT_LEN_2: + info->ctrl.len = ARM_BREAKPOINT_LEN_2; + break; + case HW_BREAKPOINT_LEN_4: + info->ctrl.len = ARM_BREAKPOINT_LEN_4; + break; + case HW_BREAKPOINT_LEN_8: + info->ctrl.len = ARM_BREAKPOINT_LEN_8; + if ((info->ctrl.type != ARM_BREAKPOINT_EXECUTE) + && max_watchpoint_len >= 8) + break; + default: + return -EINVAL; + } + + /* Address */ + info->address = bp->attr.bp_addr; + + /* Privilege */ + info->ctrl.privilege = ARM_BREAKPOINT_USER; + if (arch_check_bp_in_kernelspace(bp) && !bp_is_single_step(bp)) + info->ctrl.privilege |= ARM_BREAKPOINT_PRIV; + + /* Enabled? */ + info->ctrl.enabled = !bp->attr.disabled; + + /* Mismatch */ + info->ctrl.mismatch = 0; + + return 0; +} + +/* + * Validate the arch-specific HW Breakpoint register settings. + */ +int arch_validate_hwbkpt_settings(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + int ret = 0; + u32 bytelen, max_len, offset, alignment_mask = 0x3; + + /* Build the arch_hw_breakpoint. */ + ret = arch_build_bp_info(bp); + if (ret) + goto out; + + /* Check address alignment. */ + if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) + alignment_mask = 0x7; + if (info->address & alignment_mask) { + /* + * Try to fix the alignment. This may result in a length + * that is too large, so we must check for that. + */ + bytelen = get_hbp_len(info->ctrl.len); + max_len = info->ctrl.type == ARM_BREAKPOINT_EXECUTE ? 4 : + max_watchpoint_len; + + if (max_len >= 8) + offset = info->address & 0x7; + else + offset = info->address & 0x3; + + if (bytelen > (1 << ((max_len - (offset + 1)) >> 1))) { + ret = -EFBIG; + goto out; + } + + info->ctrl.len <<= offset; + info->address &= ~offset; + + pr_debug("breakpoint alignment fixup: length = 0x%x, " + "address = 0x%x\n", info->ctrl.len, info->address); + } + + /* + * Currently we rely on an overflow handler to take + * care of single-stepping the breakpoint when it fires. + * In the case of userspace breakpoints on a core with V7 debug, + * we can use the mismatch feature as a poor-man's hardware single-step. + */ + if (WARN_ONCE(!bp->overflow_handler && + (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_bps()), + "overflow handler required but none found")) { + ret = -EINVAL; + goto out; + } +out: + return ret; +} + +static void update_mismatch_flag(int idx, int flag) +{ + struct perf_event *bp = __get_cpu_var(bp_on_reg[idx]); + struct arch_hw_breakpoint *info; + + if (bp == NULL) + return; + + info = counter_arch_bp(bp); + + /* Update the mismatch field to enter/exit `single-step' mode */ + if (!bp->overflow_handler && info->ctrl.mismatch != flag) { + info->ctrl.mismatch = flag; + write_wb_reg(ARM_BASE_BCR + idx, encode_ctrl_reg(info->ctrl) | 0x1); + } +} + +static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) +{ + int i; + struct perf_event *bp, **slots = __get_cpu_var(wp_on_reg); + struct arch_hw_breakpoint *info; + struct perf_event_attr attr; + + /* Without a disassembler, we can only handle 1 watchpoint. */ + BUG_ON(core_num_wrps > 1); + + hw_breakpoint_init(&attr); + attr.bp_addr = regs->ARM_pc & ~0x3; + attr.bp_len = HW_BREAKPOINT_LEN_4; + attr.bp_type = HW_BREAKPOINT_X; + + for (i = 0; i < core_num_wrps; ++i) { + rcu_read_lock(); + + if (slots[i] == NULL) { + rcu_read_unlock(); + continue; + } + + /* + * The DFAR is an unknown value. Since we only allow a + * single watchpoint, we can set the trigger to the lowest + * possible faulting address. + */ + info = counter_arch_bp(slots[i]); + info->trigger = slots[i]->attr.bp_addr; + pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); + perf_bp_event(slots[i], regs); + + /* + * If no overflow handler is present, insert a temporary + * mismatch breakpoint so we can single-step over the + * watchpoint trigger. + */ + if (!slots[i]->overflow_handler) { + bp = register_user_hw_breakpoint(&attr, + wp_single_step_handler, + current); + counter_arch_bp(bp)->suspended_wp = slots[i]; + perf_event_disable(slots[i]); + } + + rcu_read_unlock(); + } +} + +static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) +{ + int i; + int mismatch; + u32 ctrl_reg, val, addr; + struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg); + struct arch_hw_breakpoint *info; + struct arch_hw_breakpoint_ctrl ctrl; + + /* The exception entry code places the amended lr in the PC. */ + addr = regs->ARM_pc; + + for (i = 0; i < core_num_brps; ++i) { + rcu_read_lock(); + + bp = slots[i]; + + if (bp == NULL) { + rcu_read_unlock(); + continue; + } + + mismatch = 0; + + /* Check if the breakpoint value matches. */ + val = read_wb_reg(ARM_BASE_BVR + i); + if (val != (addr & ~0x3)) + goto unlock; + + /* Possible match, check the byte address select to confirm. */ + ctrl_reg = read_wb_reg(ARM_BASE_BCR + i); + decode_ctrl_reg(ctrl_reg, &ctrl); + if ((1 << (addr & 0x3)) & ctrl.len) { + mismatch = 1; + info = counter_arch_bp(bp); + info->trigger = addr; + } + +unlock: + if ((mismatch && !info->ctrl.mismatch) || bp_is_single_step(bp)) { + pr_debug("breakpoint fired: address = 0x%x\n", addr); + perf_bp_event(bp, regs); + } + + update_mismatch_flag(i, mismatch); + rcu_read_unlock(); + } +} + +/* + * Called from either the Data Abort Handler [watchpoint] or the + * Prefetch Abort Handler [breakpoint]. + */ +static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + int ret = 1; /* Unhandled fault. */ + u32 dscr; + + /* We only handle watchpoints and hardware breakpoints. */ + ARM_DBG_READ(c1, 0, dscr); + + /* Perform perf callbacks. */ + switch (ARM_DSCR_MOE(dscr)) { + case ARM_ENTRY_BREAKPOINT: + breakpoint_handler(addr, regs); + break; + case ARM_ENTRY_ASYNC_WATCHPOINT: + WARN_ON("Asynchronous watchpoint exception taken. " + "Debugging results may be unreliable"); + case ARM_ENTRY_SYNC_WATCHPOINT: + watchpoint_handler(addr, regs); + break; + default: + goto out; + } + + ret = 0; +out: + return ret; +} + +/* + * One-time initialisation. + */ +static void __init reset_ctrl_regs(void *unused) +{ + int i; + + if (enable_monitor_mode()) + return; + + for (i = 0; i < core_num_brps; ++i) { + write_wb_reg(ARM_BASE_BCR + i, 0UL); + write_wb_reg(ARM_BASE_BVR + i, 0UL); + } + + for (i = 0; i < core_num_wrps; ++i) { + write_wb_reg(ARM_BASE_WCR + i, 0UL); + write_wb_reg(ARM_BASE_WVR + i, 0UL); + } +} + +static int __init arch_hw_breakpoint_init(void) +{ + int ret = 0; + u32 dscr; + + debug_arch = get_debug_arch(); + + if (debug_arch > ARM_DEBUG_ARCH_V7_ECP14) { + pr_info("debug architecture 0x%x unsupported.\n", debug_arch); + ret = -ENODEV; + goto out; + } + + /* Determine how many BRPs/WRPs are available. */ + core_num_brps = get_num_brps(); + core_num_wrps = get_num_wrps(); + + pr_info("found %d breakpoint and %d watchpoint registers.\n", + core_num_brps, core_num_wrps); + + if (core_has_mismatch_bps()) + pr_info("1 breakpoint reserved for watchpoint single-step.\n"); + + ARM_DBG_READ(c1, 0, dscr); + if (dscr & ARM_DSCR_HDBGEN) { + pr_warning("halting debug mode enabled. Assuming maximum " + "watchpoint size of 4 bytes."); + } else { + /* Work out the maximum supported watchpoint length. */ + max_watchpoint_len = get_max_wp_len(); + pr_info("maximum watchpoint size is %u bytes.\n", + max_watchpoint_len); + + /* + * Reset the breakpoint resources. We assume that a halting + * debugger will leave the world in a nice state for us. + */ + smp_call_function(reset_ctrl_regs, NULL, 1); + reset_ctrl_regs(NULL); + } + + /* Register debug fault handler. */ + hook_fault_code(2, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT, + "watchpoint debug exception"); + hook_ifault_code(2, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT, + "breakpoint debug exception"); + +out: + return ret; +} +arch_initcall(arch_hw_breakpoint_init); + +void hw_breakpoint_pmu_read(struct perf_event *bp) +{ +} + +/* + * Dummy function to register with die_notifier. + */ +int hw_breakpoint_exceptions_notify(struct notifier_block *unused, + unsigned long val, void *data) +{ + return NOTIFY_DONE; +} diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 6b4605893f1e..d9bd786ce23d 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -69,20 +69,31 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, { #ifdef CONFIG_ARM_UNWIND Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; + struct arm_unwind_mapping *maps = mod->arch.map; for (s = sechdrs; s < sechdrs_end; s++) { - if (strcmp(".ARM.exidx.init.text", secstrings + s->sh_name) == 0) - mod->arch.unw_sec_init = s; - else if (strcmp(".ARM.exidx.devinit.text", secstrings + s->sh_name) == 0) - mod->arch.unw_sec_devinit = s; - else if (strcmp(".ARM.exidx", secstrings + s->sh_name) == 0) - mod->arch.unw_sec_core = s; - else if (strcmp(".init.text", secstrings + s->sh_name) == 0) - mod->arch.sec_init_text = s; - else if (strcmp(".devinit.text", secstrings + s->sh_name) == 0) - mod->arch.sec_devinit_text = s; - else if (strcmp(".text", secstrings + s->sh_name) == 0) - mod->arch.sec_core_text = s; + char const *secname = secstrings + s->sh_name; + + if (strcmp(".ARM.exidx.init.text", secname) == 0) + maps[ARM_SEC_INIT].unw_sec = s; + else if (strcmp(".ARM.exidx.devinit.text", secname) == 0) + maps[ARM_SEC_DEVINIT].unw_sec = s; + else if (strcmp(".ARM.exidx", secname) == 0) + maps[ARM_SEC_CORE].unw_sec = s; + else if (strcmp(".ARM.exidx.exit.text", secname) == 0) + maps[ARM_SEC_EXIT].unw_sec = s; + else if (strcmp(".ARM.exidx.devexit.text", secname) == 0) + maps[ARM_SEC_DEVEXIT].unw_sec = s; + else if (strcmp(".init.text", secname) == 0) + maps[ARM_SEC_INIT].sec_text = s; + else if (strcmp(".devinit.text", secname) == 0) + maps[ARM_SEC_DEVINIT].sec_text = s; + else if (strcmp(".text", secname) == 0) + maps[ARM_SEC_CORE].sec_text = s; + else if (strcmp(".exit.text", secname) == 0) + maps[ARM_SEC_EXIT].sec_text = s; + else if (strcmp(".devexit.text", secname) == 0) + maps[ARM_SEC_DEVEXIT].sec_text = s; } #endif return 0; @@ -292,31 +303,22 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, #ifdef CONFIG_ARM_UNWIND static void register_unwind_tables(struct module *mod) { - if (mod->arch.unw_sec_init && mod->arch.sec_init_text) - mod->arch.unwind_init = - unwind_table_add(mod->arch.unw_sec_init->sh_addr, - mod->arch.unw_sec_init->sh_size, - mod->arch.sec_init_text->sh_addr, - mod->arch.sec_init_text->sh_size); - if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text) - mod->arch.unwind_devinit = - unwind_table_add(mod->arch.unw_sec_devinit->sh_addr, - mod->arch.unw_sec_devinit->sh_size, - mod->arch.sec_devinit_text->sh_addr, - mod->arch.sec_devinit_text->sh_size); - if (mod->arch.unw_sec_core && mod->arch.sec_core_text) - mod->arch.unwind_core = - unwind_table_add(mod->arch.unw_sec_core->sh_addr, - mod->arch.unw_sec_core->sh_size, - mod->arch.sec_core_text->sh_addr, - mod->arch.sec_core_text->sh_size); + int i; + for (i = 0; i < ARM_SEC_MAX; ++i) { + struct arm_unwind_mapping *map = &mod->arch.map[i]; + if (map->unw_sec && map->sec_text) + map->unwind = unwind_table_add(map->unw_sec->sh_addr, + map->unw_sec->sh_size, + map->sec_text->sh_addr, + map->sec_text->sh_size); + } } static void unregister_unwind_tables(struct module *mod) { - unwind_table_del(mod->arch.unwind_init); - unwind_table_del(mod->arch.unwind_devinit); - unwind_table_del(mod->arch.unwind_core); + int i = ARM_SEC_MAX; + while (--i >= 0) + unwind_table_del(mod->arch.map[i].unwind); } #else static inline void register_unwind_tables(struct module *mod) { } diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 66ac9c926200..e76fcaadce03 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -29,6 +29,7 @@ #include <linux/utsname.h> #include <linux/uaccess.h> #include <linux/random.h> +#include <linux/hw_breakpoint.h> #include <asm/cacheflush.h> #include <asm/leds.h> @@ -135,6 +136,25 @@ EXPORT_SYMBOL(pm_power_off); void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart; EXPORT_SYMBOL_GPL(arm_pm_restart); +static void do_nothing(void *unused) +{ +} + +/* + * cpu_idle_wait - Used to ensure that all the CPUs discard old value of + * pm_idle and update to new pm_idle value. Required while changing pm_idle + * handler on SMP systems. + * + * Caller must have changed pm_idle to the new value before the call. Old + * pm_idle value will not be used by any CPU after the return of this function. + */ +void cpu_idle_wait(void) +{ + smp_mb(); + /* kick all the CPUs so that they exit out of pm_idle */ + smp_call_function(do_nothing, NULL, 1); +} +EXPORT_SYMBOL_GPL(cpu_idle_wait); /* * This is our default idle handler. We need to disable @@ -317,6 +337,8 @@ void flush_thread(void) struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; + flush_ptrace_hw_breakpoint(tsk); + memset(thread->used_cp, 0, sizeof(thread->used_cp)); memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); memset(&thread->fpstate, 0, sizeof(union fp_state)); @@ -345,6 +367,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start, thread->cpu_context.sp = (unsigned long)childregs; thread->cpu_context.pc = (unsigned long)ret_from_fork; + clear_ptrace_hw_breakpoint(p); + if (clone_flags & CLONE_SETTLS) thread->tp_value = regs->ARM_r3; diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index f99d489822d5..e0cb6370ed14 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -19,6 +19,8 @@ #include <linux/init.h> #include <linux/signal.h> #include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/hw_breakpoint.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -847,6 +849,232 @@ static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) } #endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT +/* + * Convert a virtual register number into an index for a thread_info + * breakpoint array. Breakpoints are identified using positive numbers + * whilst watchpoints are negative. The registers are laid out as pairs + * of (address, control), each pair mapping to a unique hw_breakpoint struct. + * Register 0 is reserved for describing resource information. + */ +static int ptrace_hbp_num_to_idx(long num) +{ + if (num < 0) + num = (ARM_MAX_BRP << 1) - num; + return (num - 1) >> 1; +} + +/* + * Returns the virtual register number for the address of the + * breakpoint at index idx. + */ +static long ptrace_hbp_idx_to_num(int idx) +{ + long mid = ARM_MAX_BRP << 1; + long num = (idx << 1) + 1; + return num > mid ? mid - num : num; +} + +/* + * Handle hitting a HW-breakpoint. + */ +static void ptrace_hbptriggered(struct perf_event *bp, int unused, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); + long num; + int i; + siginfo_t info; + + for (i = 0; i < ARM_MAX_HBP_SLOTS; ++i) + if (current->thread.debug.hbp[i] == bp) + break; + + num = (i == ARM_MAX_HBP_SLOTS) ? 0 : ptrace_hbp_idx_to_num(i); + + info.si_signo = SIGTRAP; + info.si_errno = (int)num; + info.si_code = TRAP_HWBKPT; + info.si_addr = (void __user *)(bkpt->trigger); + + force_sig_info(SIGTRAP, &info, current); +} + +/* + * Set ptrace breakpoint pointers to zero for this task. + * This is required in order to prevent child processes from unregistering + * breakpoints held by their parent. + */ +void clear_ptrace_hw_breakpoint(struct task_struct *tsk) +{ + memset(tsk->thread.debug.hbp, 0, sizeof(tsk->thread.debug.hbp)); +} + +/* + * Unregister breakpoints from this task and reset the pointers in + * the thread_struct. + */ +void flush_ptrace_hw_breakpoint(struct task_struct *tsk) +{ + int i; + struct thread_struct *t = &tsk->thread; + + for (i = 0; i < ARM_MAX_HBP_SLOTS; i++) { + if (t->debug.hbp[i]) { + unregister_hw_breakpoint(t->debug.hbp[i]); + t->debug.hbp[i] = NULL; + } + } +} + +static u32 ptrace_get_hbp_resource_info(void) +{ + u8 num_brps, num_wrps, debug_arch, wp_len; + u32 reg = 0; + + num_brps = hw_breakpoint_slots(TYPE_INST); + num_wrps = hw_breakpoint_slots(TYPE_DATA); + debug_arch = arch_get_debug_arch(); + wp_len = arch_get_max_wp_len(); + + reg |= debug_arch; + reg <<= 8; + reg |= wp_len; + reg <<= 8; + reg |= num_wrps; + reg <<= 8; + reg |= num_brps; + + return reg; +} + +static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) +{ + struct perf_event_attr attr; + + ptrace_breakpoint_init(&attr); + + /* Initialise fields to sane defaults. */ + attr.bp_addr = 0; + attr.bp_len = HW_BREAKPOINT_LEN_4; + attr.bp_type = type; + attr.disabled = 1; + + return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, tsk); +} + +static int ptrace_gethbpregs(struct task_struct *tsk, long num, + unsigned long __user *data) +{ + u32 reg; + int idx, ret = 0; + struct perf_event *bp; + struct arch_hw_breakpoint_ctrl arch_ctrl; + + if (num == 0) { + reg = ptrace_get_hbp_resource_info(); + } else { + idx = ptrace_hbp_num_to_idx(num); + if (idx < 0 || idx >= ARM_MAX_HBP_SLOTS) { + ret = -EINVAL; + goto out; + } + + bp = tsk->thread.debug.hbp[idx]; + if (!bp) { + reg = 0; + goto put; + } + + arch_ctrl = counter_arch_bp(bp)->ctrl; + + /* + * Fix up the len because we may have adjusted it + * to compensate for an unaligned address. + */ + while (!(arch_ctrl.len & 0x1)) + arch_ctrl.len >>= 1; + + if (idx & 0x1) + reg = encode_ctrl_reg(arch_ctrl); + else + reg = bp->attr.bp_addr; + } + +put: + if (put_user(reg, data)) + ret = -EFAULT; + +out: + return ret; +} + +static int ptrace_sethbpregs(struct task_struct *tsk, long num, + unsigned long __user *data) +{ + int idx, gen_len, gen_type, implied_type, ret = 0; + u32 user_val; + struct perf_event *bp; + struct arch_hw_breakpoint_ctrl ctrl; + struct perf_event_attr attr; + + if (num == 0) + goto out; + else if (num < 0) + implied_type = HW_BREAKPOINT_RW; + else + implied_type = HW_BREAKPOINT_X; + + idx = ptrace_hbp_num_to_idx(num); + if (idx < 0 || idx >= ARM_MAX_HBP_SLOTS) { + ret = -EINVAL; + goto out; + } + + if (get_user(user_val, data)) { + ret = -EFAULT; + goto out; + } + + bp = tsk->thread.debug.hbp[idx]; + if (!bp) { + bp = ptrace_hbp_create(tsk, implied_type); + if (IS_ERR(bp)) { + ret = PTR_ERR(bp); + goto out; + } + tsk->thread.debug.hbp[idx] = bp; + } + + attr = bp->attr; + + if (num & 0x1) { + /* Address */ + attr.bp_addr = user_val; + } else { + /* Control */ + decode_ctrl_reg(user_val, &ctrl); + ret = arch_bp_generic_fields(ctrl, &gen_len, &gen_type); + if (ret) + goto out; + + if ((gen_type & implied_type) != gen_type) { + ret = -EINVAL; + goto out; + } + + attr.bp_len = gen_len; + attr.bp_type = gen_type; + attr.disabled = !ctrl.enabled; + } + + ret = modify_user_hw_breakpoint(bp, &attr); +out: + return ret; +} +#endif + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; @@ -916,6 +1144,17 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT + case PTRACE_GETHBPREGS: + ret = ptrace_gethbpregs(child, addr, + (unsigned long __user *)data); + break; + case PTRACE_SETHBPREGS: + ret = ptrace_sethbpregs(child, addr, + (unsigned long __user *)data); + break; +#endif + default: ret = ptrace_request(child, request, addr, data); break; diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d5231ae7355a..336f14e0e5c2 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -36,6 +36,7 @@ #include <asm/procinfo.h> #include <asm/sections.h> #include <asm/setup.h> +#include <asm/smp_plat.h> #include <asm/mach-types.h> #include <asm/cacheflush.h> #include <asm/cachetype.h> @@ -238,6 +239,35 @@ int cpu_architecture(void) return cpu_arch; } +static int cpu_has_aliasing_icache(unsigned int arch) +{ + int aliasing_icache; + unsigned int id_reg, num_sets, line_size; + + /* arch specifies the register format */ + switch (arch) { + case CPU_ARCH_ARMv7: + asm("mcr p15, 2, %0, c0, c0, 0 @ set CSSELR" + : /* No output operands */ + : "r" (1)); + isb(); + asm("mrc p15, 1, %0, c0, c0, 0 @ read CCSIDR" + : "=r" (id_reg)); + line_size = 4 << ((id_reg & 0x7) + 2); + num_sets = ((id_reg >> 13) & 0x7fff) + 1; + aliasing_icache = (line_size * num_sets) > PAGE_SIZE; + break; + case CPU_ARCH_ARMv6: + aliasing_icache = read_cpuid_cachetype() & (1 << 11); + break; + default: + /* I-cache aliases will be handled by D-cache aliasing code */ + aliasing_icache = 0; + } + + return aliasing_icache; +} + static void __init cacheid_init(void) { unsigned int cachetype = read_cpuid_cachetype(); @@ -249,10 +279,15 @@ static void __init cacheid_init(void) cacheid = CACHEID_VIPT_NONALIASING; if ((cachetype & (3 << 14)) == 1 << 14) cacheid |= CACHEID_ASID_TAGGED; - } else if (cachetype & (1 << 23)) + else if (cpu_has_aliasing_icache(CPU_ARCH_ARMv7)) + cacheid |= CACHEID_VIPT_I_ALIASING; + } else if (cachetype & (1 << 23)) { cacheid = CACHEID_VIPT_ALIASING; - else + } else { cacheid = CACHEID_VIPT_NONALIASING; + if (cpu_has_aliasing_icache(CPU_ARCH_ARMv6)) + cacheid |= CACHEID_VIPT_I_ALIASING; + } } else { cacheid = CACHEID_VIVT; } @@ -263,7 +298,7 @@ static void __init cacheid_init(void) cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown", cache_is_vivt() ? "VIVT" : icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" : - cache_is_vipt_aliasing() ? "VIPT aliasing" : + icache_is_vipt_aliasing() ? "VIPT aliasing" : cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown"); } @@ -490,7 +525,7 @@ request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc) kernel_code.start = virt_to_phys(_text); kernel_code.end = virt_to_phys(_etext - 1); - kernel_data.start = virt_to_phys(_data); + kernel_data.start = virt_to_phys(_sdata); kernel_data.end = virt_to_phys(_end - 1); for (i = 0; i < mi->nr_banks; i++) { @@ -825,7 +860,8 @@ void __init setup_arch(char **cmdline_p) request_standard_resources(&meminfo, mdesc); #ifdef CONFIG_SMP - smp_init_cpus(); + if (is_smp()) + smp_init_cpus(); #endif reserve_crashkernel(); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 40dc74f2b27f..8c1959590252 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -33,6 +33,7 @@ #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/processor.h> +#include <asm/sections.h> #include <asm/tlbflush.h> #include <asm/ptrace.h> #include <asm/localtimer.h> @@ -67,12 +68,47 @@ enum ipi_msg_type { IPI_CPU_STOP, }; +static inline void identity_mapping_add(pgd_t *pgd, unsigned long start, + unsigned long end) +{ + unsigned long addr, prot; + pmd_t *pmd; + + prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE; + if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale()) + prot |= PMD_BIT4; + + for (addr = start & PGDIR_MASK; addr < end;) { + pmd = pmd_offset(pgd + pgd_index(addr), addr); + pmd[0] = __pmd(addr | prot); + addr += SECTION_SIZE; + pmd[1] = __pmd(addr | prot); + addr += SECTION_SIZE; + flush_pmd_entry(pmd); + outer_clean_range(__pa(pmd), __pa(pmd + 1)); + } +} + +static inline void identity_mapping_del(pgd_t *pgd, unsigned long start, + unsigned long end) +{ + unsigned long addr; + pmd_t *pmd; + + for (addr = start & PGDIR_MASK; addr < end; addr += PGDIR_SIZE) { + pmd = pmd_offset(pgd + pgd_index(addr), addr); + pmd[0] = __pmd(0); + pmd[1] = __pmd(0); + clean_pmd_entry(pmd); + outer_clean_range(__pa(pmd), __pa(pmd + 1)); + } +} + int __cpuinit __cpu_up(unsigned int cpu) { struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu); struct task_struct *idle = ci->idle; pgd_t *pgd; - pmd_t *pmd; int ret; /* @@ -101,11 +137,16 @@ int __cpuinit __cpu_up(unsigned int cpu) * a 1:1 mapping for the physical address of the kernel. */ pgd = pgd_alloc(&init_mm); - pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET); - *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | - PMD_TYPE_SECT | PMD_SECT_AP_WRITE); - flush_pmd_entry(pmd); - outer_clean_range(__pa(pmd), __pa(pmd + 1)); + if (!pgd) + return -ENOMEM; + + if (PHYS_OFFSET != PAGE_OFFSET) { +#ifndef CONFIG_HOTPLUG_CPU + identity_mapping_add(pgd, __pa(__init_begin), __pa(__init_end)); +#endif + identity_mapping_add(pgd, __pa(_stext), __pa(_etext)); + identity_mapping_add(pgd, __pa(_sdata), __pa(_edata)); + } /* * We need to tell the secondary core where to find @@ -143,8 +184,14 @@ int __cpuinit __cpu_up(unsigned int cpu) secondary_data.stack = NULL; secondary_data.pgdir = 0; - *pmd = __pmd(0); - clean_pmd_entry(pmd); + if (PHYS_OFFSET != PAGE_OFFSET) { +#ifndef CONFIG_HOTPLUG_CPU + identity_mapping_del(pgd, __pa(__init_begin), __pa(__init_end)); +#endif + identity_mapping_del(pgd, __pa(_stext), __pa(_etext)); + identity_mapping_del(pgd, __pa(_sdata), __pa(_edata)); + } + pgd_free(&init_mm, pgd); if (ret) { @@ -567,7 +614,8 @@ void smp_send_stop(void) { cpumask_t mask = cpu_online_map; cpu_clear(smp_processor_id(), mask); - send_ipi_message(&mask, IPI_CPU_STOP); + if (!cpus_empty(mask)) + send_ipi_message(&mask, IPI_CPU_STOP); } /* diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index dd81a918c106..2a161765f6d5 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c @@ -146,6 +146,8 @@ static struct unwind_idx *unwind_find_idx(unsigned long addr) addr < table->end_addr) { idx = search_index(addr, table->start, table->stop - 1); + /* Move-to-front to exploit common traces */ + list_move(&table->list, &unwind_tables); break; } } diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index b16c07914b55..1953e3d21abf 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -8,6 +8,19 @@ #include <asm/memory.h> #include <asm/page.h> +#define PROC_INFO \ + VMLINUX_SYMBOL(__proc_info_begin) = .; \ + *(.proc.info.init) \ + VMLINUX_SYMBOL(__proc_info_end) = .; + +#ifdef CONFIG_HOTPLUG_CPU +#define ARM_CPU_DISCARD(x) +#define ARM_CPU_KEEP(x) x +#else +#define ARM_CPU_DISCARD(x) x +#define ARM_CPU_KEEP(x) +#endif + OUTPUT_ARCH(arm) ENTRY(stext) @@ -31,15 +44,18 @@ SECTIONS HEAD_TEXT INIT_TEXT _einittext = .; - __proc_info_begin = .; - *(.proc.info.init) - __proc_info_end = .; + ARM_CPU_DISCARD(PROC_INFO) __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; __tagtable_begin = .; *(.taglist.init) __tagtable_end = .; +#ifdef CONFIG_SMP_ON_UP + __smpalt_begin = .; + *(.alt.smp.init) + __smpalt_end = .; +#endif INIT_SETUP(16) @@ -68,10 +84,8 @@ SECTIONS /DISCARD/ : { *(.ARM.exidx.exit.text) *(.ARM.extab.exit.text) -#ifndef CONFIG_HOTPLUG_CPU - *(.ARM.exidx.cpuexit.text) - *(.ARM.extab.cpuexit.text) -#endif + ARM_CPU_DISCARD(*(.ARM.exidx.cpuexit.text)) + ARM_CPU_DISCARD(*(.ARM.extab.cpuexit.text)) #ifndef CONFIG_HOTPLUG *(.ARM.exidx.devexit.text) *(.ARM.extab.devexit.text) @@ -100,12 +114,11 @@ SECTIONS *(.glue_7) *(.glue_7t) *(.got) /* Global offset table */ + ARM_CPU_KEEP(PROC_INFO) } RO_DATA(PAGE_SIZE) - _etext = .; /* End of text and rodata section */ - #ifdef CONFIG_ARM_UNWIND /* * Stack unwinding tables @@ -123,6 +136,8 @@ SECTIONS } #endif + _etext = .; /* End of text and rodata section */ + #ifdef CONFIG_XIP_KERNEL __data_loc = ALIGN(4); /* location in binary */ . = PAGE_OFFSET + TEXT_OFFSET; @@ -237,6 +252,12 @@ SECTIONS /* Default discards */ DISCARDS + +#ifndef CONFIG_SMP_ON_UP + /DISCARD/ : { + *(.alt.smp.init) + } +#endif } /* diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index bbd5efa65099..851e8139ef9d 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -252,6 +252,12 @@ config MACH_CPU9260 Select this if you are using a Eukrea Electromatique's CPU9260 Board <http://www.eukrea.com/> +config MACH_FLEXIBITY + bool "Flexibity Connect board" + help + Select this if you are using Flexibity Connect board + <http://www.flexibity.com> + endif # ---------------------------------------------------------- diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 3a07a3696441..412b3a471a4b 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MACH_USB_A9260) += board-usb-a9260.o obj-$(CONFIG_MACH_QIL_A9260) += board-qil-a9260.o obj-$(CONFIG_MACH_AFEB9260) += board-afeb-9260v1.o obj-$(CONFIG_MACH_CPU9260) += board-cpu9krea.o +obj-$(CONFIG_MACH_FLEXIBITY) += board-flexibity.o # AT91SAM9261 board-specific support obj-$(CONFIG_MACH_AT91SAM9261EK) += board-sam9261ek.o diff --git a/arch/arm/mach-at91/board-flexibity.c b/arch/arm/mach-at91/board-flexibity.c new file mode 100644 index 000000000000..216c8ca985f4 --- /dev/null +++ b/arch/arm/mach-at91/board-flexibity.c @@ -0,0 +1,164 @@ +/* + * linux/arch/arm/mach-at91/board-flexibity.c + * + * Copyright (C) 2010 Flexibity + * Copyright (C) 2005 SAN People + * Copyright (C) 2006 Atmel + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/input.h> +#include <linux/gpio.h> + +#include <asm/mach-types.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <mach/hardware.h> +#include <mach/board.h> + +#include "generic.h" + +static void __init flexibity_map_io(void) +{ + /* Initialize processor: 18.432 MHz crystal */ + at91sam9260_initialize(18432000); + + /* DBGU on ttyS0. (Rx & Tx only) */ + at91_register_uart(0, 0, 0); + + /* set serial console to ttyS0 (ie, DBGU) */ + at91_set_serial_console(0); +} + +static void __init flexibity_init_irq(void) +{ + at91sam9260_init_interrupts(NULL); +} + +/* USB Host port */ +static struct at91_usbh_data __initdata flexibity_usbh_data = { + .ports = 2, +}; + +/* USB Device port */ +static struct at91_udc_data __initdata flexibity_udc_data = { + .vbus_pin = AT91_PIN_PC5, + .pullup_pin = 0, /* pull-up driven by UDC */ +}; + +/* SPI devices */ +static struct spi_board_info flexibity_spi_devices[] = { + { /* DataFlash chip */ + .modalias = "mtd_dataflash", + .chip_select = 1, + .max_speed_hz = 15 * 1000 * 1000, + .bus_num = 0, + }, +}; + +/* MCI (SD/MMC) */ +static struct at91_mmc_data __initdata flexibity_mmc_data = { + .slot_b = 0, + .wire4 = 1, + .det_pin = AT91_PIN_PC9, + .wp_pin = AT91_PIN_PC4, +}; + +/* LEDs */ +static struct gpio_led flexibity_leds[] = { + { + .name = "usb1:green", + .gpio = AT91_PIN_PA12, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb1:red", + .gpio = AT91_PIN_PA13, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb2:green", + .gpio = AT91_PIN_PB26, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb2:red", + .gpio = AT91_PIN_PB27, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb3:green", + .gpio = AT91_PIN_PC8, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb3:red", + .gpio = AT91_PIN_PC6, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb4:green", + .gpio = AT91_PIN_PB4, + .active_low = 1, + .default_trigger = "default-on", + }, + { + .name = "usb4:red", + .gpio = AT91_PIN_PB5, + .active_low = 1, + .default_trigger = "default-on", + } +}; + +static void __init flexibity_board_init(void) +{ + /* Serial */ + at91_add_device_serial(); + /* USB Host */ + at91_add_device_usbh(&flexibity_usbh_data); + /* USB Device */ + at91_add_device_udc(&flexibity_udc_data); + /* SPI */ + at91_add_device_spi(flexibity_spi_devices, + ARRAY_SIZE(flexibity_spi_devices)); + /* MMC */ + at91_add_device_mmc(0, &flexibity_mmc_data); + /* LEDs */ + at91_gpio_leds(flexibity_leds, ARRAY_SIZE(flexibity_leds)); +} + +MACHINE_START(FLEXIBITY, "Flexibity Connect") + /* Maintainer: Maxim Osipov */ + .phys_io = AT91_BASE_SYS, + .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc, + .boot_params = AT91_SDRAM_BASE + 0x100, + .timer = &at91sam926x_timer, + .map_io = flexibity_map_io, + .init_irq = flexibity_init_irq, + .init_machine = flexibity_board_init, +MACHINE_END diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 2fa38df28414..07c08151dfe6 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -259,6 +259,7 @@ struct mmci_platform_data realview_mmc0_plat_data = { .status = realview_mmc_status, .gpio_wp = 17, .gpio_cd = 16, + .cd_invert = true, }; struct mmci_platform_data realview_mmc1_plat_data = { @@ -266,6 +267,7 @@ struct mmci_platform_data realview_mmc1_plat_data = { .status = realview_mmc_status, .gpio_wp = 19, .gpio_cd = 18, + .cd_invert = true, }; /* diff --git a/arch/arm/mach-realview/include/mach/smp.h b/arch/arm/mach-realview/include/mach/smp.h index dd53892d44a7..d3cd265cb058 100644 --- a/arch/arm/mach-realview/include/mach/smp.h +++ b/arch/arm/mach-realview/include/mach/smp.h @@ -1,16 +1,8 @@ #ifndef ASMARM_ARCH_SMP_H #define ASMARM_ARCH_SMP_H - #include <asm/hardware/gic.h> - -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x0F; \ - }) +#include <asm/smp_mpidr.h> /* * We use IRQ1 as the IPI diff --git a/arch/arm/mach-s3c64xx/mach-smartq.c b/arch/arm/mach-s3c64xx/mach-smartq.c index 3a9639bc3d9b..cb1ebeb08763 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq.c +++ b/arch/arm/mach-s3c64xx/mach-smartq.c @@ -136,7 +136,7 @@ static struct platform_device smartq_usb_otg_vbus_dev = { .dev.platform_data = &smartq_usb_otg_vbus_pdata, }; -static int __init smartq_bl_init(struct device *dev) +static int smartq_bl_init(struct device *dev) { s3c_gpio_cfgpin(S3C64XX_GPF(15), S3C_GPIO_SFN(2)); diff --git a/arch/arm/mach-s3c64xx/mach-smartq5.c b/arch/arm/mach-s3c64xx/mach-smartq5.c index a4d59b076e3d..235e43928cb8 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq5.c +++ b/arch/arm/mach-s3c64xx/mach-smartq5.c @@ -32,7 +32,7 @@ #include "mach-smartq.h" -static struct gpio_led smartq5_leds[] __initdata = { +static struct gpio_led smartq5_leds[] = { { .name = "smartq5:green", .active_low = 1, diff --git a/arch/arm/mach-s3c64xx/mach-smartq7.c b/arch/arm/mach-s3c64xx/mach-smartq7.c index e50a7d781732..78a58c351f0a 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq7.c +++ b/arch/arm/mach-s3c64xx/mach-smartq7.c @@ -32,7 +32,7 @@ #include "mach-smartq.h" -static struct gpio_led smartq7_leds[] __initdata = { +static struct gpio_led smartq7_leds[] = { { .name = "smartq7:red", .active_low = 1, diff --git a/arch/arm/mach-s5pv310/include/mach/smp.h b/arch/arm/mach-s5pv310/include/mach/smp.h index 990f3ba88a1f..b7ec252384f4 100644 --- a/arch/arm/mach-s5pv310/include/mach/smp.h +++ b/arch/arm/mach-s5pv310/include/mach/smp.h @@ -7,17 +7,10 @@ #define ASM_ARCH_SMP_H __FILE__ #include <asm/hardware/gic.h> +#include <asm/smp_mpidr.h> extern void __iomem *gic_cpu_base_addr; -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x03; \ - }) - /* * We use IRQ1 as the IPI */ diff --git a/arch/arm/mach-tegra/include/mach/smp.h b/arch/arm/mach-tegra/include/mach/smp.h index 8b42dab79a70..e4a34a35a544 100644 --- a/arch/arm/mach-tegra/include/mach/smp.h +++ b/arch/arm/mach-tegra/include/mach/smp.h @@ -1,16 +1,8 @@ #ifndef ASMARM_ARCH_SMP_H #define ASMARM_ARCH_SMP_H - #include <asm/hardware/gic.h> - -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x0F; \ - }) +#include <asm/smp_mpidr.h> /* * We use IRQ1 as the IPI diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index 6625e5bbf4d6..2dd44a0b4615 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -21,9 +21,7 @@ config MACH_U8500_MOP bool "U8500 Development platform" select UX500_SOC_DB8500 help - Include support for mop500 development platform - based on U8500 architecture. The platform is based - on early drop silicon version of 8500. + Include support for the mop500 development platform. config MACH_U5500 bool "U5500 Development platform" @@ -39,4 +37,18 @@ config UX500_DEBUG_UART Choose the UART on which kernel low-level debug messages should be output. +config U5500_MODEM_IRQ + bool "Modem IRQ support" + depends on MACH_U5500 + default y + help + Add support for handling IRQ:s from modem side + +config U5500_MBOX + bool "Mailbox support" + depends on MACH_U5500 && U5500_MODEM_IRQ + default y + help + Add support for U5500 mailbox communication with modem side + endif diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index 4556aea9c3c5..9e27a84433cb 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -4,8 +4,12 @@ obj-y := clock.o cpu.o devices.o obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o devices-db5500.o -obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o -obj-$(CONFIG_MACH_U8500_MOP) += board-mop500.o +obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o prcmu.o +obj-$(CONFIG_MACH_U8500_MOP) += board-mop500.o board-mop500-sdi.o obj-$(CONFIG_MACH_U5500) += board-u5500.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o +obj-$(CONFIG_REGULATOR_AB8500) += board-mop500-regulators.o +obj-$(CONFIG_U5500_MODEM_IRQ) += modem_irq.o +obj-$(CONFIG_U5500_MBOX) += mbox.o diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c new file mode 100644 index 000000000000..1187f1fc2e53 --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-regulators.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Author: Sundar Iyer <sundar.iyer@stericsson.com> + * + * MOP500 board specific initialization for regulators + */ +#include <linux/kernel.h> +#include <linux/regulator/machine.h> + +/* supplies to the display/camera */ +static struct regulator_init_data ab8500_vaux1_regulator = { + .constraints = { + .name = "V-DISPLAY", + .min_uV = 2500000, + .max_uV = 2900000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE| + REGULATOR_CHANGE_STATUS, + }, +}; + +/* supplies to the on-board eMMC */ +static struct regulator_init_data ab8500_vaux2_regulator = { + .constraints = { + .name = "V-eMMC1", + .min_uV = 1100000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE| + REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for VAUX3, supplies to SDcard slots */ +static struct regulator_init_data ab8500_vaux3_regulator = { + .constraints = { + .name = "V-MMC-SD", + .min_uV = 1100000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE| + REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for tvout, gpadc, TVOUT LDO */ +static struct regulator_init_data ab8500_vtvout_init = { + .constraints = { + .name = "V-TVOUT", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for ab8500-vaudio, VAUDIO LDO */ +static struct regulator_init_data ab8500_vaudio_init = { + .constraints = { + .name = "V-AUD", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for v-anamic1 VAMic1-LDO */ +static struct regulator_init_data ab8500_vamic1_init = { + .constraints = { + .name = "V-AMIC1", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ +static struct regulator_init_data ab8500_vamic2_init = { + .constraints = { + .name = "V-AMIC2", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for v-dmic, VDMIC LDO */ +static struct regulator_init_data ab8500_vdmic_init = { + .constraints = { + .name = "V-DMIC", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for v-intcore12, VINTCORE12 LDO */ +static struct regulator_init_data ab8500_vintcore_init = { + .constraints = { + .name = "V-INTCORE", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +/* supply for U8500 CSI/DSI, VANA LDO */ +static struct regulator_init_data ab8500_vana_init = { + .constraints = { + .name = "V-CSI/DSI", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c new file mode 100644 index 000000000000..bac995665b58 --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/amba/bus.h> +#include <linux/amba/mmci.h> +#include <linux/mmc/host.h> +#include <linux/platform_device.h> + +#include <plat/pincfg.h> +#include <mach/devices.h> +#include <mach/hardware.h> + +#include "pins-db8500.h" +#include "board-mop500.h" + +static pin_cfg_t mop500_sdi_pins[] = { + /* SDI4 (on-board eMMC) */ + GPIO197_MC4_DAT3, + GPIO198_MC4_DAT2, + GPIO199_MC4_DAT1, + GPIO200_MC4_DAT0, + GPIO201_MC4_CMD, + GPIO202_MC4_FBCLK, + GPIO203_MC4_CLK, + GPIO204_MC4_DAT7, + GPIO205_MC4_DAT6, + GPIO206_MC4_DAT5, + GPIO207_MC4_DAT4, +}; + +static pin_cfg_t mop500_sdi2_pins[] = { + /* SDI2 (POP eMMC) */ + GPIO128_MC2_CLK, + GPIO129_MC2_CMD, + GPIO130_MC2_FBCLK, + GPIO131_MC2_DAT0, + GPIO132_MC2_DAT1, + GPIO133_MC2_DAT2, + GPIO134_MC2_DAT3, + GPIO135_MC2_DAT4, + GPIO136_MC2_DAT5, + GPIO137_MC2_DAT6, + GPIO138_MC2_DAT7, +}; + +/* + * SDI 2 (POP eMMC, not on DB8500ed) + */ + +static struct mmci_platform_data mop500_sdi2_data = { + .ocr_mask = MMC_VDD_165_195, + .f_max = 100000000, + .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA, + .gpio_cd = -1, + .gpio_wp = -1, +}; + +/* + * SDI 4 (on-board eMMC) + */ + +static struct mmci_platform_data mop500_sdi4_data = { + .ocr_mask = MMC_VDD_29_30, + .f_max = 100000000, + .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED, + .gpio_cd = -1, + .gpio_wp = -1, +}; + +void mop500_sdi_init(void) +{ + nmk_config_pins(mop500_sdi_pins, ARRAY_SIZE(mop500_sdi_pins)); + + u8500_sdi2_device.dev.platform_data = &mop500_sdi2_data; + u8500_sdi4_device.dev.platform_data = &mop500_sdi4_data; + + if (!cpu_is_u8500ed()) { + nmk_config_pins(mop500_sdi2_pins, ARRAY_SIZE(mop500_sdi2_pins)); + amba_device_register(&u8500_sdi2_device, &iomem_resource); + } + + /* On-board eMMC */ + amba_device_register(&u8500_sdi4_device, &iomem_resource); +} diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 0e8fd135a57d..642b8e60d119 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -28,8 +28,10 @@ #include <mach/hardware.h> #include <mach/setup.h> #include <mach/devices.h> +#include <mach/irqs.h> #include "pins-db8500.h" +#include "board-mop500.h" static pin_cfg_t mop500_pins[] = { /* SSP0 */ @@ -75,9 +77,27 @@ static struct ab8500_platform_data ab8500_platdata = { .irq_base = MOP500_AB8500_IRQ_BASE, }; -static struct spi_board_info u8500_spi_devices[] = { +static struct resource ab8500_resources[] = { + [0] = { + .start = IRQ_AB8500, + .end = IRQ_AB8500, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device ab8500_device = { + .name = "ab8500-i2c", + .id = 0, + .dev = { + .platform_data = &ab8500_platdata, + }, + .num_resources = 1, + .resource = ab8500_resources, +}; + +static struct spi_board_info ab8500_spi_devices[] = { { - .modalias = "ab8500", + .modalias = "ab8500-spi", .controller_data = &ab4500_chip_info, .platform_data = &ab8500_platdata, .max_speed_hz = 12000000, @@ -163,8 +183,14 @@ static void __init u8500_init_machine(void) platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs)); - spi_register_board_info(u8500_spi_devices, - ARRAY_SIZE(u8500_spi_devices)); + mop500_sdi_init(); + + /* If HW is early drop (ED) or V1.0 then use SPI to access AB8500 */ + if (cpu_is_u8500ed() || cpu_is_u8500v10()) + spi_register_board_info(ab8500_spi_devices, + ARRAY_SIZE(ab8500_spi_devices)); + else /* If HW is v.1.1 or later use I2C to access AB8500 */ + platform_device_register(&ab8500_device); } MACHINE_START(U8500, "ST-Ericsson MOP500 platform") diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h new file mode 100644 index 000000000000..2d240322fa6f --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __BOARD_MOP500_H +#define __BOARD_MOP500_H + +extern void mop500_sdi_init(void); + +#endif diff --git a/arch/arm/mach-ux500/cpu-db5500.c b/arch/arm/mach-ux500/cpu-db5500.c index e9278f6d67aa..2f87075e9d6f 100644 --- a/arch/arm/mach-ux500/cpu-db5500.c +++ b/arch/arm/mach-ux500/cpu-db5500.c @@ -14,6 +14,7 @@ #include <mach/hardware.h> #include <mach/devices.h> #include <mach/setup.h> +#include <mach/irqs.h> static struct map_desc u5500_io_desc[] __initdata = { __IO_DEV_DESC(U5500_GPIO0_BASE, SZ_4K), @@ -24,6 +25,90 @@ static struct map_desc u5500_io_desc[] __initdata = { __IO_DEV_DESC(U5500_PRCMU_BASE, SZ_4K), }; +static struct resource mbox0_resources[] = { + { + .name = "mbox_peer", + .start = U5500_MBOX0_PEER_START, + .end = U5500_MBOX0_PEER_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_local", + .start = U5500_MBOX0_LOCAL_START, + .end = U5500_MBOX0_LOCAL_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_irq", + .start = MBOX_PAIR0_VIRT_IRQ, + .end = MBOX_PAIR0_VIRT_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource mbox1_resources[] = { + { + .name = "mbox_peer", + .start = U5500_MBOX1_PEER_START, + .end = U5500_MBOX1_PEER_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_local", + .start = U5500_MBOX1_LOCAL_START, + .end = U5500_MBOX1_LOCAL_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_irq", + .start = MBOX_PAIR1_VIRT_IRQ, + .end = MBOX_PAIR1_VIRT_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource mbox2_resources[] = { + { + .name = "mbox_peer", + .start = U5500_MBOX2_PEER_START, + .end = U5500_MBOX2_PEER_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_local", + .start = U5500_MBOX2_LOCAL_START, + .end = U5500_MBOX2_LOCAL_END, + .flags = IORESOURCE_MEM, + }, + { + .name = "mbox_irq", + .start = MBOX_PAIR2_VIRT_IRQ, + .end = MBOX_PAIR2_VIRT_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device mbox0_device = { + .id = 0, + .name = "mbox", + .resource = mbox0_resources, + .num_resources = ARRAY_SIZE(mbox0_resources), +}; + +static struct platform_device mbox1_device = { + .id = 1, + .name = "mbox", + .resource = mbox1_resources, + .num_resources = ARRAY_SIZE(mbox1_resources), +}; + +static struct platform_device mbox2_device = { + .id = 2, + .name = "mbox", + .resource = mbox2_resources, + .num_resources = ARRAY_SIZE(mbox2_resources), +}; + static struct platform_device *u5500_platform_devs[] __initdata = { &u5500_gpio_devs[0], &u5500_gpio_devs[1], @@ -33,6 +118,9 @@ static struct platform_device *u5500_platform_devs[] __initdata = { &u5500_gpio_devs[5], &u5500_gpio_devs[6], &u5500_gpio_devs[7], + &mbox0_device, + &mbox1_device, + &mbox2_device, }; void __init u5500_map_io(void) diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index f21c444edd99..4acab7544b3c 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -38,10 +38,12 @@ static struct platform_device *platform_devs[] __initdata = { /* minimum static i/o mapping required to boot U8500 platforms */ static struct map_desc u8500_io_desc[] __initdata = { __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K), + __IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO0_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO1_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO2_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO3_BASE, SZ_4K), + __MEM_DEV_DESC(U8500_BOOT_ROM_BASE, SZ_1M), }; static struct map_desc u8500ed_io_desc[] __initdata = { @@ -53,6 +55,69 @@ static struct map_desc u8500v1_io_desc[] __initdata = { __IO_DEV_DESC(U8500_MTU0_BASE, SZ_4K), }; +/* + * Functions to differentiate between later ASICs + * We look into the end of the ROM to locate the hardcoded ASIC ID. + * This is only needed to differentiate between minor revisions and + * process variants of an ASIC, the major revisions are encoded in + * the cpuid. + */ +#define U8500_ASIC_ID_LOC_ED_V1 (U8500_BOOT_ROM_BASE + 0x1FFF4) +#define U8500_ASIC_ID_LOC_V2 (U8500_BOOT_ROM_BASE + 0x1DBF4) +#define U8500_ASIC_REV_ED 0x01 +#define U8500_ASIC_REV_V10 0xA0 +#define U8500_ASIC_REV_V11 0xA1 +#define U8500_ASIC_REV_V20 0xB0 + +/** + * struct db8500_asic_id - fields of the ASIC ID + * @process: the manufacturing process, 0x40 is 40 nm + * 0x00 is "standard" + * @partnumber: hithereto 0x8500 for DB8500 + * @revision: version code in the series + * This field definion is not formally defined but makes + * sense. + */ +struct db8500_asic_id { + u8 process; + u16 partnumber; + u8 revision; +}; + +/* This isn't going to change at runtime */ +static struct db8500_asic_id db8500_id; + +static void __init get_db8500_asic_id(void) +{ + u32 asicid; + + if (cpu_is_u8500v1() || cpu_is_u8500ed()) + asicid = readl(__io_address(U8500_ASIC_ID_LOC_ED_V1)); + else if (cpu_is_u8500v2()) + asicid = readl(__io_address(U8500_ASIC_ID_LOC_V2)); + else + BUG(); + + db8500_id.process = (asicid >> 24); + db8500_id.partnumber = (asicid >> 16) & 0xFFFFU; + db8500_id.revision = asicid & 0xFFU; +} + +bool cpu_is_u8500v10(void) +{ + return (db8500_id.revision == U8500_ASIC_REV_V10); +} + +bool cpu_is_u8500v11(void) +{ + return (db8500_id.revision == U8500_ASIC_REV_V11); +} + +bool cpu_is_u8500v20(void) +{ + return (db8500_id.revision == U8500_ASIC_REV_V20); +} + void __init u8500_map_io(void) { ux500_map_io(); @@ -63,6 +128,9 @@ void __init u8500_map_io(void) iotable_init(u8500ed_io_desc, ARRAY_SIZE(u8500ed_io_desc)); else iotable_init(u8500v1_io_desc, ARRAY_SIZE(u8500v1_io_desc)); + + /* Read out the ASIC ID as early as we can */ + get_db8500_asic_id(); } /* @@ -70,6 +138,20 @@ void __init u8500_map_io(void) */ void __init u8500_init_devices(void) { + /* Display some ASIC boilerplate */ + pr_info("DB8500: process: %02x, revision ID: 0x%02x\n", + db8500_id.process, db8500_id.revision); + if (cpu_is_u8500ed()) + pr_info("DB8500: Early Drop (ED)\n"); + else if (cpu_is_u8500v10()) + pr_info("DB8500: version 1.0\n"); + else if (cpu_is_u8500v11()) + pr_info("DB8500: version 1.1\n"); + else if (cpu_is_u8500v20()) + pr_info("DB8500: version 2.0\n"); + else + pr_warning("ASIC: UNKNOWN SILICON VERSION!\n"); + ux500_init_devices(); if (cpu_is_u8500ed()) diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index 9280d2561111..40032fecbc16 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -110,6 +110,82 @@ struct platform_device u8500_i2c4_device = { .num_resources = ARRAY_SIZE(u8500_i2c4_resources), }; +/* + * SD/MMC + */ + +struct amba_device u8500_sdi0_device = { + .dev = { + .init_name = "sdi0", + }, + .res = { + .start = U8500_SDI0_BASE, + .end = U8500_SDI0_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC0, NO_IRQ}, +}; + +struct amba_device u8500_sdi1_device = { + .dev = { + .init_name = "sdi1", + }, + .res = { + .start = U8500_SDI1_BASE, + .end = U8500_SDI1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC1, NO_IRQ}, +}; + +struct amba_device u8500_sdi2_device = { + .dev = { + .init_name = "sdi2", + }, + .res = { + .start = U8500_SDI2_BASE, + .end = U8500_SDI2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC2, NO_IRQ}, +}; + +struct amba_device u8500_sdi3_device = { + .dev = { + .init_name = "sdi3", + }, + .res = { + .start = U8500_SDI3_BASE, + .end = U8500_SDI3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC3, NO_IRQ}, +}; + +struct amba_device u8500_sdi4_device = { + .dev = { + .init_name = "sdi4", + }, + .res = { + .start = U8500_SDI4_BASE, + .end = U8500_SDI4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC4, NO_IRQ}, +}; + +struct amba_device u8500_sdi5_device = { + .dev = { + .init_name = "sdi5", + }, + .res = { + .start = U8500_SDI5_BASE, + .end = U8500_SDI5_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DB8500_SDMMC5, NO_IRQ}, +}; + static struct resource dma40_resources[] = { [0] = { .start = U8500_DMA_BASE, @@ -170,23 +246,23 @@ struct stedma40_chan_cfg dma40_memcpy_conf_log = { * Mapping between destination event lines and physical device address. * The event line is tied to a device and therefor the address is constant. */ -static const dma_addr_t dma40_tx_map[STEDMA40_NR_DEV]; +static const dma_addr_t dma40_tx_map[DB8500_DMA_NR_DEV]; /* Mapping between source event lines and physical device address */ -static const dma_addr_t dma40_rx_map[STEDMA40_NR_DEV]; +static const dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV]; /* Reserved event lines for memcpy only */ static int dma40_memcpy_event[] = { - STEDMA40_MEMCPY_TX_0, - STEDMA40_MEMCPY_TX_1, - STEDMA40_MEMCPY_TX_2, - STEDMA40_MEMCPY_TX_3, - STEDMA40_MEMCPY_TX_4, - STEDMA40_MEMCPY_TX_5, + DB8500_DMA_MEMCPY_TX_0, + DB8500_DMA_MEMCPY_TX_1, + DB8500_DMA_MEMCPY_TX_2, + DB8500_DMA_MEMCPY_TX_3, + DB8500_DMA_MEMCPY_TX_4, + DB8500_DMA_MEMCPY_TX_5, }; static struct stedma40_platform_data dma40_plat_data = { - .dev_len = STEDMA40_NR_DEV, + .dev_len = DB8500_DMA_NR_DEV, .dev_rx = dma40_rx_map, .dev_tx = dma40_tx_map, .memcpy = dma40_memcpy_event, diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c new file mode 100644 index 000000000000..b782a03024be --- /dev/null +++ b/arch/arm/mach-ux500/hotplug.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Based on ARM realview platform + * + * Author: Sundar Iyer <sundar.iyer@stericsson.com> + * + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/completion.h> + +#include <asm/cacheflush.h> + +extern volatile int pen_release; + +static DECLARE_COMPLETION(cpu_killed); + +static inline void platform_do_lowpower(unsigned int cpu) +{ + flush_cache_all(); + + /* we put the platform to just WFI */ + for (;;) { + __asm__ __volatile__("dsb\n\t" "wfi\n\t" + : : : "memory"); + if (pen_release == cpu) { + /* + * OK, proper wakeup, we're done + */ + break; + } + } +} + +int platform_cpu_kill(unsigned int cpu) +{ + return wait_for_completion_timeout(&cpu_killed, 5000); +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void platform_cpu_die(unsigned int cpu) +{ +#ifdef DEBUG + unsigned int this_cpu = hard_smp_processor_id(); + + if (cpu != this_cpu) { + printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n", + this_cpu, cpu); + BUG(); + } +#endif + + printk(KERN_NOTICE "CPU%u: shutdown\n", cpu); + complete(&cpu_killed); + + /* directly enter low power state, skipping secure registers */ + platform_do_lowpower(cpu); +} + +int platform_cpu_disable(unsigned int cpu) +{ + /* + * we don't allow CPU 0 to be shutdown (it is still too special + * e.g. clock tick interrupts) + */ + return cpu == 0 ? -EPERM : 0; +} diff --git a/arch/arm/mach-ux500/include/mach/db5500-regs.h b/arch/arm/mach-ux500/include/mach/db5500-regs.h index 545c80fc8024..3eafc0e24ba5 100644 --- a/arch/arm/mach-ux500/include/mach/db5500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db5500-regs.h @@ -100,4 +100,18 @@ #define U5500_GPIOBANK6_BASE (U5500_GPIO4_BASE + 0x80) #define U5500_GPIOBANK7_BASE (U5500_GPIO4_BASE + 0x100) +#define U5500_MBOX_BASE (U5500_MODEM_BASE + 0xFFD1000) +#define U5500_MBOX0_PEER_START (U5500_MBOX_BASE + 0x40) +#define U5500_MBOX0_PEER_END (U5500_MBOX_BASE + 0x5F) +#define U5500_MBOX0_LOCAL_START (U5500_MBOX_BASE + 0x60) +#define U5500_MBOX0_LOCAL_END (U5500_MBOX_BASE + 0x7F) +#define U5500_MBOX1_PEER_START (U5500_MBOX_BASE + 0x80) +#define U5500_MBOX1_PEER_END (U5500_MBOX_BASE + 0x9F) +#define U5500_MBOX1_LOCAL_START (U5500_MBOX_BASE + 0xA0) +#define U5500_MBOX1_LOCAL_END (U5500_MBOX_BASE + 0xBF) +#define U5500_MBOX2_PEER_START (U5500_MBOX_BASE + 0x00) +#define U5500_MBOX2_PEER_END (U5500_MBOX_BASE + 0x1F) +#define U5500_MBOX2_LOCAL_START (U5500_MBOX_BASE + 0x20) +#define U5500_MBOX2_LOCAL_END (U5500_MBOX_BASE + 0x3F) + #endif diff --git a/arch/arm/mach-ux500/include/mach/db8500-regs.h b/arch/arm/mach-ux500/include/mach/db8500-regs.h index f000218210c9..f07d0986409d 100644 --- a/arch/arm/mach-ux500/include/mach/db8500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db8500-regs.h @@ -30,8 +30,6 @@ #define U8500_ICN_BASE 0x81000000 #define U8500_BOOT_ROM_BASE 0x90000000 -/* ASIC ID is at 0xff4 offset within this region */ -#define U8500_ASIC_ID_BASE 0x9001F000 #define U8500_PER6_BASE 0xa03c0000 #define U8500_PER5_BASE 0xa03e0000 diff --git a/arch/arm/mach-ux500/include/mach/devices.h b/arch/arm/mach-ux500/include/mach/devices.h index c2b2f2574947..33a120c2e82e 100644 --- a/arch/arm/mach-ux500/include/mach/devices.h +++ b/arch/arm/mach-ux500/include/mach/devices.h @@ -27,6 +27,13 @@ extern struct platform_device u8500_i2c0_device; extern struct platform_device u8500_i2c4_device; extern struct platform_device u8500_dma40_device; +extern struct amba_device u8500_sdi0_device; +extern struct amba_device u8500_sdi1_device; +extern struct amba_device u8500_sdi2_device; +extern struct amba_device u8500_sdi3_device; +extern struct amba_device u8500_sdi4_device; +extern struct amba_device u8500_sdi5_device; + void dma40_u8500ed_fixup(void); #endif diff --git a/arch/arm/mach-ux500/include/mach/hardware.h b/arch/arm/mach-ux500/include/mach/hardware.h index 8656379a8309..32e883a8f2a2 100644 --- a/arch/arm/mach-ux500/include/mach/hardware.h +++ b/arch/arm/mach-ux500/include/mach/hardware.h @@ -104,16 +104,35 @@ static inline bool cpu_is_u8500(void) #endif } +#define CPUID_DB8500ED 0x410fc090 +#define CPUID_DB8500V1 0x411fc091 +#define CPUID_DB8500V2 0x412fc091 + static inline bool cpu_is_u8500ed(void) { - return cpu_is_u8500() && (read_cpuid_id() & 15) == 0; + return cpu_is_u8500() && (read_cpuid_id() == CPUID_DB8500ED); } static inline bool cpu_is_u8500v1(void) { - return cpu_is_u8500() && (read_cpuid_id() & 15) == 1; + return cpu_is_u8500() && (read_cpuid_id() == CPUID_DB8500V1); +} + +static inline bool cpu_is_u8500v2(void) +{ + return cpu_is_u8500() && (read_cpuid_id() == CPUID_DB8500V2); } +#ifdef CONFIG_UX500_SOC_DB8500 +bool cpu_is_u8500v10(void); +bool cpu_is_u8500v11(void); +bool cpu_is_u8500v20(void); +#else +static inline bool cpu_is_u8500v10(void) { return false; } +static inline bool cpu_is_u8500v11(void) { return false; } +static inline bool cpu_is_u8500v20(void) { return false; } +#endif + static inline bool cpu_is_u5500(void) { #ifdef CONFIG_UX500_SOC_DB5500 diff --git a/arch/arm/mach-ux500/include/mach/irqs-db5500.h b/arch/arm/mach-ux500/include/mach/irqs-db5500.h index 6fbfe5e2065a..bfa123dbec3b 100644 --- a/arch/arm/mach-ux500/include/mach/irqs-db5500.h +++ b/arch/arm/mach-ux500/include/mach/irqs-db5500.h @@ -61,6 +61,7 @@ #define IRQ_DB5500_SDMMC0 (IRQ_SHPI_START + 60) #define IRQ_DB5500_HSEM (IRQ_SHPI_START + 61) #define IRQ_DB5500_SBAG (IRQ_SHPI_START + 63) +#define IRQ_DB5500_MODEM (IRQ_SHPI_START + 65) #define IRQ_DB5500_SPI1 (IRQ_SHPI_START + 96) #define IRQ_DB5500_MSP2 (IRQ_SHPI_START + 98) #define IRQ_DB5500_SRPTIMER (IRQ_SHPI_START + 101) diff --git a/arch/arm/mach-ux500/include/mach/irqs.h b/arch/arm/mach-ux500/include/mach/irqs.h index 10385bdc2b77..693aa57de88d 100644 --- a/arch/arm/mach-ux500/include/mach/irqs.h +++ b/arch/arm/mach-ux500/include/mach/irqs.h @@ -40,7 +40,8 @@ #define IRQ_HSIR_CH1_OVRRUN (IRQ_SHPI_START + 33) #define IRQ_HSIR_CH2_OVRRUN (IRQ_SHPI_START + 34) #define IRQ_HSIR_CH3_OVRRUN (IRQ_SHPI_START + 35) -#define IRQ_AB4500 (IRQ_SHPI_START + 40) +#define IRQ_AB8500 (IRQ_SHPI_START + 40) +#define IRQ_PRCMU (IRQ_SHPI_START + 47) #define IRQ_DISP (IRQ_SHPI_START + 48) #define IRQ_SiPI3 (IRQ_SHPI_START + 49) #define IRQ_I2C4 (IRQ_SHPI_START + 51) @@ -83,6 +84,19 @@ #include <mach/irqs-board-mop500.h> #endif -#define NR_IRQS IRQ_BOARD_END +/* + * After the board specific IRQ:s we reserve a range of IRQ:s in which virtual + * IRQ:s representing modem IRQ:s can be allocated + */ +#define IRQ_MODEM_EVENTS_BASE (IRQ_BOARD_END + 1) +#define IRQ_MODEM_EVENTS_NBR 72 +#define IRQ_MODEM_EVENTS_END (IRQ_MODEM_EVENTS_BASE + IRQ_MODEM_EVENTS_NBR) + +/* List of virtual IRQ:s that are allocated from the range above */ +#define MBOX_PAIR0_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 43) +#define MBOX_PAIR1_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 45) +#define MBOX_PAIR2_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 41) + +#define NR_IRQS IRQ_MODEM_EVENTS_END #endif /* ASM_ARCH_IRQS_H */ diff --git a/arch/arm/mach-ux500/include/mach/mbox.h b/arch/arm/mach-ux500/include/mach/mbox.h new file mode 100644 index 000000000000..7f9da4d2fbda --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/mbox.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson. + * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef __INC_STE_MBOX_H +#define __INC_STE_MBOX_H + +#define MBOX_BUF_SIZE 16 +#define MBOX_NAME_SIZE 8 + +/** + * mbox_recv_cb_t - Definition of the mailbox callback. + * @mbox_msg: The mailbox message. + * @priv: The clients private data as specified in the call to mbox_setup. + * + * This function will be called upon reception of new mailbox messages. + */ +typedef void mbox_recv_cb_t (u32 mbox_msg, void *priv); + +/** + * struct mbox - Mailbox instance struct + * @list: Linked list head. + * @pdev: Pointer to device struct. + * @cb: Callback function. Will be called + * when new data is received. + * @client_data: Clients private data. Will be sent back + * in the callback function. + * @virtbase_peer: Virtual address for outgoing mailbox. + * @virtbase_local: Virtual address for incoming mailbox. + * @buffer: Then internal queue for outgoing messages. + * @name: Name of this mailbox. + * @buffer_available: Completion variable to achieve "blocking send". + * This variable will be signaled when there is + * internal buffer space available. + * @client_blocked: To keep track if any client is currently + * blocked. + * @lock: Spinlock to protect this mailbox instance. + * @write_index: Index in internal buffer to write to. + * @read_index: Index in internal buffer to read from. + * @allocated: Indicates whether this particular mailbox + * id has been allocated by someone. + */ +struct mbox { + struct list_head list; + struct platform_device *pdev; + mbox_recv_cb_t *cb; + void *client_data; + void __iomem *virtbase_peer; + void __iomem *virtbase_local; + u32 buffer[MBOX_BUF_SIZE]; + char name[MBOX_NAME_SIZE]; + struct completion buffer_available; + u8 client_blocked; + spinlock_t lock; + u8 write_index; + u8 read_index; + bool allocated; +}; + +/** + * mbox_setup - Set up a mailbox and return its instance. + * @mbox_id: The ID number of the mailbox. 0 or 1 for modem CPU, + * 2 for modem DSP. + * @mbox_cb: Pointer to the callback function to be called when a new message + * is received. + * @priv: Client user data which will be returned in the callback. + * + * Returns a mailbox instance to be specified in subsequent calls to mbox_send. + */ +struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv); + +/** + * mbox_send - Send a mailbox message. + * @mbox: Mailbox instance (returned by mbox_setup) + * @mbox_msg: The mailbox message to send. + * @block: Specifies whether this call will block until send is possible, + * or return an error if the mailbox buffer is full. + * + * Returns 0 on success or a negative error code on error. -ENOMEM indicates + * that the internal buffer is full and you have to try again later (or + * specify "block" in order to block until send is possible). + */ +int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block); + +#endif /*INC_STE_MBOX_H*/ diff --git a/arch/arm/mach-ux500/include/mach/prcmu-regs.h b/arch/arm/mach-ux500/include/mach/prcmu-regs.h new file mode 100644 index 000000000000..8885f39a6421 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/prcmu-regs.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2009 ST-Ericsson SA + * + * 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. + */ +#ifndef __MACH_PRCMU_REGS_H +#define __MACH_PRCMU_REGS_H + +#include <mach/hardware.h> + +#define _PRCMU_BASE IO_ADDRESS(U8500_PRCMU_BASE) + +#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) +#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) +#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) +#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) +#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) +#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) +#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) +#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) + +/* ARM WFI Standby signal register */ +#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) +#define PRCMU_IOCR (_PRCMU_BASE + 0x310) + +/* CPU mailbox registers */ +#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) +#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) +#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) + +/* Dual A9 core interrupt management unit registers */ +#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) +#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) +#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) +#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) +#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) +#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) +#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) +#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) +#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) +#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) +#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) + +#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) +#define ARM_WAKEUP_MODEM 0x1 + +#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C) +#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) +#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) + +#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) +#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) +#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) +#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) +#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) +#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) +#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) +#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) + +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) + +/* Level shifter and clamp control registers */ +#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) +#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) + +/* PRCMU clock/PLL/reset registers */ +#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500) +#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504) +#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044) +#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064) +#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058) +#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c) +#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530) +#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C) +#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) +#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) + +/* ePOD and memory power signal control registers */ +#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) +#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) + +/* Debug power control unit registers */ +#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) + +/* Miscellaneous unit registers */ +#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) + +#endif /* __MACH_PRCMU__REGS_H */ diff --git a/arch/arm/mach-ux500/include/mach/prcmu.h b/arch/arm/mach-ux500/include/mach/prcmu.h new file mode 100644 index 000000000000..549843ff6dbe --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/prcmu.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * PRCMU f/w APIs + */ +#ifndef __MACH_PRCMU_H +#define __MACH_PRCMU_H + +int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); + +#endif /* __MACH_PRCMU_H */ diff --git a/arch/arm/mach-ux500/include/mach/setup.h b/arch/arm/mach-ux500/include/mach/setup.h index e978dbd9e210..54bbe648bf58 100644 --- a/arch/arm/mach-ux500/include/mach/setup.h +++ b/arch/arm/mach-ux500/include/mach/setup.h @@ -38,4 +38,11 @@ extern struct sys_timer ux500_timer; .type = MT_DEVICE, \ } +#define __MEM_DEV_DESC(x, sz) { \ + .virtual = IO_ADDRESS(x), \ + .pfn = __phys_to_pfn(x), \ + .length = sz, \ + .type = MT_MEMORY, \ +} + #endif /* __ASM_ARCH_SETUP_H */ diff --git a/arch/arm/mach-ux500/include/mach/smp.h b/arch/arm/mach-ux500/include/mach/smp.h index b59f7bc9725d..197e8417375e 100644 --- a/arch/arm/mach-ux500/include/mach/smp.h +++ b/arch/arm/mach-ux500/include/mach/smp.h @@ -10,18 +10,11 @@ #define ASMARM_ARCH_SMP_H #include <asm/hardware/gic.h> +#include <asm/smp_mpidr.h> /* This is required to wakeup the secondary core */ extern void u8500_secondary_startup(void); -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x0F; \ - }) - /* * We use IRQ1 as the IPI */ diff --git a/arch/arm/mach-ux500/mbox.c b/arch/arm/mach-ux500/mbox.c new file mode 100644 index 000000000000..63435389c544 --- /dev/null +++ b/arch/arm/mach-ux500/mbox.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson. + * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/* + * Mailbox nomenclature: + * + * APE MODEM + * mbox pairX + * .......................... + * . . + * . peer . + * . send ---- . + * . --> | | . + * . | | . + * . ---- . + * . . + * . local . + * . rec ---- . + * . | | <-- . + * . | | . + * . ---- . + * ......................... + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/completion.h> +#include <mach/mbox.h> + +#define MBOX_NAME "mbox" + +#define MBOX_FIFO_DATA 0x000 +#define MBOX_FIFO_ADD 0x004 +#define MBOX_FIFO_REMOVE 0x008 +#define MBOX_FIFO_THRES_FREE 0x00C +#define MBOX_FIFO_THRES_OCCUP 0x010 +#define MBOX_FIFO_STATUS 0x014 + +#define MBOX_DISABLE_IRQ 0x4 +#define MBOX_ENABLE_IRQ 0x0 +#define MBOX_LATCH 1 + +/* Global list of all mailboxes */ +static struct list_head mboxs = LIST_HEAD_INIT(mboxs); + +static struct mbox *get_mbox_with_id(u8 id) +{ + u8 i; + struct list_head *pos = &mboxs; + for (i = 0; i <= id; i++) + pos = pos->next; + + return (struct mbox *) list_entry(pos, struct mbox, list); +} + +int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) +{ + int res = 0; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "About to buffer 0x%X to mailbox 0x%X." + " ri = %d, wi = %d\n", + mbox_msg, (u32)mbox, mbox->read_index, + mbox->write_index); + + /* Check if write buffer is full */ + while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) { + if (!block) { + dev_dbg(&(mbox->pdev->dev), + "Buffer full in non-blocking call! " + "Returning -ENOMEM!\n"); + res = -ENOMEM; + goto exit; + } + spin_unlock(&mbox->lock); + dev_dbg(&(mbox->pdev->dev), + "Buffer full in blocking call! Sleeping...\n"); + mbox->client_blocked = 1; + wait_for_completion(&mbox->buffer_available); + dev_dbg(&(mbox->pdev->dev), + "Blocking send was woken up! Trying again...\n"); + spin_lock(&mbox->lock); + } + + mbox->buffer[mbox->write_index] = mbox_msg; + mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE; + + /* + * Indicate that we want an IRQ as soon as there is a slot + * in the FIFO + */ + writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + +exit: + spin_unlock(&mbox->lock); + return res; +} +EXPORT_SYMBOL(mbox_send); + +#if defined(CONFIG_DEBUG_FS) +/* + * Expected input: <value> <nbr sends> + * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times + */ +static ssize_t mbox_write_fifo(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long mbox_mess; + unsigned long nbr_sends; + unsigned long i; + char int_buf[16]; + char *token; + char *val; + + struct mbox *mbox = (struct mbox *) dev->platform_data; + + strncpy((char *) &int_buf, buf, sizeof(int_buf)); + token = (char *) &int_buf; + + /* Parse message */ + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0)) + mbox_mess = 0xDEADBEEF; + + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0)) + nbr_sends = 1; + + dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n", + mbox_mess, nbr_sends, (u32) mbox); + + for (i = 0; i < nbr_sends; i++) + mbox_send(mbox, mbox_mess, true); + + return count; +} + +static ssize_t mbox_read_fifo(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int mbox_value; + struct mbox *mbox = (struct mbox *) dev->platform_data; + + if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) + return sprintf(buf, "Mailbox is empty\n"); + + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + return sprintf(buf, "0x%X\n", mbox_value); +} + +static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo); + +static int mbox_show(struct seq_file *s, void *data) +{ + struct list_head *pos; + u8 mbox_index = 0; + + list_for_each(pos, &mboxs) { + struct mbox *m = + (struct mbox *) list_entry(pos, struct mbox, list); + if (m == NULL) { + seq_printf(s, + "Unable to retrieve mailbox %d\n", + mbox_index); + continue; + } + + spin_lock(&m->lock); + if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) { + seq_printf(s, "MAILBOX %d not setup or corrupt\n", + mbox_index); + spin_unlock(&m->lock); + continue; + } + + seq_printf(s, + "===========================\n" + " MAILBOX %d\n" + " PEER MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + " LOCAL MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%.X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + "write_index: %d\n" + "read_index : %d\n" + "===========================\n" + "\n", + mbox_index, + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1, + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1, + m->write_index, m->read_index); + mbox_index++; + spin_unlock(&m->lock); + } + + return 0; +} + +static int mbox_open(struct inode *inode, struct file *file) +{ + return single_open(file, mbox_show, NULL); +} + +static const struct file_operations mbox_operations = { + .owner = THIS_MODULE, + .open = mbox_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static irqreturn_t mbox_irq(int irq, void *arg) +{ + u32 mbox_value; + int nbr_occup; + int nbr_free; + struct mbox *mbox = (struct mbox *) arg; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "mbox IRQ [%d] received. ri = %d, wi = %d\n", + irq, mbox->read_index, mbox->write_index); + + /* + * Check if we have any outgoing messages, and if there is space for + * them in the FIFO. + */ + if (mbox->read_index != mbox->write_index) { + /* + * Check by reading FREE for LOCAL since that indicates + * OCCUP for PEER + */ + nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS) + >> 4) & 0x7; + dev_dbg(&(mbox->pdev->dev), + "Status indicates %d empty spaces in the FIFO!\n", + nbr_free); + + while ((nbr_free > 0) && + (mbox->read_index != mbox->write_index)) { + /* Write the message and latch it into the FIFO */ + writel(mbox->buffer[mbox->read_index], + (mbox->virtbase_peer + MBOX_FIFO_DATA)); + writel(MBOX_LATCH, + (mbox->virtbase_peer + MBOX_FIFO_ADD)); + dev_dbg(&(mbox->pdev->dev), + "Wrote message 0x%X to addr 0x%X\n", + mbox->buffer[mbox->read_index], + (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA)); + + nbr_free--; + mbox->read_index = + (mbox->read_index + 1) % MBOX_BUF_SIZE; + } + + /* + * Check if we still want IRQ:s when there is free + * space to send + */ + if (mbox->read_index != mbox->write_index) { + dev_dbg(&(mbox->pdev->dev), + "Still have messages to send, but FIFO full. " + "Request IRQ again!\n"); + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } else { + dev_dbg(&(mbox->pdev->dev), + "No more messages to send. " + "Do not request IRQ again!\n"); + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } + + /* + * Check if we can signal any blocked clients that it is OK to + * start buffering again + */ + if (mbox->client_blocked && + (((mbox->write_index + 1) % MBOX_BUF_SIZE) + != mbox->read_index)) { + dev_dbg(&(mbox->pdev->dev), + "Waking up blocked client\n"); + complete(&mbox->buffer_available); + mbox->client_blocked = 0; + } + } + + /* Check if we have any incoming messages */ + nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; + if (nbr_occup == 0) + goto exit; + + if (mbox->cb == NULL) { + dev_dbg(&(mbox->pdev->dev), "No receive callback registered, " + "leaving %d incoming messages in fifo!\n", nbr_occup); + goto exit; + } + + /* Read and acknowledge the message */ + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + /* Notify consumer of new mailbox message */ + dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n", + mbox_value); + mbox->cb(mbox_value, mbox->client_data); + +exit: + dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", + mbox->read_index, mbox->write_index); + spin_unlock(&mbox->lock); + + return IRQ_HANDLED; +} + +/* Setup is executed once for each mbox pair */ +struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) +{ + struct resource *resource; + int irq; + int res; + struct mbox *mbox; + + mbox = get_mbox_with_id(mbox_id); + if (mbox == NULL) { + dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n", + mbox_id); + goto exit; + } + + /* + * Check if mailbox has been allocated to someone else, + * otherwise allocate it + */ + if (mbox->allocated) { + dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n", + mbox_id); + mbox = NULL; + goto exit; + } + mbox->allocated = true; + + dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n", + mbox_id, (u32)mbox); + + mbox->client_data = priv; + mbox->cb = mbox_cb; + + /* Get addr for peer mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_peer"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox peer resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_peer = + ioremap(resource->start, resource->end - resource->start); + if (!mbox->virtbase_peer) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + /* Get addr for local mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_local"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox local resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_local = + ioremap(resource->start, resource->end - resource->start); + if (!mbox->virtbase_local) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + init_completion(&mbox->buffer_available); + mbox->client_blocked = 0; + + /* Get IRQ for mailbox and allocate it */ + irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); + if (irq < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox irq resource\n"); + mbox = NULL; + goto exit; + } + + dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); + res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox); + if (res < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to allocate mbox irq %d\n", irq); + mbox = NULL; + goto exit; + } + + /* Set up mailbox to not launch IRQ on free space in mailbox */ + writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + + /* + * Set up mailbox to launch IRQ on new message if we have + * a callback set. If not, do not raise IRQ, but keep message + * in FIFO for manual retrieval + */ + if (mbox_cb != NULL) + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + else + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + +#if defined(CONFIG_DEBUG_FS) + res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo); + if (res != 0) + dev_warn(&(mbox->pdev->dev), + "Unable to create mbox sysfs entry"); + + (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, + NULL, &mbox_operations); +#endif + + dev_info(&(mbox->pdev->dev), + "Mailbox driver with index %d initated!\n", mbox_id); + +exit: + return mbox; +} +EXPORT_SYMBOL(mbox_setup); + + +int __init mbox_probe(struct platform_device *pdev) +{ + struct mbox local_mbox; + struct mbox *mbox; + int res = 0; + dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev); + + memset(&local_mbox, 0x0, sizeof(struct mbox)); + + /* Associate our mbox data with the platform device */ + res = platform_device_add_data(pdev, + (void *) &local_mbox, + sizeof(struct mbox)); + if (res != 0) { + dev_err(&(pdev->dev), + "Unable to allocate driver platform data!\n"); + goto exit; + } + + mbox = (struct mbox *) pdev->dev.platform_data; + mbox->pdev = pdev; + mbox->write_index = 0; + mbox->read_index = 0; + + INIT_LIST_HEAD(&(mbox->list)); + list_add_tail(&(mbox->list), &mboxs); + + sprintf(mbox->name, "%s", MBOX_NAME); + spin_lock_init(&mbox->lock); + + dev_info(&(pdev->dev), "Mailbox driver loaded\n"); + +exit: + return res; +} + +static struct platform_driver mbox_driver = { + .driver = { + .name = MBOX_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mbox_init(void) +{ + return platform_driver_probe(&mbox_driver, mbox_probe); +} + +module_init(mbox_init); + +void __exit mbox_exit(void) +{ + platform_driver_unregister(&mbox_driver); +} + +module_exit(mbox_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MBOX driver"); diff --git a/arch/arm/mach-ux500/modem_irq.c b/arch/arm/mach-ux500/modem_irq.c new file mode 100644 index 000000000000..3187f8871169 --- /dev/null +++ b/arch/arm/mach-ux500/modem_irq.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson. + * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/slab.h> + +#define MODEM_INTCON_BASE_ADDR 0xBFFD3000 +#define MODEM_INTCON_SIZE 0xFFF + +#define DEST_IRQ41_OFFSET 0x2A4 +#define DEST_IRQ43_OFFSET 0x2AC +#define DEST_IRQ45_OFFSET 0x2B4 + +#define PRIO_IRQ41_OFFSET 0x6A4 +#define PRIO_IRQ43_OFFSET 0x6AC +#define PRIO_IRQ45_OFFSET 0x6B4 + +#define ALLOW_IRQ_OFFSET 0x104 + +#define MODEM_INTCON_CPU_NBR 0x1 +#define MODEM_INTCON_PRIO_HIGH 0x0 + +#define MODEM_INTCON_ALLOW_IRQ41 0x0200 +#define MODEM_INTCON_ALLOW_IRQ43 0x0800 +#define MODEM_INTCON_ALLOW_IRQ45 0x2000 + +#define MODEM_IRQ_REG_OFFSET 0x4 + +struct modem_irq { + void __iomem *modem_intcon_base; +}; + + +static void setup_modem_intcon(void __iomem *modem_intcon_base) +{ + /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */ + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET); + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET); + writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET); + + /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */ + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET); + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET); + writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET); + + /* IC_ALLOW_ARRAY - IRQ enable */ + writel(MODEM_INTCON_ALLOW_IRQ41 | + MODEM_INTCON_ALLOW_IRQ43 | + MODEM_INTCON_ALLOW_IRQ45, + modem_intcon_base + ALLOW_IRQ_OFFSET); +} + +static irqreturn_t modem_cpu_irq_handler(int irq, void *data) +{ + int real_irq; + int virt_irq; + struct modem_irq *mi = (struct modem_irq *)data; + + /* Read modem side IRQ number from modem IRQ controller */ + real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF; + virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq; + + pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X " + "which will be 0x%X (%d) which translates to " + "virtual IRQ 0x%X (%d)!\n", + (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET, + real_irq, + real_irq & 0xFF, + real_irq & 0xFF, + virt_irq, + virt_irq); + + if (virt_irq != 0) + generic_handle_irq(virt_irq); + + pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq); + + return IRQ_HANDLED; +} + +static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip) +{ + set_irq_chip(irq, modem_irq_chip); + set_irq_handler(irq, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + + pr_debug("modem_irq: Created virtual IRQ %d\n", irq); +} + +static int modem_irq_init(void) +{ + int err; + static struct irq_chip modem_irq_chip; + struct modem_irq *mi; + + pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n", + IRQ_DB5500_MODEM); + + mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL); + if (!mi) { + pr_err("modem_irq: Could not allocate device\n"); + return -ENOMEM; + } + + mi->modem_intcon_base = + ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE); + pr_debug("modem_irq: ioremapped modem_intcon_base from " + "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR, + (u32)mi->modem_intcon_base); + + setup_modem_intcon(mi->modem_intcon_base); + + modem_irq_chip = dummy_irq_chip; + modem_irq_chip.name = "modem_irq"; + + /* Create the virtual IRQ:s needed */ + create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip); + create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip); + create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip); + + err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, + modem_cpu_irq_handler, IRQF_ONESHOT, + "modem_irq", mi); + if (err) + pr_err("modem_irq: Could not register IRQ %d\n", + IRQ_DB5500_MODEM); + + return 0; +} + +arch_initcall(modem_irq_init); diff --git a/arch/arm/mach-ux500/pins-db5500.h b/arch/arm/mach-ux500/pins-db5500.h new file mode 100644 index 000000000000..bf50c21fe69d --- /dev/null +++ b/arch/arm/mach-ux500/pins-db5500.h @@ -0,0 +1,620 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License, version 2 + * Author: Rabin Vincent <rabin.vincent@stericsson.com> + */ + +#ifndef __MACH_DB5500_PINS_H +#define __MACH_DB5500_PINS_H + +#define GPIO0_GPIO PIN_CFG(0, GPIO) +#define GPIO0_SM_CS3n PIN_CFG(0, ALT_A) + +#define GPIO1_GPIO PIN_CFG(1, GPIO) +#define GPIO1_SM_A3 PIN_CFG(1, ALT_A) + +#define GPIO2_GPIO PIN_CFG(2, GPIO) +#define GPIO2_SM_A4 PIN_CFG(2, ALT_A) +#define GPIO2_SM_AVD PIN_CFG(2, ALT_B) + +#define GPIO3_GPIO PIN_CFG(3, GPIO) +#define GPIO3_I2C1_SCL PIN_CFG(3, ALT_A) + +#define GPIO4_GPIO PIN_CFG(4, GPIO) +#define GPIO4_I2C1_SDA PIN_CFG(4, ALT_A) + +#define GPIO5_GPIO PIN_CFG(5, GPIO) +#define GPIO5_MC0_DAT0 PIN_CFG(5, ALT_A) +#define GPIO5_SM_ADQ8 PIN_CFG(5, ALT_B) + +#define GPIO6_GPIO PIN_CFG(6, GPIO) +#define GPIO6_MC0_DAT1 PIN_CFG(6, ALT_A) +#define GPIO6_SM_ADQ0 PIN_CFG(6, ALT_B) + +#define GPIO7_GPIO PIN_CFG(7, GPIO) +#define GPIO7_MC0_DAT2 PIN_CFG(7, ALT_A) +#define GPIO7_SM_ADQ9 PIN_CFG(7, ALT_B) + +#define GPIO8_GPIO PIN_CFG(8, GPIO) +#define GPIO8_MC0_DAT3 PIN_CFG(8, ALT_A) +#define GPIO8_SM_ADQ1 PIN_CFG(8, ALT_B) + +#define GPIO9_GPIO PIN_CFG(9, GPIO) +#define GPIO9_MC0_DAT4 PIN_CFG(9, ALT_A) +#define GPIO9_SM_ADQ10 PIN_CFG(9, ALT_B) + +#define GPIO10_GPIO PIN_CFG(10, GPIO) +#define GPIO10_MC0_DAT5 PIN_CFG(10, ALT_A) +#define GPIO10_SM_ADQ2 PIN_CFG(10, ALT_B) + +#define GPIO11_GPIO PIN_CFG(11, GPIO) +#define GPIO11_MC0_DAT6 PIN_CFG(11, ALT_A) +#define GPIO11_SM_ADQ11 PIN_CFG(11, ALT_B) + +#define GPIO12_GPIO PIN_CFG(12, GPIO) +#define GPIO12_MC0_DAT7 PIN_CFG(12, ALT_A) +#define GPIO12_SM_ADQ3 PIN_CFG(12, ALT_B) + +#define GPIO13_GPIO PIN_CFG(13, GPIO) +#define GPIO13_MC0_CMD PIN_CFG(13, ALT_A) +#define GPIO13_SM_BUSY0n PIN_CFG(13, ALT_B) +#define GPIO13_SM_WAIT0n PIN_CFG(13, ALT_C) + +#define GPIO14_GPIO PIN_CFG(14, GPIO) +#define GPIO14_MC0_CLK PIN_CFG(14, ALT_A) +#define GPIO14_SM_CS1n PIN_CFG(14, ALT_B) +#define GPIO14_SM_CKO PIN_CFG(14, ALT_C) + +#define GPIO15_GPIO PIN_CFG(15, GPIO) +#define GPIO15_SM_A5 PIN_CFG(15, ALT_A) +#define GPIO15_SM_CLE PIN_CFG(15, ALT_B) + +#define GPIO16_GPIO PIN_CFG(16, GPIO) +#define GPIO16_MC2_CMD PIN_CFG(16, ALT_A) +#define GPIO16_SM_OEn PIN_CFG(16, ALT_B) + +#define GPIO17_GPIO PIN_CFG(17, GPIO) +#define GPIO17_MC2_CLK PIN_CFG(17, ALT_A) +#define GPIO17_SM_WEn PIN_CFG(17, ALT_B) + +#define GPIO18_GPIO PIN_CFG(18, GPIO) +#define GPIO18_SM_A6 PIN_CFG(18, ALT_A) +#define GPIO18_SM_ALE PIN_CFG(18, ALT_B) +#define GPIO18_SM_AVDn PIN_CFG(18, ALT_C) + +#define GPIO19_GPIO PIN_CFG(19, GPIO) +#define GPIO19_MC2_DAT1 PIN_CFG(19, ALT_A) +#define GPIO19_SM_ADQ4 PIN_CFG(19, ALT_B) + +#define GPIO20_GPIO PIN_CFG(20, GPIO) +#define GPIO20_MC2_DAT3 PIN_CFG(20, ALT_A) +#define GPIO20_SM_ADQ5 PIN_CFG(20, ALT_B) + +#define GPIO21_GPIO PIN_CFG(21, GPIO) +#define GPIO21_MC2_DAT5 PIN_CFG(21, ALT_A) +#define GPIO21_SM_ADQ6 PIN_CFG(21, ALT_B) + +#define GPIO22_GPIO PIN_CFG(22, GPIO) +#define GPIO22_MC2_DAT7 PIN_CFG(22, ALT_A) +#define GPIO22_SM_ADQ7 PIN_CFG(22, ALT_B) + +#define GPIO23_GPIO PIN_CFG(23, GPIO) +#define GPIO23_MC2_DAT0 PIN_CFG(23, ALT_A) +#define GPIO23_SM_ADQ12 PIN_CFG(23, ALT_B) +#define GPIO23_MC0_DAT1 PIN_CFG(23, ALT_C) + +#define GPIO24_GPIO PIN_CFG(24, GPIO) +#define GPIO24_MC2_DAT2 PIN_CFG(24, ALT_A) +#define GPIO24_SM_ADQ13 PIN_CFG(24, ALT_B) +#define GPIO24_MC0_DAT3 PIN_CFG(24, ALT_C) + +#define GPIO25_GPIO PIN_CFG(25, GPIO) +#define GPIO25_MC2_DAT4 PIN_CFG(25, ALT_A) +#define GPIO25_SM_ADQ14 PIN_CFG(25, ALT_B) +#define GPIO25_MC0_CMD PIN_CFG(25, ALT_C) + +#define GPIO26_GPIO PIN_CFG(26, GPIO) +#define GPIO26_MC2_DAT6 PIN_CFG(26, ALT_A) +#define GPIO26_SM_ADQ15 PIN_CFG(26, ALT_B) + +#define GPIO27_GPIO PIN_CFG(27, GPIO) +#define GPIO27_SM_CS0n PIN_CFG(27, ALT_A) +#define GPIO27_SM_PS0n PIN_CFG(27, ALT_B) + +#define GPIO28_GPIO PIN_CFG(28, GPIO) +#define GPIO28_U0_TXD PIN_CFG(28, ALT_A) +#define GPIO28_SM_A0 PIN_CFG(28, ALT_B) + +#define GPIO29_GPIO PIN_CFG(29, GPIO) +#define GPIO29_U0_RXD PIN_CFG(29, ALT_A) +#define GPIO29_SM_A1 PIN_CFG(29, ALT_B) +#define GPIO29_PWM_0 PIN_CFG(29, ALT_C) + +#define GPIO30_GPIO PIN_CFG(30, GPIO) +#define GPIO30_MC0_DAT5 PIN_CFG(30, ALT_A) +#define GPIO30_SM_A2 PIN_CFG(30, ALT_B) +#define GPIO30_PWM_1 PIN_CFG(30, ALT_C) + +#define GPIO31_GPIO PIN_CFG(31, GPIO) +#define GPIO31_MC0_DAT7 PIN_CFG(31, ALT_A) +#define GPIO31_SM_CS2n PIN_CFG(31, ALT_B) +#define GPIO31_PWM_2 PIN_CFG(31, ALT_C) + +#define GPIO32_GPIO PIN_CFG(32, GPIO) +#define GPIO32_MSP0_TCK PIN_CFG(32, ALT_A) +#define GPIO32_ACCI2S0_SCK PIN_CFG(32, ALT_B) + +#define GPIO33_GPIO PIN_CFG(33, GPIO) +#define GPIO33_MSP0_TFS PIN_CFG(33, ALT_A) +#define GPIO33_ACCI2S0_WS PIN_CFG(33, ALT_B) + +#define GPIO34_GPIO PIN_CFG(34, GPIO) +#define GPIO34_MSP0_TXD PIN_CFG(34, ALT_A) +#define GPIO34_ACCI2S0_DLD PIN_CFG(34, ALT_B) + +#define GPIO35_GPIO PIN_CFG(35, GPIO) +#define GPIO35_MSP0_RXD PIN_CFG(35, ALT_A) +#define GPIO35_ACCI2S0_ULD PIN_CFG(35, ALT_B) + +#define GPIO64_GPIO PIN_CFG(64, GPIO) +#define GPIO64_USB_DAT0 PIN_CFG(64, ALT_A) +#define GPIO64_U0_TXD PIN_CFG(64, ALT_B) + +#define GPIO65_GPIO PIN_CFG(65, GPIO) +#define GPIO65_USB_DAT1 PIN_CFG(65, ALT_A) +#define GPIO65_U0_RXD PIN_CFG(65, ALT_B) + +#define GPIO66_GPIO PIN_CFG(66, GPIO) +#define GPIO66_USB_DAT2 PIN_CFG(66, ALT_A) + +#define GPIO67_GPIO PIN_CFG(67, GPIO) +#define GPIO67_USB_DAT3 PIN_CFG(67, ALT_A) + +#define GPIO68_GPIO PIN_CFG(68, GPIO) +#define GPIO68_USB_DAT4 PIN_CFG(68, ALT_A) + +#define GPIO69_GPIO PIN_CFG(69, GPIO) +#define GPIO69_USB_DAT5 PIN_CFG(69, ALT_A) + +#define GPIO70_GPIO PIN_CFG(70, GPIO) +#define GPIO70_USB_DAT6 PIN_CFG(70, ALT_A) + +#define GPIO71_GPIO PIN_CFG(71, GPIO) +#define GPIO71_USB_DAT7 PIN_CFG(71, ALT_A) + +#define GPIO72_GPIO PIN_CFG(72, GPIO) +#define GPIO72_USB_STP PIN_CFG(72, ALT_A) + +#define GPIO73_GPIO PIN_CFG(73, GPIO) +#define GPIO73_USB_DIR PIN_CFG(73, ALT_A) + +#define GPIO74_GPIO PIN_CFG(74, GPIO) +#define GPIO74_USB_NXT PIN_CFG(74, ALT_A) + +#define GPIO75_GPIO PIN_CFG(75, GPIO) +#define GPIO75_USB_XCLK PIN_CFG(75, ALT_A) + +#define GPIO76_GPIO PIN_CFG(76, GPIO) + +#define GPIO77_GPIO PIN_CFG(77, GPIO) +#define GPIO77_ACCTX_ON PIN_CFG(77, ALT_A) + +#define GPIO78_GPIO PIN_CFG(78, GPIO) +#define GPIO78_IRQn PIN_CFG(78, ALT_A) + +#define GPIO79_GPIO PIN_CFG(79, GPIO) +#define GPIO79_ACCSIM_Clk PIN_CFG(79, ALT_A) + +#define GPIO80_GPIO PIN_CFG(80, GPIO) +#define GPIO80_ACCSIM_Da PIN_CFG(80, ALT_A) + +#define GPIO81_GPIO PIN_CFG(81, GPIO) +#define GPIO81_ACCSIM_Reset PIN_CFG(81, ALT_A) + +#define GPIO82_GPIO PIN_CFG(82, GPIO) +#define GPIO82_ACCSIM_DDir PIN_CFG(82, ALT_A) + +#define GPIO96_GPIO PIN_CFG(96, GPIO) +#define GPIO96_MSP1_TCK PIN_CFG(96, ALT_A) +#define GPIO96_PRCMU_DEBUG3 PIN_CFG(96, ALT_B) +#define GPIO96_PRCMU_DEBUG7 PIN_CFG(96, ALT_C) + +#define GPIO97_GPIO PIN_CFG(97, GPIO) +#define GPIO97_MSP1_TFS PIN_CFG(97, ALT_A) +#define GPIO97_PRCMU_DEBUG2 PIN_CFG(97, ALT_B) +#define GPIO97_PRCMU_DEBUG6 PIN_CFG(97, ALT_C) + +#define GPIO98_GPIO PIN_CFG(98, GPIO) +#define GPIO98_MSP1_TXD PIN_CFG(98, ALT_A) +#define GPIO98_PRCMU_DEBUG1 PIN_CFG(98, ALT_B) +#define GPIO98_PRCMU_DEBUG5 PIN_CFG(98, ALT_C) + +#define GPIO99_GPIO PIN_CFG(99, GPIO) +#define GPIO99_MSP1_RXD PIN_CFG(99, ALT_A) +#define GPIO99_PRCMU_DEBUG0 PIN_CFG(99, ALT_B) +#define GPIO99_PRCMU_DEBUG4 PIN_CFG(99, ALT_C) + +#define GPIO100_GPIO PIN_CFG(100, GPIO) +#define GPIO100_I2C0_SCL PIN_CFG(100, ALT_A) + +#define GPIO101_GPIO PIN_CFG(101, GPIO) +#define GPIO101_I2C0_SDA PIN_CFG(101, ALT_A) + +#define GPIO128_GPIO PIN_CFG(128, GPIO) +#define GPIO128_KP_I0 PIN_CFG(128, ALT_A) +#define GPIO128_BUSMON_D0 PIN_CFG(128, ALT_B) + +#define GPIO129_GPIO PIN_CFG(129, GPIO) +#define GPIO129_KP_O0 PIN_CFG(129, ALT_A) +#define GPIO129_BUSMON_D1 PIN_CFG(129, ALT_B) + +#define GPIO130_GPIO PIN_CFG(130, GPIO) +#define GPIO130_KP_I1 PIN_CFG(130, ALT_A) +#define GPIO130_BUSMON_D2 PIN_CFG(130, ALT_B) + +#define GPIO131_GPIO PIN_CFG(131, GPIO) +#define GPIO131_KP_O1 PIN_CFG(131, ALT_A) +#define GPIO131_BUSMON_D3 PIN_CFG(131, ALT_B) + +#define GPIO132_GPIO PIN_CFG(132, GPIO) +#define GPIO132_KP_I2 PIN_CFG(132, ALT_A) +#define GPIO132_ETM_D15 PIN_CFG(132, ALT_B) +#define GPIO132_STMAPE_CLK PIN_CFG(132, ALT_C) + +#define GPIO133_GPIO PIN_CFG(133, GPIO) +#define GPIO133_KP_O2 PIN_CFG(133, ALT_A) +#define GPIO133_ETM_D14 PIN_CFG(133, ALT_B) +#define GPIO133_U0_RXD PIN_CFG(133, ALT_C) + +#define GPIO134_GPIO PIN_CFG(134, GPIO) +#define GPIO134_KP_I3 PIN_CFG(134, ALT_A) +#define GPIO134_ETM_D13 PIN_CFG(134, ALT_B) +#define GPIO134_STMAPE_DAT0 PIN_CFG(134, ALT_C) + +#define GPIO135_GPIO PIN_CFG(135, GPIO) +#define GPIO135_KP_O3 PIN_CFG(135, ALT_A) +#define GPIO135_ETM_D12 PIN_CFG(135, ALT_B) +#define GPIO135_STMAPE_DAT1 PIN_CFG(135, ALT_C) + +#define GPIO136_GPIO PIN_CFG(136, GPIO) +#define GPIO136_KP_I4 PIN_CFG(136, ALT_A) +#define GPIO136_ETM_D11 PIN_CFG(136, ALT_B) +#define GPIO136_STMAPE_DAT2 PIN_CFG(136, ALT_C) + +#define GPIO137_GPIO PIN_CFG(137, GPIO) +#define GPIO137_KP_O4 PIN_CFG(137, ALT_A) +#define GPIO137_ETM_D10 PIN_CFG(137, ALT_B) +#define GPIO137_STMAPE_DAT3 PIN_CFG(137, ALT_C) + +#define GPIO138_GPIO PIN_CFG(138, GPIO) +#define GPIO138_KP_I5 PIN_CFG(138, ALT_A) +#define GPIO138_ETM_D9 PIN_CFG(138, ALT_B) +#define GPIO138_U0_TXD PIN_CFG(138, ALT_C) + +#define GPIO139_GPIO PIN_CFG(139, GPIO) +#define GPIO139_KP_O5 PIN_CFG(139, ALT_A) +#define GPIO139_ETM_D8 PIN_CFG(139, ALT_B) +#define GPIO139_BUSMON_D11 PIN_CFG(139, ALT_C) + +#define GPIO140_GPIO PIN_CFG(140, GPIO) +#define GPIO140_KP_I6 PIN_CFG(140, ALT_A) +#define GPIO140_ETM_D7 PIN_CFG(140, ALT_B) +#define GPIO140_STMAPE_CLK PIN_CFG(140, ALT_C) + +#define GPIO141_GPIO PIN_CFG(141, GPIO) +#define GPIO141_KP_O6 PIN_CFG(141, ALT_A) +#define GPIO141_ETM_D6 PIN_CFG(141, ALT_B) +#define GPIO141_U0_RXD PIN_CFG(141, ALT_C) + +#define GPIO142_GPIO PIN_CFG(142, GPIO) +#define GPIO142_KP_I7 PIN_CFG(142, ALT_A) +#define GPIO142_ETM_D5 PIN_CFG(142, ALT_B) +#define GPIO142_STMAPE_DAT0 PIN_CFG(142, ALT_C) + +#define GPIO143_GPIO PIN_CFG(143, GPIO) +#define GPIO143_KP_O7 PIN_CFG(143, ALT_A) +#define GPIO143_ETM_D4 PIN_CFG(143, ALT_B) +#define GPIO143_STMAPE_DAT1 PIN_CFG(143, ALT_C) + +#define GPIO144_GPIO PIN_CFG(144, GPIO) +#define GPIO144_I2C3_SCL PIN_CFG(144, ALT_A) +#define GPIO144_ETM_D3 PIN_CFG(144, ALT_B) +#define GPIO144_STMAPE_DAT2 PIN_CFG(144, ALT_C) + +#define GPIO145_GPIO PIN_CFG(145, GPIO) +#define GPIO145_I2C3_SDA PIN_CFG(145, ALT_A) +#define GPIO145_ETM_D2 PIN_CFG(145, ALT_B) +#define GPIO145_STMAPE_DAT3 PIN_CFG(145, ALT_C) + +#define GPIO146_GPIO PIN_CFG(146, GPIO) +#define GPIO146_PWM_0 PIN_CFG(146, ALT_A) +#define GPIO146_ETM_D1 PIN_CFG(146, ALT_B) + +#define GPIO147_GPIO PIN_CFG(147, GPIO) +#define GPIO147_PWM_1 PIN_CFG(147, ALT_A) +#define GPIO147_ETM_D0 PIN_CFG(147, ALT_B) + +#define GPIO148_GPIO PIN_CFG(148, GPIO) +#define GPIO148_PWM_2 PIN_CFG(148, ALT_A) +#define GPIO148_ETM_CLK PIN_CFG(148, ALT_B) + +#define GPIO160_GPIO PIN_CFG(160, GPIO) +#define GPIO160_CLKOUT_REQn PIN_CFG(160, ALT_A) + +#define GPIO161_GPIO PIN_CFG(161, GPIO) +#define GPIO161_CLKOUT_0 PIN_CFG(161, ALT_A) + +#define GPIO162_GPIO PIN_CFG(162, GPIO) +#define GPIO162_CLKOUT_1 PIN_CFG(162, ALT_A) + +#define GPIO163_GPIO PIN_CFG(163, GPIO) + +#define GPIO164_GPIO PIN_CFG(164, GPIO) +#define GPIO164_GPS_START PIN_CFG(164, ALT_A) + +#define GPIO165_GPIO PIN_CFG(165, GPIO) +#define GPIO165_SPI1_CS2n PIN_CFG(165, ALT_A) +#define GPIO165_U3_RXD PIN_CFG(165, ALT_B) +#define GPIO165_BUSMON_D20 PIN_CFG(165, ALT_C) + +#define GPIO166_GPIO PIN_CFG(166, GPIO) +#define GPIO166_SPI1_CS1n PIN_CFG(166, ALT_A) +#define GPIO166_U3_TXD PIN_CFG(166, ALT_B) +#define GPIO166_BUSMON_D21 PIN_CFG(166, ALT_C) + +#define GPIO167_GPIO PIN_CFG(167, GPIO) +#define GPIO167_SPI1_CS0n PIN_CFG(167, ALT_A) +#define GPIO167_U3_RTSn PIN_CFG(167, ALT_B) +#define GPIO167_BUSMON_D22 PIN_CFG(167, ALT_C) + +#define GPIO168_GPIO PIN_CFG(168, GPIO) +#define GPIO168_SPI1_RXD PIN_CFG(168, ALT_A) +#define GPIO168_U3_CTSn PIN_CFG(168, ALT_B) +#define GPIO168_BUSMON_D23 PIN_CFG(168, ALT_C) + +#define GPIO169_GPIO PIN_CFG(169, GPIO) +#define GPIO169_SPI1_TXD PIN_CFG(169, ALT_A) +#define GPIO169_DDR_RC PIN_CFG(169, ALT_B) +#define GPIO169_BUSMON_D24 PIN_CFG(169, ALT_C) + +#define GPIO170_GPIO PIN_CFG(170, GPIO) +#define GPIO170_SPI1_CLK PIN_CFG(170, ALT_A) + +#define GPIO171_GPIO PIN_CFG(171, GPIO) +#define GPIO171_MC3_DAT0 PIN_CFG(171, ALT_A) +#define GPIO171_SPI3_RXD PIN_CFG(171, ALT_B) +#define GPIO171_BUSMON_D25 PIN_CFG(171, ALT_C) + +#define GPIO172_GPIO PIN_CFG(172, GPIO) +#define GPIO172_MC3_DAT1 PIN_CFG(172, ALT_A) +#define GPIO172_SPI3_CS1n PIN_CFG(172, ALT_B) +#define GPIO172_BUSMON_D26 PIN_CFG(172, ALT_C) + +#define GPIO173_GPIO PIN_CFG(173, GPIO) +#define GPIO173_MC3_DAT2 PIN_CFG(173, ALT_A) +#define GPIO173_SPI3_CS2n PIN_CFG(173, ALT_B) +#define GPIO173_BUSMON_D27 PIN_CFG(173, ALT_C) + +#define GPIO174_GPIO PIN_CFG(174, GPIO) +#define GPIO174_MC3_DAT3 PIN_CFG(174, ALT_A) +#define GPIO174_SPI3_CS0n PIN_CFG(174, ALT_B) +#define GPIO174_BUSMON_D28 PIN_CFG(174, ALT_C) + +#define GPIO175_GPIO PIN_CFG(175, GPIO) +#define GPIO175_MC3_CMD PIN_CFG(175, ALT_A) +#define GPIO175_SPI3_TXD PIN_CFG(175, ALT_B) +#define GPIO175_BUSMON_D29 PIN_CFG(175, ALT_C) + +#define GPIO176_GPIO PIN_CFG(176, GPIO) +#define GPIO176_MC3_CLK PIN_CFG(176, ALT_A) +#define GPIO176_SPI3_CLK PIN_CFG(176, ALT_B) + +#define GPIO177_GPIO PIN_CFG(177, GPIO) +#define GPIO177_U2_RXD PIN_CFG(177, ALT_A) +#define GPIO177_I2C3_SCL PIN_CFG(177, ALT_B) +#define GPIO177_BUSMON_D30 PIN_CFG(177, ALT_C) + +#define GPIO178_GPIO PIN_CFG(178, GPIO) +#define GPIO178_U2_TXD PIN_CFG(178, ALT_A) +#define GPIO178_I2C3_SDA PIN_CFG(178, ALT_B) +#define GPIO178_BUSMON_D31 PIN_CFG(178, ALT_C) + +#define GPIO179_GPIO PIN_CFG(179, GPIO) +#define GPIO179_U2_CTSn PIN_CFG(179, ALT_A) +#define GPIO179_U3_RXD PIN_CFG(179, ALT_B) +#define GPIO179_BUSMON_D32 PIN_CFG(179, ALT_C) + +#define GPIO180_GPIO PIN_CFG(180, GPIO) +#define GPIO180_U2_RTSn PIN_CFG(180, ALT_A) +#define GPIO180_U3_TXD PIN_CFG(180, ALT_B) +#define GPIO180_BUSMON_D33 PIN_CFG(180, ALT_C) + +#define GPIO185_GPIO PIN_CFG(185, GPIO) +#define GPIO185_SPI3_CS2n PIN_CFG(185, ALT_A) +#define GPIO185_MC4_DAT0 PIN_CFG(185, ALT_B) + +#define GPIO186_GPIO PIN_CFG(186, GPIO) +#define GPIO186_SPI3_CS1n PIN_CFG(186, ALT_A) +#define GPIO186_MC4_DAT1 PIN_CFG(186, ALT_B) + +#define GPIO187_GPIO PIN_CFG(187, GPIO) +#define GPIO187_SPI3_CS0n PIN_CFG(187, ALT_A) +#define GPIO187_MC4_DAT2 PIN_CFG(187, ALT_B) + +#define GPIO188_GPIO PIN_CFG(188, GPIO) +#define GPIO188_SPI3_RXD PIN_CFG(188, ALT_A) +#define GPIO188_MC4_DAT3 PIN_CFG(188, ALT_B) + +#define GPIO189_GPIO PIN_CFG(189, GPIO) +#define GPIO189_SPI3_TXD PIN_CFG(189, ALT_A) +#define GPIO189_MC4_CMD PIN_CFG(189, ALT_B) + +#define GPIO190_GPIO PIN_CFG(190, GPIO) +#define GPIO190_SPI3_CLK PIN_CFG(190, ALT_A) +#define GPIO190_MC4_CLK PIN_CFG(190, ALT_B) + +#define GPIO191_GPIO PIN_CFG(191, GPIO) +#define GPIO191_MC1_DAT0 PIN_CFG(191, ALT_A) +#define GPIO191_MC4_DAT4 PIN_CFG(191, ALT_B) +#define GPIO191_STMAPE_DAT0 PIN_CFG(191, ALT_C) + +#define GPIO192_GPIO PIN_CFG(192, GPIO) +#define GPIO192_MC1_DAT1 PIN_CFG(192, ALT_A) +#define GPIO192_MC4_DAT5 PIN_CFG(192, ALT_B) +#define GPIO192_STMAPE_DAT1 PIN_CFG(192, ALT_C) + +#define GPIO193_GPIO PIN_CFG(193, GPIO) +#define GPIO193_MC1_DAT2 PIN_CFG(193, ALT_A) +#define GPIO193_MC4_DAT6 PIN_CFG(193, ALT_B) +#define GPIO193_STMAPE_DAT2 PIN_CFG(193, ALT_C) + +#define GPIO194_GPIO PIN_CFG(194, GPIO) +#define GPIO194_MC1_DAT3 PIN_CFG(194, ALT_A) +#define GPIO194_MC4_DAT7 PIN_CFG(194, ALT_B) +#define GPIO194_STMAPE_DAT3 PIN_CFG(194, ALT_C) + +#define GPIO195_GPIO PIN_CFG(195, GPIO) +#define GPIO195_MC1_CLK PIN_CFG(195, ALT_A) +#define GPIO195_STMAPE_CLK PIN_CFG(195, ALT_B) +#define GPIO195_BUSMON_CLK PIN_CFG(195, ALT_C) + +#define GPIO196_GPIO PIN_CFG(196, GPIO) +#define GPIO196_MC1_CMD PIN_CFG(196, ALT_A) +#define GPIO196_U0_RXD PIN_CFG(196, ALT_B) +#define GPIO196_BUSMON_D38 PIN_CFG(196, ALT_C) + +#define GPIO197_GPIO PIN_CFG(197, GPIO) +#define GPIO197_MC1_CMDDIR PIN_CFG(197, ALT_A) +#define GPIO197_BUSMON_D39 PIN_CFG(197, ALT_B) + +#define GPIO198_GPIO PIN_CFG(198, GPIO) +#define GPIO198_MC1_FBCLK PIN_CFG(198, ALT_A) + +#define GPIO199_GPIO PIN_CFG(199, GPIO) +#define GPIO199_MC1_DAT0DIR PIN_CFG(199, ALT_A) +#define GPIO199_BUSMON_D40 PIN_CFG(199, ALT_B) + +#define GPIO200_GPIO PIN_CFG(200, GPIO) +#define GPIO200_U1_TXD PIN_CFG(200, ALT_A) +#define GPIO200_ACCU0_RTSn PIN_CFG(200, ALT_B) + +#define GPIO201_GPIO PIN_CFG(201, GPIO) +#define GPIO201_U1_RXD PIN_CFG(201, ALT_A) +#define GPIO201_ACCU0_CTSn PIN_CFG(201, ALT_B) + +#define GPIO202_GPIO PIN_CFG(202, GPIO) +#define GPIO202_U1_CTSn PIN_CFG(202, ALT_A) +#define GPIO202_ACCU0_RXD PIN_CFG(202, ALT_B) + +#define GPIO203_GPIO PIN_CFG(203, GPIO) +#define GPIO203_U1_RTSn PIN_CFG(203, ALT_A) +#define GPIO203_ACCU0_TXD PIN_CFG(203, ALT_B) + +#define GPIO204_GPIO PIN_CFG(204, GPIO) +#define GPIO204_SPI0_CS2n PIN_CFG(204, ALT_A) +#define GPIO204_ACCGPIO_000 PIN_CFG(204, ALT_B) +#define GPIO204_LCD_VSI1 PIN_CFG(204, ALT_C) + +#define GPIO205_GPIO PIN_CFG(205, GPIO) +#define GPIO205_SPI0_CS1n PIN_CFG(205, ALT_A) +#define GPIO205_ACCGPIO_001 PIN_CFG(205, ALT_B) +#define GPIO205_LCD_D3 PIN_CFG(205, ALT_C) + +#define GPIO206_GPIO PIN_CFG(206, GPIO) +#define GPIO206_SPI0_CS0n PIN_CFG(206, ALT_A) +#define GPIO206_ACCGPIO_002 PIN_CFG(206, ALT_B) +#define GPIO206_LCD_D2 PIN_CFG(206, ALT_C) + +#define GPIO207_GPIO PIN_CFG(207, GPIO) +#define GPIO207_SPI0_RXD PIN_CFG(207, ALT_A) +#define GPIO207_ACCGPIO_003 PIN_CFG(207, ALT_B) +#define GPIO207_LCD_D1 PIN_CFG(207, ALT_C) + +#define GPIO208_GPIO PIN_CFG(208, GPIO) +#define GPIO208_SPI0_TXD PIN_CFG(208, ALT_A) +#define GPIO208_ACCGPIO_004 PIN_CFG(208, ALT_B) +#define GPIO208_LCD_D0 PIN_CFG(208, ALT_C) + +#define GPIO209_GPIO PIN_CFG(209, GPIO) +#define GPIO209_SPI0_CLK PIN_CFG(209, ALT_A) +#define GPIO209_ACCGPIO_005 PIN_CFG(209, ALT_B) +#define GPIO209_LCD_CLK PIN_CFG(209, ALT_C) + +#define GPIO210_GPIO PIN_CFG(210, GPIO) +#define GPIO210_LCD_VSO PIN_CFG(210, ALT_A) +#define GPIO210_PRCMU_PWRCTRL1 PIN_CFG(210, ALT_B) + +#define GPIO211_GPIO PIN_CFG(211, GPIO) +#define GPIO211_LCD_VSI0 PIN_CFG(211, ALT_A) +#define GPIO211_PRCMU_PWRCTRL2 PIN_CFG(211, ALT_B) + +#define GPIO212_GPIO PIN_CFG(212, GPIO) +#define GPIO212_SPI2_CS2n PIN_CFG(212, ALT_A) +#define GPIO212_LCD_HSO PIN_CFG(212, ALT_B) + +#define GPIO213_GPIO PIN_CFG(213, GPIO) +#define GPIO213_SPI2_CS1n PIN_CFG(213, ALT_A) +#define GPIO213_LCD_DE PIN_CFG(213, ALT_B) +#define GPIO213_BUSMON_D16 PIN_CFG(213, ALT_C) + +#define GPIO214_GPIO PIN_CFG(214, GPIO) +#define GPIO214_SPI2_CS0n PIN_CFG(214, ALT_A) +#define GPIO214_LCD_D7 PIN_CFG(214, ALT_B) +#define GPIO214_BUSMON_D17 PIN_CFG(214, ALT_C) + +#define GPIO215_GPIO PIN_CFG(215, GPIO) +#define GPIO215_SPI2_RXD PIN_CFG(215, ALT_A) +#define GPIO215_LCD_D6 PIN_CFG(215, ALT_B) +#define GPIO215_BUSMON_D18 PIN_CFG(215, ALT_C) + +#define GPIO216_GPIO PIN_CFG(216, GPIO) +#define GPIO216_SPI2_CLK PIN_CFG(216, ALT_A) +#define GPIO216_LCD_D5 PIN_CFG(216, ALT_B) + +#define GPIO217_GPIO PIN_CFG(217, GPIO) +#define GPIO217_SPI2_TXD PIN_CFG(217, ALT_A) +#define GPIO217_LCD_D4 PIN_CFG(217, ALT_B) +#define GPIO217_BUSMON_D19 PIN_CFG(217, ALT_C) + +#define GPIO218_GPIO PIN_CFG(218, GPIO) +#define GPIO218_I2C2_SCL PIN_CFG(218, ALT_A) +#define GPIO218_LCD_VSO PIN_CFG(218, ALT_B) + +#define GPIO219_GPIO PIN_CFG(219, GPIO) +#define GPIO219_I2C2_SDA PIN_CFG(219, ALT_A) +#define GPIO219_LCD_D3 PIN_CFG(219, ALT_B) + +#define GPIO220_GPIO PIN_CFG(220, GPIO) +#define GPIO220_MSP2_TCK PIN_CFG(220, ALT_A) +#define GPIO220_LCD_D2 PIN_CFG(220, ALT_B) + +#define GPIO221_GPIO PIN_CFG(221, GPIO) +#define GPIO221_MSP2_TFS PIN_CFG(221, ALT_A) +#define GPIO221_LCD_D1 PIN_CFG(221, ALT_B) + +#define GPIO222_GPIO PIN_CFG(222, GPIO) +#define GPIO222_MSP2_TXD PIN_CFG(222, ALT_A) +#define GPIO222_LCD_D0 PIN_CFG(222, ALT_B) + +#define GPIO223_GPIO PIN_CFG(223, GPIO) +#define GPIO223_MSP2_RXD PIN_CFG(223, ALT_A) +#define GPIO223_LCD_CLK PIN_CFG(223, ALT_B) + +#define GPIO224_GPIO PIN_CFG(224, GPIO) +#define GPIO224_PRCMU_PWRCTRL0 PIN_CFG(224, ALT_A) +#define GPIO224_LCD_VSI1 PIN_CFG(224, ALT_B) + +#define GPIO225_GPIO PIN_CFG(225, GPIO) +#define GPIO225_PRCMU_PWRCTRL1 PIN_CFG(225, ALT_A) +#define GPIO225_IRDA_RXD PIN_CFG(225, ALT_B) + +#define GPIO226_GPIO PIN_CFG(226, GPIO) +#define GPIO226_PRCMU_PWRCTRL2 PIN_CFG(226, ALT_A) +#define GPIO226_IRRC_DAT PIN_CFG(226, ALT_B) + +#define GPIO227_GPIO PIN_CFG(227, GPIO) +#define GPIO227_IRRC_DAT PIN_CFG(227, ALT_A) +#define GPIO227_IRDA_TXD PIN_CFG(227, ALT_B) + +#endif diff --git a/arch/arm/mach-ux500/pins-db8500.h b/arch/arm/mach-ux500/pins-db8500.h index 9055d5d3233c..66f8761cc823 100644 --- a/arch/arm/mach-ux500/pins-db8500.h +++ b/arch/arm/mach-ux500/pins-db8500.h @@ -96,57 +96,57 @@ #define GPIO17_SLIM0_CLK PIN_CFG(17, ALT_C) #define GPIO18_GPIO PIN_CFG(18, GPIO) -#define GPIO18_MC0_CMDDIR PIN_CFG(18, ALT_A) +#define GPIO18_MC0_CMDDIR PIN_CFG_PULL(18, ALT_A, UP) #define GPIO18_U2_RXD PIN_CFG(18, ALT_B) #define GPIO18_MS_IEP PIN_CFG(18, ALT_C) #define GPIO19_GPIO PIN_CFG(19, GPIO) -#define GPIO19_MC0_DAT0DIR PIN_CFG(19, ALT_A) +#define GPIO19_MC0_DAT0DIR PIN_CFG_PULL(19, ALT_A, UP) #define GPIO19_U2_TXD PIN_CFG(19, ALT_B) #define GPIO19_MS_DAT0DIR PIN_CFG(19, ALT_C) #define GPIO20_GPIO PIN_CFG(20, GPIO) -#define GPIO20_MC0_DAT2DIR PIN_CFG(20, ALT_A) +#define GPIO20_MC0_DAT2DIR PIN_CFG_PULL(20, ALT_A, UP) #define GPIO20_UARTMOD_TXD PIN_CFG(20, ALT_B) #define GPIO20_IP_TRIGOUT PIN_CFG(20, ALT_C) #define GPIO21_GPIO PIN_CFG(21, GPIO) -#define GPIO21_MC0_DAT31DIR PIN_CFG(21, ALT_A) +#define GPIO21_MC0_DAT31DIR PIN_CFG_PULL(21, ALT_A, UP) #define GPIO21_MSP0_SCK PIN_CFG(21, ALT_B) #define GPIO21_MS_DAT31DIR PIN_CFG(21, ALT_C) #define GPIO22_GPIO PIN_CFG(22, GPIO) -#define GPIO22_MC0_FBCLK PIN_CFG(22, ALT_A) +#define GPIO22_MC0_FBCLK PIN_CFG_PULL(22, ALT_A, UP) #define GPIO22_UARTMOD_RXD PIN_CFG(22, ALT_B) #define GPIO22_MS_FBCLK PIN_CFG(22, ALT_C) #define GPIO23_GPIO PIN_CFG(23, GPIO) -#define GPIO23_MC0_CLK PIN_CFG(23, ALT_A) +#define GPIO23_MC0_CLK PIN_CFG_PULL(23, ALT_A, UP) #define GPIO23_STMMOD_CLK PIN_CFG(23, ALT_B) #define GPIO23_MS_CLK PIN_CFG(23, ALT_C) #define GPIO24_GPIO PIN_CFG(24, GPIO) -#define GPIO24_MC0_CMD PIN_CFG(24, ALT_A) +#define GPIO24_MC0_CMD PIN_CFG_PULL(24, ALT_A, UP) #define GPIO24_UARTMOD_RXD PIN_CFG(24, ALT_B) #define GPIO24_MS_BS PIN_CFG(24, ALT_C) #define GPIO25_GPIO PIN_CFG(25, GPIO) -#define GPIO25_MC0_DAT0 PIN_CFG(25, ALT_A) +#define GPIO25_MC0_DAT0 PIN_CFG_PULL(25, ALT_A, UP) #define GPIO25_STMMOD_DAT0 PIN_CFG(25, ALT_B) #define GPIO25_MS_DAT0 PIN_CFG(25, ALT_C) #define GPIO26_GPIO PIN_CFG(26, GPIO) -#define GPIO26_MC0_DAT1 PIN_CFG(26, ALT_A) +#define GPIO26_MC0_DAT1 PIN_CFG_PULL(26, ALT_A, UP) #define GPIO26_STMMOD_DAT1 PIN_CFG(26, ALT_B) #define GPIO26_MS_DAT1 PIN_CFG(26, ALT_C) #define GPIO27_GPIO PIN_CFG(27, GPIO) -#define GPIO27_MC0_DAT2 PIN_CFG(27, ALT_A) +#define GPIO27_MC0_DAT2 PIN_CFG_PULL(27, ALT_A, UP) #define GPIO27_STMMOD_DAT2 PIN_CFG(27, ALT_B) #define GPIO27_MS_DAT2 PIN_CFG(27, ALT_C) #define GPIO28_GPIO PIN_CFG(28, GPIO) -#define GPIO28_MC0_DAT3 PIN_CFG(28, ALT_A) +#define GPIO28_MC0_DAT3 PIN_CFG_PULL(28, ALT_A, UP) #define GPIO28_STMMOD_DAT3 PIN_CFG(28, ALT_B) #define GPIO28_MS_DAT3 PIN_CFG(28, ALT_C) @@ -357,48 +357,48 @@ #define GPIO97_MC5_DAT7 PIN_CFG(97, ALT_C) #define GPIO128_GPIO PIN_CFG(128, GPIO) -#define GPIO128_MC2_CLK PIN_CFG(128, ALT_A) +#define GPIO128_MC2_CLK PIN_CFG_PULL(128, ALT_A, UP) #define GPIO128_SM_CKO PIN_CFG(128, ALT_B) #define GPIO129_GPIO PIN_CFG(129, GPIO) -#define GPIO129_MC2_CMD PIN_CFG(129, ALT_A) +#define GPIO129_MC2_CMD PIN_CFG_PULL(129, ALT_A, UP) #define GPIO129_SM_WAIT0n PIN_CFG(129, ALT_B) #define GPIO130_GPIO PIN_CFG(130, GPIO) -#define GPIO130_MC2_FBCLK PIN_CFG(130, ALT_A) +#define GPIO130_MC2_FBCLK PIN_CFG_PULL(130, ALT_A, UP) #define GPIO130_SM_FBCLK PIN_CFG(130, ALT_B) #define GPIO130_MC2_RSTN PIN_CFG(130, ALT_C) #define GPIO131_GPIO PIN_CFG(131, GPIO) -#define GPIO131_MC2_DAT0 PIN_CFG(131, ALT_A) +#define GPIO131_MC2_DAT0 PIN_CFG_PULL(131, ALT_A, UP) #define GPIO131_SM_ADQ8 PIN_CFG(131, ALT_B) #define GPIO132_GPIO PIN_CFG(132, GPIO) -#define GPIO132_MC2_DAT1 PIN_CFG(132, ALT_A) +#define GPIO132_MC2_DAT1 PIN_CFG_PULL(132, ALT_A, UP) #define GPIO132_SM_ADQ9 PIN_CFG(132, ALT_B) #define GPIO133_GPIO PIN_CFG(133, GPIO) -#define GPIO133_MC2_DAT2 PIN_CFG(133, ALT_A) +#define GPIO133_MC2_DAT2 PIN_CFG_PULL(133, ALT_A, UP) #define GPIO133_SM_ADQ10 PIN_CFG(133, ALT_B) #define GPIO134_GPIO PIN_CFG(134, GPIO) -#define GPIO134_MC2_DAT3 PIN_CFG(134, ALT_A) +#define GPIO134_MC2_DAT3 PIN_CFG_PULL(134, ALT_A, UP) #define GPIO134_SM_ADQ11 PIN_CFG(134, ALT_B) #define GPIO135_GPIO PIN_CFG(135, GPIO) -#define GPIO135_MC2_DAT4 PIN_CFG(135, ALT_A) +#define GPIO135_MC2_DAT4 PIN_CFG_PULL(135, ALT_A, UP) #define GPIO135_SM_ADQ12 PIN_CFG(135, ALT_B) #define GPIO136_GPIO PIN_CFG(136, GPIO) -#define GPIO136_MC2_DAT5 PIN_CFG(136, ALT_A) +#define GPIO136_MC2_DAT5 PIN_CFG_PULL(136, ALT_A, UP) #define GPIO136_SM_ADQ13 PIN_CFG(136, ALT_B) #define GPIO137_GPIO PIN_CFG(137, GPIO) -#define GPIO137_MC2_DAT6 PIN_CFG(137, ALT_A) +#define GPIO137_MC2_DAT6 PIN_CFG_PULL(137, ALT_A, UP) #define GPIO137_SM_ADQ14 PIN_CFG(137, ALT_B) #define GPIO138_GPIO PIN_CFG(138, GPIO) -#define GPIO138_MC2_DAT7 PIN_CFG(138, ALT_A) +#define GPIO138_MC2_DAT7 PIN_CFG_PULL(138, ALT_A, UP) #define GPIO138_SM_ADQ15 PIN_CFG(138, ALT_B) #define GPIO139_GPIO PIN_CFG(139, GPIO) @@ -569,39 +569,39 @@ #define GPIO196_MSP2_RXD PIN_CFG(196, ALT_A) #define GPIO197_GPIO PIN_CFG(197, GPIO) -#define GPIO197_MC4_DAT3 PIN_CFG(197, ALT_A) +#define GPIO197_MC4_DAT3 PIN_CFG_PULL(197, ALT_A, UP) #define GPIO198_GPIO PIN_CFG(198, GPIO) -#define GPIO198_MC4_DAT2 PIN_CFG(198, ALT_A) +#define GPIO198_MC4_DAT2 PIN_CFG_PULL(198, ALT_A, UP) #define GPIO199_GPIO PIN_CFG(199, GPIO) -#define GPIO199_MC4_DAT1 PIN_CFG(199, ALT_A) +#define GPIO199_MC4_DAT1 PIN_CFG_PULL(199, ALT_A, UP) #define GPIO200_GPIO PIN_CFG(200, GPIO) -#define GPIO200_MC4_DAT0 PIN_CFG(200, ALT_A) +#define GPIO200_MC4_DAT0 PIN_CFG_PULL(200, ALT_A, UP) #define GPIO201_GPIO PIN_CFG(201, GPIO) -#define GPIO201_MC4_CMD PIN_CFG(201, ALT_A) +#define GPIO201_MC4_CMD PIN_CFG_PULL(201, ALT_A, UP) #define GPIO202_GPIO PIN_CFG(202, GPIO) -#define GPIO202_MC4_FBCLK PIN_CFG(202, ALT_A) +#define GPIO202_MC4_FBCLK PIN_CFG_PULL(202, ALT_A, UP) #define GPIO202_PWL PIN_CFG(202, ALT_B) #define GPIO202_MC4_RSTN PIN_CFG(202, ALT_C) #define GPIO203_GPIO PIN_CFG(203, GPIO) -#define GPIO203_MC4_CLK PIN_CFG(203, ALT_A) +#define GPIO203_MC4_CLK PIN_CFG_PULL(203, ALT_A, UP) #define GPIO204_GPIO PIN_CFG(204, GPIO) -#define GPIO204_MC4_DAT7 PIN_CFG(204, ALT_A) +#define GPIO204_MC4_DAT7 PIN_CFG_PULL(204, ALT_A, UP) #define GPIO205_GPIO PIN_CFG(205, GPIO) -#define GPIO205_MC4_DAT6 PIN_CFG(205, ALT_A) +#define GPIO205_MC4_DAT6 PIN_CFG_PULL(205, ALT_A, UP) #define GPIO206_GPIO PIN_CFG(206, GPIO) -#define GPIO206_MC4_DAT5 PIN_CFG(206, ALT_A) +#define GPIO206_MC4_DAT5 PIN_CFG_PULL(206, ALT_A, UP) #define GPIO207_GPIO PIN_CFG(207, GPIO) -#define GPIO207_MC4_DAT4 PIN_CFG(207, ALT_A) +#define GPIO207_MC4_DAT4 PIN_CFG_PULL(207, ALT_A, UP) #define GPIO208_GPIO PIN_CFG(208, GPIO) #define GPIO208_MC1_CLK PIN_CFG(208, ALT_A) diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index 438ef16aec90..9e4c678de785 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -78,6 +78,8 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); outer_clean_range(__pa(&pen_release), __pa(&pen_release) + 1); + smp_cross_call(cpumask_of(cpu)); + timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { if (pen_release == -1) diff --git a/arch/arm/mach-ux500/prcmu.c b/arch/arm/mach-ux500/prcmu.c new file mode 100644 index 000000000000..293274d1342a --- /dev/null +++ b/arch/arm/mach-ux500/prcmu.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) ST Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> + * + * U8500 PRCMU driver. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/jiffies.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> + +#include <mach/hardware.h> +#include <mach/prcmu-regs.h> + +#define PRCMU_TCDM_BASE __io_address(U8500_PRCMU_TCDM_BASE) + +#define REQ_MB5 (PRCMU_TCDM_BASE + 0xE44) +#define ACK_MB5 (PRCMU_TCDM_BASE + 0xDF4) + +#define REQ_MB5_I2C_SLAVE_OP (REQ_MB5) +#define REQ_MB5_I2C_HW_BITS (REQ_MB5 + 1) +#define REQ_MB5_I2C_REG (REQ_MB5 + 2) +#define REQ_MB5_I2C_VAL (REQ_MB5 + 3) + +#define ACK_MB5_I2C_STATUS (ACK_MB5 + 1) +#define ACK_MB5_I2C_VAL (ACK_MB5 + 3) + +#define I2C_WRITE(slave) ((slave) << 1) +#define I2C_READ(slave) (((slave) << 1) | BIT(0)) +#define I2C_STOP_EN BIT(3) + +enum ack_mb5_status { + I2C_WR_OK = 0x01, + I2C_RD_OK = 0x02, +}; + +#define MBOX_BIT BIT +#define NUM_MBOX 8 + +static struct { + struct mutex lock; + struct completion work; + bool failed; + struct { + u8 status; + u8 value; + } ack; +} mb5_transfer; + +/** + * prcmu_abb_read() - Read register value(s) from the ABB. + * @slave: The I2C slave address. + * @reg: The (start) register address. + * @value: The read out value(s). + * @size: The number of registers to read. + * + * Reads register value(s) from the ABB. + * @size has to be 1 for the current firmware version. + */ +int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) +{ + int r; + + if (size != 1) + return -EINVAL; + + r = mutex_lock_interruptible(&mb5_transfer.lock); + if (r) + return r; + + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) + cpu_relax(); + + writeb(I2C_READ(slave), REQ_MB5_I2C_SLAVE_OP); + writeb(I2C_STOP_EN, REQ_MB5_I2C_HW_BITS); + writeb(reg, REQ_MB5_I2C_REG); + + writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); + if (!wait_for_completion_timeout(&mb5_transfer.work, + msecs_to_jiffies(500))) { + pr_err("prcmu: prcmu_abb_read timed out.\n"); + r = -EIO; + goto unlock_and_return; + } + r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO); + if (!r) + *value = mb5_transfer.ack.value; + +unlock_and_return: + mutex_unlock(&mb5_transfer.lock); + return r; +} +EXPORT_SYMBOL(prcmu_abb_read); + +/** + * prcmu_abb_write() - Write register value(s) to the ABB. + * @slave: The I2C slave address. + * @reg: The (start) register address. + * @value: The value(s) to write. + * @size: The number of registers to write. + * + * Reads register value(s) from the ABB. + * @size has to be 1 for the current firmware version. + */ +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +{ + int r; + + if (size != 1) + return -EINVAL; + + r = mutex_lock_interruptible(&mb5_transfer.lock); + if (r) + return r; + + + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) + cpu_relax(); + + writeb(I2C_WRITE(slave), REQ_MB5_I2C_SLAVE_OP); + writeb(I2C_STOP_EN, REQ_MB5_I2C_HW_BITS); + writeb(reg, REQ_MB5_I2C_REG); + writeb(*value, REQ_MB5_I2C_VAL); + + writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); + if (!wait_for_completion_timeout(&mb5_transfer.work, + msecs_to_jiffies(500))) { + pr_err("prcmu: prcmu_abb_write timed out.\n"); + r = -EIO; + goto unlock_and_return; + } + r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO); + +unlock_and_return: + mutex_unlock(&mb5_transfer.lock); + return r; +} +EXPORT_SYMBOL(prcmu_abb_write); + +static void read_mailbox_0(void) +{ + writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_1(void) +{ + writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_2(void) +{ + writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_3(void) +{ + writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_4(void) +{ + writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_5(void) +{ + mb5_transfer.ack.status = readb(ACK_MB5_I2C_STATUS); + mb5_transfer.ack.value = readb(ACK_MB5_I2C_VAL); + complete(&mb5_transfer.work); + writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_6(void) +{ + writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); +} + +static void read_mailbox_7(void) +{ + writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR); +} + +static void (* const read_mailbox[NUM_MBOX])(void) = { + read_mailbox_0, + read_mailbox_1, + read_mailbox_2, + read_mailbox_3, + read_mailbox_4, + read_mailbox_5, + read_mailbox_6, + read_mailbox_7 +}; + +static irqreturn_t prcmu_irq_handler(int irq, void *data) +{ + u32 bits; + u8 n; + + bits = (readl(PRCM_ARM_IT1_VAL) & (MBOX_BIT(NUM_MBOX) - 1)); + if (unlikely(!bits)) + return IRQ_NONE; + + for (n = 0; bits; n++) { + if (bits & MBOX_BIT(n)) { + bits -= MBOX_BIT(n); + read_mailbox[n](); + } + } + return IRQ_HANDLED; +} + +static int __init prcmu_init(void) +{ + mutex_init(&mb5_transfer.lock); + init_completion(&mb5_transfer.work); + + /* Clean up the mailbox interrupts after pre-kernel code. */ + writel((MBOX_BIT(NUM_MBOX) - 1), PRCM_ARM_IT1_CLEAR); + + return request_irq(IRQ_PRCMU, prcmu_irq_handler, 0, "prcmu", NULL); +} + +arch_initcall(prcmu_init); diff --git a/arch/arm/mach-ux500/ste-dma40-db5500.h b/arch/arm/mach-ux500/ste-dma40-db5500.h new file mode 100644 index 000000000000..cb2110c32858 --- /dev/null +++ b/arch/arm/mach-ux500/ste-dma40-db5500.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * + * DB5500-SoC-specific configuration for DMA40 + */ + +#ifndef STE_DMA40_DB5500_H +#define STE_DMA40_DB5500_H + +#define DB5500_DMA_NR_DEV 64 + +enum dma_src_dev_type { + DB5500_DMA_DEV0_SPI0_RX = 0, + DB5500_DMA_DEV1_SPI1_RX = 1, + DB5500_DMA_DEV2_SPI2_RX = 2, + DB5500_DMA_DEV3_SPI3_RX = 3, + DB5500_DMA_DEV4_USB_OTG_IEP_1_9 = 4, + DB5500_DMA_DEV5_USB_OTG_IEP_2_10 = 5, + DB5500_DMA_DEV6_USB_OTG_IEP_3_11 = 6, + DB5500_DMA_DEV7_IRDA_RFS = 7, + DB5500_DMA_DEV8_IRDA_FIFO_RX = 8, + DB5500_DMA_DEV9_MSP0_RX = 9, + DB5500_DMA_DEV10_MSP1_RX = 10, + DB5500_DMA_DEV11_MSP2_RX = 11, + DB5500_DMA_DEV12_UART0_RX = 12, + DB5500_DMA_DEV13_UART1_RX = 13, + DB5500_DMA_DEV14_UART2_RX = 14, + DB5500_DMA_DEV15_UART3_RX = 15, + DB5500_DMA_DEV16_USB_OTG_IEP_8 = 16, + DB5500_DMA_DEV17_USB_OTG_IEP_1_9 = 17, + DB5500_DMA_DEV18_USB_OTG_IEP_2_10 = 18, + DB5500_DMA_DEV19_USB_OTG_IEP_3_11 = 19, + DB5500_DMA_DEV20_USB_OTG_IEP_4_12 = 20, + DB5500_DMA_DEV21_USB_OTG_IEP_5_13 = 21, + DB5500_DMA_DEV22_USB_OTG_IEP_6_14 = 22, + DB5500_DMA_DEV23_USB_OTG_IEP_7_15 = 23, + DB5500_DMA_DEV24_SDMMC0_RX = 24, + DB5500_DMA_DEV25_SDMMC1_RX = 25, + DB5500_DMA_DEV26_SDMMC2_RX = 26, + DB5500_DMA_DEV27_SDMMC3_RX = 27, + DB5500_DMA_DEV28_SDMMC4_RX = 28, + /* 29 - 32 not used */ + DB5500_DMA_DEV33_SDMMC0_RX = 33, + DB5500_DMA_DEV34_SDMMC1_RX = 34, + DB5500_DMA_DEV35_SDMMC2_RX = 35, + DB5500_DMA_DEV36_SDMMC3_RX = 36, + DB5500_DMA_DEV37_SDMMC4_RX = 37, + DB5500_DMA_DEV38_USB_OTG_IEP_8 = 38, + DB5500_DMA_DEV39_USB_OTG_IEP_1_9 = 39, + DB5500_DMA_DEV40_USB_OTG_IEP_2_10 = 40, + DB5500_DMA_DEV41_USB_OTG_IEP_3_11 = 41, + DB5500_DMA_DEV42_USB_OTG_IEP_4_12 = 42, + DB5500_DMA_DEV43_USB_OTG_IEP_5_13 = 43, + DB5500_DMA_DEV44_USB_OTG_IEP_6_14 = 44, + DB5500_DMA_DEV45_USB_OTG_IEP_7_15 = 45, + /* 46 not used */ + DB5500_DMA_DEV47_MCDE_RX = 47, + DB5500_DMA_DEV48_CRYPTO1_RX = 48, + /* 49, 50 not used */ + DB5500_DMA_DEV49_I2C1_RX = 51, + DB5500_DMA_DEV50_I2C3_RX = 52, + DB5500_DMA_DEV51_I2C2_RX = 53, + /* 54 - 60 not used */ + DB5500_DMA_DEV61_CRYPTO0_RX = 61, + /* 62, 63 not used */ +}; + +enum dma_dest_dev_type { + DB5500_DMA_DEV0_SPI0_TX = 0, + DB5500_DMA_DEV1_SPI1_TX = 1, + DB5500_DMA_DEV2_SPI2_TX = 2, + DB5500_DMA_DEV3_SPI3_TX = 3, + DB5500_DMA_DEV4_USB_OTG_OEP_1_9 = 4, + DB5500_DMA_DEV5_USB_OTG_OEP_2_10 = 5, + DB5500_DMA_DEV6_USB_OTG_OEP_3_11 = 6, + DB5500_DMA_DEV7_IRRC_TX = 7, + DB5500_DMA_DEV8_IRDA_FIFO_TX = 8, + DB5500_DMA_DEV9_MSP0_TX = 9, + DB5500_DMA_DEV10_MSP1_TX = 10, + DB5500_DMA_DEV11_MSP2_TX = 11, + DB5500_DMA_DEV12_UART0_TX = 12, + DB5500_DMA_DEV13_UART1_TX = 13, + DB5500_DMA_DEV14_UART2_TX = 14, + DB5500_DMA_DEV15_UART3_TX = 15, + DB5500_DMA_DEV16_USB_OTG_OEP_8 = 16, + DB5500_DMA_DEV17_USB_OTG_OEP_1_9 = 17, + DB5500_DMA_DEV18_USB_OTG_OEP_2_10 = 18, + DB5500_DMA_DEV19_USB_OTG_OEP_3_11 = 19, + DB5500_DMA_DEV20_USB_OTG_OEP_4_12 = 20, + DB5500_DMA_DEV21_USB_OTG_OEP_5_13 = 21, + DB5500_DMA_DEV22_USB_OTG_OEP_6_14 = 22, + DB5500_DMA_DEV23_USB_OTG_OEP_7_15 = 23, + DB5500_DMA_DEV24_SDMMC0_TX = 24, + DB5500_DMA_DEV25_SDMMC1_TX = 25, + DB5500_DMA_DEV26_SDMMC2_TX = 26, + DB5500_DMA_DEV27_SDMMC3_TX = 27, + DB5500_DMA_DEV28_SDMMC4_TX = 28, + /* 29 - 31 not used */ + DB5500_DMA_DEV32_FSMC_TX = 32, + DB5500_DMA_DEV33_SDMMC0_TX = 33, + DB5500_DMA_DEV34_SDMMC1_TX = 34, + DB5500_DMA_DEV35_SDMMC2_TX = 35, + DB5500_DMA_DEV36_SDMMC3_TX = 36, + DB5500_DMA_DEV37_SDMMC4_TX = 37, + DB5500_DMA_DEV38_USB_OTG_OEP_8 = 38, + DB5500_DMA_DEV39_USB_OTG_OEP_1_9 = 39, + DB5500_DMA_DEV40_USB_OTG_OEP_2_10 = 40, + DB5500_DMA_DEV41_USB_OTG_OEP_3_11 = 41, + DB5500_DMA_DEV42_USB_OTG_OEP_4_12 = 42, + DB5500_DMA_DEV43_USB_OTG_OEP_5_13 = 43, + DB5500_DMA_DEV44_USB_OTG_OEP_6_14 = 44, + DB5500_DMA_DEV45_USB_OTG_OEP_7_15 = 45, + /* 46 not used */ + DB5500_DMA_DEV47_STM_TX = 47, + DB5500_DMA_DEV48_CRYPTO1_TX = 48, + DB5500_DMA_DEV49_CRYPTO1_TX_HASH1_TX = 49, + DB5500_DMA_DEV50_HASH1_TX = 50, + DB5500_DMA_DEV51_I2C1_TX = 51, + DB5500_DMA_DEV52_I2C3_TX = 52, + DB5500_DMA_DEV53_I2C2_TX = 53, + /* 54, 55 not used */ + DB5500_DMA_MEMCPY_TX_1 = 56, + DB5500_DMA_MEMCPY_TX_2 = 57, + DB5500_DMA_MEMCPY_TX_3 = 58, + DB5500_DMA_MEMCPY_TX_4 = 59, + DB5500_DMA_MEMCPY_TX_5 = 60, + DB5500_DMA_DEV61_CRYPTO0_TX = 61, + DB5500_DMA_DEV62_CRYPTO0_TX_HASH0_TX = 62, + DB5500_DMA_DEV63_HASH0_TX = 63, +}; + +#endif diff --git a/arch/arm/mach-ux500/ste-dma40-db8500.h b/arch/arm/mach-ux500/ste-dma40-db8500.h index 9d9d3797b3b0..a616419bea76 100644 --- a/arch/arm/mach-ux500/ste-dma40-db8500.h +++ b/arch/arm/mach-ux500/ste-dma40-db8500.h @@ -10,145 +10,135 @@ #ifndef STE_DMA40_DB8500_H #define STE_DMA40_DB8500_H -#define STEDMA40_NR_DEV 64 +#define DB8500_DMA_NR_DEV 64 enum dma_src_dev_type { - STEDMA40_DEV_SPI0_RX = 0, - STEDMA40_DEV_SD_MMC0_RX = 1, - STEDMA40_DEV_SD_MMC1_RX = 2, - STEDMA40_DEV_SD_MMC2_RX = 3, - STEDMA40_DEV_I2C1_RX = 4, - STEDMA40_DEV_I2C3_RX = 5, - STEDMA40_DEV_I2C2_RX = 6, - STEDMA40_DEV_I2C4_RX = 7, /* Only on V1 */ - STEDMA40_DEV_SSP0_RX = 8, - STEDMA40_DEV_SSP1_RX = 9, - STEDMA40_DEV_MCDE_RX = 10, - STEDMA40_DEV_UART2_RX = 11, - STEDMA40_DEV_UART1_RX = 12, - STEDMA40_DEV_UART0_RX = 13, - STEDMA40_DEV_MSP2_RX = 14, - STEDMA40_DEV_I2C0_RX = 15, - STEDMA40_DEV_USB_OTG_IEP_8 = 16, - STEDMA40_DEV_USB_OTG_IEP_1_9 = 17, - STEDMA40_DEV_USB_OTG_IEP_2_10 = 18, - STEDMA40_DEV_USB_OTG_IEP_3_11 = 19, - STEDMA40_DEV_SLIM0_CH0_RX_HSI_RX_CH0 = 20, - STEDMA40_DEV_SLIM0_CH1_RX_HSI_RX_CH1 = 21, - STEDMA40_DEV_SLIM0_CH2_RX_HSI_RX_CH2 = 22, - STEDMA40_DEV_SLIM0_CH3_RX_HSI_RX_CH3 = 23, - STEDMA40_DEV_SRC_SXA0_RX_TX = 24, - STEDMA40_DEV_SRC_SXA1_RX_TX = 25, - STEDMA40_DEV_SRC_SXA2_RX_TX = 26, - STEDMA40_DEV_SRC_SXA3_RX_TX = 27, - STEDMA40_DEV_SD_MM2_RX = 28, - STEDMA40_DEV_SD_MM0_RX = 29, - STEDMA40_DEV_MSP1_RX = 30, - /* - * This channel is either SlimBus or MSP, - * never both at the same time. - */ - STEDMA40_SLIM0_CH0_RX = 31, - STEDMA40_DEV_MSP0_RX = 31, - STEDMA40_DEV_SD_MM1_RX = 32, - STEDMA40_DEV_SPI2_RX = 33, - STEDMA40_DEV_I2C3_RX2 = 34, - STEDMA40_DEV_SPI1_RX = 35, - STEDMA40_DEV_USB_OTG_IEP_4_12 = 36, - STEDMA40_DEV_USB_OTG_IEP_5_13 = 37, - STEDMA40_DEV_USB_OTG_IEP_6_14 = 38, - STEDMA40_DEV_USB_OTG_IEP_7_15 = 39, - STEDMA40_DEV_SPI3_RX = 40, - STEDMA40_DEV_SD_MM3_RX = 41, - STEDMA40_DEV_SD_MM4_RX = 42, - STEDMA40_DEV_SD_MM5_RX = 43, - STEDMA40_DEV_SRC_SXA4_RX_TX = 44, - STEDMA40_DEV_SRC_SXA5_RX_TX = 45, - STEDMA40_DEV_SRC_SXA6_RX_TX = 46, - STEDMA40_DEV_SRC_SXA7_RX_TX = 47, - STEDMA40_DEV_CAC1_RX = 48, - /* RX channels 49 and 50 are unused */ - STEDMA40_DEV_MSHC_RX = 51, - STEDMA40_DEV_SLIM1_CH0_RX_HSI_RX_CH4 = 52, - STEDMA40_DEV_SLIM1_CH1_RX_HSI_RX_CH5 = 53, - STEDMA40_DEV_SLIM1_CH2_RX_HSI_RX_CH6 = 54, - STEDMA40_DEV_SLIM1_CH3_RX_HSI_RX_CH7 = 55, - /* RX channels 56 thru 60 are unused */ - STEDMA40_DEV_CAC0_RX = 61, - /* RX channels 62 and 63 are unused */ + DB8500_DMA_DEV0_SPI0_RX = 0, + DB8500_DMA_DEV1_SD_MMC0_RX = 1, + DB8500_DMA_DEV2_SD_MMC1_RX = 2, + DB8500_DMA_DEV3_SD_MMC2_RX = 3, + DB8500_DMA_DEV4_I2C1_RX = 4, + DB8500_DMA_DEV5_I2C3_RX = 5, + DB8500_DMA_DEV6_I2C2_RX = 6, + DB8500_DMA_DEV7_I2C4_RX = 7, /* Only on V1 and later */ + DB8500_DMA_DEV8_SSP0_RX = 8, + DB8500_DMA_DEV9_SSP1_RX = 9, + DB8500_DMA_DEV10_MCDE_RX = 10, + DB8500_DMA_DEV11_UART2_RX = 11, + DB8500_DMA_DEV12_UART1_RX = 12, + DB8500_DMA_DEV13_UART0_RX = 13, + DB8500_DMA_DEV14_MSP2_RX = 14, + DB8500_DMA_DEV15_I2C0_RX = 15, + DB8500_DMA_DEV16_USB_OTG_IEP_7_15 = 16, + DB8500_DMA_DEV17_USB_OTG_IEP_6_14 = 17, + DB8500_DMA_DEV18_USB_OTG_IEP_5_13 = 18, + DB8500_DMA_DEV19_USB_OTG_IEP_4_12 = 19, + DB8500_DMA_DEV20_SLIM0_CH0_RX_HSI_RX_CH0 = 20, + DB8500_DMA_DEV21_SLIM0_CH1_RX_HSI_RX_CH1 = 21, + DB8500_DMA_DEV22_SLIM0_CH2_RX_HSI_RX_CH2 = 22, + DB8500_DMA_DEV23_SLIM0_CH3_RX_HSI_RX_CH3 = 23, + DB8500_DMA_DEV24_SRC_SXA0_RX_TX = 24, + DB8500_DMA_DEV25_SRC_SXA1_RX_TX = 25, + DB8500_DMA_DEV26_SRC_SXA2_RX_TX = 26, + DB8500_DMA_DEV27_SRC_SXA3_RX_TX = 27, + DB8500_DMA_DEV28_SD_MM2_RX = 28, + DB8500_DMA_DEV29_SD_MM0_RX = 29, + DB8500_DMA_DEV30_MSP1_RX = 30, + /* On DB8500v2, MSP3 RX replaces MSP1 RX */ + DB8500_DMA_DEV30_MSP3_RX = 30, + DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX = 31, + DB8500_DMA_DEV32_SD_MM1_RX = 32, + DB8500_DMA_DEV33_SPI2_RX = 33, + DB8500_DMA_DEV34_I2C3_RX2 = 34, + DB8500_DMA_DEV35_SPI1_RX = 35, + DB8500_DMA_DEV36_USB_OTG_IEP_3_11 = 36, + DB8500_DMA_DEV37_USB_OTG_IEP_2_10 = 37, + DB8500_DMA_DEV38_USB_OTG_IEP_1_9 = 38, + DB8500_DMA_DEV39_USB_OTG_IEP_8 = 39, + DB8500_DMA_DEV40_SPI3_RX = 40, + DB8500_DMA_DEV41_SD_MM3_RX = 41, + DB8500_DMA_DEV42_SD_MM4_RX = 42, + DB8500_DMA_DEV43_SD_MM5_RX = 43, + DB8500_DMA_DEV44_SRC_SXA4_RX_TX = 44, + DB8500_DMA_DEV45_SRC_SXA5_RX_TX = 45, + DB8500_DMA_DEV46_SLIM0_CH8_RX_SRC_SXA6_RX_TX = 46, + DB8500_DMA_DEV47_SLIM0_CH9_RX_SRC_SXA7_RX_TX = 47, + DB8500_DMA_DEV48_CAC1_RX = 48, + /* 49, 50 and 51 are not used */ + DB8500_DMA_DEV52_SLIM0_CH4_RX_HSI_RX_CH4 = 52, + DB8500_DMA_DEV53_SLIM0_CH5_RX_HSI_RX_CH5 = 53, + DB8500_DMA_DEV54_SLIM0_CH6_RX_HSI_RX_CH6 = 54, + DB8500_DMA_DEV55_SLIM0_CH7_RX_HSI_RX_CH7 = 55, + /* 56, 57, 58, 59 and 60 are not used */ + DB8500_DMA_DEV61_CAC0_RX = 61, + /* 62 and 63 are not used */ }; enum dma_dest_dev_type { - STEDMA40_DEV_SPI0_TX = 0, - STEDMA40_DEV_SD_MMC0_TX = 1, - STEDMA40_DEV_SD_MMC1_TX = 2, - STEDMA40_DEV_SD_MMC2_TX = 3, - STEDMA40_DEV_I2C1_TX = 4, - STEDMA40_DEV_I2C3_TX = 5, - STEDMA40_DEV_I2C2_TX = 6, - STEDMA50_DEV_I2C4_TX = 7, /* Only on V1 */ - STEDMA40_DEV_SSP0_TX = 8, - STEDMA40_DEV_SSP1_TX = 9, - /* TX channel 10 is unused */ - STEDMA40_DEV_UART2_TX = 11, - STEDMA40_DEV_UART1_TX = 12, - STEDMA40_DEV_UART0_TX= 13, - STEDMA40_DEV_MSP2_TX = 14, - STEDMA40_DEV_I2C0_TX = 15, - STEDMA40_DEV_USB_OTG_OEP_8 = 16, - STEDMA40_DEV_USB_OTG_OEP_1_9 = 17, - STEDMA40_DEV_USB_OTG_OEP_2_10= 18, - STEDMA40_DEV_USB_OTG_OEP_3_11 = 19, - STEDMA40_DEV_SLIM0_CH0_TX_HSI_TX_CH0 = 20, - STEDMA40_DEV_SLIM0_CH1_TX_HSI_TX_CH1 = 21, - STEDMA40_DEV_SLIM0_CH2_TX_HSI_TX_CH2 = 22, - STEDMA40_DEV_SLIM0_CH3_TX_HSI_TX_CH3 = 23, - STEDMA40_DEV_DST_SXA0_RX_TX = 24, - STEDMA40_DEV_DST_SXA1_RX_TX = 25, - STEDMA40_DEV_DST_SXA2_RX_TX = 26, - STEDMA40_DEV_DST_SXA3_RX_TX = 27, - STEDMA40_DEV_SD_MM2_TX = 28, - STEDMA40_DEV_SD_MM0_TX = 29, - STEDMA40_DEV_MSP1_TX = 30, - /* - * This channel is either SlimBus or MSP, - * never both at the same time. - */ - STEDMA40_SLIM0_CH0_TX = 31, - STEDMA40_DEV_MSP0_TX = 31, - STEDMA40_DEV_SD_MM1_TX = 32, - STEDMA40_DEV_SPI2_TX = 33, - /* Secondary I2C3 channel */ - STEDMA40_DEV_I2C3_TX2 = 34, - STEDMA40_DEV_SPI1_TX = 35, - STEDMA40_DEV_USB_OTG_OEP_4_12 = 36, - STEDMA40_DEV_USB_OTG_OEP_5_13 = 37, - STEDMA40_DEV_USB_OTG_OEP_6_14 = 38, - STEDMA40_DEV_USB_OTG_OEP_7_15 = 39, - STEDMA40_DEV_SPI3_TX = 40, - STEDMA40_DEV_SD_MM3_TX = 41, - STEDMA40_DEV_SD_MM4_TX = 42, - STEDMA40_DEV_SD_MM5_TX = 43, - STEDMA40_DEV_DST_SXA4_RX_TX = 44, - STEDMA40_DEV_DST_SXA5_RX_TX = 45, - STEDMA40_DEV_DST_SXA6_RX_TX = 46, - STEDMA40_DEV_DST_SXA7_RX_TX = 47, - STEDMA40_DEV_CAC1_TX = 48, - STEDMA40_DEV_CAC1_TX_HAC1_TX = 49, - STEDMA40_DEV_HAC1_TX = 50, - STEDMA40_MEMCPY_TX_0 = 51, - STEDMA40_DEV_SLIM1_CH0_TX_HSI_TX_CH4 = 52, - STEDMA40_DEV_SLIM1_CH1_TX_HSI_TX_CH5 = 53, - STEDMA40_DEV_SLIM1_CH2_TX_HSI_TX_CH6 = 54, - STEDMA40_DEV_SLIM1_CH3_TX_HSI_TX_CH7 = 55, - STEDMA40_MEMCPY_TX_1 = 56, - STEDMA40_MEMCPY_TX_2 = 57, - STEDMA40_MEMCPY_TX_3 = 58, - STEDMA40_MEMCPY_TX_4 = 59, - STEDMA40_MEMCPY_TX_5 = 60, - STEDMA40_DEV_CAC0_TX = 61, - STEDMA40_DEV_CAC0_TX_HAC0_TX = 62, - STEDMA40_DEV_HAC0_TX = 63, + DB8500_DMA_DEV0_SPI0_TX = 0, + DB8500_DMA_DEV1_SD_MMC0_TX = 1, + DB8500_DMA_DEV2_SD_MMC1_TX = 2, + DB8500_DMA_DEV3_SD_MMC2_TX = 3, + DB8500_DMA_DEV4_I2C1_TX = 4, + DB8500_DMA_DEV5_I2C3_TX = 5, + DB8500_DMA_DEV6_I2C2_TX = 6, + DB8500_DMA_DEV7_I2C4_TX = 7, /* Only on V1 and later */ + DB8500_DMA_DEV8_SSP0_TX = 8, + DB8500_DMA_DEV9_SSP1_TX = 9, + /* 10 is not used*/ + DB8500_DMA_DEV11_UART2_TX = 11, + DB8500_DMA_DEV12_UART1_TX = 12, + DB8500_DMA_DEV13_UART0_TX = 13, + DB8500_DMA_DEV14_MSP2_TX = 14, + DB8500_DMA_DEV15_I2C0_TX = 15, + DB8500_DMA_DEV16_USB_OTG_OEP_7_15 = 16, + DB8500_DMA_DEV17_USB_OTG_OEP_6_14 = 17, + DB8500_DMA_DEV18_USB_OTG_OEP_5_13 = 18, + DB8500_DMA_DEV19_USB_OTG_OEP_4_12 = 19, + DB8500_DMA_DEV20_SLIM0_CH0_TX_HSI_TX_CH0 = 20, + DB8500_DMA_DEV21_SLIM0_CH1_TX_HSI_TX_CH1 = 21, + DB8500_DMA_DEV22_SLIM0_CH2_TX_HSI_TX_CH2 = 22, + DB8500_DMA_DEV23_SLIM0_CH3_TX_HSI_TX_CH3 = 23, + DB8500_DMA_DEV24_DST_SXA0_RX_TX = 24, + DB8500_DMA_DEV25_DST_SXA1_RX_TX = 25, + DB8500_DMA_DEV26_DST_SXA2_RX_TX = 26, + DB8500_DMA_DEV27_DST_SXA3_RX_TX = 27, + DB8500_DMA_DEV28_SD_MM2_TX = 28, + DB8500_DMA_DEV29_SD_MM0_TX = 29, + DB8500_DMA_DEV30_MSP1_TX = 30, + DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX = 31, + DB8500_DMA_DEV32_SD_MM1_TX = 32, + DB8500_DMA_DEV33_SPI2_TX = 33, + DB8500_DMA_DEV34_I2C3_TX2 = 34, + DB8500_DMA_DEV35_SPI1_TX = 35, + DB8500_DMA_DEV36_USB_OTG_OEP_3_11 = 36, + DB8500_DMA_DEV37_USB_OTG_OEP_2_10 = 37, + DB8500_DMA_DEV38_USB_OTG_OEP_1_9 = 38, + DB8500_DMA_DEV39_USB_OTG_OEP_8 = 39, + DB8500_DMA_DEV40_SPI3_TX = 40, + DB8500_DMA_DEV41_SD_MM3_TX = 41, + DB8500_DMA_DEV42_SD_MM4_TX = 42, + DB8500_DMA_DEV43_SD_MM5_TX = 43, + DB8500_DMA_DEV44_DST_SXA4_RX_TX = 44, + DB8500_DMA_DEV45_DST_SXA5_RX_TX = 45, + DB8500_DMA_DEV46_SLIM0_CH8_TX_DST_SXA6_RX_TX = 46, + DB8500_DMA_DEV47_SLIM0_CH9_TX_DST_SXA7_RX_TX = 47, + DB8500_DMA_DEV48_CAC1_TX = 48, + DB8500_DMA_DEV49_CAC1_TX_HAC1_TX = 49, + DB8500_DMA_DEV50_HAC1_TX = 50, + DB8500_DMA_MEMCPY_TX_0 = 51, + DB8500_DMA_DEV52_SLIM1_CH4_TX_HSI_TX_CH4 = 52, + DB8500_DMA_DEV53_SLIM1_CH5_TX_HSI_TX_CH5 = 53, + DB8500_DMA_DEV54_SLIM1_CH6_TX_HSI_TX_CH6 = 54, + DB8500_DMA_DEV55_SLIM1_CH7_TX_HSI_TX_CH7 = 55, + DB8500_DMA_MEMCPY_TX_1 = 56, + DB8500_DMA_MEMCPY_TX_2 = 57, + DB8500_DMA_MEMCPY_TX_3 = 58, + DB8500_DMA_MEMCPY_TX_4 = 59, + DB8500_DMA_MEMCPY_TX_5 = 60, + DB8500_DMA_DEV61_CAC0_TX = 61, + DB8500_DMA_DEV62_CAC0_TX_HAC0_TX = 62, + DB8500_DMA_DEV63_HAC0_TX = 63, }; #endif diff --git a/arch/arm/mach-vexpress/include/mach/smp.h b/arch/arm/mach-vexpress/include/mach/smp.h index 72a9621ed087..5a6da4fd247e 100644 --- a/arch/arm/mach-vexpress/include/mach/smp.h +++ b/arch/arm/mach-vexpress/include/mach/smp.h @@ -2,14 +2,7 @@ #define __MACH_SMP_H #include <asm/hardware/gic.h> - -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x0F; \ - }) +#include <asm/smp_mpidr.h> /* * We use IRQ1 as the IPI diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 86aa689ef1aa..99fa688dfadd 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -21,18 +21,22 @@ #define D_CACHE_LINE_SIZE 32 #define BTB_FLUSH_SIZE 8 -#ifdef CONFIG_ARM_ERRATA_411920 /* - * Invalidate the entire I cache (this code is a workaround for the ARM1136 - * erratum 411920 - Invalidate Instruction Cache operation can fail. This - * erratum is present in 1136, 1156 and 1176. It does not affect the MPCore. + * v6_flush_icache_all() + * + * Flush the whole I-cache. * - * Registers: - * r0 - set to 0 - * r1 - corrupted + * ARM1136 erratum 411920 - Invalidate Instruction Cache operation can fail. + * This erratum is present in 1136, 1156 and 1176. It does not affect the + * MPCore. + * + * Registers: + * r0 - set to 0 + * r1 - corrupted */ -ENTRY(v6_icache_inval_all) +ENTRY(v6_flush_icache_all) mov r0, #0 +#ifdef CONFIG_ARM_ERRATA_411920 mrs r1, cpsr cpsid ifa @ disable interrupts mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache @@ -43,8 +47,11 @@ ENTRY(v6_icache_inval_all) .rept 11 @ ARM Ltd recommends at least nop @ 11 NOPs .endr - mov pc, lr +#else + mcr p15, 0, r0, c7, c5, 0 @ invalidate I-cache #endif + mov pc, lr +ENDPROC(v6_flush_icache_all) /* * v6_flush_cache_all() @@ -60,7 +67,7 @@ ENTRY(v6_flush_kern_cache_all) #ifndef CONFIG_ARM_ERRATA_411920 mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate #else - b v6_icache_inval_all + b v6_flush_icache_all #endif #else mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate @@ -138,7 +145,7 @@ ENTRY(v6_coherent_user_range) #ifndef CONFIG_ARM_ERRATA_411920 mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate #else - b v6_icache_inval_all + b v6_flush_icache_all #endif #else mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB @@ -312,6 +319,7 @@ ENDPROC(v6_dma_unmap_area) .type v6_cache_fns, #object ENTRY(v6_cache_fns) + .long v6_flush_icache_all .long v6_flush_kern_cache_all .long v6_flush_user_cache_all .long v6_flush_user_cache_range diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 37c8157e116e..a3ebf7a4f49b 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -18,6 +18,21 @@ #include "proc-macros.S" /* + * v7_flush_icache_all() + * + * Flush the whole I-cache. + * + * Registers: + * r0 - set to 0 + */ +ENTRY(v7_flush_icache_all) + mov r0, #0 + ALT_SMP(mcr p15, 0, r0, c7, c1, 0) @ invalidate I-cache inner shareable + ALT_UP(mcr p15, 0, r0, c7, c5, 0) @ I+BTB cache invalidate + mov pc, lr +ENDPROC(v7_flush_icache_all) + +/* * v7_flush_dcache_all() * * Flush the whole D-cache. @@ -91,11 +106,8 @@ ENTRY(v7_flush_kern_cache_all) THUMB( stmfd sp!, {r4-r7, r9-r11, lr} ) bl v7_flush_dcache_all mov r0, #0 -#ifdef CONFIG_SMP - mcr p15, 0, r0, c7, c1, 0 @ invalidate I-cache inner shareable -#else - mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate -#endif + ALT_SMP(mcr p15, 0, r0, c7, c1, 0) @ invalidate I-cache inner shareable + ALT_UP(mcr p15, 0, r0, c7, c5, 0) @ I+BTB cache invalidate ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} ) THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} ) mov pc, lr @@ -171,11 +183,8 @@ ENTRY(v7_coherent_user_range) cmp r0, r1 blo 1b mov r0, #0 -#ifdef CONFIG_SMP - mcr p15, 0, r0, c7, c1, 6 @ invalidate BTB Inner Shareable -#else - mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB -#endif + ALT_SMP(mcr p15, 0, r0, c7, c1, 6) @ invalidate BTB Inner Shareable + ALT_UP(mcr p15, 0, r0, c7, c5, 6) @ invalidate BTB dsb isb mov pc, lr @@ -309,6 +318,7 @@ ENDPROC(v7_dma_unmap_area) .type v7_cache_fns, #object ENTRY(v7_cache_fns) + .long v7_flush_icache_all .long v7_flush_kern_cache_all .long v7_flush_user_cache_all .long v7_flush_user_cache_range diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c index 598c51ad5071..b8061519ce77 100644 --- a/arch/arm/mm/copypage-v4mc.c +++ b/arch/arm/mm/copypage-v4mc.c @@ -73,7 +73,7 @@ void v4_mc_copy_user_highpage(struct page *to, struct page *from, { void *kto = kmap_atomic(to, KM_USER1); - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); spin_lock(&minicache_lock); diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index f55fa1044f72..bdba6c65c901 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -79,7 +79,7 @@ static void v6_copy_user_highpage_aliasing(struct page *to, unsigned int offset = CACHE_COLOUR(vaddr); unsigned long kfrom, kto; - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); /* FIXME: not highmem safe */ diff --git a/arch/arm/mm/copypage-xscale.c b/arch/arm/mm/copypage-xscale.c index 9920c0ae2096..649bbcd325bf 100644 --- a/arch/arm/mm/copypage-xscale.c +++ b/arch/arm/mm/copypage-xscale.c @@ -95,7 +95,7 @@ void xscale_mc_copy_user_highpage(struct page *to, struct page *from, { void *kto = kmap_atomic(to, KM_USER1); - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); spin_lock(&minicache_lock); diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 4bc43e535d3b..e4dd0646e859 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -523,6 +523,12 @@ void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, outer_inv_range(paddr, paddr + size); dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + + /* + * Mark the D-cache clean for this page to avoid extra flushing. + */ + if (dir != DMA_TO_DEVICE && off == 0 && size >= PAGE_SIZE) + set_bit(PG_dcache_clean, &page->flags); } EXPORT_SYMBOL(___dma_page_dev_to_cpu); diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 9b906dec1ca1..8440d952ba6d 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -28,6 +28,7 @@ static unsigned long shared_pte_mask = L_PTE_MT_BUFFERABLE; +#if __LINUX_ARM_ARCH__ < 6 /* * We take the easy way out of this problem - we make the * PTE uncacheable. However, we leave the write buffer on. @@ -141,7 +142,7 @@ make_coherent(struct address_space *mapping, struct vm_area_struct *vma, * a page table, or changing an existing PTE. Basically, there are two * things that we need to take care of: * - * 1. If PG_dcache_dirty is set for the page, we need to ensure + * 1. If PG_dcache_clean is not set for the page, we need to ensure * that any cache entries for the kernels virtual memory * range are written back to the page. * 2. If we have multiple shared mappings of the same space in @@ -168,10 +169,8 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, return; mapping = page_mapping(page); -#ifndef CONFIG_SMP - if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) + if (!test_and_set_bit(PG_dcache_clean, &page->flags)) __flush_dcache_page(mapping, page); -#endif if (mapping) { if (cache_is_vivt()) make_coherent(mapping, vma, addr, ptep, pfn); @@ -179,6 +178,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, __flush_icache_all(); } } +#endif /* __LINUX_ARM_ARCH__ < 6 */ /* * Check whether the write buffer has physical address aliasing diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 23b0b03af5ea..1e21e125fe3a 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -581,6 +581,19 @@ static struct fsr_info ifsr_info[] = { { do_bad, SIGBUS, 0, "unknown 31" }, }; +void __init +hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), + int sig, int code, const char *name) +{ + if (nr < 0 || nr >= ARRAY_SIZE(ifsr_info)) + BUG(); + + ifsr_info[nr].fn = fn; + ifsr_info[nr].sig = sig; + ifsr_info[nr].code = code; + ifsr_info[nr].name = name; +} + asmlinkage void __exception do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) { diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index c6844cb9b508..391ffae75098 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -17,6 +17,7 @@ #include <asm/smp_plat.h> #include <asm/system.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h" @@ -39,6 +40,18 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) : "cc"); } +static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len) +{ + unsigned long colour = CACHE_COLOUR(vaddr); + unsigned long offset = vaddr & (PAGE_SIZE - 1); + unsigned long to; + + set_pte_ext(TOP_PTE(ALIAS_FLUSH_START) + colour, pfn_pte(pfn, PAGE_KERNEL), 0); + to = ALIAS_FLUSH_START + (colour << PAGE_SHIFT) + offset; + flush_tlb_kernel_page(to); + flush_icache_range(to, to + len); +} + void flush_cache_mm(struct mm_struct *mm) { if (cache_is_vivt()) { @@ -89,16 +102,16 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) __flush_icache_all(); } + #else -#define flush_pfn_alias(pfn,vaddr) do { } while (0) +#define flush_pfn_alias(pfn,vaddr) do { } while (0) +#define flush_icache_alias(pfn,vaddr,len) do { } while (0) #endif -#ifdef CONFIG_SMP static void flush_ptrace_access_other(void *args) { __flush_icache_all(); } -#endif static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, @@ -118,15 +131,16 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, return; } - /* VIPT non-aliasing cache */ + /* VIPT non-aliasing D-cache */ if (vma->vm_flags & VM_EXEC) { unsigned long addr = (unsigned long)kaddr; - __cpuc_coherent_kern_range(addr, addr + len); -#ifdef CONFIG_SMP + if (icache_is_vipt_aliasing()) + flush_icache_alias(page_to_pfn(page), uaddr, len); + else + __cpuc_coherent_kern_range(addr, addr + len); if (cache_ops_need_broadcast()) smp_call_function(flush_ptrace_access_other, NULL, 1); -#endif } } @@ -215,6 +229,36 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p flush_dcache_mmap_unlock(mapping); } +#if __LINUX_ARM_ARCH__ >= 6 +void __sync_icache_dcache(pte_t pteval) +{ + unsigned long pfn; + struct page *page; + struct address_space *mapping; + + if (!pte_present_user(pteval)) + return; + if (cache_is_vipt_nonaliasing() && !pte_exec(pteval)) + /* only flush non-aliasing VIPT caches for exec mappings */ + return; + pfn = pte_pfn(pteval); + if (!pfn_valid(pfn)) + return; + + page = pfn_to_page(pfn); + if (cache_is_vipt_aliasing()) + mapping = page_mapping(page); + else + mapping = NULL; + + if (!test_and_set_bit(PG_dcache_clean, &page->flags)) + __flush_dcache_page(mapping, page); + /* pte_exec() already checked above for non-aliasing VIPT cache */ + if (cache_is_vipt_nonaliasing() || pte_exec(pteval)) + __flush_icache_all(); +} +#endif + /* * Ensure cache coherency between kernel mapping and userspace mapping * of this page. @@ -246,17 +290,16 @@ void flush_dcache_page(struct page *page) mapping = page_mapping(page); -#ifndef CONFIG_SMP - if (!PageHighMem(page) && mapping && !mapping_mapped(mapping)) - set_bit(PG_dcache_dirty, &page->flags); - else -#endif - { + if (!cache_ops_need_broadcast() && + mapping && !mapping_mapped(mapping)) + clear_bit(PG_dcache_clean, &page->flags); + else { __flush_dcache_page(mapping, page); if (mapping && cache_is_vivt()) __flush_dcache_aliases(mapping, page); else if (mapping) __flush_icache_all(); + set_bit(PG_dcache_clean, &page->flags); } } EXPORT_SYMBOL(flush_dcache_page); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 7185b00650fe..36c4553ffcce 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -277,7 +277,7 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) /* Register the kernel text, kernel data and initrd with memblock. */ #ifdef CONFIG_XIP_KERNEL - memblock_reserve(__pa(_data), _end - _data); + memblock_reserve(__pa(_sdata), _end - _sdata); #else memblock_reserve(__pa(_stext), _end - _stext); #endif @@ -545,7 +545,7 @@ void __init mem_init(void) MLK_ROUNDUP(__init_begin, __init_end), MLK_ROUNDUP(_text, _etext), - MLK_ROUNDUP(_data, _edata)); + MLK_ROUNDUP(_sdata, _edata)); #undef MLK #undef MLM diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e8ed9dc461fe..c32f731d56d3 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -310,9 +310,8 @@ static void __init build_mem_type_table(void) cachepolicy = CPOLICY_WRITEBACK; ecc_mask = 0; } -#ifdef CONFIG_SMP - cachepolicy = CPOLICY_WRITEALLOC; -#endif + if (is_smp()) + cachepolicy = CPOLICY_WRITEALLOC; /* * Strip out features not present on earlier architectures. @@ -406,13 +405,11 @@ static void __init build_mem_type_table(void) cp = &cache_policies[cachepolicy]; vecs_pgprot = kern_pgprot = user_pgprot = cp->pte; -#ifndef CONFIG_SMP /* * Only use write-through for non-SMP systems */ - if (cpu_arch >= CPU_ARCH_ARMv5 && cachepolicy > CPOLICY_WRITETHROUGH) + if (!is_smp() && cpu_arch >= CPU_ARCH_ARMv5 && cachepolicy > CPOLICY_WRITETHROUGH) vecs_pgprot = cache_policies[CPOLICY_WRITETHROUGH].pte; -#endif /* * Enable CPU-specific coherency if supported. @@ -436,22 +433,23 @@ static void __init build_mem_type_table(void) mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; -#ifdef CONFIG_SMP - /* - * Mark memory with the "shared" attribute for SMP systems - */ - user_pgprot |= L_PTE_SHARED; - kern_pgprot |= L_PTE_SHARED; - vecs_pgprot |= L_PTE_SHARED; - mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S; - mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED; - mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S; - mem_types[MT_DEVICE_CACHED].prot_pte |= L_PTE_SHARED; - mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S; - mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED; - mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S; - mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED; -#endif + if (is_smp()) { + /* + * Mark memory with the "shared" attribute + * for SMP systems + */ + user_pgprot |= L_PTE_SHARED; + kern_pgprot |= L_PTE_SHARED; + vecs_pgprot |= L_PTE_SHARED; + mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S; + mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED; + mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S; + mem_types[MT_DEVICE_CACHED].prot_pte |= L_PTE_SHARED; + mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED; + mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED; + } } /* @@ -829,8 +827,7 @@ static void __init sanity_check_meminfo(void) * rather difficult. */ reason = "with VIPT aliasing cache"; -#ifdef CONFIG_SMP - } else if (tlb_ops_need_broadcast()) { + } else if (is_smp() && tlb_ops_need_broadcast()) { /* * kmap_high needs to occasionally flush TLB entries, * however, if the TLB entries need to be broadcast @@ -840,7 +837,6 @@ static void __init sanity_check_meminfo(void) * (must not be called with irqs off) */ reason = "without hardware TLB ops broadcasting"; -#endif } if (reason) { printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n", diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index 203a4e944d9e..a6f5f8475b96 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -430,7 +430,7 @@ ENTRY(cpu_arm1020_set_pte_ext) #endif /* CONFIG_MMU */ mov pc, lr - __INIT + __CPUINIT .type __arm1020_setup, #function __arm1020_setup: diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index 1a511e765909..afc06b9c3133 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -412,7 +412,7 @@ ENTRY(cpu_arm1020e_set_pte_ext) #endif /* CONFIG_MMU */ mov pc, lr - __INIT + __CPUINIT .type __arm1020e_setup, #function __arm1020e_setup: diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 1ffa4eb9c34f..8915e0ba3fe5 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -394,7 +394,7 @@ ENTRY(cpu_arm1022_set_pte_ext) #endif /* CONFIG_MMU */ mov pc, lr - __INIT + __CPUINIT .type __arm1022_setup, #function __arm1022_setup: diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index 5697c34b95b0..ff446c5d476f 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -384,7 +384,7 @@ ENTRY(cpu_arm1026_set_pte_ext) mov pc, lr - __INIT + __CPUINIT .type __arm1026_setup, #function __arm1026_setup: diff --git a/arch/arm/mm/proc-arm6_7.S b/arch/arm/mm/proc-arm6_7.S index 64e0b327c7c5..6a7be1863edd 100644 --- a/arch/arm/mm/proc-arm6_7.S +++ b/arch/arm/mm/proc-arm6_7.S @@ -238,7 +238,7 @@ ENTRY(cpu_arm7_reset) mcr p15, 0, r1, c1, c0, 0 @ turn off MMU etc mov pc, r0 - __INIT + __CPUINIT .type __arm6_setup, #function __arm6_setup: mov r0, #0 diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S index 9d96824134fc..c285395f44b2 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -113,7 +113,7 @@ ENTRY(cpu_arm720_reset) mcr p15, 0, ip, c1, c0, 0 @ ctrl register mov pc, r0 - __INIT + __CPUINIT .type __arm710_setup, #function __arm710_setup: diff --git a/arch/arm/mm/proc-arm740.S b/arch/arm/mm/proc-arm740.S index 6c1a9ab059ae..38b27dcba727 100644 --- a/arch/arm/mm/proc-arm740.S +++ b/arch/arm/mm/proc-arm740.S @@ -55,7 +55,7 @@ ENTRY(cpu_arm740_reset) mcr p15, 0, ip, c1, c0, 0 @ ctrl register mov pc, r0 - __INIT + __CPUINIT .type __arm740_setup, #function __arm740_setup: diff --git a/arch/arm/mm/proc-arm7tdmi.S b/arch/arm/mm/proc-arm7tdmi.S index 6a850dbba22e..0c9786de20af 100644 --- a/arch/arm/mm/proc-arm7tdmi.S +++ b/arch/arm/mm/proc-arm7tdmi.S @@ -46,7 +46,7 @@ ENTRY(cpu_arm7tdmi_proc_fin) ENTRY(cpu_arm7tdmi_reset) mov pc, r0 - __INIT + __CPUINIT .type __arm7tdmi_setup, #function __arm7tdmi_setup: diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index 86f80aa56216..fecf570939f3 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -375,7 +375,7 @@ ENTRY(cpu_arm920_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __arm920_setup, #function __arm920_setup: diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index f76ce9b62883..e3cbf87c9480 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -379,7 +379,7 @@ ENTRY(cpu_arm922_set_pte_ext) #endif /* CONFIG_MMU */ mov pc, lr - __INIT + __CPUINIT .type __arm922_setup, #function __arm922_setup: diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index 657bd3f7c153..572424c867b5 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -428,7 +428,7 @@ ENTRY(cpu_arm925_set_pte_ext) #endif /* CONFIG_MMU */ mov pc, lr - __INIT + __CPUINIT .type __arm925_setup, #function __arm925_setup: diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 73f1f3c68910..63d168b4ebe6 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -389,7 +389,7 @@ ENTRY(cpu_arm926_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __arm926_setup, #function __arm926_setup: diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S index fffb061a45a5..f6a62822418e 100644 --- a/arch/arm/mm/proc-arm940.S +++ b/arch/arm/mm/proc-arm940.S @@ -264,7 +264,7 @@ ENTRY(arm940_cache_fns) .long arm940_dma_unmap_area .long arm940_dma_flush_range - __INIT + __CPUINIT .type __arm940_setup, #function __arm940_setup: diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S index 249a6053760a..ea2e7f2eb95b 100644 --- a/arch/arm/mm/proc-arm946.S +++ b/arch/arm/mm/proc-arm946.S @@ -317,7 +317,7 @@ ENTRY(cpu_arm946_dcache_clean_area) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr - __INIT + __CPUINIT .type __arm946_setup, #function __arm946_setup: diff --git a/arch/arm/mm/proc-arm9tdmi.S b/arch/arm/mm/proc-arm9tdmi.S index db475667fac2..db67e3134d7a 100644 --- a/arch/arm/mm/proc-arm9tdmi.S +++ b/arch/arm/mm/proc-arm9tdmi.S @@ -46,7 +46,7 @@ ENTRY(cpu_arm9tdmi_proc_fin) ENTRY(cpu_arm9tdmi_reset) mov pc, r0 - __INIT + __CPUINIT .type __arm9tdmi_setup, #function __arm9tdmi_setup: diff --git a/arch/arm/mm/proc-fa526.S b/arch/arm/mm/proc-fa526.S index 7803fdf70029..7c9ad621f0e6 100644 --- a/arch/arm/mm/proc-fa526.S +++ b/arch/arm/mm/proc-fa526.S @@ -134,7 +134,7 @@ ENTRY(cpu_fa526_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __fa526_setup, #function __fa526_setup: diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S index b304d0104a4e..578da69200cf 100644 --- a/arch/arm/mm/proc-feroceon.S +++ b/arch/arm/mm/proc-feroceon.S @@ -494,7 +494,7 @@ ENTRY(cpu_feroceon_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __feroceon_setup, #function __feroceon_setup: diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S index 5f6892fcc167..4458ee6aa713 100644 --- a/arch/arm/mm/proc-mohawk.S +++ b/arch/arm/mm/proc-mohawk.S @@ -338,7 +338,7 @@ ENTRY(cpu_mohawk_set_pte_ext) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr - __INIT + __CPUINIT .type __mohawk_setup, #function __mohawk_setup: diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index a201eb04b5e1..5aa8d59c2e85 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -156,7 +156,7 @@ ENTRY(cpu_sa110_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __sa110_setup, #function __sa110_setup: diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S index 7ddc4805bf97..2ac4e6f10713 100644 --- a/arch/arm/mm/proc-sa1100.S +++ b/arch/arm/mm/proc-sa1100.S @@ -169,7 +169,7 @@ ENTRY(cpu_sa1100_set_pte_ext) #endif mov pc, lr - __INIT + __CPUINIT .type __sa1100_setup, #function __sa1100_setup: diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 22aac8515196..59a7e1ffe7bc 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -30,13 +30,10 @@ #define TTB_RGN_WT (2 << 3) #define TTB_RGN_WB (3 << 3) -#ifndef CONFIG_SMP -#define TTB_FLAGS TTB_RGN_WBWA -#define PMD_FLAGS PMD_SECT_WB -#else -#define TTB_FLAGS TTB_RGN_WBWA|TTB_S -#define PMD_FLAGS PMD_SECT_WBWA|PMD_SECT_S -#endif +#define TTB_FLAGS_UP TTB_RGN_WBWA +#define PMD_FLAGS_UP PMD_SECT_WB +#define TTB_FLAGS_SMP TTB_RGN_WBWA|TTB_S +#define PMD_FLAGS_SMP PMD_SECT_WBWA|PMD_SECT_S ENTRY(cpu_v6_proc_init) mov pc, lr @@ -97,7 +94,8 @@ ENTRY(cpu_v6_switch_mm) #ifdef CONFIG_MMU mov r2, #0 ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id - orr r0, r0, #TTB_FLAGS + ALT_SMP(orr r0, r0, #TTB_FLAGS_SMP) + ALT_UP(orr r0, r0, #TTB_FLAGS_UP) mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB mcr p15, 0, r2, c7, c10, 4 @ drain write buffer mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 @@ -137,7 +135,7 @@ cpu_pj4_name: .align - __INIT + __CPUINIT /* * __v6_setup @@ -156,9 +154,11 @@ cpu_pj4_name: */ __v6_setup: #ifdef CONFIG_SMP - mrc p15, 0, r0, c1, c0, 1 @ Enable SMP/nAMP mode + ALT_SMP(mrc p15, 0, r0, c1, c0, 1) @ Enable SMP/nAMP mode + ALT_UP(nop) orr r0, r0, #0x20 - mcr p15, 0, r0, c1, c0, 1 + ALT_SMP(mcr p15, 0, r0, c1, c0, 1) + ALT_UP(nop) #endif mov r0, #0 @@ -169,7 +169,8 @@ __v6_setup: #ifdef CONFIG_MMU mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs mcr p15, 0, r0, c2, c0, 2 @ TTB control register - orr r4, r4, #TTB_FLAGS + ALT_SMP(orr r4, r4, #TTB_FLAGS_SMP) + ALT_UP(orr r4, r4, #TTB_FLAGS_UP) mcr p15, 0, r4, c2, c0, 1 @ load TTB1 #endif /* CONFIG_MMU */ adr r5, v6_crval @@ -192,6 +193,8 @@ __v6_setup: v6_crval: crval clear=0x01e0fb7f, mmuset=0x00c0387d, ucset=0x00c0187c + __INITDATA + .type v6_processor_functions, #object ENTRY(v6_processor_functions) .word v6_early_abort @@ -205,6 +208,8 @@ ENTRY(v6_processor_functions) .word cpu_v6_set_pte_ext .size v6_processor_functions, . - v6_processor_functions + .section ".rodata" + .type cpu_arch_name, #object cpu_arch_name: .asciz "armv6" @@ -225,10 +230,16 @@ cpu_elf_name: __v6_proc_info: .long 0x0007b000 .long 0x0007f000 - .long PMD_TYPE_SECT | \ + ALT_SMP(.long \ + PMD_TYPE_SECT | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ | \ - PMD_FLAGS + PMD_FLAGS_SMP) + ALT_UP(.long \ + PMD_TYPE_SECT | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ | \ + PMD_FLAGS_UP) .long PMD_TYPE_SECT | \ PMD_SECT_XN | \ PMD_SECT_AP_WRITE | \ @@ -249,10 +260,16 @@ __v6_proc_info: __pj4_v6_proc_info: .long 0x560f5810 .long 0xff0ffff0 - .long PMD_TYPE_SECT | \ + ALT_SMP(.long \ + PMD_TYPE_SECT | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ | \ + PMD_FLAGS_SMP) + ALT_UP(.long \ + PMD_TYPE_SECT | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ | \ - PMD_FLAGS + PMD_FLAGS_UP) .long PMD_TYPE_SECT | \ PMD_SECT_XN | \ PMD_SECT_AP_WRITE | \ diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 197f21bed5e9..53cbe2225153 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -30,15 +30,13 @@ #define TTB_IRGN_WT ((1 << 0) | (0 << 6)) #define TTB_IRGN_WB ((1 << 0) | (1 << 6)) -#ifndef CONFIG_SMP /* PTWs cacheable, inner WB not shareable, outer WB not shareable */ -#define TTB_FLAGS TTB_IRGN_WB|TTB_RGN_OC_WB -#define PMD_FLAGS PMD_SECT_WB -#else +#define TTB_FLAGS_UP TTB_IRGN_WB|TTB_RGN_OC_WB +#define PMD_FLAGS_UP PMD_SECT_WB + /* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */ -#define TTB_FLAGS TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA -#define PMD_FLAGS PMD_SECT_WBWA|PMD_SECT_S -#endif +#define TTB_FLAGS_SMP TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA +#define PMD_FLAGS_SMP PMD_SECT_WBWA|PMD_SECT_S ENTRY(cpu_v7_proc_init) mov pc, lr @@ -105,7 +103,8 @@ ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU mov r2, #0 ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id - orr r0, r0, #TTB_FLAGS + ALT_SMP(orr r0, r0, #TTB_FLAGS_SMP) + ALT_UP(orr r0, r0, #TTB_FLAGS_UP) #ifdef CONFIG_ARM_ERRATA_430973 mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB #endif @@ -169,7 +168,7 @@ cpu_v7_name: .ascii "ARMv7 Processor" .align - __INIT + __CPUINIT /* * __v7_setup @@ -188,7 +187,8 @@ cpu_v7_name: */ __v7_ca9mp_setup: #ifdef CONFIG_SMP - mrc p15, 0, r0, c1, c0, 1 + ALT_SMP(mrc p15, 0, r0, c1, c0, 1) + ALT_UP(mov r0, #(1 << 6)) @ fake it for UP tst r0, #(1 << 6) @ SMP/nAMP mode enabled? orreq r0, r0, #(1 << 6) | (1 << 0) @ Enable SMP/nAMP mode and mcreq p15, 0, r0, c1, c0, 1 @ TLB ops broadcasting @@ -270,7 +270,8 @@ __v7_setup: #ifdef CONFIG_MMU mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs mcr p15, 0, r10, c2, c0, 2 @ TTB control register - orr r4, r4, #TTB_FLAGS + ALT_SMP(orr r4, r4, #TTB_FLAGS_SMP) + ALT_UP(orr r4, r4, #TTB_FLAGS_UP) mcr p15, 0, r4, c2, c0, 1 @ load TTB1 mov r10, #0x1f @ domains 0, 1 = manager mcr p15, 0, r10, c3, c0, 0 @ load domain access register @@ -332,6 +333,8 @@ v7_crval: __v7_setup_stack: .space 4 * 11 @ 11 registers + __INITDATA + .type v7_processor_functions, #object ENTRY(v7_processor_functions) .word v7_early_abort @@ -345,6 +348,8 @@ ENTRY(v7_processor_functions) .word cpu_v7_set_pte_ext .size v7_processor_functions, . - v7_processor_functions + .section ".rodata" + .type cpu_arch_name, #object cpu_arch_name: .asciz "armv7" @@ -362,10 +367,16 @@ cpu_elf_name: __v7_ca9mp_proc_info: .long 0x410fc090 @ Required ID value .long 0xff0ffff0 @ Mask for ID - .long PMD_TYPE_SECT | \ + ALT_SMP(.long \ + PMD_TYPE_SECT | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ | \ - PMD_FLAGS + PMD_FLAGS_SMP) + ALT_UP(.long \ + PMD_TYPE_SECT | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ | \ + PMD_FLAGS_UP) .long PMD_TYPE_SECT | \ PMD_SECT_XN | \ PMD_SECT_AP_WRITE | \ @@ -388,10 +399,16 @@ __v7_ca9mp_proc_info: __v7_proc_info: .long 0x000f0000 @ Required ID value .long 0x000f0000 @ Mask for ID - .long PMD_TYPE_SECT | \ + ALT_SMP(.long \ + PMD_TYPE_SECT | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ | \ + PMD_FLAGS_SMP) + ALT_UP(.long \ + PMD_TYPE_SECT | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ | \ - PMD_FLAGS + PMD_FLAGS_UP) .long PMD_TYPE_SECT | \ PMD_SECT_XN | \ PMD_SECT_AP_WRITE | \ diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S index 361a51e49030..cad07e403044 100644 --- a/arch/arm/mm/proc-xsc3.S +++ b/arch/arm/mm/proc-xsc3.S @@ -404,7 +404,7 @@ ENTRY(cpu_xsc3_set_pte_ext) .align - __INIT + __CPUINIT .type __xsc3_setup, #function __xsc3_setup: diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 14075979bcba..cb245edb2c2b 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -506,7 +506,7 @@ ENTRY(cpu_xscale_set_pte_ext) .align - __INIT + __CPUINIT .type __xscale_setup, #function __xscale_setup: diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S index f3f288a9546d..53cd5b454673 100644 --- a/arch/arm/mm/tlb-v7.S +++ b/arch/arm/mm/tlb-v7.S @@ -13,6 +13,7 @@ */ #include <linux/init.h> #include <linux/linkage.h> +#include <asm/assembler.h> #include <asm/asm-offsets.h> #include <asm/page.h> #include <asm/tlbflush.h> @@ -41,20 +42,15 @@ ENTRY(v7wbi_flush_user_tlb_range) orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA mov r1, r1, lsl #PAGE_SHIFT 1: -#ifdef CONFIG_SMP - mcr p15, 0, r0, c8, c3, 1 @ TLB invalidate U MVA (shareable) -#else - mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate U MVA -#endif + ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) + ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA + add r0, r0, #PAGE_SZ cmp r0, r1 blo 1b mov ip, #0 -#ifdef CONFIG_SMP - mcr p15, 0, ip, c7, c1, 6 @ flush BTAC/BTB Inner Shareable -#else - mcr p15, 0, ip, c7, c5, 6 @ flush BTAC/BTB -#endif + ALT_SMP(mcr p15, 0, ip, c7, c1, 6) @ flush BTAC/BTB Inner Shareable + ALT_UP(mcr p15, 0, ip, c7, c5, 6) @ flush BTAC/BTB dsb mov pc, lr ENDPROC(v7wbi_flush_user_tlb_range) @@ -74,20 +70,14 @@ ENTRY(v7wbi_flush_kern_tlb_range) mov r0, r0, lsl #PAGE_SHIFT mov r1, r1, lsl #PAGE_SHIFT 1: -#ifdef CONFIG_SMP - mcr p15, 0, r0, c8, c3, 1 @ TLB invalidate U MVA (shareable) -#else - mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate U MVA -#endif + ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) + ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA add r0, r0, #PAGE_SZ cmp r0, r1 blo 1b mov r2, #0 -#ifdef CONFIG_SMP - mcr p15, 0, r2, c7, c1, 6 @ flush BTAC/BTB Inner Shareable -#else - mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB -#endif + ALT_SMP(mcr p15, 0, r2, c7, c1, 6) @ flush BTAC/BTB Inner Shareable + ALT_UP(mcr p15, 0, r2, c7, c5, 6) @ flush BTAC/BTB dsb isb mov pc, lr @@ -99,5 +89,6 @@ ENDPROC(v7wbi_flush_kern_tlb_range) ENTRY(v7wbi_tlb_fns) .long v7wbi_flush_user_tlb_range .long v7wbi_flush_kern_tlb_range - .long v7wbi_tlb_flags + ALT_SMP(.long v7wbi_tlb_flags_smp) + ALT_UP(.long v7wbi_tlb_flags_up) .size v7wbi_tlb_fns, . - v7wbi_tlb_fns diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c index 977c8f9a07a2..85e6fd212a41 100644 --- a/arch/arm/plat-nomadik/gpio.c +++ b/arch/arm/plat-nomadik/gpio.c @@ -102,6 +102,22 @@ static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip, writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); } +static void __nmk_gpio_set_output(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int val) +{ + if (val) + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATS); + else + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DATC); +} + +static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, + unsigned offset, int val) +{ + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); + __nmk_gpio_set_output(nmk_chip, offset, val); +} + static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, pin_cfg_t cfg) { @@ -118,20 +134,29 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, [3] /* illegal */ = "??" }; static const char *slpmnames[] = { - [NMK_GPIO_SLPM_INPUT] = "input", - [NMK_GPIO_SLPM_NOCHANGE] = "no-change", + [NMK_GPIO_SLPM_INPUT] = "input/wakeup", + [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", }; int pin = PIN_NUM(cfg); int pull = PIN_PULL(cfg); int af = PIN_ALT(cfg); int slpm = PIN_SLPM(cfg); + int output = PIN_DIR(cfg); + int val = PIN_VAL(cfg); - dev_dbg(nmk_chip->chip.dev, "pin %d: af %s, pull %s, slpm %s\n", - pin, afnames[af], pullnames[pull], slpmnames[slpm]); + dev_dbg(nmk_chip->chip.dev, "pin %d: af %s, pull %s, slpm %s (%s%s)\n", + pin, afnames[af], pullnames[pull], slpmnames[slpm], + output ? "output " : "input", + output ? (val ? "high" : "low") : ""); + + if (output) + __nmk_gpio_make_output(nmk_chip, offset, val); + else { + __nmk_gpio_make_input(nmk_chip, offset); + __nmk_gpio_set_pull(nmk_chip, offset, pull); + } - __nmk_gpio_make_input(nmk_chip, offset); - __nmk_gpio_set_pull(nmk_chip, offset, pull); __nmk_gpio_set_slpm(nmk_chip, offset, slpm); __nmk_gpio_set_mode(nmk_chip, offset, af); } @@ -200,6 +225,10 @@ EXPORT_SYMBOL(nmk_config_pins); * changed to an input (with pullup/down enabled) in sleep and deep sleep. If * @mode is NMK_GPIO_SLPM_NOCHANGE, the pin remains in the state it was * configured even when in sleep and deep sleep. + * + * On DB8500v2 onwards, this setting loses the previous meaning and instead + * indicates if wakeup detection is enabled on the pin. Note that + * enable_irq_wake() will automatically enable wakeup detection. */ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) { @@ -367,7 +396,27 @@ static void nmk_gpio_irq_unmask(unsigned int irq) static int nmk_gpio_irq_set_wake(unsigned int irq, unsigned int on) { - return nmk_gpio_irq_modify(irq, WAKE, on); + struct nmk_gpio_chip *nmk_chip; + unsigned long flags; + int gpio; + + gpio = NOMADIK_IRQ_TO_GPIO(irq); + nmk_chip = get_irq_chip_data(irq); + if (!nmk_chip) + return -EINVAL; + + spin_lock_irqsave(&nmk_chip->lock, flags); +#ifdef CONFIG_ARCH_U8500 + if (cpu_is_u8500v2()) { + __nmk_gpio_set_slpm(nmk_chip, gpio, + on ? NMK_GPIO_SLPM_WAKEUP_ENABLE + : NMK_GPIO_SLPM_WAKEUP_DISABLE); + } +#endif + __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); + spin_unlock_irqrestore(&nmk_chip->lock, flags); + + return 0; } static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) @@ -495,12 +544,8 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, { struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); - u32 bit = 1 << offset; - if (val) - writel(bit, nmk_chip->addr + NMK_GPIO_DATS); - else - writel(bit, nmk_chip->addr + NMK_GPIO_DATC); + __nmk_gpio_set_output(nmk_chip, offset, val); } static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, @@ -509,8 +554,7 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); - writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); - nmk_gpio_set_output(chip, offset, val); + __nmk_gpio_make_output(nmk_chip, offset, val); return 0; } @@ -534,7 +578,7 @@ static struct gpio_chip nmk_gpio_template = { .can_sleep = 0, }; -static int __init nmk_gpio_probe(struct platform_device *dev) +static int __devinit nmk_gpio_probe(struct platform_device *dev) { struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; struct nmk_gpio_chip *nmk_chip; diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h index aba355101f49..67b113d639d8 100644 --- a/arch/arm/plat-nomadik/include/plat/gpio.h +++ b/arch/arm/plat-nomadik/include/plat/gpio.h @@ -65,7 +65,9 @@ enum nmk_gpio_pull { /* Sleep mode */ enum nmk_gpio_slpm { NMK_GPIO_SLPM_INPUT, + NMK_GPIO_SLPM_WAKEUP_ENABLE = NMK_GPIO_SLPM_INPUT, NMK_GPIO_SLPM_NOCHANGE, + NMK_GPIO_SLPM_WAKEUP_DISABLE = NMK_GPIO_SLPM_NOCHANGE, }; extern int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode); diff --git a/arch/arm/plat-nomadik/include/plat/pincfg.h b/arch/arm/plat-nomadik/include/plat/pincfg.h index 7eed11c1038d..8c5ae3f2acf8 100644 --- a/arch/arm/plat-nomadik/include/plat/pincfg.h +++ b/arch/arm/plat-nomadik/include/plat/pincfg.h @@ -19,12 +19,16 @@ * bit 9..10 - Alternate Function Selection * bit 11..12 - Pull up/down state * bit 13 - Sleep mode behaviour + * bit 14 - (sleep mode) Direction + * bit 15 - (sleep mode) Value (if output) * * to facilitate the definition, the following macros are provided * * PIN_CFG_DEFAULT - default config (0): * pull up/down = disabled - * sleep mode = input + * sleep mode = input/wakeup + * (sleep mode) direction = input + * (sleep mode) value = low * * PIN_CFG - default config with alternate function * PIN_CFG_PULL - default config with alternate function and pull up/down @@ -53,8 +57,36 @@ typedef unsigned long pin_cfg_t; #define PIN_SLPM_SHIFT 13 #define PIN_SLPM_MASK (0x1 << PIN_SLPM_SHIFT) #define PIN_SLPM(x) (((x) & PIN_SLPM_MASK) >> PIN_SLPM_SHIFT) -#define PIN_SLPM_INPUT (NMK_GPIO_SLPM_INPUT << PIN_SLPM_SHIFT) +#define PIN_SLPM_MAKE_INPUT (NMK_GPIO_SLPM_INPUT << PIN_SLPM_SHIFT) #define PIN_SLPM_NOCHANGE (NMK_GPIO_SLPM_NOCHANGE << PIN_SLPM_SHIFT) +/* These two replace the above in DB8500v2+ */ +#define PIN_SLPM_WAKEUP_ENABLE (NMK_GPIO_SLPM_WAKEUP_ENABLE << PIN_SLPM_SHIFT) +#define PIN_SLPM_WAKEUP_DISABLE (NMK_GPIO_SLPM_WAKEUP_DISABLE << PIN_SLPM_SHIFT) + +#define PIN_DIR_SHIFT 14 +#define PIN_DIR_MASK (0x1 << PIN_DIR_SHIFT) +#define PIN_DIR(x) (((x) & PIN_DIR_MASK) >> PIN_DIR_SHIFT) +#define PIN_DIR_INPUT (0 << PIN_DIR_SHIFT) +#define PIN_DIR_OUTPUT (1 << PIN_DIR_SHIFT) + +#define PIN_VAL_SHIFT 15 +#define PIN_VAL_MASK (0x1 << PIN_VAL_SHIFT) +#define PIN_VAL(x) (((x) & PIN_VAL_MASK) >> PIN_VAL_SHIFT) +#define PIN_VAL_LOW (0 << PIN_VAL_SHIFT) +#define PIN_VAL_HIGH (1 << PIN_VAL_SHIFT) + +/* Shortcuts. Use these instead of separate DIR and VAL. */ +#define PIN_INPUT PIN_DIR_INPUT +#define PIN_OUTPUT_LOW (PIN_DIR_OUTPUT | PIN_VAL_LOW) +#define PIN_OUTPUT_HIGH (PIN_DIR_OUTPUT | PIN_VAL_HIGH) + +/* + * These are the same as the ones above, but should make more sense to the + * reader when seen along with a setting a pin to AF mode. + */ +#define PIN_SLPM_INPUT PIN_INPUT +#define PIN_SLPM_OUTPUT_LOW PIN_OUTPUT_LOW +#define PIN_SLPM_OUTPUT_HIGH PIN_OUTPUT_HIGH #define PIN_CFG_DEFAULT (PIN_PULL_NONE | PIN_SLPM_INPUT) diff --git a/arch/arm/plat-omap/include/plat/smp.h b/arch/arm/plat-omap/include/plat/smp.h index 5177a9c5a25a..ecd6a488c497 100644 --- a/arch/arm/plat-omap/include/plat/smp.h +++ b/arch/arm/plat-omap/include/plat/smp.h @@ -18,6 +18,7 @@ #define OMAP_ARCH_SMP_H #include <asm/hardware/gic.h> +#include <asm/smp_mpidr.h> /* Needed for secondary core boot */ extern void omap_secondary_startup(void); @@ -33,15 +34,4 @@ static inline void smp_cross_call(const struct cpumask *mask) gic_raise_softirq(mask, 1); } -/* - * Read MPIDR: Multiprocessor affinity register - */ -#define hard_smp_processor_id() \ - ({ \ - unsigned int cpunum; \ - __asm__("mrc p15, 0, %0, c0, c0, 5" \ - : "=r" (cpunum)); \ - cpunum &= 0x0F; \ - }) - #endif |