]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge tag 'arm64-suspend' of git://linux-arm.org/linux-2.6-lp into upstream
authorCatalin Marinas <catalin.marinas@arm.com>
Thu, 19 Dec 2013 17:57:51 +0000 (17:57 +0000)
committerCatalin Marinas <catalin.marinas@arm.com>
Thu, 19 Dec 2013 17:57:51 +0000 (17:57 +0000)
* tag 'arm64-suspend' of git://linux-arm.org/linux-2.6-lp:
  arm64: add CPU power management menu/entries
  arm64: kernel: add PM build infrastructure
  arm64: kernel: add CPU idle call
  arm64: enable generic clockevent broadcast
  arm64: kernel: implement HW breakpoints CPU PM notifier
  arm64: kernel: refactor code to install/uninstall breakpoints
  arm: kvm: implement CPU PM notifier
  arm64: kernel: implement fpsimd CPU PM notifier
  arm64: kernel: cpu_{suspend/resume} implementation
  arm64: kernel: suspend/resume registers save/restore
  arm64: kernel: build MPIDR_EL1 hash function data structure
  arm64: kernel: add MPIDR_EL1 accessors macros

Conflicts:
arch/arm64/Kconfig

1  2 
arch/arm64/Kconfig
arch/arm64/include/asm/cputype.h
arch/arm64/kernel/process.c
arch/arm64/kernel/setup.c
arch/arm64/kernel/smp.c

diff --combined arch/arm64/Kconfig
index 101683d336c915ef93081038870d10eec766a2ce,99589e782834f5fd76344da64ee64e1b95994b98..249acb9da4e3560a10731ac217402014175ba73c
@@@ -2,6 -2,7 +2,7 @@@ config ARM6
        def_bool y
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select ARCH_USE_CMPXCHG_LOCKREF
+       select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
        select ARCH_WANT_OPTIONAL_GPIOLIB
        select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
        select ARCH_WANT_FRAME_POINTERS
        select BUILDTIME_EXTABLE_SORT
        select CLONE_BACKWARDS
        select COMMON_CLK
+       select CPU_PM if (SUSPEND || CPU_IDLE)
 +      select DCACHE_WORD_ACCESS
        select GENERIC_CLOCKEVENTS
+       select GENERIC_CLOCKEVENTS_BROADCAST if SMP
        select GENERIC_IOMAP
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
        select GENERIC_SCHED_CLOCK
        select GENERIC_SMP_IDLE_THREAD
 +      select GENERIC_STRNCPY_FROM_USER
 +      select GENERIC_STRNLEN_USER
        select GENERIC_TIME_VSYSCALL
        select HARDIRQS_SW_RESEND
        select HAVE_ARCH_TRACEHOOK
@@@ -27,8 -27,6 +30,8 @@@
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DMA_API_DEBUG
        select HAVE_DMA_ATTRS
 +      select HAVE_DMA_CONTIGUOUS
 +      select HAVE_EFFICIENT_UNALIGNED_ACCESS
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_HW_BREAKPOINT if PERF_EVENTS
        select HAVE_MEMBLOCK
@@@ -280,6 -278,24 +283,24 @@@ config SYSVIPC_COMPA
  
  endmenu
  
+ menu "Power management options"
+ source "kernel/power/Kconfig"
+ config ARCH_SUSPEND_POSSIBLE
+       def_bool y
+ config ARM64_CPU_SUSPEND
+       def_bool PM_SLEEP
+ endmenu
+ menu "CPU Power Management"
+ source "drivers/cpuidle/Kconfig"
+ endmenu
  source "net/Kconfig"
  
  source "drivers/Kconfig"
index e1af1b4200d58b3eb76a326c9d62311e1a0c63e3,e371936b8ab2ed2a66a73a3f148af16bba50a40b..c404fb0df3a673710285603c8ba7571fa42a86f8
  #ifndef __ASM_CPUTYPE_H
  #define __ASM_CPUTYPE_H
  
 -#define ID_MIDR_EL1           "midr_el1"
 -#define ID_MPIDR_EL1          "mpidr_el1"
 -#define ID_CTR_EL0            "ctr_el0"
 -
 -#define ID_AA64PFR0_EL1               "id_aa64pfr0_el1"
 -#define ID_AA64DFR0_EL1               "id_aa64dfr0_el1"
 -#define ID_AA64AFR0_EL1               "id_aa64afr0_el1"
 -#define ID_AA64ISAR0_EL1      "id_aa64isar0_el1"
 -#define ID_AA64MMFR0_EL1      "id_aa64mmfr0_el1"
 -
  #define INVALID_HWID          ULONG_MAX
  
  #define MPIDR_HWID_BITMASK    0xff00ffffff
  
+ #define MPIDR_LEVEL_BITS_SHIFT        3
+ #define MPIDR_LEVEL_BITS      (1 << MPIDR_LEVEL_BITS_SHIFT)
+ #define MPIDR_LEVEL_MASK      ((1 << MPIDR_LEVEL_BITS) - 1)
+ #define MPIDR_LEVEL_SHIFT(level) \
+       (((1 << level) >> 1) << MPIDR_LEVEL_BITS_SHIFT)
+ #define MPIDR_AFFINITY_LEVEL(mpidr, level) \
+       ((mpidr >> MPIDR_LEVEL_SHIFT(level)) & MPIDR_LEVEL_MASK)
  #define read_cpuid(reg) ({                                            \
        u64 __val;                                                      \
 -      asm("mrs        %0, " reg : "=r" (__val));                      \
 +      asm("mrs        %0, " #reg : "=r" (__val));                     \
        __val;                                                          \
  })
  
   */
  static inline u32 __attribute_const__ read_cpuid_id(void)
  {
 -      return read_cpuid(ID_MIDR_EL1);
 +      return read_cpuid(MIDR_EL1);
  }
  
  static inline u64 __attribute_const__ read_cpuid_mpidr(void)
  {
 -      return read_cpuid(ID_MPIDR_EL1);
 +      return read_cpuid(MPIDR_EL1);
  }
  
  static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
@@@ -64,7 -84,7 +74,7 @@@ static inline unsigned int __attribute_
  
  static inline u32 __attribute_const__ read_cpuid_cachetype(void)
  {
 -      return read_cpuid(ID_CTR_EL0);
 +      return read_cpuid(CTR_EL0);
  }
  
  #endif /* __ASSEMBLY__ */
index 0adb8f0f45491dd70b018c162cba4f57cf26fa18,50491ec4de34a5f291a04ed5cde3b3b47220bc66..248a15db37f2c05b86345e03d7a9294db2eaa3fc
@@@ -33,6 -33,7 +33,7 @@@
  #include <linux/kallsyms.h>
  #include <linux/init.h>
  #include <linux/cpu.h>
+ #include <linux/cpuidle.h>
  #include <linux/elfcore.h>
  #include <linux/pm.h>
  #include <linux/tick.h>
@@@ -98,8 -99,10 +99,10 @@@ void arch_cpu_idle(void
         * This should do all the clock switching and wait for interrupt
         * tricks
         */
-       cpu_do_idle();
-       local_irq_enable();
+       if (cpuidle_idle_call()) {
+               cpu_do_idle();
+               local_irq_enable();
+       }
  }
  
  #ifdef CONFIG_HOTPLUG_CPU
@@@ -308,7 -311,6 +311,7 @@@ struct task_struct *__switch_to(struct 
  unsigned long get_wchan(struct task_struct *p)
  {
        struct stackframe frame;
 +      unsigned long stack_page;
        int count = 0;
        if (!p || p == current || p->state == TASK_RUNNING)
                return 0;
        frame.fp = thread_saved_fp(p);
        frame.sp = thread_saved_sp(p);
        frame.pc = thread_saved_pc(p);
 +      stack_page = (unsigned long)task_stack_page(p);
        do {
 -              int ret = unwind_frame(&frame);
 -              if (ret < 0)
 +              if (frame.sp < stack_page ||
 +                  frame.sp >= stack_page + THREAD_SIZE ||
 +                  unwind_frame(&frame))
                        return 0;
                if (!in_sched_functions(frame.pc))
                        return frame.pc;
index bb33fff09ba2e515ab1294cd8a82a199a50ce6d4,87ddfce35cb5360ddb38f0d0a1a1f28ae6b086a9..c8e9effe52e10f6b59d39de293aca7607f7d5bce
@@@ -108,26 -108,89 +108,95 @@@ void __init early_print(const char *str
        printk("%s", buf);
  }
  
 +void __init smp_setup_processor_id(void)
 +{
 +      /*
 +       * clear __my_cpu_offset on boot CPU to avoid hang caused by
 +       * using percpu variable early, for example, lockdep will
 +       * access percpu variable inside lock_release
 +       */
 +      set_my_cpu_offset(0);
 +}
 +
  bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
  {
        return phys_id == cpu_logical_map(cpu);
  }
  
+ struct mpidr_hash mpidr_hash;
+ #ifdef CONFIG_SMP
+ /**
+  * smp_build_mpidr_hash - Pre-compute shifts required at each affinity
+  *                      level in order to build a linear index from an
+  *                      MPIDR value. Resulting algorithm is a collision
+  *                      free hash carried out through shifting and ORing
+  */
+ static void __init smp_build_mpidr_hash(void)
+ {
+       u32 i, affinity, fs[4], bits[4], ls;
+       u64 mask = 0;
+       /*
+        * Pre-scan the list of MPIDRS and filter out bits that do
+        * not contribute to affinity levels, ie they never toggle.
+        */
+       for_each_possible_cpu(i)
+               mask |= (cpu_logical_map(i) ^ cpu_logical_map(0));
+       pr_debug("mask of set bits %#llx\n", mask);
+       /*
+        * Find and stash the last and first bit set at all affinity levels to
+        * check how many bits are required to represent them.
+        */
+       for (i = 0; i < 4; i++) {
+               affinity = MPIDR_AFFINITY_LEVEL(mask, i);
+               /*
+                * Find the MSB bit and LSB bits position
+                * to determine how many bits are required
+                * to express the affinity level.
+                */
+               ls = fls(affinity);
+               fs[i] = affinity ? ffs(affinity) - 1 : 0;
+               bits[i] = ls - fs[i];
+       }
+       /*
+        * An index can be created from the MPIDR_EL1 by isolating the
+        * significant bits at each affinity level and by shifting
+        * them in order to compress the 32 bits values space to a
+        * compressed set of values. This is equivalent to hashing
+        * the MPIDR_EL1 through shifting and ORing. It is a collision free
+        * hash though not minimal since some levels might contain a number
+        * of CPUs that is not an exact power of 2 and their bit
+        * representation might contain holes, eg MPIDR_EL1[7:0] = {0x2, 0x80}.
+        */
+       mpidr_hash.shift_aff[0] = MPIDR_LEVEL_SHIFT(0) + fs[0];
+       mpidr_hash.shift_aff[1] = MPIDR_LEVEL_SHIFT(1) + fs[1] - bits[0];
+       mpidr_hash.shift_aff[2] = MPIDR_LEVEL_SHIFT(2) + fs[2] -
+                                               (bits[1] + bits[0]);
+       mpidr_hash.shift_aff[3] = MPIDR_LEVEL_SHIFT(3) +
+                                 fs[3] - (bits[2] + bits[1] + bits[0]);
+       mpidr_hash.mask = mask;
+       mpidr_hash.bits = bits[3] + bits[2] + bits[1] + bits[0];
+       pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n",
+               mpidr_hash.shift_aff[0],
+               mpidr_hash.shift_aff[1],
+               mpidr_hash.shift_aff[2],
+               mpidr_hash.shift_aff[3],
+               mpidr_hash.mask,
+               mpidr_hash.bits);
+       /*
+        * 4x is an arbitrary value used to warn on a hash table much bigger
+        * than expected on most systems.
+        */
+       if (mpidr_hash_size() > 4 * num_possible_cpus())
+               pr_warn("Large number of MPIDR hash buckets detected\n");
+       __flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash));
+ }
+ #endif
  static void __init setup_processor(void)
  {
        struct cpu_info *cpu_info;
 +      u64 features, block;
  
 -      /*
 -       * locate processor in the list of supported processor
 -       * types.  The linker builds this table for us from the
 -       * entries in arch/arm/mm/proc.S
 -       */
        cpu_info = lookup_processor_type(read_cpuid_id());
        if (!cpu_info) {
                printk("CPU configuration botched (ID %08x), unable to continue.\n",
  
        sprintf(init_utsname()->machine, ELF_PLATFORM);
        elf_hwcap = 0;
 +
 +      /*
 +       * ID_AA64ISAR0_EL1 contains 4-bit wide signed feature blocks.
 +       * The blocks we test below represent incremental functionality
 +       * for non-negative values. Negative values are reserved.
 +       */
 +      features = read_cpuid(ID_AA64ISAR0_EL1);
 +      block = (features >> 4) & 0xf;
 +      if (!(block & 0x8)) {
 +              switch (block) {
 +              default:
 +              case 2:
 +                      elf_hwcap |= HWCAP_PMULL;
 +              case 1:
 +                      elf_hwcap |= HWCAP_AES;
 +              case 0:
 +                      break;
 +              }
 +      }
 +
 +      block = (features >> 8) & 0xf;
 +      if (block && !(block & 0x8))
 +              elf_hwcap |= HWCAP_SHA1;
 +
 +      block = (features >> 12) & 0xf;
 +      if (block && !(block & 0x8))
 +              elf_hwcap |= HWCAP_SHA2;
 +
 +      block = (features >> 16) & 0xf;
 +      if (block && !(block & 0x8))
 +              elf_hwcap |= HWCAP_CRC32;
  }
  
  static void __init setup_machine_fdt(phys_addr_t dt_phys)
@@@ -273,6 -305,7 +342,7 @@@ void __init setup_arch(char **cmdline_p
        cpu_read_bootcpu_ops();
  #ifdef CONFIG_SMP
        smp_init_cpus();
+       smp_build_mpidr_hash();
  #endif
  
  #ifdef CONFIG_VT
@@@ -312,11 -345,6 +382,11 @@@ static const char *hwcap_str[] = 
        "fp",
        "asimd",
        "evtstrm",
 +      "aes",
 +      "pmull",
 +      "sha1",
 +      "sha2",
 +      "crc32",
        NULL
  };
  
diff --combined arch/arm64/kernel/smp.c
index b5d2031c12c6185d5b668f2f0c04ff7df9c8837c,0b8c859e744fb0257148beb7ac26650db280a4d0..1b7617ab499be776b3c524286b06b77ea70e8bc8
@@@ -61,6 -61,7 +61,7 @@@ enum ipi_msg_type 
        IPI_CALL_FUNC,
        IPI_CALL_FUNC_SINGLE,
        IPI_CPU_STOP,
+       IPI_TIMER,
  };
  
  /*
@@@ -122,6 -123,8 +123,6 @@@ asmlinkage void secondary_start_kernel(
        struct mm_struct *mm = &init_mm;
        unsigned int cpu = smp_processor_id();
  
 -      printk("CPU%u: Booted secondary processor\n", cpu);
 -
        /*
         * All kernel threads share the same mm context; grab a
         * reference and switch to it.
        current->active_mm = mm;
        cpumask_set_cpu(cpu, mm_cpumask(mm));
  
 +      set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
 +      printk("CPU%u: Booted secondary processor\n", cpu);
 +
        /*
         * TTBR0 is only used for the identity mapping at this stage. Make it
         * point to zero page to avoid speculatively fetching new entries.
@@@ -272,7 -272,6 +273,7 @@@ void __init smp_cpus_done(unsigned int 
  
  void __init smp_prepare_boot_cpu(void)
  {
 +      set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
  }
  
  static void (*smp_cross_call)(const struct cpumask *, unsigned int);
@@@ -449,6 -448,7 +450,7 @@@ static const char *ipi_types[NR_IPI] = 
        S(IPI_CALL_FUNC, "Function call interrupts"),
        S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
        S(IPI_CPU_STOP, "CPU stop interrupts"),
+       S(IPI_TIMER, "Timer broadcast interrupts"),
  };
  
  void show_ipi_list(struct seq_file *p, int prec)
@@@ -534,6 -534,14 +536,14 @@@ void handle_IPI(int ipinr, struct pt_re
                irq_exit();
                break;
  
+ #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+       case IPI_TIMER:
+               irq_enter();
+               tick_receive_broadcast();
+               irq_exit();
+               break;
+ #endif
        default:
                pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
                break;
@@@ -546,6 -554,13 +556,13 @@@ void smp_send_reschedule(int cpu
        smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
  }
  
+ #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+ void tick_broadcast(const struct cpumask *mask)
+ {
+       smp_cross_call(mask, IPI_TIMER);
+ }
+ #endif
  void smp_send_stop(void)
  {
        unsigned long timeout;