diff options
Diffstat (limited to 'arch')
90 files changed, 1556 insertions, 214 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 26b0e2397a5..4b0669cbb3b 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -178,4 +178,7 @@ config HAVE_ARCH_MUTEX_CPU_RELAX config HAVE_RCU_TABLE_FREE bool +config ARCH_HAVE_NMI_SAFE_CMPXCHG + bool + source "kernel/gcov/Kconfig" diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index ca2da8da6e9..60cde53d266 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -14,6 +14,7 @@ config ALPHA select AUTO_IRQ_AFFINITY if SMP select GENERIC_IRQ_SHOW select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_HAVE_NMI_SAFE_CMPXCHG help The Alpha is a 64-bit general-purpose processor designed and marketed by the Digital Equipment Corporation of blessed memory, diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 5e1e5419722..1a347f481e5 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -30,6 +30,7 @@ #include <linux/uaccess.h> #include <linux/random.h> #include <linux/hw_breakpoint.h> +#include <linux/cpuidle.h> #include <asm/cacheflush.h> #include <asm/leds.h> @@ -196,7 +197,8 @@ void cpu_idle(void) cpu_relax(); } else { stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); start_critical_timings(); /* * This will eventually be removed - pm_idle diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index e9d689b7c83..197e96f7040 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -10,6 +10,7 @@ config AVR32 select GENERIC_IRQ_PROBE select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW + select ARCH_HAVE_NMI_SAFE_CMPXCHG help AVR32 is a high-performance 32-bit RISC microprocessor core, designed for cost-sensitive embedded applications, with particular diff --git a/arch/cris/arch-v10/drivers/sync_serial.c b/arch/cris/arch-v10/drivers/sync_serial.c index 85026537361..466af40c582 100644 --- a/arch/cris/arch-v10/drivers/sync_serial.c +++ b/arch/cris/arch-v10/drivers/sync_serial.c @@ -158,7 +158,7 @@ static int sync_serial_open(struct inode *inode, struct file *file); static int sync_serial_release(struct inode *inode, struct file *file); static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); -static int sync_serial_ioctl(struct file *file, +static long sync_serial_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static ssize_t sync_serial_write(struct file *file, const char *buf, size_t count, loff_t *ppos); @@ -625,11 +625,11 @@ static int sync_serial_open(struct inode *inode, struct file *file) *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev)); } - ret = 0; + err = 0; out: mutex_unlock(&sync_serial_mutex); - return ret; + return err; } static int sync_serial_release(struct inode *inode, struct file *file) diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index 907cfb5a873..ba0e5965d6e 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -20,6 +20,9 @@ #define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); #define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); +extern void kgdb_init(void); +extern void breakpoint(void); + /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is * global just so that the kernel gdb can use it. */ diff --git a/arch/cris/include/asm/thread_info.h b/arch/cris/include/asm/thread_info.h index 29b74a10583..332f19c5455 100644 --- a/arch/cris/include/asm/thread_info.h +++ b/arch/cris/include/asm/thread_info.h @@ -11,8 +11,6 @@ #ifdef __KERNEL__ -#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR - #ifndef __ASSEMBLY__ #include <asm/types.h> #include <asm/processor.h> @@ -67,8 +65,10 @@ struct thread_info { #define init_thread_info (init_thread_union.thread_info) +#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR /* thread information allocation */ -#define alloc_thread_info(tsk, node) ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define alloc_thread_info_node(tsk, node) \ + ((struct thread_info *) __get_free_pages(GFP_KERNEL, 1)) #define free_thread_info(ti) free_pages((unsigned long) (ti), 1) #endif /* !__ASSEMBLY__ */ diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index cb884e48942..bad27a6ff40 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -7,6 +7,7 @@ config FRV select HAVE_PERF_EVENTS select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW + select ARCH_HAVE_NMI_SAFE_CMPXCHG config ZONE_DMA bool diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 64c7ab7e7a8..12485471495 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -28,6 +28,7 @@ config IA64 select IRQ_PER_CPU select GENERIC_IRQ_SHOW select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_HAVE_NMI_SAFE_CMPXCHG default y help The Itanium Processor Family is Intel's 64-bit successor to diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 284cd3771ea..9e8ee9d2b8c 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -6,6 +6,7 @@ config M68K select GENERIC_ATOMIC64 if MMU select HAVE_GENERIC_HARDIRQS if !MMU select GENERIC_IRQ_SHOW if !MMU + select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS config RWSEM_GENERIC_SPINLOCK bool diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 65adc86a230..e077b0bf56c 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -15,6 +15,7 @@ config PARISC select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select IRQ_PER_CPU + select ARCH_HAVE_NMI_SAFE_CMPXCHG help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h index b1dc71f5534..4054b31e0fa 100644 --- a/arch/parisc/include/asm/atomic.h +++ b/arch/parisc/include/asm/atomic.h @@ -258,10 +258,10 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u) #define ATOMIC64_INIT(i) ((atomic64_t) { (i) }) -static __inline__ int +static __inline__ s64 __atomic64_add_return(s64 i, atomic64_t *v) { - int ret; + s64 ret; unsigned long flags; _atomic_spin_lock_irqsave(v, flags); diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h index 67a33cc27ef..2388bdb3283 100644 --- a/arch/parisc/include/asm/futex.h +++ b/arch/parisc/include/asm/futex.h @@ -5,11 +5,14 @@ #include <linux/futex.h> #include <linux/uaccess.h> +#include <asm/atomic.h> #include <asm/errno.h> static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { + unsigned long int flags; + u32 val; int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; @@ -18,21 +21,58 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr))) return -EFAULT; pagefault_disable(); + _atomic_spin_lock_irqsave(uaddr, flags); + switch (op) { case FUTEX_OP_SET: + /* *(int *)UADDR2 = OPARG; */ + ret = get_user(oldval, uaddr); + if (!ret) + ret = put_user(oparg, uaddr); + break; case FUTEX_OP_ADD: + /* *(int *)UADDR2 += OPARG; */ + ret = get_user(oldval, uaddr); + if (!ret) { + val = oldval + oparg; + ret = put_user(val, uaddr); + } + break; case FUTEX_OP_OR: + /* *(int *)UADDR2 |= OPARG; */ + ret = get_user(oldval, uaddr); + if (!ret) { + val = oldval | oparg; + ret = put_user(val, uaddr); + } + break; case FUTEX_OP_ANDN: + /* *(int *)UADDR2 &= ~OPARG; */ + ret = get_user(oldval, uaddr); + if (!ret) { + val = oldval & ~oparg; + ret = put_user(val, uaddr); + } + break; case FUTEX_OP_XOR: + /* *(int *)UADDR2 ^= OPARG; */ + ret = get_user(oldval, uaddr); + if (!ret) { + val = oldval ^ oparg; + ret = put_user(val, uaddr); + } + break; default: ret = -ENOSYS; } + _atomic_spin_unlock_irqrestore(uaddr, flags); + pagefault_enable(); if (!ret) { @@ -54,7 +94,9 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval) { + int ret; u32 val; + unsigned long flags; /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is * our gateway page, and causes no end of trouble... @@ -65,12 +107,24 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; - if (get_user(val, uaddr)) - return -EFAULT; - if (val == oldval && put_user(newval, uaddr)) - return -EFAULT; + /* HPPA has no cmpxchg in hardware and therefore the + * best we can do here is use an array of locks. The + * lock selected is based on a hash of the userspace + * address. This should scale to a couple of CPUs. + */ + + _atomic_spin_lock_irqsave(uaddr, flags); + + ret = get_user(val, uaddr); + + if (!ret && val == oldval) + ret = put_user(newval, uaddr); + *uval = val; - return 0; + + _atomic_spin_unlock_irqrestore(uaddr, flags); + + return ret; } #endif /*__KERNEL__*/ diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index 3392de3e7be..d61de64f990 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -821,8 +821,9 @@ #define __NR_open_by_handle_at (__NR_Linux + 326) #define __NR_syncfs (__NR_Linux + 327) #define __NR_setns (__NR_Linux + 328) +#define __NR_sendmmsg (__NR_Linux + 329) -#define __NR_Linux_syscalls (__NR_setns + 1) +#define __NR_Linux_syscalls (__NR_sendmmsg + 1) #define __IGNORE_select /* newselect */ diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 34a4f5a2fff..e66366fd2ab 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -427,6 +427,7 @@ ENTRY_COMP(open_by_handle_at) ENTRY_SAME(syncfs) ENTRY_SAME(setns) + ENTRY_COMP(sendmmsg) /* Nothing yet */ diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 374c475e56a..6926b61acfe 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -136,6 +136,7 @@ config PPC select HAVE_SYSCALL_TRACEPOINTS select HAVE_BPF_JIT if (PPC64 && NET) select HAVE_ARCH_JUMP_LABEL + select ARCH_HAVE_NMI_SAFE_CMPXCHG config EARLY_PRINTK bool diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index c03fef7a9c2..ed5cb5af528 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -81,6 +81,7 @@ config S390 select INIT_ALL_POSSIBLE select HAVE_IRQ_WORK select HAVE_PERF_EVENTS + select ARCH_HAVE_NMI_SAFE_CMPXCHG select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZMA @@ -273,11 +274,11 @@ config MARCH_Z10 on older machines. config MARCH_Z196 - bool "IBM zEnterprise 196" + bool "IBM zEnterprise 114 and 196" help - Select this to enable optimizations for IBM zEnterprise 196 - (2817 series). The kernel will be slightly faster but will not work - on older machines. + Select this to enable optimizations for IBM zEnterprise 114 and 196 + (2818 and 2817 series). The kernel will be slightly faster but will + not work on older machines. endchoice diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h index 5e95d95450b..97cc4403fab 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h @@ -167,5 +167,6 @@ enum diag308_rc { }; extern int diag308(unsigned long subcode, void *addr); +extern void diag308_reset(void); #endif /* _ASM_S390_IPL_H */ diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index f26280d9e88..e85c911aabf 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -18,6 +18,7 @@ void system_call(void); void pgm_check_handler(void); void mcck_int_handler(void); void io_int_handler(void); +void psw_restart_int_handler(void); #ifdef CONFIG_32BIT @@ -150,7 +151,10 @@ struct _lowcore { */ __u32 ipib; /* 0x0e00 */ __u32 ipib_checksum; /* 0x0e04 */ - __u8 pad_0x0e08[0x0f00-0x0e08]; /* 0x0e08 */ + + /* 64 bit save area */ + __u64 save_area_64; /* 0x0e08 */ + __u8 pad_0x0e10[0x0f00-0x0e10]; /* 0x0e10 */ /* Extended facility list */ __u64 stfle_fac_list[32]; /* 0x0f00 */ @@ -286,7 +290,10 @@ struct _lowcore { */ __u64 ipib; /* 0x0e00 */ __u32 ipib_checksum; /* 0x0e08 */ - __u8 pad_0x0e0c[0x0f00-0x0e0c]; /* 0x0e0c */ + + /* 64 bit save area */ + __u64 save_area_64; /* 0x0e0c */ + __u8 pad_0x0e14[0x0f00-0x0e14]; /* 0x0e14 */ /* Extended facility list */ __u64 stfle_fac_list[32]; /* 0x0f00 */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 55dfcc8bdc0..a4b6229e5d4 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -119,14 +119,12 @@ struct stack_frame { * Do necessary setup to start up a new thread. */ #define start_thread(regs, new_psw, new_stackp) do { \ - set_fs(USER_DS); \ regs->psw.mask = psw_user_bits; \ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ regs->gprs[15] = new_stackp; \ } while (0) #define start_thread31(regs, new_psw, new_stackp) do { \ - set_fs(USER_DS); \ regs->psw.mask = psw_user32_bits; \ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ regs->gprs[15] = new_stackp; \ diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h index d382629a017..6582f69f238 100644 --- a/arch/s390/include/asm/system.h +++ b/arch/s390/include/asm/system.h @@ -113,6 +113,7 @@ extern void pfault_fini(void); extern void cmma_init(void); extern int memcpy_real(void *, void *, size_t); +extern void copy_to_absolute_zero(void *dest, void *src, size_t count); #define finish_arch_switch(prev) do { \ set_fs(current->thread.mm_segment); \ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 05d8f38734e..532fd432215 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -27,12 +27,9 @@ int main(void) BLANK(); DEFINE(__TASK_pid, offsetof(struct task_struct, pid)); BLANK(); - DEFINE(__THREAD_per_cause, - offsetof(struct task_struct, thread.per_event.cause)); - DEFINE(__THREAD_per_address, - offsetof(struct task_struct, thread.per_event.address)); - DEFINE(__THREAD_per_paid, - offsetof(struct task_struct, thread.per_event.paid)); + DEFINE(__THREAD_per_cause, offsetof(struct task_struct, thread.per_event.cause)); + DEFINE(__THREAD_per_address, offsetof(struct task_struct, thread.per_event.address)); + DEFINE(__THREAD_per_paid, offsetof(struct task_struct, thread.per_event.paid)); BLANK(); DEFINE(__TI_task, offsetof(struct thread_info, task)); DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain)); @@ -142,6 +139,7 @@ int main(void) DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area)); DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area)); DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area)); + DEFINE(__LC_SAVE_AREA_64, offsetof(struct _lowcore, save_area_64)); #ifdef CONFIG_32BIT DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr)); #else /* CONFIG_32BIT */ diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S index 209938c1dfc..255435663bf 100644 --- a/arch/s390/kernel/base.S +++ b/arch/s390/kernel/base.S @@ -76,6 +76,42 @@ s390_base_pgm_handler_fn: .quad 0 .previous +# +# Calls diag 308 subcode 1 and continues execution +# +# The following conditions must be ensured before calling this function: +# * Prefix register = 0 +# * Lowcore protection is disabled +# +ENTRY(diag308_reset) + larl %r4,.Lctlregs # Save control registers + stctg %c0,%c15,0(%r4) + larl %r4,.Lrestart_psw # Setup restart PSW at absolute 0 + lghi %r3,0 + lg %r4,0(%r4) # Save PSW + sturg %r4,%r3 # Use sturg, because of large pages + lghi %r1,1 + diag %r1,%r1,0x308 +.Lrestart_part2: + lhi %r0,0 # Load r0 with zero + lhi %r1,2 # Use mode 2 = ESAME (dump) + sigp %r1,%r0,0x12 # Switch to ESAME mode + sam64 # Switch to 64 bit addressing mode + larl %r4,.Lctlregs # Restore control registers + lctlg %c0,%c15,0(%r4) + br %r14 +.align 16 +.Lrestart_psw: + .long 0x00080000,0x80000000 + .Lrestart_part2 + + .section .bss +.align 8 +.Lctlregs: + .rept 16 + .quad 0 + .endr + .previous + #else /* CONFIG_64BIT */ ENTRY(s390_base_mcck_handler) diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index eee999853a7..a9a285b8c4a 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -380,20 +380,13 @@ asmlinkage long sys32_sigreturn(void) goto badframe; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - + set_current_blocked(&set); if (restore_sigregs32(regs, &frame->sregs)) goto badframe; if (restore_sigregs_gprs_high(regs, frame->gprs_high)) goto badframe; - return regs->gprs[2]; - badframe: force_sig(SIGSEGV, current); return 0; @@ -413,31 +406,22 @@ asmlinkage long sys32_rt_sigreturn(void) goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - + set_current_blocked(&set); if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) goto badframe; if (restore_sigregs_gprs_high(regs, frame->gprs_high)) goto badframe; - err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp); st.ss_sp = compat_ptr(ss_sp); err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size); err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags); if (err) goto badframe; - set_fs (KERNEL_DS); do_sigaltstack((stack_t __force __user *)&st, NULL, regs->gprs[15]); set_fs (old_fs); - return regs->gprs[2]; - badframe: force_sig(SIGSEGV, current); return 0; @@ -605,10 +589,10 @@ give_sigsegv: * OK, we're invoking a handler */ -int -handle_signal32(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { + sigset_t blocked; int ret; /* Set up the stack frame */ @@ -616,15 +600,12 @@ handle_signal32(unsigned long sig, struct k_sigaction *ka, ret = setup_rt_frame32(sig, ka, info, oldset, regs); else ret = setup_frame32(sig, ka, oldset, regs); - - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - return ret; + if (ret) + return ret; + sigorsets(&blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(&blocked, sig); + set_current_blocked(&blocked); + return 0; } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 3eab7cfab07..02ec8fe7d03 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -849,6 +849,34 @@ restart_crash: restart_go: #endif +# +# PSW restart interrupt handler +# +ENTRY(psw_restart_int_handler) + st %r15,__LC_SAVE_AREA_64(%r0) # save r15 + basr %r15,0 +0: l %r15,.Lrestart_stack-0b(%r15) # load restart stack + l %r15,0(%r15) + ahi %r15,-SP_SIZE # make room for pt_regs + stm %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack + mvc SP_R15(4,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack + mvc SP_PSW(8,%r15),__LC_RST_OLD_PSW(%r0) # store restart old psw + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0 + basr %r14,0 +1: l %r14,.Ldo_restart-1b(%r14) + basr %r14,%r14 + + basr %r14,0 # load disabled wait PSW if +2: lpsw restart_psw_crash-2b(%r14) # do_restart returns + .align 4 +.Ldo_restart: + .long do_restart +.Lrestart_stack: + .long restart_stack + .align 8 +restart_psw_crash: + .long 0x000a0000,0x00000000 + restart_psw_crash + .section .kprobes.text, "ax" #ifdef CONFIG_CHECK_STACK diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 7a0fd426ca9..5f729d627ce 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -865,6 +865,26 @@ restart_crash: restart_go: #endif +# +# PSW restart interrupt handler +# +ENTRY(psw_restart_int_handler) + stg %r15,__LC_SAVE_AREA_64(%r0) # save r15 + larl %r15,restart_stack # load restart stack + lg %r15,0(%r15) + aghi %r15,-SP_SIZE # make room for pt_regs + stmg %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack + mvc SP_R15(8,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack + mvc SP_PSW(16,%r15),__LC_RST_OLD_PSW(%r0)# store restart old psw + xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0 + brasl %r14,do_restart + + larl %r14,restart_psw_crash # load disabled wait PSW if + lpswe 0(%r14) # do_restart returns + .align 8 +restart_psw_crash: + .quad 0x0002000080000000,0x0000000000000000 + restart_psw_crash + .section .kprobes.text, "ax" #ifdef CONFIG_CHECK_STACK diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index a689070be28..04361d5a427 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -45,11 +45,13 @@ * - halt * - power off * - reipl + * - restart */ #define ON_PANIC_STR "on_panic" #define ON_HALT_STR "on_halt" #define ON_POFF_STR "on_poff" #define ON_REIPL_STR "on_reboot" +#define ON_RESTART_STR "on_restart" struct shutdown_action; struct shutdown_trigger { @@ -1544,17 +1546,20 @@ static char vmcmd_on_reboot[128]; static char vmcmd_on_panic[128]; static char vmcmd_on_halt[128]; static char vmcmd_on_poff[128]; +static char vmcmd_on_restart[128]; DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); +DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart); static struct attribute *vmcmd_attrs[] = { &sys_vmcmd_on_reboot_attr.attr, &sys_vmcmd_on_panic_attr.attr, &sys_vmcmd_on_halt_attr.attr, &sys_vmcmd_on_poff_attr.attr, + &sys_vmcmd_on_restart_attr.attr, NULL, }; @@ -1576,6 +1581,8 @@ static void vmcmd_run(struct shutdown_trigger *trigger) cmd = vmcmd_on_halt; else if (strcmp(trigger->name, ON_POFF_STR) == 0) cmd = vmcmd_on_poff; + else if (strcmp(trigger->name, ON_RESTART_STR) == 0) + cmd = vmcmd_on_restart; else return; @@ -1707,6 +1714,34 @@ static void do_panic(void) stop_run(&on_panic_trigger); } +/* on restart */ + +static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR, + &reipl_action}; + +static ssize_t on_restart_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", on_restart_trigger.action->name); +} + +static ssize_t on_restart_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return set_trigger(buf, &on_restart_trigger, len); +} + +static struct kobj_attribute on_restart_attr = + __ATTR(on_restart, 0644, on_restart_show, on_restart_store); + +void do_restart(void) +{ + smp_send_stop(); + on_restart_trigger.action->fn(&on_restart_trigger); + stop_run(&on_restart_trigger); +} + /* on halt */ static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; @@ -1783,7 +1818,9 @@ static void __init shutdown_triggers_init(void) if (sysfs_create_file(&shutdown_actions_kset->kobj, &on_poff_attr.attr)) goto fail; - + if (sysfs_create_file(&shutdown_actions_kset->kobj, + &on_restart_attr.attr)) + goto fail; return; fail: panic("shutdown_triggers_init failed\n"); @@ -1959,6 +1996,12 @@ static void do_reset_calls(void) { struct reset_call *reset; +#ifdef CONFIG_64BIT + if (diag308_set_works) { + diag308_reset(); + return; + } +#endif list_for_each_entry(reset, &rcall, list) reset->fn(); } diff --git a/arch/s390/kernel/reipl64.S b/arch/s390/kernel/reipl64.S index 78eb7cfbd3d..e690975403f 100644 --- a/arch/s390/kernel/reipl64.S +++ b/arch/s390/kernel/reipl64.S @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp 2000,2009 + * Copyright IBM Corp 2000,2011 * Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com>, * Denis Joseph Barrow, */ @@ -8,6 +8,64 @@ #include <asm/asm-offsets.h> # +# store_status +# +# Prerequisites to run this function: +# - Prefix register is set to zero +# - Original prefix register is stored in "dump_prefix_page" +# - Lowcore protection is off +# +ENTRY(store_status) + /* Save register one and load save area base */ + stg %r1,__LC_SAVE_AREA_64(%r0) + lghi %r1,SAVE_AREA_BASE + /* General purpose registers */ + stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + lg %r2,__LC_SAVE_AREA_64(%r0) + stg %r2,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE+8(%r1) + /* Control registers */ + stctg %c0,%c15,__LC_CREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + /* Access registers */ + stam %a0,%a15,__LC_AREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + /* Floating point registers */ + std %f0, 0x00 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f1, 0x08 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f2, 0x10 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f3, 0x18 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f4, 0x20 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f5, 0x28 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f6, 0x30 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f7, 0x38 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f8, 0x40 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f9, 0x48 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f10,0x50 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f11,0x58 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f12,0x60 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f13,0x68 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f14,0x70 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + std %f15,0x78 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1) + /* Floating point control register */ + stfpc __LC_FP_CREG_SAVE_AREA-SAVE_AREA_BASE(%r1) + /* CPU timer */ + stpt __LC_CPU_TIMER_SAVE_AREA-SAVE_AREA_BASE(%r1) + /* Saved prefix register */ + larl %r2,dump_prefix_page + mvc __LC_PREFIX_SAVE_AREA-SAVE_AREA_BASE(4,%r1),0(%r2) + /* Clock comparator - seven bytes */ + larl %r2,.Lclkcmp + stckc 0(%r2) + mvc __LC_CLOCK_COMP_SAVE_AREA-SAVE_AREA_BASE + 1(7,%r1),1(%r2) + /* Program status word */ + epsw %r2,%r3 + st %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 0(%r1) + st %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1) + larl %r2,store_status + stg %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1) + br %r14 +.align 8 +.Lclkcmp: .quad 0x0000000000000000 + +# # do_reipl_asm # Parameter: r2 = schid of reipl device # @@ -15,22 +73,7 @@ ENTRY(do_reipl_asm) basr %r13,0 .Lpg0: lpswe .Lnewpsw-.Lpg0(%r13) -.Lpg1: # do store status of all registers - - stg %r1,.Lregsave-.Lpg0(%r13) - lghi %r1,0x1000 - stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1) - lg %r0,.Lregsave-.Lpg0(%r13) - stg %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1) - stctg %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1) - stam %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1) - lg %r10,.Ldump_pfx-.Lpg0(%r13) - mvc __LC_PREFIX_SAVE_AREA-0x1000(4,%r1),0(%r10) - stfpc __LC_FP_CREG_SAVE_AREA-0x1000(%r1) - stckc .Lclkcmp-.Lpg0(%r13) - mvc __LC_CLOCK_COMP_SAVE_AREA-0x1000(7,%r1),.Lclkcmp-.Lpg0(%r13) - stpt __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1) - stg %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1) +.Lpg1: brasl %r14,store_status lctlg %c6,%c6,.Lall-.Lpg0(%r13) lgr %r1,%r2 @@ -67,10 +110,7 @@ ENTRY(do_reipl_asm) st %r14,.Ldispsw+12-.Lpg0(%r13) lpswe .Ldispsw-.Lpg0(%r13) .align 8 -.Lclkcmp: .quad 0x0000000000000000 .Lall: .quad 0x00000000ff000000 -.Ldump_pfx: .quad dump_prefix_page -.Lregsave: .quad 0x0000000000000000 .align 16 /* * These addresses have to be 31 bit otherwise diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 0c35dee10b0..7b371c37061 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -346,7 +346,7 @@ setup_lowcore(void) lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0); lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; lc->restart_psw.addr = - PSW_ADDR_AMODE | (unsigned long) restart_int_handler; + PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler; if (user_mode != HOME_SPACE_MODE) lc->restart_psw.mask |= PSW_ASC_HOME; lc->external_new_psw.mask = psw_kernel_bits; @@ -529,6 +529,27 @@ static void __init setup_memory_end(void) memory_end = memory_size; } +void *restart_stack __attribute__((__section__(".data"))); + +/* + * Setup new PSW and allocate stack for PSW restart interrupt + */ +static void __init setup_restart_psw(void) +{ + psw_t psw; + + restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0); + restart_stack += ASYNC_SIZE; + + /* + * Setup restart PSW for absolute zero lowcore. This is necesary + * if PSW restart is done on an offline CPU that has lowcore zero + */ + psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; + psw.addr = PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler; + copy_to_absolute_zero(&S390_lowcore.restart_psw, &psw, sizeof(psw)); +} + static void __init setup_memory(void) { @@ -731,6 +752,7 @@ static void __init setup_hwcaps(void) strcpy(elf_platform, "z10"); break; case 0x2817: + case 0x2818: strcpy(elf_platform, "z196"); break; } @@ -792,6 +814,7 @@ setup_arch(char **cmdline_p) setup_addressing_mode(); setup_memory(); setup_resources(); + setup_restart_psw(); setup_lowcore(); cpu_init(); diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index abbb3c3c7aa..9a40e1cc5ec 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -57,17 +57,15 @@ typedef struct */ SYSCALL_DEFINE3(sigsuspend, int, history0, int, history1, old_sigset_t, mask) { - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - current->saved_sigmask = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + sigset_t blocked; + current->saved_sigmask = current->blocked; + mask &= _BLOCKABLE; + siginitset(&blocked, mask); + set_current_blocked(&blocked); set_current_state(TASK_INTERRUPTIBLE); schedule(); - set_thread_flag(TIF_RESTORE_SIGMASK); - + set_restore_sigmask(); return -ERESTARTNOHAND; } @@ -172,18 +170,11 @@ SYSCALL_DEFINE0(sigreturn) goto badframe; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - + set_current_blocked(&set); if (restore_sigregs(regs, &frame->sregs)) goto badframe; - return regs->gprs[2]; - badframe: force_sig(SIGSEGV, current); return 0; @@ -199,21 +190,14 @@ SYSCALL_DEFINE0(rt_sigreturn) goto badframe; if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - + set_current_blocked(&set); if (restore_sigregs(regs, &frame->uc.uc_mcontext)) goto badframe; - if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]) == -EFAULT) goto badframe; return regs->gprs[2]; - badframe: force_sig(SIGSEGV, current); return 0; @@ -385,14 +369,11 @@ give_sigsegv: return -EFAULT; } -/* - * OK, we're invoking a handler - */ - -static int -handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +static int handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs) { + sigset_t blocked; int ret; /* Set up the stack frame */ @@ -400,17 +381,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ret = setup_rt_frame(sig, ka, info, oldset, regs); else ret = setup_frame(sig, ka, oldset, regs); - - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - return ret; + if (ret) + return ret; + sigorsets(&blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(&blocked, sig); + set_current_blocked(&blocked); + return 0; } /* diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index a6d85c0a7f2..6ab16ac64d2 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -452,23 +452,27 @@ out: */ int __cpuinit start_secondary(void *cpuvoid) { - /* Setup the cpu */ cpu_init(); preempt_disable(); - /* Enable TOD clock interrupts on the secondary cpu. */ init_cpu_timer(); - /* Enable cpu timer interrupts on the secondary cpu. */ init_cpu_vtimer(); - /* Enable pfault pseudo page faults on this cpu. */ pfault_init(); - /* call cpu notifiers */ notify_cpu_starting(smp_processor_id()); - /* Mark this cpu as online */ ipi_call_lock(); set_cpu_online(smp_processor_id(), true); ipi_call_unlock(); - /* Switch on interrupts */ + __ctl_clear_bit(0, 28); /* Disable lowcore protection */ + S390_lowcore.restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; + S390_lowcore.restart_psw.addr = + PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler; + __ctl_set_bit(0, 28); /* Enable lowcore protection */ + /* + * Wait until the cpu which brought this one up marked it + * active before enabling interrupts. + */ + while (!cpumask_test_cpu(smp_processor_id(), cpu_active_mask)) + cpu_relax(); local_irq_enable(); /* cpu_idle will call schedule for us */ cpu_idle(); @@ -507,7 +511,11 @@ static int __cpuinit smp_alloc_lowcore(int cpu) memset((char *)lowcore + 512, 0, sizeof(*lowcore) - 512); lowcore->async_stack = async_stack + ASYNC_SIZE; lowcore->panic_stack = panic_stack + PAGE_SIZE; - + lowcore->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; + lowcore->restart_psw.addr = + PSW_ADDR_AMODE | (unsigned long) restart_int_handler; + if (user_mode != HOME_SPACE_MODE) + lowcore->restart_psw.mask |= PSW_ASC_HOME; #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) { unsigned long save_area; diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 51e5cd9b906..5dbbaa6e594 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -85,3 +85,19 @@ int memcpy_real(void *dest, void *src, size_t count) arch_local_irq_restore(flags); return rc; } + +/* + * Copy memory to absolute zero + */ +void copy_to_absolute_zero(void *dest, void *src, size_t count) +{ + unsigned long cr0; + + BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore)); + preempt_disable(); + __ctl_store(cr0, 0, 0); + __ctl_clear_bit(0, 28); /* disable lowcore protection */ + memcpy_real(dest + store_prefix(), src, count); + __ctl_load(cr0, 0, 0); + preempt_enable(); +} diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 2adb23938a7..4d1f2bce87b 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -528,6 +528,7 @@ static inline void page_table_free_pgste(unsigned long *table) static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, unsigned long vmaddr) { + return NULL; } static inline void page_table_free_pgste(unsigned long *table) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 748ff192006..ff9177c8f64 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -11,6 +11,7 @@ config SUPERH select HAVE_DMA_ATTRS select HAVE_IRQ_WORK select HAVE_PERF_EVENTS + select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A) select PERF_USE_VMALLOC select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c index 84db0d6ccd0..32114e0941a 100644 --- a/arch/sh/kernel/idle.c +++ b/arch/sh/kernel/idle.c @@ -16,12 +16,13 @@ #include <linux/thread_info.h> #include <linux/irqflags.h> #include <linux/smp.h> +#include <linux/cpuidle.h> #include <asm/pgalloc.h> #include <asm/system.h> #include <linux/atomic.h> #include <asm/smp.h> -void (*pm_idle)(void) = NULL; +static void (*pm_idle)(void); static int hlt_counter; @@ -100,7 +101,8 @@ void cpu_idle(void) local_irq_disable(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); /* * Sanity check to ensure that pm_idle() returns * with IRQs enabled diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 1074dddcb10..42c67beadca 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -54,6 +54,7 @@ config SPARC64 select HAVE_PERF_EVENTS select PERF_USE_VMALLOC select IRQ_PREFLOW_FASTEOI + select ARCH_HAVE_NMI_SAFE_CMPXCHG config ARCH_DEFCONFIG string diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 0249b8b4db5..b30f71ac0d0 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -12,6 +12,7 @@ config TILE select GENERIC_PENDING_IRQ if SMP select GENERIC_IRQ_SHOW select SYS_HYPERVISOR + select ARCH_HAVE_NMI_SAFE_CMPXCHG if !M386 # FIXME: investigate whether we need/want these options. # select HAVE_IOREMAP_PROT diff --git a/arch/tile/include/asm/Kbuild b/arch/tile/include/asm/Kbuild index 849ab2fa1f5..aec60dc0600 100644 --- a/arch/tile/include/asm/Kbuild +++ b/arch/tile/include/asm/Kbuild @@ -2,3 +2,41 @@ include include/asm-generic/Kbuild.asm header-y += ucontext.h header-y += hardwall.h + +generic-y += bug.h +generic-y += bugs.h +generic-y += cputime.h +generic-y += device.h +generic-y += div64.h +generic-y += emergency-restart.h +generic-y += errno.h +generic-y += fb.h +generic-y += fcntl.h +generic-y += ioctl.h +generic-y += ioctls.h +generic-y += ipc.h +generic-y += ipcbuf.h +generic-y += irq_regs.h +generic-y += kdebug.h +generic-y += local.h +generic-y += module.h +generic-y += msgbuf.h +generic-y += mutex.h +generic-y += param.h +generic-y += parport.h +generic-y += poll.h +generic-y += posix_types.h +generic-y += resource.h +generic-y += scatterlist.h +generic-y += sembuf.h +generic-y += serial.h +generic-y += shmbuf.h +generic-y += shmparam.h +generic-y += socket.h +generic-y += sockios.h +generic-y += statfs.h +generic-y += termbits.h +generic-y += termios.h +generic-y += types.h +generic-y += ucontext.h +generic-y += xor.h diff --git a/arch/tile/include/asm/bug.h b/arch/tile/include/asm/bug.h deleted file mode 100644 index b12fd89e42e..00000000000 --- a/arch/tile/include/asm/bug.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/bug.h> diff --git a/arch/tile/include/asm/bugs.h b/arch/tile/include/asm/bugs.h deleted file mode 100644 index 61791e1ad9f..00000000000 --- a/arch/tile/include/asm/bugs.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/bugs.h> diff --git a/arch/tile/include/asm/cputime.h b/arch/tile/include/asm/cputime.h deleted file mode 100644 index 6d68ad7e0ea..00000000000 --- a/arch/tile/include/asm/cputime.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/cputime.h> diff --git a/arch/tile/include/asm/device.h b/arch/tile/include/asm/device.h deleted file mode 100644 index f0a4c256403..00000000000 --- a/arch/tile/include/asm/device.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/device.h> diff --git a/arch/tile/include/asm/div64.h b/arch/tile/include/asm/div64.h deleted file mode 100644 index 6cd978cefb2..00000000000 --- a/arch/tile/include/asm/div64.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/div64.h> diff --git a/arch/tile/include/asm/emergency-restart.h b/arch/tile/include/asm/emergency-restart.h deleted file mode 100644 index 3711bd9d50b..00000000000 --- a/arch/tile/include/asm/emergency-restart.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/emergency-restart.h> diff --git a/arch/tile/include/asm/errno.h b/arch/tile/include/asm/errno.h deleted file mode 100644 index 4c82b503d92..00000000000 --- a/arch/tile/include/asm/errno.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/errno.h> diff --git a/arch/tile/include/asm/fb.h b/arch/tile/include/asm/fb.h deleted file mode 100644 index 3a4988e8df4..00000000000 --- a/arch/tile/include/asm/fb.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/fb.h> diff --git a/arch/tile/include/asm/fcntl.h b/arch/tile/include/asm/fcntl.h deleted file mode 100644 index 46ab12db573..00000000000 --- a/arch/tile/include/asm/fcntl.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/fcntl.h> diff --git a/arch/tile/include/asm/fixmap.h b/arch/tile/include/asm/fixmap.h index 51537ff9265..c66f7933bea 100644 --- a/arch/tile/include/asm/fixmap.h +++ b/arch/tile/include/asm/fixmap.h @@ -75,12 +75,6 @@ extern void __set_fixmap(enum fixed_addresses idx, #define set_fixmap(idx, phys) \ __set_fixmap(idx, phys, PAGE_KERNEL) -/* - * Some hardware wants to get fixmapped without caching. - */ -#define set_fixmap_nocache(idx, phys) \ - __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE) - #define clear_fixmap(idx) \ __set_fixmap(idx, 0, __pgprot(0)) diff --git a/arch/tile/include/asm/ioctl.h b/arch/tile/include/asm/ioctl.h deleted file mode 100644 index b279fe06dfe..00000000000 --- a/arch/tile/include/asm/ioctl.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/ioctl.h> diff --git a/arch/tile/include/asm/ioctls.h b/arch/tile/include/asm/ioctls.h deleted file mode 100644 index ec34c760665..00000000000 --- a/arch/tile/include/asm/ioctls.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/ioctls.h> diff --git a/arch/tile/include/asm/ipc.h b/arch/tile/include/asm/ipc.h deleted file mode 100644 index a46e3d9c2a3..00000000000 --- a/arch/tile/include/asm/ipc.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/ipc.h> diff --git a/arch/tile/include/asm/ipcbuf.h b/arch/tile/include/asm/ipcbuf.h deleted file mode 100644 index 84c7e51cb6d..00000000000 --- a/arch/tile/include/asm/ipcbuf.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/ipcbuf.h> diff --git a/arch/tile/include/asm/irq_regs.h b/arch/tile/include/asm/irq_regs.h deleted file mode 100644 index 3dd9c0b7027..00000000000 --- a/arch/tile/include/asm/irq_regs.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/irq_regs.h> diff --git a/arch/tile/include/asm/kdebug.h b/arch/tile/include/asm/kdebug.h deleted file mode 100644 index 6ece1b03766..00000000000 --- a/arch/tile/include/asm/kdebug.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/kdebug.h> diff --git a/arch/tile/include/asm/local.h b/arch/tile/include/asm/local.h deleted file mode 100644 index c11c530f74d..00000000000 --- a/arch/tile/include/asm/local.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/local.h> diff --git a/arch/tile/include/asm/module.h b/arch/tile/include/asm/module.h deleted file mode 100644 index 1e4b79fe858..00000000000 --- a/arch/tile/include/asm/module.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/module.h> diff --git a/arch/tile/include/asm/msgbuf.h b/arch/tile/include/asm/msgbuf.h deleted file mode 100644 index 809134c644a..00000000000 --- a/arch/tile/include/asm/msgbuf.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/msgbuf.h> diff --git a/arch/tile/include/asm/mutex.h b/arch/tile/include/asm/mutex.h deleted file mode 100644 index ff6101aa2c7..00000000000 --- a/arch/tile/include/asm/mutex.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/mutex-dec.h> diff --git a/arch/tile/include/asm/param.h b/arch/tile/include/asm/param.h deleted file mode 100644 index 965d4542797..00000000000 --- a/arch/tile/include/asm/param.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/param.h> diff --git a/arch/tile/include/asm/parport.h b/arch/tile/include/asm/parport.h deleted file mode 100644 index cf252af6459..00000000000 --- a/arch/tile/include/asm/parport.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/parport.h> diff --git a/arch/tile/include/asm/poll.h b/arch/tile/include/asm/poll.h deleted file mode 100644 index c98509d3149..00000000000 --- a/arch/tile/include/asm/poll.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/poll.h> diff --git a/arch/tile/include/asm/posix_types.h b/arch/tile/include/asm/posix_types.h deleted file mode 100644 index 22cae6230ce..00000000000 --- a/arch/tile/include/asm/posix_types.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/posix_types.h> diff --git a/arch/tile/include/asm/resource.h b/arch/tile/include/asm/resource.h deleted file mode 100644 index 04bc4db8921..00000000000 --- a/arch/tile/include/asm/resource.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/resource.h> diff --git a/arch/tile/include/asm/scatterlist.h b/arch/tile/include/asm/scatterlist.h deleted file mode 100644 index 35d786fe93a..00000000000 --- a/arch/tile/include/asm/scatterlist.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/scatterlist.h> diff --git a/arch/tile/include/asm/sembuf.h b/arch/tile/include/asm/sembuf.h deleted file mode 100644 index 7673b83cfef..00000000000 --- a/arch/tile/include/asm/sembuf.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/sembuf.h> diff --git a/arch/tile/include/asm/serial.h b/arch/tile/include/asm/serial.h deleted file mode 100644 index a0cb0caff15..00000000000 --- a/arch/tile/include/asm/serial.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/serial.h> diff --git a/arch/tile/include/asm/shmbuf.h b/arch/tile/include/asm/shmbuf.h deleted file mode 100644 index 83c05fc2de3..00000000000 --- a/arch/tile/include/asm/shmbuf.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/shmbuf.h> diff --git a/arch/tile/include/asm/shmparam.h b/arch/tile/include/asm/shmparam.h deleted file mode 100644 index 93f30deb95d..00000000000 --- a/arch/tile/include/asm/shmparam.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/shmparam.h> diff --git a/arch/tile/include/asm/socket.h b/arch/tile/include/asm/socket.h deleted file mode 100644 index 6b71384b9d8..00000000000 --- a/arch/tile/include/asm/socket.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/socket.h> diff --git a/arch/tile/include/asm/sockios.h b/arch/tile/include/asm/sockios.h deleted file mode 100644 index def6d4746ee..00000000000 --- a/arch/tile/include/asm/sockios.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/sockios.h> diff --git a/arch/tile/include/asm/statfs.h b/arch/tile/include/asm/statfs.h deleted file mode 100644 index 0b91fe198c2..00000000000 --- a/arch/tile/include/asm/statfs.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/statfs.h> diff --git a/arch/tile/include/asm/termbits.h b/arch/tile/include/asm/termbits.h deleted file mode 100644 index 3935b106de7..00000000000 --- a/arch/tile/include/asm/termbits.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/termbits.h> diff --git a/arch/tile/include/asm/termios.h b/arch/tile/include/asm/termios.h deleted file mode 100644 index 280d78a9d96..00000000000 --- a/arch/tile/include/asm/termios.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/termios.h> diff --git a/arch/tile/include/asm/types.h b/arch/tile/include/asm/types.h deleted file mode 100644 index b9e79bc580d..00000000000 --- a/arch/tile/include/asm/types.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/types.h> diff --git a/arch/tile/include/asm/ucontext.h b/arch/tile/include/asm/ucontext.h deleted file mode 100644 index 9bc07b9f30f..00000000000 --- a/arch/tile/include/asm/ucontext.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/ucontext.h> diff --git a/arch/tile/include/asm/xor.h b/arch/tile/include/asm/xor.h deleted file mode 100644 index c82eb12a5b1..00000000000 --- a/arch/tile/include/asm/xor.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-generic/xor.h> diff --git a/arch/tile/include/hv/drv_srom_intf.h b/arch/tile/include/hv/drv_srom_intf.h new file mode 100644 index 00000000000..6395faa6d9e --- /dev/null +++ b/arch/tile/include/hv/drv_srom_intf.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/** + * @file drv_srom_intf.h + * Interface definitions for the SPI Flash ROM driver. + */ + +#ifndef _SYS_HV_INCLUDE_DRV_SROM_INTF_H +#define _SYS_HV_INCLUDE_DRV_SROM_INTF_H + +/** Read this offset to get the total device size. */ +#define SROM_TOTAL_SIZE_OFF 0xF0000000 + +/** Read this offset to get the device sector size. */ +#define SROM_SECTOR_SIZE_OFF 0xF0000004 + +/** Read this offset to get the device page size. */ +#define SROM_PAGE_SIZE_OFF 0xF0000008 + +/** Write this offset to flush any pending writes. */ +#define SROM_FLUSH_OFF 0xF1000000 + +/** Write this offset, plus the byte offset of the start of a sector, to + * erase a sector. Any write data is ignored, but there must be at least + * one byte of write data. Only applies when the driver is in MTD mode. + */ +#define SROM_ERASE_OFF 0xF2000000 + +#endif /* _SYS_HV_INCLUDE_DRV_SROM_INTF_H */ diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c index c4be58cc5d5..f6f50f2a5e3 100644 --- a/arch/tile/kernel/time.c +++ b/arch/tile/kernel/time.c @@ -78,7 +78,6 @@ static struct clocksource cycle_counter_cs = { .rating = 300, .read = clocksource_get_cycles, .mask = CLOCKSOURCE_MASK(64), - .shift = 22, /* typical value, e.g. x86 tsc uses this */ .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -91,8 +90,6 @@ void __init setup_clock(void) cycles_per_sec = hv_sysconf(HV_SYSCONF_CPU_SPEED); sched_clock_mult = clocksource_hz2mult(cycles_per_sec, SCHED_CLOCK_SHIFT); - cycle_counter_cs.mult = - clocksource_hz2mult(cycles_per_sec, cycle_counter_cs.shift); } void __init calibrate_delay(void) @@ -107,7 +104,7 @@ void __init calibrate_delay(void) void __init time_init(void) { /* Initialize and register the clock source. */ - clocksource_register(&cycle_counter_cs); + clocksource_register_hz(&cycle_counter_cs, cycles_per_sec); /* Start up the tile-timer interrupt source on the boot cpu. */ setup_tile_timer(); diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index 4e10c402302..7309988c979 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -836,8 +836,7 @@ void __init mem_init(void) #endif #ifdef CONFIG_FLATMEM - if (!mem_map) - BUG(); + BUG_ON(!mem_map); #endif #ifdef CONFIG_HIGHMEM diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7cf916fc1ce..6a47bb22657 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -72,6 +72,7 @@ config X86 select USE_GENERIC_SMP_HELPERS if SMP select HAVE_BPF_JIT if (X86_64 && NET) select CLKEVT_I8253 + select ARCH_HAVE_NMI_SAFE_CMPXCHG config INSTRUCTION_DECODER def_bool (KPROBES || PERF_EVENTS) diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h index d02804d650c..d8e8eefbe24 100644 --- a/arch/x86/include/asm/io.h +++ b/arch/x86/include/asm/io.h @@ -40,8 +40,6 @@ #include <linux/compiler.h> #include <asm/page.h> -#include <xen/xen.h> - #define build_mmio_read(name, size, type, reg, barrier) \ static inline type name(const volatile void __iomem *addr) \ { type ret; asm volatile("mov" size " %1,%0":reg (ret) \ @@ -334,6 +332,7 @@ extern void fixup_early_ioremap(void); extern bool is_early_ioremap_ptep(pte_t *ptep); #ifdef CONFIG_XEN +#include <xen/xen.h> struct bio_vec; extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 219371546af..0d1171c9772 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -751,8 +751,6 @@ static inline void __sti_mwait(unsigned long eax, unsigned long ecx) :: "a" (eax), "c" (ecx)); } -extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx); - extern void select_idle_routine(const struct cpuinfo_x86 *c); extern void init_amd_e400_c1e_mask(void); diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c index 5812404a0d4..f50e7fb2a20 100644 --- a/arch/x86/kernel/acpi/cstate.c +++ b/arch/x86/kernel/acpi/cstate.c @@ -149,6 +149,29 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu, } EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); +/* + * This uses new MONITOR/MWAIT instructions on P4 processors with PNI, + * which can obviate IPI to trigger checking of need_resched. + * We execute MONITOR against need_resched and enter optimized wait state + * through MWAIT. Whenever someone changes need_resched, we would be woken + * up from MWAIT (without an IPI). + * + * New with Core Duo processors, MWAIT can take some hints based on CPU + * capability. + */ +void mwait_idle_with_hints(unsigned long ax, unsigned long cx) +{ + if (!need_resched()) { + if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) + clflush((void *)¤t_thread_info()->flags); + + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(ax, cx); + } +} + void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) { unsigned int cpu = smp_processor_id(); diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index e1ba8cb24e4..e7e3b019c43 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -438,29 +438,6 @@ void cpu_idle_wait(void) } EXPORT_SYMBOL_GPL(cpu_idle_wait); -/* - * This uses new MONITOR/MWAIT instructions on P4 processors with PNI, - * which can obviate IPI to trigger checking of need_resched. - * We execute MONITOR against need_resched and enter optimized wait state - * through MWAIT. Whenever someone changes need_resched, we would be woken - * up from MWAIT (without an IPI). - * - * New with Core Duo processors, MWAIT can take some hints based on CPU - * capability. - */ -void mwait_idle_with_hints(unsigned long ax, unsigned long cx) -{ - if (!need_resched()) { - if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) - clflush((void *)¤t_thread_info()->flags); - - __monitor((void *)¤t_thread_info()->flags, 0, 0); - smp_mb(); - if (!need_resched()) - __mwait(ax, cx); - } -} - /* Default MONITOR/MWAIT with no hints, used for default C1 state */ static void mwait_idle(void) { diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index a3d0dc59067..7a3b65107a2 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -38,6 +38,7 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/kdebug.h> +#include <linux/cpuidle.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -109,7 +110,8 @@ void cpu_idle(void) local_irq_disable(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); start_critical_timings(); } tick_nohz_restart_sched_tick(); diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index ca6f7ab8df3..f693e44e1bf 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -37,6 +37,7 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/ftrace.h> +#include <linux/cpuidle.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -136,7 +137,8 @@ void cpu_idle(void) enter_idle(); /* Don't trace irqs off for idle */ stop_critical_timings(); - pm_idle(); + if (cpuidle_idle_call()) + pm_idle(); start_critical_timings(); /* In many cases the interrupt that ended idle diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile index f61ccdd4934..1ea38775a6d 100644 --- a/arch/x86/platform/mrst/Makefile +++ b/arch/x86/platform/mrst/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_X86_MRST) += mrst.o obj-$(CONFIG_X86_MRST) += vrtc.o obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o +obj-$(CONFIG_X86_MRST) += pmu.o diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c new file mode 100644 index 00000000000..9281da7d91b --- /dev/null +++ b/arch/x86/platform/mrst/pmu.c @@ -0,0 +1,817 @@ +/* + * mrst/pmu.c - driver for MRST Power Management Unit + * + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/cpuidle.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/seq_file.h> +#include <linux/sfi.h> +#include <asm/intel_scu_ipc.h> +#include "pmu.h" + +#define IPCMSG_FW_REVISION 0xF4 + +struct mrst_device { + u16 pci_dev_num; /* DEBUG only */ + u16 lss; + u16 latest_request; + unsigned int pci_state_counts[PCI_D3cold + 1]; /* DEBUG only */ +}; + +/* + * comlete list of MRST PCI devices + */ +static struct mrst_device mrst_devs[] = { +/* 0 */ { 0x0800, LSS_SPI0 }, /* Moorestown SPI Ctrl 0 */ +/* 1 */ { 0x0801, LSS_SPI1 }, /* Moorestown SPI Ctrl 1 */ +/* 2 */ { 0x0802, LSS_I2C0 }, /* Moorestown I2C 0 */ +/* 3 */ { 0x0803, LSS_I2C1 }, /* Moorestown I2C 1 */ +/* 4 */ { 0x0804, LSS_I2C2 }, /* Moorestown I2C 2 */ +/* 5 */ { 0x0805, LSS_KBD }, /* Moorestown Keyboard Ctrl */ +/* 6 */ { 0x0806, LSS_USB_HC }, /* Moorestown USB Ctrl */ +/* 7 */ { 0x0807, LSS_SD_HC0 }, /* Moorestown SD Host Ctrl 0 */ +/* 8 */ { 0x0808, LSS_SD_HC1 }, /* Moorestown SD Host Ctrl 1 */ +/* 9 */ { 0x0809, LSS_NAND }, /* Moorestown NAND Ctrl */ +/* 10 */ { 0x080a, LSS_AUDIO }, /* Moorestown Audio Ctrl */ +/* 11 */ { 0x080b, LSS_IMAGING }, /* Moorestown ISP */ +/* 12 */ { 0x080c, LSS_SECURITY }, /* Moorestown Security Controller */ +/* 13 */ { 0x080d, LSS_DISPLAY }, /* Moorestown External Displays */ +/* 14 */ { 0x080e, 0 }, /* Moorestown SCU IPC */ +/* 15 */ { 0x080f, LSS_GPIO }, /* Moorestown GPIO Controller */ +/* 16 */ { 0x0810, 0 }, /* Moorestown Power Management Unit */ +/* 17 */ { 0x0811, LSS_USB_OTG }, /* Moorestown OTG Ctrl */ +/* 18 */ { 0x0812, LSS_SPI2 }, /* Moorestown SPI Ctrl 2 */ +/* 19 */ { 0x0813, 0 }, /* Moorestown SC DMA */ +/* 20 */ { 0x0814, LSS_AUDIO_LPE }, /* Moorestown LPE DMA */ +/* 21 */ { 0x0815, LSS_AUDIO_SSP }, /* Moorestown SSP0 */ + +/* 22 */ { 0x084F, LSS_SD_HC2 }, /* Moorestown SD Host Ctrl 2 */ + +/* 23 */ { 0x4102, 0 }, /* Lincroft */ +/* 24 */ { 0x4110, 0 }, /* Lincroft */ +}; + +/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */ +static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0}; +static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803, + 0x0804, 0x0805, 0x080f, 0}; + +/* handle concurrent SMP invokations of pmu_pci_set_power_state() */ +static spinlock_t mrst_pmu_power_state_lock; + +static unsigned int wake_counters[MRST_NUM_LSS]; /* DEBUG only */ +static unsigned int pmu_irq_stats[INT_INVALID + 1]; /* DEBUG only */ + +static int graphics_is_off; +static int lss_s0i3_enabled; +static bool mrst_pmu_s0i3_enable; + +/* debug counters */ +static u32 pmu_wait_ready_calls; +static u32 pmu_wait_ready_udelays; +static u32 pmu_wait_ready_udelays_max; +static u32 pmu_wait_done_calls; +static u32 pmu_wait_done_udelays; +static u32 pmu_wait_done_udelays_max; +static u32 pmu_set_power_state_entry; +static u32 pmu_set_power_state_send_cmd; + +static struct mrst_device *pci_id_2_mrst_dev(u16 pci_dev_num) +{ + int index = 0; + + if ((pci_dev_num >= 0x0800) && (pci_dev_num <= 0x815)) + index = pci_dev_num - 0x800; + else if (pci_dev_num == 0x084F) + index = 22; + else if (pci_dev_num == 0x4102) + index = 23; + else if (pci_dev_num == 0x4110) + index = 24; + + if (pci_dev_num != mrst_devs[index].pci_dev_num) { + WARN_ONCE(1, FW_BUG "Unknown PCI device 0x%04X\n", pci_dev_num); + return 0; + } + + return &mrst_devs[index]; +} + +/** + * mrst_pmu_validate_cstates + * @dev: cpuidle_device + * + * Certain states are not appropriate for governor to pick in some cases. + * This function will be called as cpuidle_device's prepare callback and + * thus tells governor to ignore such states when selecting the next state + * to enter. + */ + +#define IDLE_STATE4_IS_C6 4 +#define IDLE_STATE5_IS_S0I3 5 + +int mrst_pmu_invalid_cstates(void) +{ + int cpu = smp_processor_id(); + + /* + * Demote to C4 if the PMU is busy. + * Since LSS changes leave the busy bit clear... + * busy means either the PMU is waiting for an ACK-C6 that + * isn't coming due to an MWAIT that returned immediately; + * or we returned from S0i3 successfully, and the PMU + * is not done sending us interrupts. + */ + if (pmu_read_busy_status()) + return 1 << IDLE_STATE4_IS_C6 | 1 << IDLE_STATE5_IS_S0I3; + + /* + * Disallow S0i3 if: PMU is not initialized, or CPU1 is active, + * or if device LSS is insufficient, or the GPU is active, + * or if it has been explicitly disabled. + */ + if (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)) || + !lss_s0i3_enabled || !graphics_is_off || !mrst_pmu_s0i3_enable) + return 1 << IDLE_STATE5_IS_S0I3; + else + return 0; +} + +/* + * pmu_update_wake_counters(): read PM_WKS, update wake_counters[] + * DEBUG only. + */ +static void pmu_update_wake_counters(void) +{ + int lss; + u32 wake_status; + + wake_status = pmu_read_wks(); + + for (lss = 0; lss < MRST_NUM_LSS; ++lss) { + if (wake_status & (1 << lss)) + wake_counters[lss]++; + } +} + +int mrst_pmu_s0i3_entry(void) +{ + int status; + + /* Clear any possible error conditions */ + pmu_write_ics(0x300); + + /* set wake control to current D-states */ + pmu_write_wssc(S0I3_SSS_TARGET); + + status = mrst_s0i3_entry(PM_S0I3_COMMAND, &pmu_reg->pm_cmd); + pmu_update_wake_counters(); + return status; +} + +/* poll for maximum of 5ms for busy bit to clear */ +static int pmu_wait_ready(void) +{ + int udelays; + + pmu_wait_ready_calls++; + + for (udelays = 0; udelays < 500; ++udelays) { + if (udelays > pmu_wait_ready_udelays_max) + pmu_wait_ready_udelays_max = udelays; + + if (pmu_read_busy_status() == 0) + return 0; + + udelay(10); + pmu_wait_ready_udelays++; + } + + /* + * if this fires, observe + * /sys/kernel/debug/mrst_pmu_wait_ready_calls + * /sys/kernel/debug/mrst_pmu_wait_ready_udelays + */ + WARN_ONCE(1, "SCU not ready for 5ms"); + return -EBUSY; +} +/* poll for maximum of 50ms us for busy bit to clear */ +static int pmu_wait_done(void) +{ + int udelays; + + pmu_wait_done_calls++; + + for (udelays = 0; udelays < 500; ++udelays) { + if (udelays > pmu_wait_done_udelays_max) + pmu_wait_done_udelays_max = udelays; + + if (pmu_read_busy_status() == 0) + return 0; + + udelay(100); + pmu_wait_done_udelays++; + } + + /* + * if this fires, observe + * /sys/kernel/debug/mrst_pmu_wait_done_calls + * /sys/kernel/debug/mrst_pmu_wait_done_udelays + */ + WARN_ONCE(1, "SCU not done for 50ms"); + return -EBUSY; +} + +u32 mrst_pmu_msi_is_disabled(void) +{ + return pmu_msi_is_disabled(); +} + +void mrst_pmu_enable_msi(void) +{ + pmu_msi_enable(); +} + +/** + * pmu_irq - pmu driver interrupt handler + * Context: interrupt context + */ +static irqreturn_t pmu_irq(int irq, void *dummy) +{ + union pmu_pm_ics pmu_ics; + + pmu_ics.value = pmu_read_ics(); + + if (!pmu_ics.bits.pending) + return IRQ_NONE; + + switch (pmu_ics.bits.cause) { + case INT_SPURIOUS: + case INT_CMD_DONE: + case INT_CMD_ERR: + case INT_WAKE_RX: + case INT_SS_ERROR: + case INT_S0IX_MISS: + case INT_NO_ACKC6: + pmu_irq_stats[pmu_ics.bits.cause]++; + break; + default: + pmu_irq_stats[INT_INVALID]++; + } + + pmu_write_ics(pmu_ics.value); /* Clear pending interrupt */ + + return IRQ_HANDLED; +} + +/* + * Translate PCI power management to MRST LSS D-states + */ +static int pci_2_mrst_state(int lss, pci_power_t pci_state) +{ + switch (pci_state) { + case PCI_D0: + if (SSMSK(D0i1, lss) & D0I1_ACG_SSS_TARGET) + return D0i1; + else + return D0; + case PCI_D1: + return D0i1; + case PCI_D2: + return D0i2; + case PCI_D3hot: + case PCI_D3cold: + return D0i3; + default: + WARN(1, "pci_state %d\n", pci_state); + return 0; + } +} + +static int pmu_issue_command(u32 pm_ssc) +{ + union pmu_pm_set_cfg_cmd_t command; + + if (pmu_read_busy_status()) { + pr_debug("pmu is busy, Operation not permitted\n"); + return -1; + } + + /* + * enable interrupts in PMU so that interrupts are + * propagated when ioc bit for a particular set + * command is set + */ + + pmu_irq_enable(); + + /* Configure the sub systems for pmu2 */ + + pmu_write_ssc(pm_ssc); + + /* + * Send the set config command for pmu its configured + * for mode CM_IMMEDIATE & hence with No Trigger + */ + + command.pmu2_params.d_param.cfg_mode = CM_IMMEDIATE; + command.pmu2_params.d_param.cfg_delay = 0; + command.pmu2_params.d_param.rsvd = 0; + + /* construct the command to send SET_CFG to particular PMU */ + command.pmu2_params.d_param.cmd = SET_CFG_CMD; + command.pmu2_params.d_param.ioc = 0; + command.pmu2_params.d_param.mode_id = 0; + command.pmu2_params.d_param.sys_state = SYS_STATE_S0I0; + + /* write the value of PM_CMD into particular PMU */ + pr_debug("pmu command being written %x\n", + command.pmu_pm_set_cfg_cmd_value); + + pmu_write_cmd(command.pmu_pm_set_cfg_cmd_value); + + return 0; +} + +static u16 pmu_min_lss_pci_req(u16 *ids, u16 pci_state) +{ + u16 existing_request; + int i; + + for (i = 0; ids[i]; ++i) { + struct mrst_device *mrst_dev; + + mrst_dev = pci_id_2_mrst_dev(ids[i]); + if (unlikely(!mrst_dev)) + continue; + + existing_request = mrst_dev->latest_request; + if (existing_request < pci_state) + pci_state = existing_request; + } + return pci_state; +} + +/** + * pmu_pci_set_power_state - Callback function is used by all the PCI devices + * for a platform specific device power on/shutdown. + */ + +int pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t pci_state) +{ + u32 old_sss, new_sss; + int status = 0; + struct mrst_device *mrst_dev; + + pmu_set_power_state_entry++; + + BUG_ON(pdev->vendor != PCI_VENDOR_ID_INTEL); + BUG_ON(pci_state < PCI_D0 || pci_state > PCI_D3cold); + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + if (unlikely(!mrst_dev)) + return -ENODEV; + + mrst_dev->pci_state_counts[pci_state]++; /* count invocations */ + + /* PMU driver calls self as part of PCI initialization, ignore */ + if (pdev->device == PCI_DEV_ID_MRST_PMU) + return 0; + + BUG_ON(!pmu_reg); /* SW bug if called before initialized */ + + spin_lock(&mrst_pmu_power_state_lock); + + if (pdev->d3_delay) { + dev_dbg(&pdev->dev, "d3_delay %d, should be 0\n", + pdev->d3_delay); + pdev->d3_delay = 0; + } + /* + * If Lincroft graphics, simply remember state + */ + if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY + && !((pdev->class & PCI_SUB_CLASS_MASK) >> 8)) { + if (pci_state == PCI_D0) + graphics_is_off = 0; + else + graphics_is_off = 1; + goto ret; + } + + if (!mrst_dev->lss) + goto ret; /* device with no LSS */ + + if (mrst_dev->latest_request == pci_state) + goto ret; /* no change */ + + mrst_dev->latest_request = pci_state; /* record latest request */ + + /* + * LSS9 and LSS10 contain multiple PCI devices. + * Use the lowest numbered (highest power) state in the LSS + */ + if (mrst_dev->lss == 9) + pci_state = pmu_min_lss_pci_req(mrst_lss9_pci_ids, pci_state); + else if (mrst_dev->lss == 10) + pci_state = pmu_min_lss_pci_req(mrst_lss10_pci_ids, pci_state); + + status = pmu_wait_ready(); + if (status) + goto ret; + + old_sss = pmu_read_sss(); + new_sss = old_sss & ~SSMSK(3, mrst_dev->lss); + new_sss |= SSMSK(pci_2_mrst_state(mrst_dev->lss, pci_state), + mrst_dev->lss); + + if (new_sss == old_sss) + goto ret; /* nothing to do */ + + pmu_set_power_state_send_cmd++; + + status = pmu_issue_command(new_sss); + + if (unlikely(status != 0)) { + dev_err(&pdev->dev, "Failed to Issue a PM command\n"); + goto ret; + } + + if (pmu_wait_done()) + goto ret; + + lss_s0i3_enabled = + ((pmu_read_sss() & S0I3_SSS_TARGET) == S0I3_SSS_TARGET); +ret: + spin_unlock(&mrst_pmu_power_state_lock); + return status; +} + +#ifdef CONFIG_DEBUG_FS +static char *d0ix_names[] = {"D0", "D0i1", "D0i2", "D0i3"}; + +static inline const char *d0ix_name(int state) +{ + return d0ix_names[(int) state]; +} + +static int debug_mrst_pmu_show(struct seq_file *s, void *unused) +{ + struct pci_dev *pdev = NULL; + u32 cur_pmsss; + int lss; + + seq_printf(s, "0x%08X D0I1_ACG_SSS_TARGET\n", D0I1_ACG_SSS_TARGET); + + cur_pmsss = pmu_read_sss(); + + seq_printf(s, "0x%08X S0I3_SSS_TARGET\n", S0I3_SSS_TARGET); + + seq_printf(s, "0x%08X Current SSS ", cur_pmsss); + seq_printf(s, lss_s0i3_enabled ? "\n" : "[BLOCKS s0i3]\n"); + + if (cpumask_equal(cpu_online_mask, cpumask_of(0))) + seq_printf(s, "cpu0 is only cpu online\n"); + else + seq_printf(s, "cpu0 is NOT only cpu online [BLOCKS S0i3]\n"); + + seq_printf(s, "GFX: %s\n", graphics_is_off ? "" : "[BLOCKS s0i3]"); + + + for_each_pci_dev(pdev) { + int pos; + u16 pmcsr; + struct mrst_device *mrst_dev; + int i; + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + + seq_printf(s, "%s %04x/%04X %-16.16s ", + dev_name(&pdev->dev), + pdev->vendor, pdev->device, + dev_driver_string(&pdev->dev)); + + if (unlikely (!mrst_dev)) { + seq_printf(s, " UNKNOWN\n"); + continue; + } + + if (mrst_dev->lss) + seq_printf(s, "LSS %2d %-4s ", mrst_dev->lss, + d0ix_name(((cur_pmsss >> + (mrst_dev->lss * 2)) & 0x3))); + else + seq_printf(s, " "); + + /* PCI PM config space setting */ + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pos != 0) { + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); + seq_printf(s, "PCI-%-4s", + pci_power_name(pmcsr & PCI_PM_CTRL_STATE_MASK)); + } else { + seq_printf(s, " "); + } + + seq_printf(s, " %s ", pci_power_name(mrst_dev->latest_request)); + for (i = 0; i <= PCI_D3cold; ++i) + seq_printf(s, "%d ", mrst_dev->pci_state_counts[i]); + + if (mrst_dev->lss) { + unsigned int lssmask; + + lssmask = SSMSK(D0i3, mrst_dev->lss); + + if ((lssmask & S0I3_SSS_TARGET) && + ((lssmask & cur_pmsss) != + (lssmask & S0I3_SSS_TARGET))) + seq_printf(s , "[BLOCKS s0i3]"); + } + + seq_printf(s, "\n"); + } + seq_printf(s, "Wake Counters:\n"); + for (lss = 0; lss < MRST_NUM_LSS; ++lss) + seq_printf(s, "LSS%d %d\n", lss, wake_counters[lss]); + + seq_printf(s, "Interrupt Counters:\n"); + seq_printf(s, + "INT_SPURIOUS \t%8u\n" "INT_CMD_DONE \t%8u\n" + "INT_CMD_ERR \t%8u\n" "INT_WAKE_RX \t%8u\n" + "INT_SS_ERROR \t%8u\n" "INT_S0IX_MISS\t%8u\n" + "INT_NO_ACKC6 \t%8u\n" "INT_INVALID \t%8u\n", + pmu_irq_stats[INT_SPURIOUS], pmu_irq_stats[INT_CMD_DONE], + pmu_irq_stats[INT_CMD_ERR], pmu_irq_stats[INT_WAKE_RX], + pmu_irq_stats[INT_SS_ERROR], pmu_irq_stats[INT_S0IX_MISS], + pmu_irq_stats[INT_NO_ACKC6], pmu_irq_stats[INT_INVALID]); + + seq_printf(s, "mrst_pmu_wait_ready_calls %8d\n", + pmu_wait_ready_calls); + seq_printf(s, "mrst_pmu_wait_ready_udelays %8d\n", + pmu_wait_ready_udelays); + seq_printf(s, "mrst_pmu_wait_ready_udelays_max %8d\n", + pmu_wait_ready_udelays_max); + seq_printf(s, "mrst_pmu_wait_done_calls %8d\n", + pmu_wait_done_calls); + seq_printf(s, "mrst_pmu_wait_done_udelays %8d\n", + pmu_wait_done_udelays); + seq_printf(s, "mrst_pmu_wait_done_udelays_max %8d\n", + pmu_wait_done_udelays_max); + seq_printf(s, "mrst_pmu_set_power_state_entry %8d\n", + pmu_set_power_state_entry); + seq_printf(s, "mrst_pmu_set_power_state_send_cmd %8d\n", + pmu_set_power_state_send_cmd); + seq_printf(s, "SCU busy: %d\n", pmu_read_busy_status()); + + return 0; +} + +static int debug_mrst_pmu_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_mrst_pmu_show, NULL); +} + +static const struct file_operations devices_state_operations = { + .open = debug_mrst_pmu_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* DEBUG_FS */ + +/* + * Validate SCU PCI shim PCI vendor capability byte + * against LSS hard-coded in mrst_devs[] above. + * DEBUG only. + */ +static void pmu_scu_firmware_debug(void) +{ + struct pci_dev *pdev = NULL; + + for_each_pci_dev(pdev) { + struct mrst_device *mrst_dev; + u8 pci_config_lss; + int pos; + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + if (unlikely(!mrst_dev)) { + printk(KERN_ERR FW_BUG "pmu: Unknown " + "PCI device 0x%04X\n", pdev->device); + continue; + } + + if (mrst_dev->lss == 0) + continue; /* no LSS in our table */ + + pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR); + if (!pos != 0) { + printk(KERN_ERR FW_BUG "pmu: 0x%04X " + "missing PCI Vendor Capability\n", + pdev->device); + continue; + } + pci_read_config_byte(pdev, pos + 4, &pci_config_lss); + if (!(pci_config_lss & PCI_VENDOR_CAP_LOG_SS_MASK)) { + printk(KERN_ERR FW_BUG "pmu: 0x%04X " + "invalid PCI Vendor Capability 0x%x " + " expected LSS 0x%X\n", + pdev->device, pci_config_lss, mrst_dev->lss); + continue; + } + pci_config_lss &= PCI_VENDOR_CAP_LOG_ID_MASK; + + if (mrst_dev->lss == pci_config_lss) + continue; + + printk(KERN_ERR FW_BUG "pmu: 0x%04X LSS = %d, expected %d\n", + pdev->device, pci_config_lss, mrst_dev->lss); + } +} + +/** + * pmu_probe + */ +static int __devinit pmu_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + int ret; + struct mrst_pmu_reg *pmu; + + /* Init the device */ + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Unable to Enable PCI device\n"); + return ret; + } + + ret = pci_request_regions(pdev, MRST_PMU_DRV_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); + goto out_err1; + } + + /* Map the memory of PMU reg base */ + pmu = pci_iomap(pdev, 0, 0); + if (!pmu) { + dev_err(&pdev->dev, "Unable to map the PMU address space\n"); + ret = -ENOMEM; + goto out_err2; + } + +#ifdef CONFIG_DEBUG_FS + /* /sys/kernel/debug/mrst_pmu */ + (void) debugfs_create_file("mrst_pmu", S_IFREG | S_IRUGO, + NULL, NULL, &devices_state_operations); +#endif + pmu_reg = pmu; /* success */ + + if (request_irq(pdev->irq, pmu_irq, 0, MRST_PMU_DRV_NAME, NULL)) { + dev_err(&pdev->dev, "Registering isr has failed\n"); + ret = -1; + goto out_err3; + } + + pmu_scu_firmware_debug(); + + pmu_write_wkc(S0I3_WAKE_SOURCES); /* Enable S0i3 wakeup sources */ + + pmu_wait_ready(); + + pmu_write_ssc(D0I1_ACG_SSS_TARGET); /* Enable Auto-Clock_Gating */ + pmu_write_cmd(0x201); + + spin_lock_init(&mrst_pmu_power_state_lock); + + /* Enable the hardware interrupt */ + pmu_irq_enable(); + return 0; + +out_err3: + free_irq(pdev->irq, NULL); + pci_iounmap(pdev, pmu_reg); + pmu_reg = NULL; +out_err2: + pci_release_region(pdev, 0); +out_err1: + pci_disable_device(pdev); + return ret; +} + +static void __devexit pmu_remove(struct pci_dev *pdev) +{ + dev_err(&pdev->dev, "Mid PM pmu_remove called\n"); + + /* Freeing up the irq */ + free_irq(pdev->irq, NULL); + + pci_iounmap(pdev, pmu_reg); + pmu_reg = NULL; + + /* disable the current PCI device */ + pci_release_region(pdev, 0); + pci_disable_device(pdev); +} + +static DEFINE_PCI_DEVICE_TABLE(pmu_pci_ids) = { + { PCI_VDEVICE(INTEL, PCI_DEV_ID_MRST_PMU), 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, pmu_pci_ids); + +static struct pci_driver driver = { + .name = MRST_PMU_DRV_NAME, + .id_table = pmu_pci_ids, + .probe = pmu_probe, + .remove = __devexit_p(pmu_remove), +}; + +/** + * pmu_pci_register - register the PMU driver as PCI device + */ +static int __init pmu_pci_register(void) +{ + return pci_register_driver(&driver); +} + +/* Register and probe via fs_initcall() to preceed device_initcall() */ +fs_initcall(pmu_pci_register); + +static void __exit mid_pci_cleanup(void) +{ + pci_unregister_driver(&driver); +} + +static int ia_major; +static int ia_minor; + +static int pmu_sfi_parse_oem(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + + sb = (struct sfi_table_simple *)table; + ia_major = (sb->pentry[1] >> 0) & 0xFFFF; + ia_minor = (sb->pentry[1] >> 16) & 0xFFFF; + printk(KERN_INFO "mrst_pmu: IA FW version v%x.%x\n", + ia_major, ia_minor); + + return 0; +} + +static int __init scu_fw_check(void) +{ + int ret; + u32 fw_version; + + if (!pmu_reg) + return 0; /* this driver didn't probe-out */ + + sfi_table_parse("OEMB", NULL, NULL, pmu_sfi_parse_oem); + + if (ia_major < 0x6005 || ia_minor < 0x1525) { + WARN(1, "mrst_pmu: IA FW version too old\n"); + return -1; + } + + ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0, + &fw_version, 1); + + if (ret) { + WARN(1, "mrst_pmu: IPC FW version? %d\n", ret); + } else { + int scu_major = (fw_version >> 8) & 0xFF; + int scu_minor = (fw_version >> 0) & 0xFF; + + printk(KERN_INFO "mrst_pmu: firmware v%x\n", fw_version); + + if ((scu_major >= 0xC0) && (scu_minor >= 0x49)) { + printk(KERN_INFO "mrst_pmu: enabling S0i3\n"); + mrst_pmu_s0i3_enable = true; + } else { + WARN(1, "mrst_pmu: S0i3 disabled, old firmware %X.%X", + scu_major, scu_minor); + } + } + return 0; +} +late_initcall(scu_fw_check); +module_exit(mid_pci_cleanup); diff --git a/arch/x86/platform/mrst/pmu.h b/arch/x86/platform/mrst/pmu.h new file mode 100644 index 00000000000..bfbfe64b167 --- /dev/null +++ b/arch/x86/platform/mrst/pmu.h @@ -0,0 +1,234 @@ +/* + * mrst/pmu.h - private definitions for MRST Power Management Unit mrst/pmu.c + * + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MRST_PMU_H_ +#define _MRST_PMU_H_ + +#define PCI_DEV_ID_MRST_PMU 0x0810 +#define MRST_PMU_DRV_NAME "mrst_pmu" +#define PCI_SUB_CLASS_MASK 0xFF00 + +#define PCI_VENDOR_CAP_LOG_ID_MASK 0x7F +#define PCI_VENDOR_CAP_LOG_SS_MASK 0x80 + +#define SUB_SYS_ALL_D0I1 0x01155555 +#define S0I3_WAKE_SOURCES 0x00001FFF + +#define PM_S0I3_COMMAND \ + ((0 << 31) | /* Reserved */ \ + (0 << 30) | /* Core must be idle */ \ + (0xc2 << 22) | /* ACK C6 trigger */ \ + (3 << 19) | /* Trigger on DMI message */ \ + (3 << 16) | /* Enter S0i3 */ \ + (0 << 13) | /* Numeric mode ID (sw) */ \ + (3 << 9) | /* Trigger mode */ \ + (0 << 8) | /* Do not interrupt */ \ + (1 << 0)) /* Set configuration */ + +#define LSS_DMI 0 +#define LSS_SD_HC0 1 +#define LSS_SD_HC1 2 +#define LSS_NAND 3 +#define LSS_IMAGING 4 +#define LSS_SECURITY 5 +#define LSS_DISPLAY 6 +#define LSS_USB_HC 7 +#define LSS_USB_OTG 8 +#define LSS_AUDIO 9 +#define LSS_AUDIO_LPE 9 +#define LSS_AUDIO_SSP 9 +#define LSS_I2C0 10 +#define LSS_I2C1 10 +#define LSS_I2C2 10 +#define LSS_KBD 10 +#define LSS_SPI0 10 +#define LSS_SPI1 10 +#define LSS_SPI2 10 +#define LSS_GPIO 10 +#define LSS_SRAM 11 /* used by SCU, do not touch */ +#define LSS_SD_HC2 12 +/* LSS hardware bits 15,14,13 are hardwired to 0, thus unusable */ +#define MRST_NUM_LSS 13 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define SSMSK(mask, lss) ((mask) << ((lss) * 2)) +#define D0 0 +#define D0i1 1 +#define D0i2 2 +#define D0i3 3 + +#define S0I3_SSS_TARGET ( \ + SSMSK(D0i1, LSS_DMI) | \ + SSMSK(D0i3, LSS_SD_HC0) | \ + SSMSK(D0i3, LSS_SD_HC1) | \ + SSMSK(D0i3, LSS_NAND) | \ + SSMSK(D0i3, LSS_SD_HC2) | \ + SSMSK(D0i3, LSS_IMAGING) | \ + SSMSK(D0i3, LSS_SECURITY) | \ + SSMSK(D0i3, LSS_DISPLAY) | \ + SSMSK(D0i3, LSS_USB_HC) | \ + SSMSK(D0i3, LSS_USB_OTG) | \ + SSMSK(D0i3, LSS_AUDIO) | \ + SSMSK(D0i1, LSS_I2C0)) + +/* + * D0i1 on Langwell is Autonomous Clock Gating (ACG). + * Enable ACG on every LSS except camera and audio + */ +#define D0I1_ACG_SSS_TARGET \ + (SUB_SYS_ALL_D0I1 & ~SSMSK(D0i1, LSS_IMAGING) & ~SSMSK(D0i1, LSS_AUDIO)) + +enum cm_mode { + CM_NOP, /* ignore the config mode value */ + CM_IMMEDIATE, + CM_DELAY, + CM_TRIGGER, + CM_INVALID +}; + +enum sys_state { + SYS_STATE_S0I0, + SYS_STATE_S0I1, + SYS_STATE_S0I2, + SYS_STATE_S0I3, + SYS_STATE_S3, + SYS_STATE_S5 +}; + +#define SET_CFG_CMD 1 + +enum int_status { + INT_SPURIOUS = 0, + INT_CMD_DONE = 1, + INT_CMD_ERR = 2, + INT_WAKE_RX = 3, + INT_SS_ERROR = 4, + INT_S0IX_MISS = 5, + INT_NO_ACKC6 = 6, + INT_INVALID = 7, +}; + +/* PMU register interface */ +static struct mrst_pmu_reg { + u32 pm_sts; /* 0x00 */ + u32 pm_cmd; /* 0x04 */ + u32 pm_ics; /* 0x08 */ + u32 _resv1; /* 0x0C */ + u32 pm_wkc[2]; /* 0x10 */ + u32 pm_wks[2]; /* 0x18 */ + u32 pm_ssc[4]; /* 0x20 */ + u32 pm_sss[4]; /* 0x30 */ + u32 pm_wssc[4]; /* 0x40 */ + u32 pm_c3c4; /* 0x50 */ + u32 pm_c5c6; /* 0x54 */ + u32 pm_msi_disable; /* 0x58 */ +} *pmu_reg; + +static inline u32 pmu_read_sts(void) { return readl(&pmu_reg->pm_sts); } +static inline u32 pmu_read_ics(void) { return readl(&pmu_reg->pm_ics); } +static inline u32 pmu_read_wks(void) { return readl(&pmu_reg->pm_wks[0]); } +static inline u32 pmu_read_sss(void) { return readl(&pmu_reg->pm_sss[0]); } + +static inline void pmu_write_cmd(u32 arg) { writel(arg, &pmu_reg->pm_cmd); } +static inline void pmu_write_ics(u32 arg) { writel(arg, &pmu_reg->pm_ics); } +static inline void pmu_write_wkc(u32 arg) { writel(arg, &pmu_reg->pm_wkc[0]); } +static inline void pmu_write_ssc(u32 arg) { writel(arg, &pmu_reg->pm_ssc[0]); } +static inline void pmu_write_wssc(u32 arg) + { writel(arg, &pmu_reg->pm_wssc[0]); } + +static inline void pmu_msi_enable(void) { writel(0, &pmu_reg->pm_msi_disable); } +static inline u32 pmu_msi_is_disabled(void) + { return readl(&pmu_reg->pm_msi_disable); } + +union pmu_pm_ics { + struct { + u32 cause:8; + u32 enable:1; + u32 pending:1; + u32 reserved:22; + } bits; + u32 value; +}; + +static inline void pmu_irq_enable(void) +{ + union pmu_pm_ics pmu_ics; + + pmu_ics.value = pmu_read_ics(); + pmu_ics.bits.enable = 1; + pmu_write_ics(pmu_ics.value); +} + +union pmu_pm_status { + struct { + u32 pmu_rev:8; + u32 pmu_busy:1; + u32 mode_id:4; + u32 Reserved:19; + } pmu_status_parts; + u32 pmu_status_value; +}; + +static inline int pmu_read_busy_status(void) +{ + union pmu_pm_status result; + + result.pmu_status_value = pmu_read_sts(); + + return result.pmu_status_parts.pmu_busy; +} + +/* pmu set config parameters */ +struct cfg_delay_param_t { + u32 cmd:8; + u32 ioc:1; + u32 cfg_mode:4; + u32 mode_id:3; + u32 sys_state:3; + u32 cfg_delay:8; + u32 rsvd:5; +}; + +struct cfg_trig_param_t { + u32 cmd:8; + u32 ioc:1; + u32 cfg_mode:4; + u32 mode_id:3; + u32 sys_state:3; + u32 cfg_trig_type:3; + u32 cfg_trig_val:8; + u32 cmbi:1; + u32 rsvd1:1; +}; + +union pmu_pm_set_cfg_cmd_t { + union { + struct cfg_delay_param_t d_param; + struct cfg_trig_param_t t_param; + } pmu2_params; + u32 pmu_pm_set_cfg_cmd_value; +}; + +#ifdef FUTURE_PATCH +extern int mrst_s0i3_entry(u32 regval, u32 *regaddr); +#else +static inline int mrst_s0i3_entry(u32 regval, u32 *regaddr) { return -1; } +#endif +#endif diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 60aeeb56948..a9627e2e329 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -9,6 +9,7 @@ #include <linux/mm.h> #include <linux/pm.h> #include <linux/memblock.h> +#include <linux/cpuidle.h> #include <asm/elf.h> #include <asm/vdso.h> @@ -426,7 +427,7 @@ void __init xen_arch_setup(void) #ifdef CONFIG_X86_32 boot_cpu_data.hlt_works_ok = 1; #endif - pm_idle = default_idle; + disable_cpuidle(); boot_option_idle_override = IDLE_HALT; fiddle_vdso(); |