Merge tag 'pm2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Aug 2012 18:48:54 +0000 (11:48 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Aug 2012 18:48:54 +0000 (11:48 -0700)
Pull arm-soc cpuidle enablement for OMAP from Olof Johansson:
 "Coupled cpuidle was meant to merge for 3.5 through Len Brown's tree,
  but didn't go in because the pull request ended up rejected.  So it
  just got merged, and we got this staged branch that enables the
  coupled cpuidle code on OMAP.

  With a stable git workflow from the other maintainer we could have
  staged this earlier, but that wasn't the case so we have had to merge
  it late.

  The alternative is to hold it off until 3.7 but given that the code is
  well-isolated to OMAP and they are eager to see it go in, I didn't
  push back hard in that direction."

* tag 'pm2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc:
  ARM: OMAP4: CPUidle: Open broadcast clock-event device.
  ARM: OMAP4: CPUidle: add synchronization for coupled idle states
  ARM: OMAP4: CPUidle: Use coupled cpuidle states to implement SMP cpuidle.
  ARM: OMAP: timer: allow gp timer clock-event to be used on both cpus

1  2 
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/cpuidle44xx.c
arch/arm/mach-omap2/timer.c

index dd0fbf76ac793d87580c1d750bad8983a25ef62e,cc83f5e13d5c5093d6018e98becf6d148ee55b66..dd2db025f7787e590d94bb229cb143559f9a8317
@@@ -9,7 -9,7 +9,7 @@@ config ARCH_OMAP2PLUS_TYPICA
        select REGULATOR
        select PM_RUNTIME
        select VFP
 -      select NEON if ARCH_OMAP3 || ARCH_OMAP4
 +      select NEON if ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5
        select SERIAL_OMAP
        select SERIAL_OMAP_CONSOLE
        select I2C
        help
          Compile a kernel suitable for booting most boards
  
 +config SOC_HAS_OMAP2_SDRC
 +      bool "OMAP2 SDRAM Controller support"
 +
  config ARCH_OMAP2
        bool "TI OMAP2"
        depends on ARCH_OMAP2PLUS
        default y
        select CPU_V6
        select MULTI_IRQ_HANDLER
 +      select SOC_HAS_OMAP2_SDRC
  
  config ARCH_OMAP3
        bool "TI OMAP3"
        select CPU_V7
        select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select ARCH_HAS_OPP
 +      select PM_RUNTIME if CPU_IDLE
        select PM_OPP if PM
        select ARM_CPU_SUSPEND if PM
        select MULTI_IRQ_HANDLER
 +      select SOC_HAS_OMAP2_SDRC
  
  config ARCH_OMAP4
        bool "TI OMAP4"
        select PL310_ERRATA_727915
        select ARM_ERRATA_720789
        select ARCH_HAS_OPP
 +      select PM_RUNTIME if CPU_IDLE
        select PM_OPP if PM
        select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select ARM_CPU_SUSPEND if PM
+       select ARCH_NEEDS_CPU_IDLE_COUPLED
  
 +config SOC_OMAP5
 +      bool "TI OMAP5"
 +      select CPU_V7
 +      select ARM_GIC
 +      select HAVE_SMP
 +
  comment "OMAP Core Type"
        depends on ARCH_OMAP2
  
@@@ -77,19 -65,19 +78,19 @@@ config SOC_OMAP242
        depends on ARCH_OMAP2
        default y
        select OMAP_DM_TIMER
 -      select ARCH_OMAP_OTG
 +      select SOC_HAS_OMAP2_SDRC
  
  config SOC_OMAP2430
        bool "OMAP2430 support"
        depends on ARCH_OMAP2
        default y
 -      select ARCH_OMAP_OTG
 +      select SOC_HAS_OMAP2_SDRC
  
  config SOC_OMAP3430
        bool "OMAP3430 support"
        depends on ARCH_OMAP3
        default y
 -      select ARCH_OMAP_OTG
 +      select SOC_HAS_OMAP2_SDRC
  
  config SOC_TI81XX
        bool "TI81XX support"
  
  config SOC_AM33XX
        bool "AM33XX support"
 -      depends on ARCH_OMAP3
        default y
 +      select CPU_V7
 +      select ARM_CPU_SUSPEND if PM
 +      select MULTI_IRQ_HANDLER
  
  config OMAP_PACKAGE_ZAF
         bool
index 02d15bbd4e35c2eb4d90cf892085d143d0334e81,45e6a54d58187844b66336bf42c29b9946f1a4e1..ee05e193fc61e317b21b368c583f31ce71bbb76b
  #include "common.h"
  #include "pm.h"
  #include "prm.h"
+ #include "clockdomain.h"
  
 -#ifdef CONFIG_CPU_IDLE
 -
  /* Machine specific information */
  struct omap4_idle_statedata {
        u32 cpu_state;
@@@ -47,10 -50,14 +48,14 @@@ static struct omap4_idle_statedata omap
        },
  };
  
- static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
+ static struct powerdomain *mpu_pd, *cpu_pd[NR_CPUS];
+ static struct clockdomain *cpu_clkdm[NR_CPUS];
+ static atomic_t abort_barrier;
+ static bool cpu_done[NR_CPUS];
  
  /**
-  * omap4_enter_idle - Programs OMAP4 to enter the specified state
+  * omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions
   * @dev: cpuidle device
   * @drv: cpuidle driver
   * @index: the index of state to be entered
   * specified low power state selected by the governor.
   * Returns the amount of time spent in the low power state.
   */
- static int omap4_enter_idle(struct cpuidle_device *dev,
+ static int omap4_enter_idle_simple(struct cpuidle_device *dev,
+                       struct cpuidle_driver *drv,
+                       int index)
+ {
+       local_fiq_disable();
+       omap_do_wfi();
+       local_fiq_enable();
+       return index;
+ }
+ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index)
  {
        struct omap4_idle_statedata *cx = &omap4_idle_data[index];
-       u32 cpu1_state;
        int cpu_id = smp_processor_id();
  
        local_fiq_disable();
  
        /*
-        * CPU0 has to stay ON (i.e in C1) until CPU1 is OFF state.
+        * CPU0 has to wait and stay ON until CPU1 is OFF state.
         * This is necessary to honour hardware recommondation
         * of triggeing all the possible low power modes once CPU1 is
         * out of coherency and in OFF mode.
-        * Update dev->last_state so that governor stats reflects right
-        * data.
         */
-       cpu1_state = pwrdm_read_pwrst(cpu1_pd);
-       if (cpu1_state != PWRDM_POWER_OFF) {
-               index = drv->safe_state_index;
-               cx = &omap4_idle_data[index];
+       if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
+               while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) {
+                       cpu_relax();
+                       /*
+                        * CPU1 could have already entered & exited idle
+                        * without hitting off because of a wakeup
+                        * or a failed attempt to hit off mode.  Check for
+                        * that here, otherwise we could spin forever
+                        * waiting for CPU1 off.
+                        */
+                       if (cpu_done[1])
+                           goto fail;
+               }
        }
  
-       if (index > 0)
-               clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+       clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
  
        /*
         * Call idle CPU PM enter notifier chain so that
         * VFP and per CPU interrupt context is saved.
         */
-       if (cx->cpu_state == PWRDM_POWER_OFF)
-               cpu_pm_enter();
-       pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
-       omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
-       /*
-        * Call idle CPU cluster PM enter notifier chain
-        * to save GIC and wakeupgen context.
-        */
-       if ((cx->mpu_state == PWRDM_POWER_RET) &&
-               (cx->mpu_logic_state == PWRDM_POWER_OFF))
-                       cpu_cluster_pm_enter();
+       cpu_pm_enter();
+       if (dev->cpu == 0) {
+               pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
+               omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
+               /*
+                * Call idle CPU cluster PM enter notifier chain
+                * to save GIC and wakeupgen context.
+                */
+               if ((cx->mpu_state == PWRDM_POWER_RET) &&
+                       (cx->mpu_logic_state == PWRDM_POWER_OFF))
+                               cpu_cluster_pm_enter();
+       }
  
        omap4_enter_lowpower(dev->cpu, cx->cpu_state);
+       cpu_done[dev->cpu] = true;
+       /* Wakeup CPU1 only if it is not offlined */
+       if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
+               clkdm_wakeup(cpu_clkdm[1]);
+               clkdm_allow_idle(cpu_clkdm[1]);
+       }
  
        /*
         * Call idle CPU PM exit notifier chain to restore
-        * VFP and per CPU IRQ context. Only CPU0 state is
-        * considered since CPU1 is managed by CPU hotplug.
+        * VFP and per CPU IRQ context.
         */
-       if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
-               cpu_pm_exit();
+       cpu_pm_exit();
  
        /*
         * Call idle CPU cluster PM exit notifier chain
        if (omap4_mpuss_read_prev_context_state())
                cpu_cluster_pm_exit();
  
-       if (index > 0)
-               clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+       clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+ fail:
+       cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
+       cpu_done[dev->cpu] = false;
  
        local_fiq_enable();
  
@@@ -141,7 -175,7 +173,7 @@@ struct cpuidle_driver omap4_idle_drive
                        .exit_latency = 2 + 2,
                        .target_residency = 5,
                        .flags = CPUIDLE_FLAG_TIME_VALID,
-                       .enter = omap4_enter_idle,
+                       .enter = omap4_enter_idle_simple,
                        .name = "C1",
                        .desc = "MPUSS ON"
                },
                          /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
                        .exit_latency = 328 + 440,
                        .target_residency = 960,
-                       .flags = CPUIDLE_FLAG_TIME_VALID,
-                       .enter = omap4_enter_idle,
+                       .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
+                       .enter = omap4_enter_idle_coupled,
                        .name = "C2",
                        .desc = "MPUSS CSWR",
                },
                        /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
                        .exit_latency = 460 + 518,
                        .target_residency = 1100,
-                       .flags = CPUIDLE_FLAG_TIME_VALID,
-                       .enter = omap4_enter_idle,
+                       .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
+                       .enter = omap4_enter_idle_coupled,
                        .name = "C3",
                        .desc = "MPUSS OSWR",
                },
        .safe_state_index = 0,
  };
  
+ /*
+  * For each cpu, setup the broadcast timer because local timers
+  * stops for the states above C1.
+  */
+ static void omap_setup_broadcast_timer(void *arg)
+ {
+       int cpu = smp_processor_id();
+       clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
+ }
  /**
   * omap4_idle_init - Init routine for OMAP4 idle
   *
@@@ -180,20 -224,37 +222,31 @@@ int __init omap4_idle_init(void
        unsigned int cpu_id = 0;
  
        mpu_pd = pwrdm_lookup("mpu_pwrdm");
-       cpu0_pd = pwrdm_lookup("cpu0_pwrdm");
-       cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
-       if ((!mpu_pd) || (!cpu0_pd) || (!cpu1_pd))
+       cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
+       cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
+       if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1]))
                return -ENODEV;
  
-       dev = &per_cpu(omap4_idle_dev, cpu_id);
-       dev->cpu = cpu_id;
+       cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm");
+       cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm");
+       if (!cpu_clkdm[0] || !cpu_clkdm[1])
+               return -ENODEV;
+       /* Configure the broadcast timer on each cpu */
+       on_each_cpu(omap_setup_broadcast_timer, NULL, 1);
+       for_each_cpu(cpu_id, cpu_online_mask) {
+               dev = &per_cpu(omap4_idle_dev, cpu_id);
+               dev->cpu = cpu_id;
+               dev->coupled_cpus = *cpu_online_mask;
  
-       cpuidle_register_driver(&omap4_idle_driver);
+               cpuidle_register_driver(&omap4_idle_driver);
  
-       if (cpuidle_register_device(dev)) {
-               pr_err("%s: CPUidle register device failed\n", __func__);
-               return -EIO;
+               if (cpuidle_register_device(dev)) {
+                       pr_err("%s: CPUidle register failed\n", __func__);
+                       return -EIO;
+               }
        }
  
        return 0;
  }
 -#else
 -int __init omap4_idle_init(void)
 -{
 -      return 0;
 -}
 -#endif /* CONFIG_CPU_IDLE */
index 13d20c8a283dd352515d659ec4c39956228a954a,9b7a073606109cecbe5329b43ebfe8f9a4e395b9..2ff6d41ec6c6c004ace041b525ec1821d6389653
  #define OMAP3_SECURE_TIMER    1
  #endif
  
 -/* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
 -#define MAX_GPTIMER_ID                12
 -
 -static u32 sys_timer_reserved;
 -
  /* Clockevent code */
  
  static struct omap_dm_timer clkev;
@@@ -130,6 -135,7 +130,7 @@@ static struct clock_event_device clocke
        .name           = "gp_timer",
        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
        .shift          = 32,
+       .rating         = 300,
        .set_next_event = omap2_gp_timer_set_next_event,
        .set_mode       = omap2_gp_timer_set_mode,
  };
@@@ -168,14 -174,14 +169,14 @@@ static int __init omap_dm_timer_init_on
                return -ENXIO;
  
        /* After the dmtimer is using hwmod these clocks won't be needed */
 -      sprintf(name, "gpt%d_fck", gptimer_id);
 -      timer->fclk = clk_get(NULL, name);
 +      timer->fclk = clk_get(NULL, omap_hwmod_get_main_clk(oh));
        if (IS_ERR(timer->fclk))
                return -ENODEV;
  
        omap_hwmod_enable(oh);
  
 -      sys_timer_reserved |= (1 << (gptimer_id - 1));
 +      if (omap_dm_timer_reserve_systimer(gptimer_id))
 +              return -ENODEV;
  
        if (gptimer_id != 12) {
                struct clk *src;
@@@ -223,7 -229,8 +224,8 @@@ static void __init omap2_gp_clockevent_
                clockevent_delta2ns(3, &clockevent_gpt);
                /* Timer internal resynch latency. */
  
-       clockevent_gpt.cpumask = cpumask_of(0);
+       clockevent_gpt.cpumask = cpu_possible_mask;
+       clockevent_gpt.irq = omap_dm_timer_get_irq(&clkev);
        clockevents_register_device(&clockevent_gpt);
  
        pr_info("OMAP clockevent source: GPTIMER%d at %lu Hz\n",
@@@ -363,11 -370,6 +365,11 @@@ OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SEC
  OMAP_SYS_TIMER(3_secure)
  #endif
  
 +#ifdef CONFIG_SOC_AM33XX
 +OMAP_SYS_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, 2, OMAP4_MPU_SOURCE)
 +OMAP_SYS_TIMER(3_am33xx)
 +#endif
 +
  #ifdef CONFIG_ARCH_OMAP4
  #ifdef CONFIG_LOCAL_TIMERS
  static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
@@@ -393,10 -395,65 +395,10 @@@ static void __init omap4_timer_init(voi
  OMAP_SYS_TIMER(4)
  #endif
  
 -/**
 - * omap2_dm_timer_set_src - change the timer input clock source
 - * @pdev:     timer platform device pointer
 - * @source:   array index of parent clock source
 - */
 -static int omap2_dm_timer_set_src(struct platform_device *pdev, int source)
 -{
 -      int ret;
 -      struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
 -      struct clk *fclk, *parent;
 -      char *parent_name = NULL;
 -
 -      fclk = clk_get(&pdev->dev, "fck");
 -      if (IS_ERR_OR_NULL(fclk)) {
 -              dev_err(&pdev->dev, "%s: %d: clk_get() FAILED\n",
 -                              __func__, __LINE__);
 -              return -EINVAL;
 -      }
 -
 -      switch (source) {
 -      case OMAP_TIMER_SRC_SYS_CLK:
 -              parent_name = "sys_ck";
 -              break;
 -
 -      case OMAP_TIMER_SRC_32_KHZ:
 -              parent_name = "32k_ck";
 -              break;
 -
 -      case OMAP_TIMER_SRC_EXT_CLK:
 -              if (pdata->timer_ip_version == OMAP_TIMER_IP_VERSION_1) {
 -                      parent_name = "alt_ck";
 -                      break;
 -              }
 -              dev_err(&pdev->dev, "%s: %d: invalid clk src.\n",
 -                      __func__, __LINE__);
 -              clk_put(fclk);
 -              return -EINVAL;
 -      }
 -
 -      parent = clk_get(&pdev->dev, parent_name);
 -      if (IS_ERR_OR_NULL(parent)) {
 -              dev_err(&pdev->dev, "%s: %d: clk_get() %s FAILED\n",
 -                      __func__, __LINE__, parent_name);
 -              clk_put(fclk);
 -              return -EINVAL;
 -      }
 -
 -      ret = clk_set_parent(fclk, parent);
 -      if (IS_ERR_VALUE(ret)) {
 -              dev_err(&pdev->dev, "%s: clk_set_parent() to %s FAILED\n",
 -                      __func__, parent_name);
 -              ret = -EINVAL;
 -      }
 -
 -      clk_put(parent);
 -      clk_put(fclk);
 -
 -      return ret;
 -}
 +#ifdef CONFIG_SOC_OMAP5
 +OMAP_SYS_TIMER_INIT(5, 1, OMAP4_CLKEV_SOURCE, 2, OMAP4_MPU_SOURCE)
 +OMAP_SYS_TIMER(5)
 +#endif
  
  /**
   * omap_timer_init - build and register timer device with an
@@@ -418,6 -475,7 +420,6 @@@ static int __init omap_timer_init(struc
        struct dmtimer_platform_data *pdata;
        struct platform_device *pdev;
        struct omap_timer_capability_dev_attr *timer_dev_attr;
 -      struct powerdomain *pwrdm;
  
        pr_debug("%s: %s\n", __func__, oh->name);
  
         */
        sscanf(oh->name, "timer%2d", &id);
  
 -      pdata->set_timer_src = omap2_dm_timer_set_src;
 -      pdata->timer_ip_version = oh->class->rev;
 -
 -      /* Mark clocksource and clockevent timers as reserved */
 -      if ((sys_timer_reserved >> (id - 1)) & 0x1)
 -              pdata->reserved = 1;
 +      if (timer_dev_attr)
 +              pdata->timer_capability = timer_dev_attr->timer_capability;
  
 -      pwrdm = omap_hwmod_get_pwrdm(oh);
 -      pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
 -#ifdef CONFIG_PM
 -      pdata->get_context_loss_count = omap_pm_get_dev_context_loss_count;
 -#endif
        pdev = omap_device_build(name, id, oh, pdata, sizeof(*pdata),
                                 NULL, 0, 0);