Merge remote-tracking branch 'lsk/v3.10/topic/arm64-misc' into linux-linaro-lsk
authorMark Brown <broonie@kernel.org>
Fri, 16 Jan 2015 22:50:34 +0000 (22:50 +0000)
committerMark Brown <broonie@kernel.org>
Sat, 17 Jan 2015 01:23:42 +0000 (01:23 +0000)
Conflicts:
arch/arm/include/asm/psci.h
arch/arm/kernel/psci.c
arch/arm/kernel/psci_smp.c
arch/arm/kernel/setup.c
arch/arm64/Kconfig
arch/arm64/include/asm/cpu_ops.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/psci.h
arch/arm64/kernel/head.S
arch/arm64/kernel/psci.c
arch/arm64/kernel/ptrace.c

16 files changed:
1  2 
arch/arm/include/asm/psci.h
arch/arm/kernel/psci.c
arch/arm/kernel/psci_smp.c
arch/arm/kernel/setup.c
arch/arm64/Kconfig
arch/arm64/include/asm/cpu_ops.h
arch/arm64/include/asm/processor.h
arch/arm64/kernel/arm64ksyms.c
arch/arm64/kernel/head.S
arch/arm64/kernel/process.c
arch/arm64/kernel/psci.c
arch/arm64/kernel/ptrace.c
arch/arm64/kernel/smp.c
arch/arm64/kernel/time.c
arch/arm64/mm/proc.S
arch/x86/kernel/reboot.c

Simple merge
index 0daf4f2522845306424c274594257867a2f545b2,f73891b6b7300dc67e353a8694a035e9a212c2d7..f705c1b27bb3d8d94a13af9d7321cca461907bdc
  
  #include <linux/init.h>
  #include <linux/of.h>
 +#include <linux/string.h>
+ #include <linux/reboot.h>
+ #include <linux/pm.h>
+ #include <uapi/linux/psci.h>
  
  #include <asm/compiler.h>
  #include <asm/errno.h>
  
  struct psci_operations psci_ops;
  
- static unsigned int psci;
 +/* Type of psci support. Currently can only be enabled or disabled */
 +#define PSCI_SUP_DISABLED             0
 +#define PSCI_SUP_ENABLED              1
 +
  static int (*invoke_psci_fn)(u32, u32, u32, u32);
+ typedef int (*psci_initcall_t)(const struct device_node *);
  
  enum psci_function {
        PSCI_FN_CPU_SUSPEND,
@@@ -55,14 -50,12 +55,14 @@@ static int psci_to_linux_errno(int errn
        switch (errno) {
        case PSCI_RET_SUCCESS:
                return 0;
-       case PSCI_RET_EOPNOTSUPP:
+       case PSCI_RET_NOT_SUPPORTED:
                return -EOPNOTSUPP;
-       case PSCI_RET_EINVAL:
+       case PSCI_RET_INVALID_PARAMS:
                return -EINVAL;
-       case PSCI_RET_EPERM:
+       case PSCI_RET_DENIED:
                return -EPERM;
-       case PSCI_RET_EALREADYON:
++      case PSCI_RET_ALREADY_ON:
 +              return -EAGAIN;
        };
  
        return -EINVAL;
index 23a11424c568c4bd11968cee55fbe7700977e273,fc1dbd25825520c0c4d20ef09f9b5c09b66c0258..3af4190e4d9db7d8ab029eedc515cc4264780ae9
   */
  
  #include <linux/init.h>
 +#include <linux/irqchip/arm-gic.h>
 +#include <linux/smp.h>
 +#include <linux/of.h>
+ #include <linux/smp.h>
+ #include <linux/of.h>
+ #include <linux/delay.h>
+ #include <uapi/linux/psci.h>
  
  #include <asm/psci.h>
  #include <asm/smp_plat.h>
Simple merge
index 279594b83781796340db6d2b2e0a81cb2c0b41af,4142cab9d4ff733c3843dcfc52e3519a76427c1c..d64c1a0e9dd0e5a0d25c832f62ffd8811fd95359
@@@ -1,10 -1,8 +1,11 @@@
  config ARM64
        def_bool y
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+       select ARCH_HAS_OPP
        select ARCH_USE_CMPXCHG_LOCKREF
 +      select ARCH_HAS_OPP
 +      select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
 +      select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_WANT_OPTIONAL_GPIOLIB
        select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
        select ARCH_WANT_FRAME_POINTERS
        select GENERIC_STRNLEN_USER
        select GENERIC_TIME_VSYSCALL
        select HARDIRQS_SW_RESEND
 +      select HAVE_ARCH_JUMP_LABEL
        select HAVE_ARCH_KGDB
        select HAVE_ARCH_TRACEHOOK
 +      select HAVE_C_RECORDMCOUNT
+       select HAVE_CC_STACKPROTECTOR
        select HAVE_DEBUG_BUGVERBOSE
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DMA_API_DEBUG
index 152413076503ba4731ce13d693ae2fa70d197795,27207fee2521037270c10f18c2c0c816e56447f2..d7b4b38a8e8625f25131bfc5d72c79aecfbbc4f8
@@@ -39,9 -39,7 +39,10 @@@ struct device_node
   *            from the cpu to be killed.
   * @cpu_die:  Makes a cpu leave the kernel. Must not fail. Called from the
   *            cpu being killed.
+  * @cpu_kill:  Ensures a cpu has left the kernel. Called from another cpu.
 + * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
 + *               to wrong parameters or error conditions. Called from the
 + *               CPU being suspended. Must be called with IRQs disabled.
   */
  struct cpu_operations {
        const char      *name;
  #ifdef CONFIG_HOTPLUG_CPU
        int             (*cpu_disable)(unsigned int cpu);
        void            (*cpu_die)(unsigned int cpu);
+       int             (*cpu_kill)(unsigned int cpu);
  #endif
 +#ifdef CONFIG_ARM64_CPU_SUSPEND
 +      int             (*cpu_suspend)(unsigned long);
 +#endif
  };
  
  extern const struct cpu_operations *cpu_ops[NR_CPUS];
Simple merge
Simple merge
Simple merge
Simple merge
index 0e32ab453e5b022695829ab44308fa301f621082,9e9798f91172ecb9d340c975f6260b1889ddec99..e58a67974f98fcaef60dd7fc822b523d558c917a
  #include <linux/init.h>
  #include <linux/of.h>
  #include <linux/smp.h>
 +#include <linux/slab.h>
+ #include <linux/reboot.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
+ #include <uapi/linux/psci.h>
  
  #include <asm/compiler.h>
  #include <asm/cpu_ops.h>
  #include <asm/errno.h>
  #include <asm/psci.h>
  #include <asm/smp_plat.h>
 +#include <asm/suspend.h>
+ #include <asm/system_misc.h>
  
  #define PSCI_POWER_STATE_TYPE_STANDBY         0
  #define PSCI_POWER_STATE_TYPE_POWER_DOWN      1
@@@ -57,15 -65,8 +68,10 @@@ enum psci_function 
        PSCI_FN_MAX,
  };
  
 +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
 +
  static u32 psci_function_id[PSCI_FN_MAX];
  
- #define PSCI_RET_SUCCESS              0
- #define PSCI_RET_EOPNOTSUPP           -1
- #define PSCI_RET_EINVAL                       -2
- #define PSCI_RET_EPERM                        -3
  static int psci_to_linux_errno(int errno)
  {
        switch (errno) {
        return -EINVAL;
  }
  
- #define PSCI_POWER_STATE_ID_MASK      0xffff
- #define PSCI_POWER_STATE_ID_SHIFT     0
- #define PSCI_POWER_STATE_TYPE_MASK    0x1
- #define PSCI_POWER_STATE_TYPE_SHIFT   16
- #define PSCI_POWER_STATE_AFFL_MASK    0x3
- #define PSCI_POWER_STATE_AFFL_SHIFT   24
  static u32 psci_power_state_pack(struct psci_power_state state)
  {
-       return  ((state.id & PSCI_POWER_STATE_ID_MASK)
-                       << PSCI_POWER_STATE_ID_SHIFT)   |
-               ((state.type & PSCI_POWER_STATE_TYPE_MASK)
-                       << PSCI_POWER_STATE_TYPE_SHIFT) |
-               ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
-                       << PSCI_POWER_STATE_AFFL_SHIFT);
+       return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
+                       & PSCI_0_2_POWER_STATE_ID_MASK) |
+               ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
+                & PSCI_0_2_POWER_STATE_TYPE_MASK) |
+               ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
+                & PSCI_0_2_POWER_STATE_AFFL_MASK);
  }
  
-       state->id = (power_state >> PSCI_POWER_STATE_ID_SHIFT)
-                       & PSCI_POWER_STATE_ID_MASK;
-       state->type = (power_state >> PSCI_POWER_STATE_TYPE_SHIFT)
-                       & PSCI_POWER_STATE_TYPE_MASK;
-       state->affinity_level = (power_state >> PSCI_POWER_STATE_AFFL_SHIFT)
-                       & PSCI_POWER_STATE_AFFL_MASK;
 +static void psci_power_state_unpack(u32 power_state,
 +                                  struct psci_power_state *state)
 +{
++      state->id = (power_state >> PSCI_0_2_POWER_STATE_ID_SHIFT)
++                      & PSCI_0_2_POWER_STATE_ID_MASK;
++      state->type = (power_state >> PSCI_0_2_POWER_STATE_TYPE_SHIFT)
++                      & PSCI_0_2_POWER_STATE_TYPE_MASK;
++      state->affinity_level = (power_state >> PSCI_0_2_POWER_STATE_AFFL_SHIFT)
++                      & PSCI_0_2_POWER_STATE_AFFL_MASK;
 +}
 +
  /*
   * The following two functions are invoked via the invoke_psci_fn pointer
   * and will not be inlined, allowing us to piggyback on the AAPCS.
@@@ -187,107 -178,135 +194,135 @@@ static int psci_migrate(unsigned long c
        return psci_to_linux_errno(err);
  }
  
- static const struct of_device_id psci_of_match[] __initconst = {
-       { .compatible = "arm,psci",     },
-       {},
- };
+ static int psci_affinity_info(unsigned long target_affinity,
+               unsigned long lowest_affinity_level)
+ {
+       int err;
+       u32 fn;
  
- int __init psci_dt_register_idle_states(struct cpuidle_driver *drv,
-                                       struct device_node *state_nodes[])
+       fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
+       err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
+       return err;
+ }
+ static int psci_migrate_info_type(void)
  {
-       int cpu, i;
-       struct psci_power_state *psci_states;
-       const struct cpu_operations *cpu_ops_ptr;
+       int err;
+       u32 fn;
+       fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
+       err = invoke_psci_fn(fn, 0, 0, 0);
+       return err;
+ }
+ static int get_set_conduit_method(struct device_node *np)
+ {
+       const char *method;
+       pr_info("probing for conduit method from DT.\n");
+       if (of_property_read_string(np, "method", &method)) {
+               pr_warn("missing \"method\" property\n");
+               return -ENXIO;
+       }
  
-       if (!state_nodes)
+       if (!strcmp("hvc", method)) {
+               invoke_psci_fn = __invoke_psci_fn_hvc;
+       } else if (!strcmp("smc", method)) {
+               invoke_psci_fn = __invoke_psci_fn_smc;
+       } else {
+               pr_warn("invalid \"method\" property: %s\n", method);
                return -EINVAL;
-       /*
-        * This is belt-and-braces: make sure that if the idle
-        * specified protocol is psci, the cpu_ops have been
-        * initialized to psci operations. Anything else is
-        * a recipe for mayhem.
-        */
-       for_each_cpu(cpu, drv->cpumask) {
-               cpu_ops_ptr = cpu_ops[cpu];
-               if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci")))
-                       return -EOPNOTSUPP;
        }
+       return 0;
+ }
  
-       psci_states = kcalloc(drv->state_count, sizeof(*psci_states),
-                             GFP_KERNEL);
 -static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
++static void psci_sys_reset(char str, const char *cmd)
+ {
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+ }
  
-       if (!psci_states) {
-               pr_warn("psci idle state allocation failed\n");
-               return -ENOMEM;
-       }
+ static void psci_sys_poweroff(void)
+ {
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+ }
+ /*
+  * PSCI Function IDs for v0.2+ are well defined so use
+  * standard values.
+  */
+ static int psci_0_2_init(struct device_node *np)
+ {
+       int err, ver;
+       err = get_set_conduit_method(np);
  
-       for_each_cpu(cpu, drv->cpumask) {
-               if (per_cpu(psci_power_state, cpu)) {
-                       pr_warn("idle states already initialized on cpu %u\n",
-                               cpu);
-                       continue;
+       if (err)
+               goto out_put_node;
+       ver = psci_get_version();
+       if (ver == PSCI_RET_NOT_SUPPORTED) {
+               /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
+               pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
+               err = -EOPNOTSUPP;
+               goto out_put_node;
+       } else {
+               pr_info("PSCIv%d.%d detected in firmware.\n",
+                               PSCI_VERSION_MAJOR(ver),
+                               PSCI_VERSION_MINOR(ver));
+               if (PSCI_VERSION_MAJOR(ver) == 0 &&
+                               PSCI_VERSION_MINOR(ver) < 2) {
+                       err = -EINVAL;
+                       pr_err("Conflicting PSCI version detected.\n");
+                       goto out_put_node;
                }
-               per_cpu(psci_power_state, cpu) = psci_states;
        }
  
+       pr_info("Using standard PSCI v0.2 function IDs\n");
+       psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
+       psci_ops.cpu_suspend = psci_cpu_suspend;
  
-       for (i = 0; i < drv->state_count; i++) {
-               u32 psci_power_state;
+       psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
+       psci_ops.cpu_off = psci_cpu_off;
  
-               if (!state_nodes[i]) {
-                       /*
-                        * An index with a missing node pointer falls back to
-                        * simple STANDBYWFI
-                        */
-                       psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY;
-                       continue;
-               }
+       psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
+       psci_ops.cpu_on = psci_cpu_on;
  
-               if (of_property_read_u32(state_nodes[i], "entry-method-param",
-                                        &psci_power_state)) {
-                       pr_warn(" * %s missing entry-method-param property\n",
-                               state_nodes[i]->full_name);
-                       /*
-                        * If entry-method-param property is missing, fall
-                        * back to STANDBYWFI state
-                        */
-                       psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY;
-                       continue;
-               }
+       psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
+       psci_ops.migrate = psci_migrate;
  
-               pr_debug("psci-power-state %#x index %u\n",
-                        psci_power_state, i);
-               psci_power_state_unpack(psci_power_state, &psci_states[i]);
-       }
+       psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
+       psci_ops.affinity_info = psci_affinity_info;
  
-       return 0;
+       psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
+               PSCI_0_2_FN_MIGRATE_INFO_TYPE;
+       psci_ops.migrate_info_type = psci_migrate_info_type;
+       arm_pm_restart = psci_sys_reset;
+       pm_power_off = psci_sys_poweroff;
+ out_put_node:
+       of_node_put(np);
+       return err;
  }
  
- void __init psci_init(void)
+ /*
+  * PSCI < v0.2 get PSCI Function IDs via DT.
+  */
+ static int psci_0_1_init(struct device_node *np)
  {
-       struct device_node *np;
-       const char *method;
        u32 id;
+       int err;
  
-       np = of_find_matching_node(NULL, psci_of_match);
-       if (!np)
-               return;
-       pr_info("probing function IDs from device-tree\n");
+       err = get_set_conduit_method(np);
  
-       if (of_property_read_string(np, "method", &method)) {
-               pr_warning("missing \"method\" property\n");
+       if (err)
                goto out_put_node;
-       }
  
-       if (!strcmp("hvc", method)) {
-               invoke_psci_fn = __invoke_psci_fn_hvc;
-       } else if (!strcmp("smc", method)) {
-               invoke_psci_fn = __invoke_psci_fn_smc;
-       } else {
-               pr_warning("invalid \"method\" property: %s\n", method);
-               goto out_put_node;
-       }
+       pr_info("Using PSCI v0.1 Function IDs from DT\n");
  
        if (!of_property_read_u32(np, "cpu_suspend", &id)) {
                psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
@@@ -364,20 -404,37 +420,49 @@@ static void cpu_psci_cpu_die(unsigned i
  
        pr_crit("unable to power off CPU%u (%d)\n", cpu, ret);
  }
+ static int cpu_psci_cpu_kill(unsigned int cpu)
+ {
+       int err, i;
+       if (!psci_ops.affinity_info)
+               return 1;
+       /*
+        * cpu_kill could race with cpu_die and we can
+        * potentially end up declaring this cpu undead
+        * while it is dying. So, try again a few times.
+        */
+       for (i = 0; i < 10; i++) {
+               err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+               if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+                       pr_info("CPU%d killed.\n", cpu);
+                       return 1;
+               }
+               msleep(10);
+               pr_info("Retrying again to check for CPU kill\n");
+       }
+       pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
+                       cpu, err);
+       /* Make op_cpu_kill() fail. */
+       return 0;
+ }
  #endif
  
 +#ifdef CONFIG_ARM64_CPU_SUSPEND
 +static int cpu_psci_cpu_suspend(unsigned long index)
 +{
 +      struct psci_power_state *state = __get_cpu_var(psci_power_state);
 +
 +      if (!state)
 +              return -EOPNOTSUPP;
 +
 +      return psci_ops.cpu_suspend(state[index], virt_to_phys(cpu_resume));
 +}
 +#endif
 +
  const struct cpu_operations cpu_psci_ops = {
        .name           = "psci",
        .cpu_init       = cpu_psci_cpu_init,
  #ifdef CONFIG_HOTPLUG_CPU
        .cpu_disable    = cpu_psci_cpu_disable,
        .cpu_die        = cpu_psci_cpu_die,
+       .cpu_kill       = cpu_psci_cpu_kill,
  #endif
 +#ifdef CONFIG_ARM64_CPU_SUSPEND
 +      .cpu_suspend    = cpu_psci_cpu_suspend,
 +#endif
  };
  
  #endif
index bc09a147454fe1348e74905b3cf2d42a1937fe1f,71c6a623e226e594a499954653bd91ee307bd50a..8ba6b0fa175363cf8f75511a1ee068cebcfccd76
@@@ -643,23 -638,28 +643,28 @@@ static int compat_gpr_get(struct task_s
  
                switch (idx) {
                case 15:
 -                      reg = (void *)&task_pt_regs(target)->pc;
 +                      reg = task_pt_regs(target)->pc;
                        break;
                case 16:
 -                      reg = (void *)&task_pt_regs(target)->pstate;
 +                      reg = task_pt_regs(target)->pstate;
                        break;
                case 17:
 -                      reg = (void *)&task_pt_regs(target)->orig_x0;
 +                      reg = task_pt_regs(target)->orig_x0;
                        break;
                default:
 -                      reg = (void *)&task_pt_regs(target)->regs[idx];
 +                      reg = task_pt_regs(target)->regs[idx];
                }
  
-               ret = copy_to_user(ubuf, &reg, sizeof(reg));
-               if (ret)
-                       break;
-               ubuf += sizeof(reg);
+               if (kbuf) {
+                       memcpy(kbuf, &reg, sizeof(reg));
+                       kbuf += sizeof(reg);
+               } else {
+                       ret = copy_to_user(ubuf, &reg, sizeof(reg));
+                       if (ret)
+                               break;
+                       ubuf += sizeof(reg);
+               }
        }
  
        return ret;
Simple merge
Simple merge
Simple merge
Simple merge