Merge branch 'topic-0620/samsung-pm-3.4' into chromeos-exynos-3.4
authorOlof Johansson <olofj@chromium.org>
Wed, 20 Jun 2012 18:53:33 +0000 (11:53 -0700)
committerOlof Johansson <olofj@chromium.org>
Wed, 20 Jun 2012 18:53:33 +0000 (11:53 -0700)
* topic-0620/samsung-pm-3.4: (32 commits)
  CHROMIUM: exynos: pwm: Cosmetic tidy of PWM variable names
  CHROMIUM: exynos: Stop the PWM timer while configuring it.
  CHROMIUM: exynos: Ensure the manual update bit is off before setting it.
  exynos5: cpufreq: updated asv voltage table for cpufreq
  exynos: daisy: Add support for controlling the 32KHz peripheral clock
  trivial: regulator: Fix indentation in MAX77686
  ARM: exynos: Add thermal sensor driver platform data support
  thermal: exynos: Register the tmu sensor with the kernel thermal layer
  thermal: exynos5: Add exynos5 thermal sensor driver support
  hwmon: exynos4: Move thermal sensor driver to driver/thermal directory
  thermal: Add generic cpufreq cooling implementation
  ARM: EXYNOS5: Fix i2c suspend/resume issue
  ARM: Add missing clock definition
  ARM: EXYNOS: Add WDT reset register definitions
  regulator: Support for PMIC-MAX77686.
  mfd: Add suport for MAX77686.
  i2c: exynos: Add fix for i2c suspend/resume
  ARM: EXYNOS: Select ARM_CPU_SUSPEND & S5P_SLEEP if CPU_IDLE enabled
  UPSTREAM: cpufreq: exynos: Show list of available frequencies
  UPSTREAM: arm: exynos: Adapt to cpuidle core time keeping and irq enable
  ...

Conflicts:
arch/arm/mach-exynos/include/mach/regs-pmu.h

Change-Id: I1d871adc49be46453f87e6f4487a1711065fc2f1

39 files changed:
Documentation/hwmon/exynos4_tmu [deleted file]
Documentation/thermal/cpu-cooling-api.txt [new file with mode: 0644]
Documentation/thermal/exynos_thermal [new file with mode: 0644]
arch/arm/mach-exynos/Kconfig
arch/arm/mach-exynos/Makefile
arch/arm/mach-exynos/clock-exynos5.c
arch/arm/mach-exynos/common.c
arch/arm/mach-exynos/cpuidle.c
arch/arm/mach-exynos/hotplug.c
arch/arm/mach-exynos/include/mach/pm-core.h
arch/arm/mach-exynos/include/mach/regs-clock.h
arch/arm/mach-exynos/include/mach/regs-pmu.h
arch/arm/mach-exynos/pm.c
arch/arm/mach-exynos/pmu.c
arch/arm/plat-samsung/include/plat/pm.h
arch/arm/plat-samsung/pwm.c
drivers/cpufreq/exynos-cpufreq.c
drivers/cpufreq/exynos5250-cpufreq.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/exynos4_tmu.c [deleted file]
drivers/i2c/busses/i2c-s3c2410.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/max77686-irq.c [new file with mode: 0644]
drivers/mfd/max77686.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/max77686.c [new file with mode: 0644]
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/cpu_cooling.c [new file with mode: 0644]
drivers/thermal/exynos_thermal.c [new file with mode: 0644]
drivers/tty/serial/samsung.c
include/linux/cpu_cooling.h [new file with mode: 0644]
include/linux/mfd/max77686-private.h [new file with mode: 0644]
include/linux/mfd/max77686.h [new file with mode: 0644]
include/linux/platform_data/exynos4_tmu.h [deleted file]
include/linux/platform_data/exynos_thermal.h [new file with mode: 0644]

diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/hwmon/exynos4_tmu
deleted file mode 100644 (file)
index c3c6b41..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-Kernel driver exynos4_tmu
-=================
-
-Supported chips:
-* ARM SAMSUNG EXYNOS4 series of SoC
-  Prefix: 'exynos4-tmu'
-  Datasheet: Not publicly available
-
-Authors: Donggeun Kim <dg77.kim@samsung.com>
-
-Description
------------
-
-This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
-
-The chip only exposes the measured 8-bit temperature code value
-through a register.
-Temperature can be taken from the temperature code.
-There are three equations converting from temperature to temperature code.
-
-The three equations are:
-  1. Two point trimming
-       Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
-
-  2. One point trimming
-       Tc = T + TI1 - 25
-
-  3. No trimming
-       Tc = T + 50
-
-  Tc: Temperature code, T: Temperature,
-  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 25 degree Celsius which is unchanged
-  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 85 degree Celsius which is unchanged
-
-TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
-when temperature exceeds pre-defined levels.
-The maximum number of configurable threshold is four.
-The threshold levels are defined as follows:
-  Level_0: current temperature > trigger_level_0 + threshold
-  Level_1: current temperature > trigger_level_1 + threshold
-  Level_2: current temperature > trigger_level_2 + threshold
-  Level_3: current temperature > trigger_level_3 + threshold
-
-  The threshold and each trigger_level are set
-  through the corresponding registers.
-
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
-Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name           name of the temperature sensor
-               RO
-
-temp1_input    temperature
-               RO
-
-temp1_max      temperature for level_1 interrupt
-               RO
-
-temp1_crit     temperature for level_2 interrupt
-               RO
-
-temp1_emergency        temperature for level_3 interrupt
-               RO
-
-temp1_max_alarm        alarm for level_1 interrupt
-               RO
-
-temp1_crit_alarm
-               alarm for level_2 interrupt
-               RO
-
-temp1_emergency_alarm
-               alarm for level_3 interrupt
-               RO
diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt
new file mode 100644 (file)
index 0000000..557adb8
--- /dev/null
@@ -0,0 +1,60 @@
+CPU cooling APIs How To
+===================================
+
+Written by Amit Daniel Kachhap <amit.kachhap@linaro.org>
+
+Updated: 12 May 2012
+
+Copyright (c)  2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
+
+0. Introduction
+
+The generic cpu cooling(freq clipping, cpuhotplug etc) provides
+registration/unregistration APIs to the caller. The binding of the cooling
+devices to the trip point is left for the user. The registration APIs returns
+the cooling device pointer.
+
+1. cpu cooling APIs
+
+1.1 cpufreq registration/unregistration APIs
+1.1.1 struct thermal_cooling_device *cpufreq_cooling_register(
+       struct freq_clip_table *tab_ptr, unsigned int tab_size)
+
+    This interface function registers the cpufreq cooling device with the name
+    "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
+    cooling devices.
+
+    tab_ptr: The table containing the maximum value of frequency to be clipped
+    for each cooling state.
+       .freq_clip_max: Value of frequency to be clipped for each allowed
+        cpus.
+       .temp_level: Temperature level at which the frequency clamping will
+       happen.
+       .mask_val: cpumask of the allowed cpu's
+    tab_size: the total number of cpufreq cooling states.
+
+1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
+
+    This interface function unregisters the "thermal-cpufreq-%x" cooling device.
+
+    cdev: Cooling device pointer which has to be unregistered.
+
+
+1.2 CPU cooling action notifier register/unregister interface
+1.2.1 int cputherm_register_notifier(struct notifier_block *nb,
+       unsigned int list)
+
+    This interface registers a driver with cpu cooling layer. The driver will
+    be notified when any cpu cooling action is called.
+
+    nb: notifier function to register
+    list: CPUFREQ_COOLING_START or CPUFREQ_COOLING_STOP
+
+1.2.2 int cputherm_unregister_notifier(struct notifier_block *nb,
+       unsigned int list)
+
+    This interface registers a driver with cpu cooling layer. The driver will
+    be notified when any cpu cooling action is called.
+
+    nb: notifier function to register
+    list: CPUFREQ_COOLING_START or CPUFREQ_COOLING_STOP
diff --git a/Documentation/thermal/exynos_thermal b/Documentation/thermal/exynos_thermal
new file mode 100644 (file)
index 0000000..2b46f67
--- /dev/null
@@ -0,0 +1,52 @@
+Kernel driver exynos4_tmu
+=================
+
+Supported chips:
+* ARM SAMSUNG EXYNOS4 series of SoC
+  Prefix: 'exynos4-tmu'
+  Datasheet: Not publicly available
+
+Authors: Donggeun Kim <dg77.kim@samsung.com>
+
+Description
+-----------
+
+This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
+
+The chip only exposes the measured 8-bit temperature code value
+through a register.
+Temperature can be taken from the temperature code.
+There are three equations converting from temperature to temperature code.
+
+The three equations are:
+  1. Two point trimming
+       Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
+
+  2. One point trimming
+       Tc = T + TI1 - 25
+
+  3. No trimming
+       Tc = T + 50
+
+  Tc: Temperature code, T: Temperature,
+  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 25 degree Celsius which is unchanged
+  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 85 degree Celsius which is unchanged
+
+TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
+when temperature exceeds pre-defined levels.
+The maximum number of configurable threshold is four.
+The threshold levels are defined as follows:
+  Level_0: current temperature > trigger_level_0 + threshold
+  Level_1: current temperature > trigger_level_1 + threshold
+  Level_2: current temperature > trigger_level_2 + threshold
+  Level_3: current temperature > trigger_level_3 + threshold
+
+  The threshold and each trigger_level are set
+  through the corresponding registers.
+
+When an interrupt occurs, this driver notify kernel thermal framework
+with the function exynos4_report_trigger.
+Although an interrupt condition for level_0 can be set,
+it can be used to synchronize the cooling action.
index c6d3226..8b317d0 100644 (file)
@@ -62,6 +62,10 @@ config SOC_EXYNOS5250
        default y
        depends on ARCH_EXYNOS5
        select SAMSUNG_DMADEV
+       select ARM_CPU_SUSPEND if (PM || CPU_IDLE)
+       select S5P_PM if PM
+       select S5P_SLEEP if (PM || CPU_IDLE)
+
        help
          Enable EXYNOS5250 SoC support
 
index 3123fc7..41146cd 100644 (file)
@@ -22,7 +22,7 @@ obj-$(CONFIG_PM)              += pm.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 
-obj-$(CONFIG_ARCH_EXYNOS4)     += pmu.o
+obj-$(CONFIG_ARCH_EXYNOS     += pmu.o
 
 obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
 
index ec232a2..c2c962d 100644 (file)
 
 #ifdef CONFIG_PM_SLEEP
 static struct sleep_save exynos5_clock_save[] = {
-       /* will be implemented */
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_TOP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_DISP1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_MFC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_G3D),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GEN),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GPS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_BLOCK),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKDIV_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GEN),
+       SAVE_ITEM(EXYNOS5_CLKDIV_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC4),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC5),
+       SAVE_ITEM(EXYNOS5_SCLK_DIV_ISP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP2),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP3),
+       SAVE_ITEM(EXYNOS5_CLKSRC_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC1),
+       SAVE_ITEM(EXYNOS5_SCLK_SRC_ISP),
+       SAVE_ITEM(EXYNOS5_EPLL_CON0),
+       SAVE_ITEM(EXYNOS5_EPLL_CON1),
+       SAVE_ITEM(EXYNOS5_EPLL_CON2),
+       SAVE_ITEM(EXYNOS5_VPLL_CON0),
+       SAVE_ITEM(EXYNOS5_VPLL_CON1),
+       SAVE_ITEM(EXYNOS5_VPLL_CON2),
 };
 #endif
 
index c61453c..76a2cdb 100644 (file)
@@ -671,6 +671,8 @@ void __init exynos5_init_irq(void)
         * uses GIC instead of VIC.
         */
        s5p_init_irq(NULL, 0);
+
+       gic_arch_extn.irq_set_wake = s3c_irq_wake;
 }
 
 struct bus_type exynos_subsys = {
index 33ab4e7..c32710d 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/smp_scu.h>
 #include <asm/suspend.h>
 #include <asm/unified.h>
+#include <asm/cpuidle.h>
 #include <mach/regs-pmu.h>
 #include <mach/pmu.h>
 
 
 #define S5P_CHECK_AFTR         0xFCBA0D10
 
-static int exynos4_enter_idle(struct cpuidle_device *dev,
-                       struct cpuidle_driver *drv,
-                             int index);
 static int exynos4_enter_lowpower(struct cpuidle_device *dev,
                                struct cpuidle_driver *drv,
                                int index);
 
 static struct cpuidle_state exynos4_cpuidle_set[] __initdata = {
-       [0] = {
-               .enter                  = exynos4_enter_idle,
-               .exit_latency           = 1,
-               .target_residency       = 100000,
-               .flags                  = CPUIDLE_FLAG_TIME_VALID,
-               .name                   = "C0",
-               .desc                   = "ARM clock gating(WFI)",
-       },
+       [0] = ARM_CPUIDLE_WFI_STATE,
        [1] = {
                .enter                  = exynos4_enter_lowpower,
                .exit_latency           = 300,
@@ -63,8 +54,9 @@ static struct cpuidle_state exynos4_cpuidle_set[] __initdata = {
 static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device);
 
 static struct cpuidle_driver exynos4_idle_driver = {
-       .name           = "exynos4_idle",
-       .owner          = THIS_MODULE,
+       .name                   = "exynos4_idle",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
 };
 
 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
@@ -103,13 +95,8 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
                                struct cpuidle_driver *drv,
                                int index)
 {
-       struct timeval before, after;
-       int idle_time;
        unsigned long tmp;
 
-       local_irq_disable();
-       do_gettimeofday(&before);
-
        exynos4_set_wakeupmask();
 
        /* Set value of power down register for aftr mode */
@@ -129,7 +116,8 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
        cpu_suspend(0, idle_finisher);
 
 #ifdef CONFIG_SMP
-       scu_enable(S5P_VA_SCU);
+       if (!soc_is_exynos5250())
+               scu_enable(S5P_VA_SCU);
 #endif
        cpu_pm_exit();
 
@@ -150,34 +138,6 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
        /* Clear wakeup state register */
        __raw_writel(0x0, S5P_WAKEUP_STAT);
 
-       do_gettimeofday(&after);
-
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                   (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
-       return index;
-}
-
-static int exynos4_enter_idle(struct cpuidle_device *dev,
-                               struct cpuidle_driver *drv,
-                               int index)
-{
-       struct timeval before, after;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
-
-       cpu_do_idle();
-
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                   (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
        return index;
 }
 
@@ -192,7 +152,7 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev,
                new_index = drv->safe_state_index;
 
        if (new_index == 0)
-               return exynos4_enter_idle(dev, drv, new_index);
+               return arm_cpuidle_simple_enter(dev, drv, new_index);
        else
                return exynos4_enter_core0_aftr(dev, drv, new_index);
 }
index 9c17a0a..ae36153 100644 (file)
 #include <asm/cp15.h>
 #include <asm/smp_plat.h>
 
+#include <plat/cpu.h>
 #include <mach/regs-pmu.h>
 
 extern volatile int pen_release;
 
-static inline void cpu_enter_lowpower(void)
+static inline void cpu_enter_lowpower_a9(void)
 {
        unsigned int v;
 
@@ -45,6 +46,35 @@ static inline void cpu_enter_lowpower(void)
          : "cc");
 }
 
+static inline void cpu_enter_lowpower_a15(void)
+{
+       unsigned int v;
+
+       asm volatile(
+       "       mrc     p15, 0, %0, c1, c0, 0\n"
+       "       bic     %0, %0, %1\n"
+       "       mcr     p15, 0, %0, c1, c0, 0\n"
+         : "=&r" (v)
+         : "Ir" (CR_C)
+         : "cc");
+
+       flush_cache_all();
+
+       asm volatile(
+       /*
+       * Turn off coherency
+       */
+       "       mrc     p15, 0, %0, c1, c0, 1\n"
+       "       bic     %0, %0, %1\n"
+       "       mcr     p15, 0, %0, c1, c0, 1\n"
+       : "=&r" (v)
+       : "Ir" (0x40)
+       : "cc");
+
+       isb();
+       dsb();
+}
+
 static inline void cpu_leave_lowpower(void)
 {
        unsigned int v;
@@ -112,7 +142,10 @@ void platform_cpu_die(unsigned int cpu)
        /*
         * we're ready for shutdown now, so do it
         */
-       cpu_enter_lowpower();
+       if (soc_is_exynos5250())
+               cpu_enter_lowpower_a15();
+       else
+               cpu_enter_lowpower_a9();
        platform_do_lowpower(cpu, &spurious);
 
        /*
index 9d8da51..a67ecfa 100644 (file)
@@ -33,7 +33,7 @@ static inline void s3c_pm_arch_prepare_irqs(void)
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
        __raw_writel(s3c_irqwake_intmask, S5P_WAKEUP_MASK);
-       __raw_writel(s3c_irqwake_eintmask, S5P_EINT_WAKEUP_MASK);
+       __raw_writel(s3c_irqwake_eintmask & 0xFFFFFFFE, S5P_EINT_WAKEUP_MASK);
 }
 
 static inline void s3c_pm_arch_stop_clocks(void)
index cf4cf00..d281027 100644 (file)
 
 #define EXYNOS5_CLKDIV_ACP                     EXYNOS_CLKREG(0x08500)
 
-#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_EPLL_CON0                      EXYNOS_CLKREG(0x10130)
 #define EXYNOS5_EPLL_CON1                      EXYNOS_CLKREG(0x10134)
+#define EXYNOS5_EPLL_CON2                      EXYNOS_CLKREG(0x10138)
 #define EXYNOS5_VPLL_CON0                      EXYNOS_CLKREG(0x10140)
 #define EXYNOS5_VPLL_CON1                      EXYNOS_CLKREG(0x10144)
+#define EXYNOS5_VPLL_CON2                      EXYNOS_CLKREG(0x10148)
 #define EXYNOS5_CPLL_CON0                      EXYNOS_CLKREG(0x10120)
 
 #define EXYNOS5_CLKSRC_TOP0                    EXYNOS_CLKREG(0x10210)
+#define EXYNOS5_CLKSRC_TOP1                    EXYNOS_CLKREG(0x10214)
+#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_CLKSRC_TOP3                    EXYNOS_CLKREG(0x1021C)
 #define EXYNOS5_CLKSRC_GSCL                    EXYNOS_CLKREG(0x10220)
 #define EXYNOS5_CLKSRC_DISP1_0                 EXYNOS_CLKREG(0x1022C)
+#define EXYNOS5_CLKSRC_MAUDIO                  EXYNOS_CLKREG(0x10240)
 #define EXYNOS5_CLKSRC_FSYS                    EXYNOS_CLKREG(0x10244)
 #define EXYNOS5_CLKSRC_PERIC0                  EXYNOS_CLKREG(0x10250)
+#define EXYNOS5_CLKSRC_PERIC1                  EXYNOS_CLKREG(0x10254)
+#define EXYNOS5_SCLK_SRC_ISP                   EXYNOS_CLKREG(0x10270)
 
 #define EXYNOS5_CLKSRC_MASK_TOP                        EXYNOS_CLKREG(0x10310)
 #define EXYNOS5_CLKSRC_MASK_GSCL               EXYNOS_CLKREG(0x10320)
 #define EXYNOS5_CLKSRC_MASK_DISP1_0            EXYNOS_CLKREG(0x1032C)
+#define EXYNOS5_CLKSRC_MASK_MAUDIO             EXYNOS_CLKREG(0x10334)
 #define EXYNOS5_CLKSRC_MASK_FSYS               EXYNOS_CLKREG(0x10340)
 #define EXYNOS5_CLKSRC_MASK_PERIC0             EXYNOS_CLKREG(0x10350)
 #define EXYNOS5_CLKSRC_MASK_PERIC1             EXYNOS_CLKREG(0x10354)
 #define EXYNOS5_CLKDIV_GSCL                    EXYNOS_CLKREG(0x10520)
 #define EXYNOS5_CLKDIV_DISP1_0                 EXYNOS_CLKREG(0x1052C)
 #define EXYNOS5_CLKDIV_GEN                     EXYNOS_CLKREG(0x1053C)
+#define EXYNOS5_CLKDIV_MAUDIO                  EXYNOS_CLKREG(0x10544)
 #define EXYNOS5_CLKDIV_FSYS0                   EXYNOS_CLKREG(0x10548)
 #define EXYNOS5_CLKDIV_FSYS1                   EXYNOS_CLKREG(0x1054C)
 #define EXYNOS5_CLKDIV_FSYS2                   EXYNOS_CLKREG(0x10550)
 #define EXYNOS5_CLKDIV_FSYS3                   EXYNOS_CLKREG(0x10554)
 #define EXYNOS5_CLKDIV_PERIC0                  EXYNOS_CLKREG(0x10558)
+#define EXYNOS5_CLKDIV_PERIC1                  EXYNOS_CLKREG(0x1055C)
+#define EXYNOS5_CLKDIV_PERIC2                  EXYNOS_CLKREG(0x10560)
+#define EXYNOS5_CLKDIV_PERIC3                  EXYNOS_CLKREG(0x10564)
+#define EXYNOS5_CLKDIV_PERIC4                  EXYNOS_CLKREG(0x10568)
+#define EXYNOS5_CLKDIV_PERIC5                  EXYNOS_CLKREG(0x1056C)
+#define EXYNOS5_SCLK_DIV_ISP                   EXYNOS_CLKREG(0x10580)
+#define EXYNOS5_CLKDIV_STAT_TOP0               EXYNOS_CLKREG(0x10610)
 
 #define EXYNOS5_CLKGATE_IP_ACP                 EXYNOS_CLKREG(0x08800)
 #define EXYNOS5_CLKGATE_IP_ISP0                 EXYNOS_CLKREG(0x0C800)
 #define EXYNOS5_CLKGATE_IP_GSCL                        EXYNOS_CLKREG(0x10920)
 #define EXYNOS5_CLKGATE_IP_DISP1               EXYNOS_CLKREG(0x10928)
 #define EXYNOS5_CLKGATE_IP_MFC                 EXYNOS_CLKREG(0x1092C)
+#define EXYNOS5_CLKGATE_IP_G3D                 EXYNOS_CLKREG(0x10930)
 #define EXYNOS5_CLKGATE_IP_GEN                 EXYNOS_CLKREG(0x10934)
 #define EXYNOS5_CLKGATE_IP_FSYS                        EXYNOS_CLKREG(0x10944)
 #define EXYNOS5_CLKGATE_IP_GPS                 EXYNOS_CLKREG(0x1094C)
 #define EXYNOS5_CLKGATE_IP_PERIC               EXYNOS_CLKREG(0x10950)
 #define EXYNOS5_CLKGATE_IP_PERIS               EXYNOS_CLKREG(0x10960)
 #define EXYNOS5_CLKGATE_BLOCK                  EXYNOS_CLKREG(0x10980)
+#define EXYNOS5_CLKOUT_CMU_TOP                 EXYNOS_CLKREG(0x10A00)
 
 #define EXYNOS5_PLL_DIV2_SEL                   EXYNOS_CLKREG(0x20A24)
 
index df406b4..108407d 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/include/mach/regs-pmu.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4 - Power management unit definition
+ * EXYNOS - Power management unit definition
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -16,6 +15,7 @@
 #include <mach/map.h>
 
 #define S5P_PMUREG(x)                          (S5P_VA_PMU + (x))
+#define S5P_SYSREG(x)                          (S3C_VA_SYS + (x))
 
 #define S5P_CENTRAL_SEQ_CONFIGURATION          S5P_PMUREG(0x0200)
 
 #define S5P_SECSS_MEM_OPTION                   S5P_PMUREG(0x2EC8)
 #define S5P_ROTATOR_MEM_OPTION                 S5P_PMUREG(0x2F48)
 
+/* For EXYNOS5 */
+
+#define EXYNOS5_GPS_LPI                                        S5P_PMUREG(0x0004)
+
+#define EXYNOS5_USB_CFG                                        S5P_PMUREG(0x0230)
+
+#define EXYNOS5_SYS_WDTRESET                           (1 << 20)
+#define EXYNOS5_AUTOMATIC_WDT_RESET_DISABLE            S5P_PMUREG(0x0408)
+#define EXYNOS5_MASK_WDT_RESET_REQUEST                 S5P_PMUREG(0x040C)
+
+#define EXYNOS5_ARM_CORE0_SYS_PWR_REG                  S5P_PMUREG(0x1000)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG    S5P_PMUREG(0x1004)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG  S5P_PMUREG(0x1008)
+#define EXYNOS5_ARM_CORE1_SYS_PWR_REG                  S5P_PMUREG(0x1010)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG    S5P_PMUREG(0x1014)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG  S5P_PMUREG(0x1018)
+#define EXYNOS5_FSYS_ARM_SYS_PWR_REG                   S5P_PMUREG(0x1040)
+#define EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG   S5P_PMUREG(0x1048)
+#define EXYNOS5_ISP_ARM_SYS_PWR_REG                    S5P_PMUREG(0x1050)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG      S5P_PMUREG(0x1054)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG    S5P_PMUREG(0x1058)
+#define EXYNOS5_ARM_COMMON_SYS_PWR_REG                 S5P_PMUREG(0x1080)
+#define EXYNOS5_ARM_L2_SYS_PWR_REG                     S5P_PMUREG(0x10C0)
+#define EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG               S5P_PMUREG(0x1100)
+#define EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG               S5P_PMUREG(0x1104)
+#define EXYNOS5_CMU_RESET_SYS_PWR_REG                  S5P_PMUREG(0x110C)
+#define EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG                S5P_PMUREG(0x1120)
+#define EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG                S5P_PMUREG(0x1124)
+#define EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG           S5P_PMUREG(0x112C)
+#define EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG             S5P_PMUREG(0x1130)
+#define EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG              S5P_PMUREG(0x1134)
+#define EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG             S5P_PMUREG(0x1138)
+#define EXYNOS5_APLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x1140)
+#define EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x1144)
+#define EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x1148)
+#define EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x114C)
+#define EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x1150)
+#define EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG                        S5P_PMUREG(0x1154)
+#define EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG            S5P_PMUREG(0x1164)
+#define EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG            S5P_PMUREG(0x1170)
+#define EXYNOS5_TOP_BUS_SYS_PWR_REG                    S5P_PMUREG(0x1180)
+#define EXYNOS5_TOP_RETENTION_SYS_PWR_REG              S5P_PMUREG(0x1184)
+#define EXYNOS5_TOP_PWR_SYS_PWR_REG                    S5P_PMUREG(0x1188)
+#define EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG             S5P_PMUREG(0x1190)
+#define EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG       S5P_PMUREG(0x1194)
+#define EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG             S5P_PMUREG(0x1198)
+#define EXYNOS5_LOGIC_RESET_SYS_PWR_REG                        S5P_PMUREG(0x11A0)
+#define EXYNOS5_OSCCLK_GATE_SYS_PWR_REG                        S5P_PMUREG(0x11A4)
+#define EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG         S5P_PMUREG(0x11B0)
+#define EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG         S5P_PMUREG(0x11B4)
+#define EXYNOS5_USBOTG_MEM_SYS_PWR_REG                 S5P_PMUREG(0x11C0)
+#define EXYNOS5_G2D_MEM_SYS_PWR_REG                    S5P_PMUREG(0x11C8)
+#define EXYNOS5_USBDRD_MEM_SYS_PWR_REG                 S5P_PMUREG(0x11CC)
+#define EXYNOS5_SDMMC_MEM_SYS_PWR_REG                  S5P_PMUREG(0x11D0)
+#define EXYNOS5_CSSYS_MEM_SYS_PWR_REG                  S5P_PMUREG(0x11D4)
+#define EXYNOS5_SECSS_MEM_SYS_PWR_REG                  S5P_PMUREG(0x11D8)
+#define EXYNOS5_ROTATOR_MEM_SYS_PWR_REG                        S5P_PMUREG(0x11DC)
+#define EXYNOS5_INTRAM_MEM_SYS_PWR_REG                 S5P_PMUREG(0x11E0)
+#define EXYNOS5_INTROM_MEM_SYS_PWR_REG                 S5P_PMUREG(0x11E4)
+#define EXYNOS5_JPEG_MEM_SYS_PWR_REG                   S5P_PMUREG(0x11E8)
+#define EXYNOS5_HSI_MEM_SYS_PWR_REG                    S5P_PMUREG(0x11EC)
+#define EXYNOS5_MCUIOP_MEM_SYS_PWR_REG                 S5P_PMUREG(0x11F4)
+#define EXYNOS5_SATA_MEM_SYS_PWR_REG                   S5P_PMUREG(0x11FC)
+#define EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG         S5P_PMUREG(0x1200)
+#define EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG          S5P_PMUREG(0x1204)
+#define EXYNOS5_PAD_RETENTION_EFNAND_SYS_PWR_REG       S5P_PMUREG(0x1208)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG         S5P_PMUREG(0x1220)
+#define EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG         S5P_PMUREG(0x1224)
+#define EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG         S5P_PMUREG(0x1228)
+#define EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG         S5P_PMUREG(0x122C)
+#define EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG         S5P_PMUREG(0x1230)
+#define EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG         S5P_PMUREG(0x1234)
+#define EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG          S5P_PMUREG(0x1238)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG  S5P_PMUREG(0x123C)
+#define EXYNOS5_PAD_ISOLATION_SYS_PWR_REG              S5P_PMUREG(0x1240)
+#define EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG       S5P_PMUREG(0x1250)
+#define EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG                        S5P_PMUREG(0x1260)
+#define EXYNOS5_XUSBXTI_SYS_PWR_REG                    S5P_PMUREG(0x1280)
+#define EXYNOS5_XXTI_SYS_PWR_REG                       S5P_PMUREG(0x1284)
+#define EXYNOS5_EXT_REGULATOR_SYS_PWR_REG              S5P_PMUREG(0x12C0)
+#define EXYNOS5_GPIO_MODE_SYS_PWR_REG                  S5P_PMUREG(0x1300)
+#define EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG           S5P_PMUREG(0x1320)
+#define EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG              S5P_PMUREG(0x1340)
+#define EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG              S5P_PMUREG(0x1344)
+#define EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG          S5P_PMUREG(0x1348)
+#define EXYNOS5_GSCL_SYS_PWR_REG                       S5P_PMUREG(0x1400)
+#define EXYNOS5_ISP_SYS_PWR_REG                                S5P_PMUREG(0x1404)
+#define EXYNOS5_MFC_SYS_PWR_REG                                S5P_PMUREG(0x1408)
+#define EXYNOS5_G3D_SYS_PWR_REG                                S5P_PMUREG(0x140C)
+#define EXYNOS5_DISP1_SYS_PWR_REG                      S5P_PMUREG(0x1414)
+#define EXYNOS5_MAU_SYS_PWR_REG                                S5P_PMUREG(0x1418)
+#define EXYNOS5_GPS_SYS_PWR_REG                                S5P_PMUREG(0x141C)
+#define EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG           S5P_PMUREG(0x1480)
+#define EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG            S5P_PMUREG(0x1484)
+#define EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG            S5P_PMUREG(0x1488)
+#define EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG            S5P_PMUREG(0x148C)
+#define EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG          S5P_PMUREG(0x1494)
+#define EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG            S5P_PMUREG(0x1498)
+#define EXYNOS5_CMU_CLKSTOP_GPS_SYS_PWR_REG            S5P_PMUREG(0x149C)
+#define EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG            S5P_PMUREG(0x14C0)
+#define EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG             S5P_PMUREG(0x14C4)
+#define EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG             S5P_PMUREG(0x14C8)
+#define EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG             S5P_PMUREG(0x14CC)
+#define EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG           S5P_PMUREG(0x14D4)
+#define EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG             S5P_PMUREG(0x14D8)
+#define EXYNOS5_CMU_SYSCLK_GPS_SYS_PWR_REG             S5P_PMUREG(0x14DC)
+#define EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG             S5P_PMUREG(0x1580)
+#define EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG              S5P_PMUREG(0x1584)
+#define EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG              S5P_PMUREG(0x1588)
+#define EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG              S5P_PMUREG(0x158C)
+#define EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG            S5P_PMUREG(0x1594)
+#define EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG              S5P_PMUREG(0x1598)
+#define EXYNOS5_CMU_RESET_GPS_SYS_PWR_REG              S5P_PMUREG(0x159C)
+
+#define EXYNOS5_ARM_CORE0_OPTION                       S5P_PMUREG(0x2008)
+#define EXYNOS5_ARM_CORE1_OPTION                       S5P_PMUREG(0x2088)
+#define EXYNOS5_FSYS_ARM_OPTION                                S5P_PMUREG(0x2208)
+#define EXYNOS5_ISP_ARM_OPTION                         S5P_PMUREG(0x2288)
+#define EXYNOS5_ARM_COMMON_OPTION                      S5P_PMUREG(0x2408)
+#define EXYNOS5_TOP_PWR_OPTION                         S5P_PMUREG(0x2C48)
+#define EXYNOS5_TOP_PWR_SYSMEM_OPTION                  S5P_PMUREG(0x2CC8)
+#define EXYNOS5_JPEG_MEM_OPTION                                S5P_PMUREG(0x2F48)
+#define EXYNOS5_GSCL_STATUS                            S5P_PMUREG(0x4004)
+#define EXYNOS5_ISP_STATUS                             S5P_PMUREG(0x4024)
+#define EXYNOS5_GSCL_OPTION                            S5P_PMUREG(0x4008)
+#define EXYNOS5_ISP_OPTION                             S5P_PMUREG(0x4028)
+#define EXYNOS5_MFC_OPTION                             S5P_PMUREG(0x4048)
+#define EXYNOS5_G3D_CONFIGURATION                      S5P_PMUREG(0x4060)
+#define EXYNOS5_G3D_STATUS                             S5P_PMUREG(0x4064)
+#define EXYNOS5_G3D_OPTION                             S5P_PMUREG(0x4068)
+#define EXYNOS5_DISP1_OPTION                           S5P_PMUREG(0x40A8)
+#define EXYNOS5_MAU_OPTION                             S5P_PMUREG(0x40C8)
+#define EXYNOS5_GPS_OPTION                             S5P_PMUREG(0x40E8)
+
+#define EXYNOS5_USE_SC_FEEDBACK                                        (1 << 1)
+#define EXYNOS5_USE_SC_COUNTER                                 (1 << 0)
+
+#define EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL                    (1 << 2)
+#define EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN                 (1 << 7)
+
+#define EXYNOS5_OPTION_USE_STANDBYWFE                          (1 << 24)
+#define EXYNOS5_OPTION_USE_STANDBYWFI                          (1 << 16)
+
+#define EXYNOS5_OPTION_USE_RETENTION                           (1 << 4)
+
+#define EXYNOS5_SYS_I2C_CFG                                    S5P_SYSREG(0x234)
 #define EXYNOS5_SYS_DISP1BLK_CFG               S5P_SYSREG(0x214)
 #define ENABLE_FIMDBYPASS_DISP1                        (1 << 15)
 
index 7164aa9..d41378b 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/pm.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4210 - Power Management support
+ * EXYNOS - Power Management support
  *
  * Based on arch/arm/mach-s3c2410/pm.c
  * Copyright (c) 2006 Simtec Electronics
@@ -63,90 +62,11 @@ static struct sleep_save exynos4_vpll_save[] = {
        SAVE_ITEM(EXYNOS4_VPLL_CON1),
 };
 
-static struct sleep_save exynos4_core_save[] = {
-       /* GIC side */
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x008),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x00C),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x014),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x018),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x100),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x104),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x108),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x300),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x304),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x308),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x400),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x404),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x408),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x40C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x410),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x414),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x418),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x41C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x420),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x424),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x428),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x42C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x430),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x434),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x438),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x43C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x440),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x444),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x448),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x44C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x450),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x454),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x458),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x45C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x800),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x804),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x808),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x80C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x810),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x814),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x818),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x81C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x820),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x824),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x828),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x82C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x830),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x834),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x838),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x83C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x840),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x844),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x848),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x84C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x850),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x854),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x858),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x85C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC00),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC04),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC08),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC0C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC10),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC14),
-
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x000),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x010),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x020),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x030),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x040),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x050),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x060),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090),
+static struct sleep_save exynos5_sys_save[] = {
+       SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
+};
 
+static struct sleep_save exynos_core_save[] = {
        /* SROM side */
        SAVE_ITEM(S5P_SROM_BW),
        SAVE_ITEM(S5P_SROM_BC0),
@@ -159,9 +79,16 @@ static struct sleep_save exynos4_core_save[] = {
 /* For Cortex-A9 Diagnostic and Power control register */
 static unsigned int save_arm_register[2];
 
-static int exynos4_cpu_suspend(unsigned long arg)
+static int exynos_cpu_suspend(unsigned long arg)
 {
+#ifdef CONFIG_CACHE_L2X0
        outer_flush_all();
+#endif
+       /*
+        * To enter suspend mode, GPS LPI register must be set.
+        */
+       if (soc_is_exynos5250())
+               __raw_writel(0x10000, EXYNOS5_GPS_LPI);
 
        /* issue the standby signal into the pm unit. */
        cpu_do_idle();
@@ -170,38 +97,48 @@ static int exynos4_cpu_suspend(unsigned long arg)
        panic("sleep resumed to originator?");
 }
 
-static void exynos4_pm_prepare(void)
+static void exynos_pm_prepare(void)
 {
-       u32 tmp;
-
-       s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
-       s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
-       s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
+       unsigned int tmp;
 
-       tmp = __raw_readl(S5P_INFORM1);
+       s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
+
+       if (!soc_is_exynos5250()) {
+               s3c_pm_do_save(exynos4_epll_save,
+                               ARRAY_SIZE(exynos4_epll_save));
+               s3c_pm_do_save(exynos4_vpll_save,
+                               ARRAY_SIZE(exynos4_vpll_save));
+       } else {
+               s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
+               /* Disable USE_RETENTION of JPEG_MEM_OPTION */
+               tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
+               tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
+               __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
+       }
 
        /* Set value of power down register for sleep mode */
-
        exynos4_sys_powerdown_conf(SYS_SLEEP);
        __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
 
        /* ensure at least INFORM0 has the resume address */
-
        __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0);
 
-       /* Before enter central sequence mode, clock src register have to set */
-
-       s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
-
+       /*
+        * Before enter central sequence mode,
+        * clock src register have to set.
+        */
+       if (!soc_is_exynos5250())
+               s3c_pm_do_restore_core(exynos4_set_clksrc,
+                               ARRAY_SIZE(exynos4_set_clksrc));
        if (soc_is_exynos4210())
                s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc));
 
 }
 
-static int exynos4_pm_add(struct device *dev, struct subsys_interface *sif)
+static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
 {
-       pm_cpu_prep = exynos4_pm_prepare;
-       pm_cpu_sleep = exynos4_cpu_suspend;
+       pm_cpu_prep = exynos_pm_prepare;
+       pm_cpu_sleep = exynos_cpu_suspend;
 
        return 0;
 }
@@ -273,13 +210,13 @@ static void exynos4_restore_pll(void)
        } while (epll_wait || vpll_wait);
 }
 
-static struct subsys_interface exynos4_pm_interface = {
-       .name           = "exynos4_pm",
+static struct subsys_interface exynos_pm_interface = {
+       .name           = "exynos_pm",
        .subsys         = &exynos_subsys,
-       .add_dev        = exynos4_pm_add,
+       .add_dev        = exynos_pm_add,
 };
 
-static __init int exynos4_pm_drvinit(void)
+static __init int exynos_pm_drvinit(void)
 {
        struct clk *pll_base;
        unsigned int tmp;
@@ -292,48 +229,47 @@ static __init int exynos4_pm_drvinit(void)
        tmp |= ((0xFF << 8) | (0x1F << 1));
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
-       pll_base = clk_get(NULL, "xtal");
+       if (!soc_is_exynos5250()) {
+               pll_base = clk_get(NULL, "xtal");
 
-       if (!IS_ERR(pll_base)) {
-               pll_base_rate = clk_get_rate(pll_base);
-               clk_put(pll_base);
+               if (!IS_ERR(pll_base)) {
+                       pll_base_rate = clk_get_rate(pll_base);
+                       clk_put(pll_base);
+               }
        }
 
-       return subsys_interface_register(&exynos4_pm_interface);
+       return subsys_interface_register(&exynos_pm_interface);
 }
-arch_initcall(exynos4_pm_drvinit);
+arch_initcall(exynos_pm_drvinit);
 
-static int exynos4_pm_suspend(void)
+static int exynos_pm_suspend(void)
 {
        unsigned long tmp;
 
        /* Setting Central Sequence Register for power down mode */
-
        tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
        tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
        __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
 
-       if (soc_is_exynos4212()) {
-               tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION);
-               tmp &= ~(S5P_USE_STANDBYWFI_ISP_ARM |
-                        S5P_USE_STANDBYWFE_ISP_ARM);
-               __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
-       }
-
-       /* Save Power control register */
-       asm ("mrc p15, 0, %0, c15, c0, 0"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[0] = tmp;
+       /* Setting SEQ_OPTION register */
+       tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
+       __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
 
-       /* Save Diagnostic register */
-       asm ("mrc p15, 0, %0, c15, c0, 1"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[1] = tmp;
+       if (!soc_is_exynos5250()) {
+               /* Save Power control register */
+               asm ("mrc p15, 0, %0, c15, c0, 0"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[0] = tmp;
 
+               /* Save Diagnostic register */
+               asm ("mrc p15, 0, %0, c15, c0, 1"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[1] = tmp;
+       }
        return 0;
 }
 
-static void exynos4_pm_resume(void)
+static void exynos_pm_resume(void)
 {
        unsigned long tmp;
 
@@ -350,18 +286,20 @@ static void exynos4_pm_resume(void)
                /* No need to perform below restore code */
                goto early_wakeup;
        }
-       /* Restore Power control register */
-       tmp = save_arm_register[0];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 0"
-                     : : "r" (tmp)
-                     : "cc");
-
-       /* Restore Diagnostic register */
-       tmp = save_arm_register[1];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 1"
-                     : : "r" (tmp)
-                     : "cc");
 
+       if (!soc_is_exynos5250()) {
+               /* Restore Power control register */
+               tmp = save_arm_register[0];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 0"
+                             : : "r" (tmp)
+                             : "cc");
+
+               /* Restore Diagnostic register */
+               tmp = save_arm_register[1];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 1"
+                             : : "r" (tmp)
+                             : "cc");
+       }
        /* For release retention */
 
        __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
@@ -372,26 +310,34 @@ static void exynos4_pm_resume(void)
        __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
        __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);
 
-       s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
+       if (soc_is_exynos5250())
+               s3c_pm_do_restore(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
+
+       s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       exynos4_restore_pll();
+       if (!soc_is_exynos5250()) {
+               exynos4_restore_pll();
 
 #ifdef CONFIG_SMP
        scu_enable(S5P_VA_SCU);
 #endif
-
+       }
 early_wakeup:
+
+       /* Clear SLEEP mode set in INFORM1 */
+       __raw_writel(0x0, S5P_INFORM1);
+
        return;
 }
 
-static struct syscore_ops exynos4_pm_syscore_ops = {
-       .suspend        = exynos4_pm_suspend,
-       .resume         = exynos4_pm_resume,
+static struct syscore_ops exynos_pm_syscore_ops = {
+       .suspend        = exynos_pm_suspend,
+       .resume         = exynos_pm_resume,
 };
 
-static __init int exynos4_pm_syscore_init(void)
+static __init int exynos_pm_syscore_init(void)
 {
-       register_syscore_ops(&exynos4_pm_syscore_ops);
+       register_syscore_ops(&exynos_pm_syscore_ops);
        return 0;
 }
-arch_initcall(exynos4_pm_syscore_init);
+arch_initcall(exynos_pm_syscore_init);
index bba48f5..589f781 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/bug.h>
 
 #include <mach/regs-clock.h>
 #include <mach/pmu.h>
@@ -202,10 +203,175 @@ static struct exynos4_pmu_conf exynos4212_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
+static struct exynos4_pmu_conf exynos5250_pmu_config[] = {
+       /* { .reg = address, .val = { AFTR, LPA, SLEEP } */
+       { EXYNOS5_ARM_CORE0_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_CORE1_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_FSYS_ARM_SYS_PWR_REG,                 { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG, { 0x1, 0x1, 0x1} },
+       { EXYNOS5_ISP_ARM_SYS_PWR_REG,                  { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_COMMON_SYS_PWR_REG,               { 0x0, 0x0, 0x2} },
+       { EXYNOS5_ARM_L2_SYS_PWR_REG,                   { 0x3, 0x3, 0x3} },
+       { EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYS_PWR_REG,                { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG,         { 0x1, 0x1, 0x0} },
+       { EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_APLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_TOP_BUS_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYS_PWR_REG,            { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYS_PWR_REG,                  { 0x3, 0x0, 0x3} },
+       { EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x3} },
+       { EXYNOS5_LOGIC_RESET_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYS_PWR_REG,              { 0x1, 0x0, 0x1} },
+       { EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG,       { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG,       { 0x1, 0x0, 0x1} },
+       { EXYNOS5_USBOTG_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_G2D_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_USBDRD_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SDMMC_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_CSSYS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SECSS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_ROTATOR_MEM_SYS_PWR_REG,              { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTRAM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTROM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_JPEG_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_HSI_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_MCUIOP_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SATA_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG,        { 0x1, 0x1, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_XUSBXTI_SYS_PWR_REG,                  { 0x1, 0x1, 0x1} },
+       { EXYNOS5_XXTI_SYS_PWR_REG,                     { 0x1, 0x1, 0x0} },
+       { EXYNOS5_EXT_REGULATOR_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYS_PWR_REG,                { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG,        { 0x1, 0x0, 0x1} },
+       { EXYNOS5_GSCL_SYS_PWR_REG,                     { 0x7, 0x0, 0x0} },
+       { EXYNOS5_ISP_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MFC_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_G3D_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_DISP1_SYS_PWR_REG,                    { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MAU_SYS_PWR_REG,                      { 0x7, 0x7, 0x0} },
+       { EXYNOS5_GPS_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG,          { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_GPS_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG,           { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_GPS_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_RESET_GPS_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { PMU_TABLE_END,},
+};
+
+void __iomem *exynos5_list_both_cnt_feed[] = {
+       EXYNOS5_ARM_CORE0_OPTION,
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_ARM_COMMON_OPTION,
+       EXYNOS5_GSCL_OPTION,
+       EXYNOS5_ISP_OPTION,
+       EXYNOS5_MFC_OPTION,
+       EXYNOS5_G3D_OPTION,
+       EXYNOS5_DISP1_OPTION,
+       EXYNOS5_MAU_OPTION,
+       EXYNOS5_TOP_PWR_OPTION,
+       EXYNOS5_TOP_PWR_SYSMEM_OPTION,
+};
+
+void __iomem *exynos5_list_diable_wfi_wfe[] = {
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_FSYS_ARM_OPTION,
+       EXYNOS5_ISP_ARM_OPTION,
+};
+
+static void exynos5_init_pmu(void)
+{
+       unsigned int i;
+       unsigned int tmp;
+
+       /*
+        * Enable both SC_FEEDBACK and SC_COUNTER
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) {
+               tmp = __raw_readl(exynos5_list_both_cnt_feed[i]);
+               tmp |= (EXYNOS5_USE_SC_FEEDBACK |
+                       EXYNOS5_USE_SC_COUNTER);
+               __raw_writel(tmp, exynos5_list_both_cnt_feed[i]);
+       }
+
+       /*
+        * SKIP_DEACTIVATE_ACEACP_IN_PWDN_BITFIELD Enable
+        * MANUAL_L2RSTDISABLE_CONTROL_BITFIELD Enable
+        */
+       tmp = __raw_readl(EXYNOS5_ARM_COMMON_OPTION);
+       tmp |= (EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL |
+               EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN);
+       __raw_writel(tmp, EXYNOS5_ARM_COMMON_OPTION);
+
+       /*
+        * Disable WFI/WFE on XXX_OPTION
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) {
+               tmp = __raw_readl(exynos5_list_diable_wfi_wfe[i]);
+               tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
+                        EXYNOS5_OPTION_USE_STANDBYWFI);
+               __raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
+       }
+}
+
 void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
 {
        unsigned int i;
 
+       if (soc_is_exynos5250())
+               exynos5_init_pmu();
+
        for (i = 0; (exynos4_pmu_config[i].reg != PMU_TABLE_END) ; i++)
                __raw_writel(exynos4_pmu_config[i].val[mode],
                                exynos4_pmu_config[i].reg);
@@ -221,6 +387,9 @@ static int __init exynos4_pmu_init(void)
        } else if (soc_is_exynos4212()) {
                exynos4_pmu_config = exynos4212_pmu_config;
                pr_info("EXYNOS4212 PMU Initialize\n");
+       } else if (soc_is_exynos5250()) {
+               exynos4_pmu_config = exynos5250_pmu_config;
+               pr_info("EXYNOS5250 PMU Initialize\n");
        } else {
                pr_info("EXYNOS4: PMU not supported\n");
        }
index 61fc537..887a0c9 100644 (file)
@@ -107,10 +107,12 @@ extern void s3c_pm_do_restore(struct sleep_save *ptr, int count);
 extern void s3c_pm_do_restore_core(struct sleep_save *ptr, int count);
 
 #ifdef CONFIG_PM
+extern int s3c_irq_wake(struct irq_data *data, unsigned int state);
 extern int s3c_irqext_wake(struct irq_data *data, unsigned int state);
 extern int s3c24xx_irq_suspend(void);
 extern void s3c24xx_irq_resume(void);
 #else
+#define s3c_irq_wake NULL
 #define s3c_irqext_wake NULL
 #define s3c24xx_irq_suspend NULL
 #define s3c24xx_irq_resume  NULL
index c559d84..dddb2b1 100644 (file)
@@ -156,23 +156,24 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
        return tin_parent_rate / 16;
 }
 
-#define NS_IN_HZ (1000000000UL)
+#define NS_IN_SEC (1000000000UL)
 
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
        unsigned long tin_rate;
        unsigned long tin_ns;
-       unsigned long period;
+       unsigned long frequency;
        unsigned long flags;
        unsigned long tcon;
        unsigned long tcnt;
        long tcmp;
+       int pwm_was_enabled;
 
        /* We currently avoid using 64bit arithmetic by using the
         * fact that anything faster than 1Hz is easily representable
         * by 32bits. */
 
-       if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
+       if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC)
                return -ERANGE;
 
        if (duty_ns > period_ns)
@@ -188,16 +189,16 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
        tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));
        tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
 
-       period = NS_IN_HZ / period_ns;
+       frequency = NS_IN_SEC / period_ns;
 
        pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
-               duty_ns, period_ns, period);
+               duty_ns, period_ns, frequency);
 
        /* Check to see if we are changing the clock rate of the PWM */
 
        if (pwm->period_ns != period_ns) {
                if (pwm_is_tdiv(pwm)) {
-                       tin_rate = pwm_calc_tin(pwm, period);
+                       tin_rate = pwm_calc_tin(pwm, frequency);
                        clk_set_rate(pwm->clk_div, tin_rate);
                } else
                        tin_rate = clk_get_rate(pwm->clk);
@@ -206,10 +207,10 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 
                pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
 
-               tin_ns = NS_IN_HZ / tin_rate;
+               tin_ns = NS_IN_SEC / tin_rate;
                tcnt = period_ns / tin_ns;
        } else
-               tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
+               tin_ns = NS_IN_SEC / clk_get_rate(pwm->clk);
 
        /* Note, counters count down */
 
@@ -233,6 +234,13 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
        __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
 
        tcon = __raw_readl(S3C2410_TCON);
+       pwm_was_enabled = (tcon & pwm_tcon_start(pwm)) != 0;
+
+       /* Ensure manual update is off before turning it on. */
+       tcon &= ~pwm_tcon_manulupdate(pwm);
+       tcon &= ~pwm_tcon_start(pwm);
+       __raw_writel(tcon, S3C2410_TCON);
+
        tcon |= pwm_tcon_manulupdate(pwm);
        tcon |= pwm_tcon_autoreload(pwm);
        __raw_writel(tcon, S3C2410_TCON);
@@ -240,6 +248,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
        tcon &= ~pwm_tcon_manulupdate(pwm);
        __raw_writel(tcon, S3C2410_TCON);
 
+       if (pwm_was_enabled) {
+               tcon |= pwm_tcon_start(pwm);
+               __raw_writel(tcon, S3C2410_TCON);
+       }
+
        local_irq_restore(flags);
 
        return 0;
index b243a7e..d5b86c3 100644 (file)
@@ -62,8 +62,17 @@ static int exynos_target(struct cpufreq_policy *policy,
                goto out;
        }
 
-       if (cpufreq_frequency_table_target(policy, freq_table,
-                                          freqs.old, relation, &old_index)) {
+       /*
+        * The policy may have been changed so that we cannot get proper
+        * old_index with cpufreq_frequency_table_target(). Thus, ignore
+        * policy and get the index from the raw frequency table.
+        */
+       for (old_index = 0;
+            freq_table[old_index].frequency != CPUFREQ_TABLE_END;
+            old_index++)
+               if (freq_table[old_index].frequency == freqs.old)
+                       break;
+       if (freq_table[old_index].frequency == CPUFREQ_TABLE_END) {
                ret = -EINVAL;
                goto out;
        }
@@ -206,6 +215,8 @@ static struct notifier_block exynos_cpufreq_nb = {
 
 static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       int ret;
+
        policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
 
        cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
@@ -228,16 +239,35 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
                cpumask_setall(policy->cpus);
        }
 
-       return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
+       ret = cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
+       if (ret)
+               return ret;
+
+       cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
+       return 0;
+
 }
 
+static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+       cpufreq_frequency_table_put_attr(policy->cpu);
+       return 0;
+}
+
+static struct freq_attr *exynos_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
 static struct cpufreq_driver exynos_driver = {
        .flags          = CPUFREQ_STICKY,
        .verify         = exynos_verify_speed,
        .target         = exynos_target,
        .get            = exynos_getspeed,
        .init           = exynos_cpufreq_cpu_init,
+       .exit           = exynos_cpufreq_cpu_exit,
        .name           = "exynos_cpufreq",
+       .attr           = exynos_cpufreq_attr,
 #ifdef CONFIG_PM
        .suspend        = exynos_cpufreq_suspend,
        .resume         = exynos_cpufreq_resume,
index a883316..a265c80 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
+ * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
  * EXYNOS5250 - CPU frequency scaling support
@@ -65,20 +65,20 @@ static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
         * Clock divider value for following
         * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
         */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1700 MHz - N/A */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1600 MHz - N/A */
-       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1500 MHz - N/A */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1400 MHz */
-       { 0, 3, 7, 7, 6, 1, 3, 0 },     /* 1300 MHz */
-       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1200 MHz */
-       { 0, 2, 7, 7, 5, 1, 2, 0 },     /* 1100 MHz */
-       { 0, 2, 7, 7, 4, 1, 2, 0 },     /* 1000 MHz */
-       { 0, 2, 7, 7, 4, 1, 2, 0 },     /* 900 MHz */
-       { 0, 2, 7, 7, 3, 1, 1, 0 },     /* 800 MHz */
+       { 0, 3, 7, 7, 7, 2, 5, 0 },     /* 1700 MHz */
+       { 0, 3, 7, 7, 7, 1, 4, 0 },     /* 1600 MHz */
+       { 0, 2, 7, 7, 7, 1, 4, 0 },     /* 1500 MHz */
+       { 0, 2, 7, 7, 6, 1, 4, 0 },     /* 1400 MHz */
+       { 0, 2, 7, 7, 6, 1, 3, 0 },     /* 1300 MHz */
+       { 0, 2, 7, 7, 5, 1, 3, 0 },     /* 1200 MHz */
+       { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1100 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 1000 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 900 MHz */
+       { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 800 MHz */
        { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 700 MHz */
-       { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 600 MHz */
+       { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 600 MHz */
        { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 500 MHz */
-       { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 400 MHz */
+       { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 400 MHz */
        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 300 MHz */
        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 200 MHz */
 };
@@ -87,9 +87,9 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
        /* Clock divider value for following
         * { COPY, HPM }
         */
-       { 0, 2 },       /* 1700 MHz - N/A */
-       { 0, 2 },       /* 1600 MHz - N/A */
-       { 0, 2 },       /* 1500 MHz - N/A */
+       { 0, 2 },       /* 1700 MHz */
+       { 0, 2 },       /* 1600 MHz */
+       { 0, 2 },       /* 1500 MHz */
        { 0, 2 },       /* 1400 MHz */
        { 0, 2 },       /* 1300 MHz */
        { 0, 2 },       /* 1200 MHz */
@@ -106,10 +106,10 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
 };
 
 static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
-       (0),                            /* 1700 MHz - N/A */
-       (0),                            /* 1600 MHz - N/A */
-       (0),                            /* 1500 MHz - N/A */
-       (0),                            /* 1400 MHz */
+       ((425 << 16) | (6 << 8) | 0),   /* 1700 MHz */
+       ((200 << 16) | (3 << 8) | 0),   /* 1600 MHz */
+       ((250 << 16) | (4 << 8) | 0),   /* 1500 MHz */
+       ((175 << 16) | (3 << 8) | 0),   /* 1400 MHz */
        ((325 << 16) | (6 << 8) | 0),   /* 1300 MHz */
        ((200 << 16) | (4 << 8) | 0),   /* 1200 MHz */
        ((275 << 16) | (6 << 8) | 0),   /* 1100 MHz */
@@ -126,9 +126,22 @@ static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
 
 /* ASV group voltage table */
 static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
-       0, 0, 0, 0, 0, 0, 0,    /* 1700 MHz ~ 1100 MHz Not supported */
-       1175000, 1125000, 1075000, 1050000, 1000000,
-       950000, 925000, 925000, 900000
+       1300000,    /* L0 */
+       1250000,    /* L1 */
+       1225000,    /* L2 */
+       1200000,    /* L3 */
+       1150000,    /* L4 */
+       1125000,    /* L5 */
+       1100000,    /* L6 */
+       1075000,    /* L7 */
+       1050000,    /* L8 */
+       1025000,    /* L9 */
+       1012500,    /* L10 */
+       1000000,    /* L11 */
+       975000,     /* L12 */
+       950000,     /* L13 */
+       937500,     /* L14 */
+       925000,     /* L15 */
 };
 
 static void set_clkdiv(unsigned int div_index)
@@ -247,16 +260,7 @@ static void exynos5250_set_frequency(unsigned int old_index,
 static void __init set_volt_table(void)
 {
        unsigned int i;
-
-       exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
-       exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
-
-       max_support_idx = L7;
+       max_support_idx = L0;
 
        for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
                exynos5250_volt_table[i] = asv_voltage_5250[i];
index 8deedc1..e63d02d 100644 (file)
@@ -314,16 +314,6 @@ config SENSORS_DS1621
          This driver can also be built as a module.  If so, the module
          will be called ds1621.
 
-config SENSORS_EXYNOS4_TMU
-       tristate "Temperature sensor on Samsung EXYNOS4"
-       depends on ARCH_EXYNOS4
-       help
-         If you say yes here you get support for TMU (Thermal Managment
-         Unit) on SAMSUNG EXYNOS4 series of SoC.
-
-         This driver can also be built as a module. If so, the module
-         will be called exynos4-tmu.
-
 config SENSORS_I5K_AMB
        tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
        depends on PCI && EXPERIMENTAL
index 6d3f11f..b3e6d6e 100644 (file)
@@ -48,7 +48,6 @@ obj-$(CONFIG_SENSORS_DS1621)  += ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)  += emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)  += emc2103.o
 obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
-obj-$(CONFIG_SENSORS_EXYNOS4_TMU)      += exynos4_tmu.o
 obj-$(CONFIG_SENSORS_F71805F)  += f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)  += f75375s.o
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
deleted file mode 100644 (file)
index f2359a0..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
-#include <linux/kobject.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <linux/platform_data/exynos4_tmu.h>
-
-#define EXYNOS4_TMU_REG_TRIMINFO       0x0
-#define EXYNOS4_TMU_REG_CONTROL                0x20
-#define EXYNOS4_TMU_REG_STATUS         0x28
-#define EXYNOS4_TMU_REG_CURRENT_TEMP   0x40
-#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
-#define EXYNOS4_TMU_REG_TRIG_LEVEL0    0x50
-#define EXYNOS4_TMU_REG_TRIG_LEVEL1    0x54
-#define EXYNOS4_TMU_REG_TRIG_LEVEL2    0x58
-#define EXYNOS4_TMU_REG_TRIG_LEVEL3    0x5C
-#define EXYNOS4_TMU_REG_PAST_TEMP0     0x60
-#define EXYNOS4_TMU_REG_PAST_TEMP1     0x64
-#define EXYNOS4_TMU_REG_PAST_TEMP2     0x68
-#define EXYNOS4_TMU_REG_PAST_TEMP3     0x6C
-#define EXYNOS4_TMU_REG_INTEN          0x70
-#define EXYNOS4_TMU_REG_INTSTAT                0x74
-#define EXYNOS4_TMU_REG_INTCLEAR       0x78
-
-#define EXYNOS4_TMU_GAIN_SHIFT         8
-#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT  24
-
-#define EXYNOS4_TMU_TRIM_TEMP_MASK     0xff
-#define EXYNOS4_TMU_CORE_ON    3
-#define EXYNOS4_TMU_CORE_OFF   2
-#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET    50
-#define EXYNOS4_TMU_TRIG_LEVEL0_MASK   0x1
-#define EXYNOS4_TMU_TRIG_LEVEL1_MASK   0x10
-#define EXYNOS4_TMU_TRIG_LEVEL2_MASK   0x100
-#define EXYNOS4_TMU_TRIG_LEVEL3_MASK   0x1000
-#define EXYNOS4_TMU_INTCLEAR_VAL       0x1111
-
-struct exynos4_tmu_data {
-       struct exynos4_tmu_platform_data *pdata;
-       struct device *hwmon_dev;
-       struct resource *mem;
-       void __iomem *base;
-       int irq;
-       struct work_struct irq_work;
-       struct mutex lock;
-       struct clk *clk;
-       u8 temp_error1, temp_error2;
-};
-
-/*
- * TMU treats temperature as a mapped temperature code.
- * The temperature is converted differently depending on the calibration type.
- */
-static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
-{
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       int temp_code;
-
-       /* temp should range between 25 and 125 */
-       if (temp < 25 || temp > 125) {
-               temp_code = -EINVAL;
-               goto out;
-       }
-
-       switch (pdata->cal_type) {
-       case TYPE_TWO_POINT_TRIMMING:
-               temp_code = (temp - 25) *
-                   (data->temp_error2 - data->temp_error1) /
-                   (85 - 25) + data->temp_error1;
-               break;
-       case TYPE_ONE_POINT_TRIMMING:
-               temp_code = temp + data->temp_error1 - 25;
-               break;
-       default:
-               temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
-               break;
-       }
-out:
-       return temp_code;
-}
-
-/*
- * Calculate a temperature value from a temperature code.
- * The unit of the temperature is degree Celsius.
- */
-static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
-{
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       int temp;
-
-       /* temp_code should range between 75 and 175 */
-       if (temp_code < 75 || temp_code > 175) {
-               temp = -ENODATA;
-               goto out;
-       }
-
-       switch (pdata->cal_type) {
-       case TYPE_TWO_POINT_TRIMMING:
-               temp = (temp_code - data->temp_error1) * (85 - 25) /
-                   (data->temp_error2 - data->temp_error1) + 25;
-               break;
-       case TYPE_ONE_POINT_TRIMMING:
-               temp = temp_code - data->temp_error1 + 25;
-               break;
-       default:
-               temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
-               break;
-       }
-out:
-       return temp;
-}
-
-static int exynos4_tmu_initialize(struct platform_device *pdev)
-{
-       struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       unsigned int status, trim_info;
-       int ret = 0, threshold_code;
-
-       mutex_lock(&data->lock);
-       clk_enable(data->clk);
-
-       status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
-       if (!status) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       /* Save trimming info in order to perform calibration */
-       trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
-       data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
-       data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
-
-       /* Write temperature code for threshold */
-       threshold_code = temp_to_code(data, pdata->threshold);
-       if (threshold_code < 0) {
-               ret = threshold_code;
-               goto out;
-       }
-       writeb(threshold_code,
-               data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
-
-       writeb(pdata->trigger_levels[0],
-               data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
-       writeb(pdata->trigger_levels[1],
-               data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
-       writeb(pdata->trigger_levels[2],
-               data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
-       writeb(pdata->trigger_levels[3],
-               data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
-
-       writel(EXYNOS4_TMU_INTCLEAR_VAL,
-               data->base + EXYNOS4_TMU_REG_INTCLEAR);
-out:
-       clk_disable(data->clk);
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static void exynos4_tmu_control(struct platform_device *pdev, bool on)
-{
-       struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       unsigned int con, interrupt_en;
-
-       mutex_lock(&data->lock);
-       clk_enable(data->clk);
-
-       con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
-               pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
-       if (on) {
-               con |= EXYNOS4_TMU_CORE_ON;
-               interrupt_en = pdata->trigger_level3_en << 12 |
-                       pdata->trigger_level2_en << 8 |
-                       pdata->trigger_level1_en << 4 |
-                       pdata->trigger_level0_en;
-       } else {
-               con |= EXYNOS4_TMU_CORE_OFF;
-               interrupt_en = 0; /* Disable all interrupts */
-       }
-       writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
-       writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
-
-       clk_disable(data->clk);
-       mutex_unlock(&data->lock);
-}
-
-static int exynos4_tmu_read(struct exynos4_tmu_data *data)
-{
-       u8 temp_code;
-       int temp;
-
-       mutex_lock(&data->lock);
-       clk_enable(data->clk);
-
-       temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
-       temp = code_to_temp(data, temp_code);
-
-       clk_disable(data->clk);
-       mutex_unlock(&data->lock);
-
-       return temp;
-}
-
-static void exynos4_tmu_work(struct work_struct *work)
-{
-       struct exynos4_tmu_data *data = container_of(work,
-                       struct exynos4_tmu_data, irq_work);
-
-       mutex_lock(&data->lock);
-       clk_enable(data->clk);
-
-       writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
-
-       kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-       enable_irq(data->irq);
-
-       clk_disable(data->clk);
-       mutex_unlock(&data->lock);
-}
-
-static irqreturn_t exynos4_tmu_irq(int irq, void *id)
-{
-       struct exynos4_tmu_data *data = id;
-
-       disable_irq_nosync(irq);
-       schedule_work(&data->irq_work);
-
-       return IRQ_HANDLED;
-}
-
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       ret = exynos4_tmu_read(data);
-       if (ret < 0)
-               return ret;
-
-       /* convert from degree Celsius to millidegree Celsius */
-       return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-               struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       int temp;
-       unsigned int trigger_level;
-
-       temp = exynos4_tmu_read(data);
-       if (temp < 0)
-               return temp;
-
-       trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-       return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-               struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-       struct exynos4_tmu_platform_data *pdata = data->pdata;
-       unsigned int temp = pdata->threshold +
-                       pdata->trigger_levels[attr->index];
-
-       return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-               exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-               exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-               exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-               exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-       &dev_attr_name.attr,
-       &sensor_dev_attr_temp1_input.dev_attr.attr,
-       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_max.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit.dev_attr.attr,
-       &sensor_dev_attr_temp1_emergency.dev_attr.attr,
-       NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-       .attrs = exynos4_tmu_attributes,
-};
-
-static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
-{
-       struct exynos4_tmu_data *data;
-       struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-       int ret;
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "No platform init data supplied.\n");
-               return -ENODEV;
-       }
-
-       data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
-       if (!data) {
-               dev_err(&pdev->dev, "Failed to allocate driver structure\n");
-               return -ENOMEM;
-       }
-
-       data->irq = platform_get_irq(pdev, 0);
-       if (data->irq < 0) {
-               ret = data->irq;
-               dev_err(&pdev->dev, "Failed to get platform irq\n");
-               goto err_free;
-       }
-
-       INIT_WORK(&data->irq_work, exynos4_tmu_work);
-
-       data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!data->mem) {
-               ret = -ENOENT;
-               dev_err(&pdev->dev, "Failed to get platform resource\n");
-               goto err_free;
-       }
-
-       data->mem = request_mem_region(data->mem->start,
-                       resource_size(data->mem), pdev->name);
-       if (!data->mem) {
-               ret = -ENODEV;
-               dev_err(&pdev->dev, "Failed to request memory region\n");
-               goto err_free;
-       }
-
-       data->base = ioremap(data->mem->start, resource_size(data->mem));
-       if (!data->base) {
-               ret = -ENODEV;
-               dev_err(&pdev->dev, "Failed to ioremap memory\n");
-               goto err_mem_region;
-       }
-
-       ret = request_irq(data->irq, exynos4_tmu_irq,
-               IRQF_TRIGGER_RISING,
-               "exynos4-tmu", data);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
-               goto err_io_remap;
-       }
-
-       data->clk = clk_get(NULL, "tmu_apbif");
-       if (IS_ERR(data->clk)) {
-               ret = PTR_ERR(data->clk);
-               dev_err(&pdev->dev, "Failed to get clock\n");
-               goto err_irq;
-       }
-
-       data->pdata = pdata;
-       platform_set_drvdata(pdev, data);
-       mutex_init(&data->lock);
-
-       ret = exynos4_tmu_initialize(pdev);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize TMU\n");
-               goto err_clk;
-       }
-
-       ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to create sysfs group\n");
-               goto err_clk;
-       }
-
-       data->hwmon_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               ret = PTR_ERR(data->hwmon_dev);
-               dev_err(&pdev->dev, "Failed to register hwmon device\n");
-               goto err_create_group;
-       }
-
-       exynos4_tmu_control(pdev, true);
-
-       return 0;
-
-err_create_group:
-       sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-err_clk:
-       platform_set_drvdata(pdev, NULL);
-       clk_put(data->clk);
-err_irq:
-       free_irq(data->irq, data);
-err_io_remap:
-       iounmap(data->base);
-err_mem_region:
-       release_mem_region(data->mem->start, resource_size(data->mem));
-err_free:
-       kfree(data);
-
-       return ret;
-}
-
-static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
-{
-       struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-
-       exynos4_tmu_control(pdev, false);
-
-       hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-
-       clk_put(data->clk);
-
-       free_irq(data->irq, data);
-
-       iounmap(data->base);
-       release_mem_region(data->mem->start, resource_size(data->mem));
-
-       platform_set_drvdata(pdev, NULL);
-
-       kfree(data);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       exynos4_tmu_control(pdev, false);
-
-       return 0;
-}
-
-static int exynos4_tmu_resume(struct platform_device *pdev)
-{
-       exynos4_tmu_initialize(pdev);
-       exynos4_tmu_control(pdev, true);
-
-       return 0;
-}
-#else
-#define exynos4_tmu_suspend NULL
-#define exynos4_tmu_resume NULL
-#endif
-
-static struct platform_driver exynos4_tmu_driver = {
-       .driver = {
-               .name   = "exynos4-tmu",
-               .owner  = THIS_MODULE,
-       },
-       .probe = exynos4_tmu_probe,
-       .remove = __devexit_p(exynos4_tmu_remove),
-       .suspend = exynos4_tmu_suspend,
-       .resume = exynos4_tmu_resume,
-};
-
-module_platform_driver(exynos4_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos4-tmu");
index 371a85b..cbc5645 100644 (file)
@@ -806,6 +806,7 @@ static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
                        dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
                        goto free_gpio;
                }
+               i2c->gpios[idx] = gpio;
 
                ret = gpio_request(gpio, "i2c-bus");
                if (ret) {
@@ -1131,6 +1132,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
 
+       s3c24xx_i2c_dt_gpio_free(i2c);
        i2c->suspended = 1;
 
        return 0;
index 11e4438..f6cb543 100644 (file)
@@ -441,6 +441,26 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
+config MFD_MAX77686
+       bool "Maxim Semiconductor MAX77686 PMIC Support"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select MFD_CORE
+       help
+         Say yes here to support for Maxim Semiconductor MAX77686.
+         This is a Power Management IC with RTC on chip.
+         This driver provides common support for accessing the device;
+         additional drivers must be enabled in order to use the functionality
+         of the device.
+
+config DEBUG_MAX77686
+       bool "MAX77686 PMIC debugging"
+       depends on MFD_MAX77686
+       help
+         Say yes, if you need enable debug messages in
+         MFD_MAX77686 driver.
+         Further for enabling/disabling particular type of debug
+         messages set max77686_debug_mask accordingly.
+
 config MFD_S5M_CORE
        bool "SAMSUNG S5M Series Support"
        depends on I2C=y && GENERIC_HARDIRQS
index 05fa538..19ecca9 100644 (file)
@@ -79,6 +79,7 @@ max8925-objs                  := max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)      += max8925.o
 obj-$(CONFIG_MFD_MAX8997)      += max8997.o max8997-irq.o
 obj-$(CONFIG_MFD_MAX8998)      += max8998.o max8998-irq.o
+obj-$(CONFIG_MFD_MAX77686)     += max77686.o max77686-irq.o
 
 pcf50633-objs                  := pcf50633-core.o pcf50633-irq.o
 obj-$(CONFIG_MFD_PCF50633)     += pcf50633.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644 (file)
index 0000000..b03d390
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <woong.byun@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+
+int max77686_debug_mask = MAX77686_DEBUG_INFO; /* enable debug prints */
+
+static const u8 max77686_mask_reg[] = {
+       [PMIC_INT1]     = MAX77686_REG_INT1MSK,
+       [PMIC_INT2]     = MAX77686_REG_INT2MSK,
+       [RTC_INT]       = MAX77686_RTC_INTM,
+};
+
+static struct i2c_client *max77686_get_i2c(struct max77686_dev *max77686,
+                                          enum max77686_irq_source src)
+{
+       switch (src) {
+       case PMIC_INT1...PMIC_INT2:
+               return max77686->i2c;
+       case RTC_INT:
+               return max77686->rtc;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+}
+
+struct max77686_irq_data {
+       int mask;
+       enum max77686_irq_source group;
+};
+
+static const struct max77686_irq_data max77686_irqs[] = {
+       [MAX77686_PMICIRQ_PWRONF]       = { .group = PMIC_INT1,
+                                               .mask = 1 << 0 },
+       [MAX77686_PMICIRQ_PWRONR]       = { .group = PMIC_INT1,
+                                               .mask = 1 << 1 },
+       [MAX77686_PMICIRQ_JIGONBF]      = { .group = PMIC_INT1,
+                                               .mask = 1 << 2 },
+       [MAX77686_PMICIRQ_JIGONBR]      = { .group = PMIC_INT1,
+                                               .mask = 1 << 3 },
+       [MAX77686_PMICIRQ_ACOKBF]       = { .group = PMIC_INT1,
+                                               .mask = 1 << 4 },
+       [MAX77686_PMICIRQ_ACOKBR]       = { .group = PMIC_INT1,
+                                               .mask = 1 << 5 },
+       [MAX77686_PMICIRQ_ONKEY1S]      = { .group = PMIC_INT1,
+                                               .mask = 1 << 6 },
+       [MAX77686_PMICIRQ_MRSTB]        = { .group = PMIC_INT1,
+                                               .mask = 1 << 7 },
+       [MAX77686_PMICIRQ_140C]         = { .group = PMIC_INT2,
+                                               .mask = 1 << 0 },
+       [MAX77686_PMICIRQ_120C]         = { .group = PMIC_INT2,
+                                               .mask = 1 << 1 },
+       [MAX77686_RTCIRQ_RTC60S]        = { .group = RTC_INT,
+                                               .mask = 1 << 0 },
+       [MAX77686_RTCIRQ_RTCA1]         = { .group = RTC_INT,
+                                               .mask = 1 << 1 },
+       [MAX77686_RTCIRQ_RTCA2]         = { .group = RTC_INT,
+                                               .mask = 1 << 2 },
+       [MAX77686_RTCIRQ_SMPL]          = { .group = RTC_INT,
+                                               .mask = 1 << 3 },
+       [MAX77686_RTCIRQ_RTC1S]         = { .group = RTC_INT,
+                                               .mask = 1 << 4 },
+       [MAX77686_RTCIRQ_WTSR]          = { .group = RTC_INT,
+                                               .mask = 1 << 5 },
+};
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+       mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       int i;
+
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+               u8 mask_reg = max77686_mask_reg[i];
+               struct i2c_client *i2c = max77686_get_i2c(max77686, i);
+
+               dbg_mask("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+                       __func__, i, mask_reg, max77686->irq_masks_cur[i]);
+
+               if (mask_reg == MAX77686_REG_INVALID || IS_ERR_OR_NULL(i2c))
+                       continue;
+
+               max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+               max77686_write_reg(i2c, max77686_mask_reg[i],
+                                  max77686->irq_masks_cur[i]);
+       }
+
+       mutex_unlock(&max77686->irqlock);
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       const struct max77686_irq_data *irq_data = &max77686_irqs[data->hwirq];
+
+       max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
+       dbg_mask("%s: group=%d, cur=0x%x\n",
+               __func__, irq_data->group,
+               max77686->irq_masks_cur[irq_data->group]);
+
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+       struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+       const struct max77686_irq_data *irq_data = &max77686_irqs[data->hwirq];
+
+       max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+       dbg_mask("%s: group=%d, cur=0x%x\n",
+               __func__, irq_data->group,
+               max77686->irq_masks_cur[irq_data->group]);
+
+}
+
+static struct irq_chip max77686_irq_chip = {
+       .name = "max77686",
+       .irq_bus_lock = max77686_irq_lock,
+       .irq_bus_sync_unlock = max77686_irq_sync_unlock,
+       .irq_mask = max77686_irq_mask,
+       .irq_unmask = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+       struct max77686_dev *max77686 = data;
+       u8 irq_reg[MAX77686_IRQ_GROUP_NR] = { };
+       u8 irq_src;
+       int ret, i, cur_irq;
+
+       ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src);
+       if (ret < 0) {
+               dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       dbg_int("%s: irq_src=0x%x\n", __func__, irq_src);
+
+       if (irq_src == MAX77686_IRQSRC_PMIC) {
+               ret = max77686_bulk_read(max77686->i2c, MAX77686_REG_INT1,
+                                        2, irq_reg);
+               if (ret < 0) {
+                       dev_err(max77686->dev,
+                               "Failed to read pmic interrupt: %d\n", ret);
+                       return IRQ_NONE;
+               }
+
+               dbg_int("%s: int1=0x%x, int2=0x%x\n", __func__,
+                       irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+       }
+
+       if (irq_src & MAX77686_IRQSRC_RTC) {
+               ret = max77686_read_reg(max77686->rtc, MAX77686_RTC_INT,
+                                       &irq_reg[RTC_INT]);
+               if (ret < 0) {
+                       dev_err(max77686->dev,
+                               "Failed to read rtc interrupt: %d\n", ret);
+                       return IRQ_NONE;
+               }
+               dbg_int("%s: rtc int=0x%x\n", __func__,
+                       irq_reg[RTC_INT]);
+
+       }
+
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++)
+               irq_reg[i] &= ~max77686->irq_masks_cur[i];
+
+       for (i = 0; i < MAX77686_IRQ_NR; i++) {
+               if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
+                       cur_irq = irq_find_mapping(max77686->irq_domain, i);
+                       if (cur_irq)
+                               handle_nested_irq(cur_irq);
+               }
+       }
+
+       dbg_info("%s returning\n", __func__);
+
+       return IRQ_HANDLED;
+}
+
+int max77686_irq_resume(struct max77686_dev *max77686)
+{
+       if (max77686->irq && max77686->irq_domain)
+               max77686_irq_thread(0, max77686);
+
+       return 0;
+}
+
+static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
+                                       irq_hw_number_t hw)
+{
+       struct max77686_dev *max77686 = d->host_data;
+
+       irq_set_chip_data(irq, max77686);
+       irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+       set_irq_flags(irq, IRQF_VALID);
+#else
+       irq_set_noprobe(irq);
+#endif
+       return 0;
+}
+
+static struct irq_domain_ops max77686_irq_domain_ops = {
+       .map = max77686_irq_domain_map,
+};
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+       int i;
+       int ret;
+       struct irq_domain *domain;
+
+       if (!max77686->irq) {
+                       dev_warn(max77686->dev,
+                                "No interrupt specified.\n");
+                       return 0;
+       }
+
+       mutex_init(&max77686->irqlock);
+
+       /* Mask individual interrupt sources */
+       for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+               struct i2c_client *i2c;
+
+               max77686->irq_masks_cur[i] = 0xff;
+               max77686->irq_masks_cache[i] = 0xff;
+               i2c = max77686_get_i2c(max77686, i);
+
+               if (IS_ERR_OR_NULL(i2c))
+                       continue;
+               if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+                       continue;
+
+               max77686_write_reg(i2c, max77686_mask_reg[i], 0xff);
+       }
+
+       domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
+                                       &max77686_irq_domain_ops, &max77686);
+       if (!domain) {
+               dev_err(max77686->dev, "could not create irq domain\n");
+               return -ENODEV;
+       }
+
+       ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "max77686-irq", max77686);
+
+       if (ret) {
+               dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+                       max77686->irq, ret);
+               return ret;
+       }
+
+       dbg_info("%s : returning\n", __func__);
+
+       return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+       if (max77686->irq)
+               free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644 (file)
index 0000000..a83f959
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#define I2C_ADDR_RTC    (0x0C >> 1)
+
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
+       {.compatible = "maxim,max77686-pmic",   .data = TYPE_MAX77686},
+       {},
+};
+#endif
+
+static struct mfd_cell max77686_devs[] = {
+       {.name = "max77686-pmic",},
+       {.name = "max77686-rtc",},
+};
+
+int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+       int ret;
+
+       mutex_lock(&max77686->iolock);
+       ret = i2c_smbus_read_byte_data(i2c, reg);
+       mutex_unlock(&max77686->iolock);
+       if (ret < 0)
+               return ret;
+
+       ret &= 0xff;
+       *dest = ret;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_read_reg);
+
+int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+       int ret;
+
+       mutex_lock(&max77686->iolock);
+       ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+       mutex_unlock(&max77686->iolock);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_read);
+
+int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+       int ret;
+
+       mutex_lock(&max77686->iolock);
+       ret = i2c_smbus_write_byte_data(i2c, reg, value);
+       mutex_unlock(&max77686->iolock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_write_reg);
+
+int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+       int ret;
+
+       mutex_lock(&max77686->iolock);
+       ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+       mutex_unlock(&max77686->iolock);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_write);
+
+int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+       int ret;
+
+       mutex_lock(&max77686->iolock);
+       ret = i2c_smbus_read_byte_data(i2c, reg);
+       if (ret >= 0) {
+               u8 old_val = ret & 0xff;
+               u8 new_val = (val & mask) | (old_val & (~mask));
+               ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+       }
+       mutex_unlock(&max77686->iolock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_update_reg);
+
+#ifdef CONFIG_OF
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+                                                                 *dev)
+{
+       struct max77686_platform_data *pd;
+
+       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd) {
+               dev_err(dev, "could not allocate memory for pdata\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (of_get_property(dev->of_node, "max77686,wakeup", NULL))
+               pd->wakeup = true;
+
+       return pd;
+}
+#else
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+                                                                 *dev)
+{
+       return 0;
+}
+#endif
+
+static inline int max77686_i2c_get_driver_data(struct i2c_client *i2c,
+                                              const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+       if (i2c->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(max77686_pmic_dt_match,
+                               i2c->dev.of_node);
+               return (int)match->data;
+       }
+#endif
+       return (int)id->driver_data;
+}
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct max77686_dev *max77686;
+       struct max77686_platform_data *pdata = i2c->dev.platform_data;
+       int ret = 0;
+       u8 data;
+
+       max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+       if (max77686 == NULL) {
+               dev_err(max77686->dev, "could not allocate memory\n");
+               return -ENOMEM;
+       }
+
+       max77686->dev = &i2c->dev;
+
+       if (max77686->dev->of_node) {
+               pdata = max77686_i2c_parse_dt_pdata(max77686->dev);
+               if (IS_ERR(pdata)) {
+                       ret = PTR_ERR(pdata);
+                       goto err;
+               }
+       }
+
+       if (!pdata) {
+               ret = -ENODEV;
+               dbg_info("%s : No platform data found\n", __func__);
+               goto err;
+       }
+
+       i2c_set_clientdata(i2c, max77686);
+       max77686->i2c = i2c;
+       max77686->irq = i2c->irq;
+       max77686->type = max77686_i2c_get_driver_data(i2c, id);
+
+       max77686->pdata = pdata;
+       max77686->wakeup = pdata->wakeup;
+
+       mutex_init(&max77686->iolock);
+
+       max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+       i2c_set_clientdata(max77686->rtc, max77686);
+       max77686_irq_init(max77686);
+
+       ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+                             ARRAY_SIZE(max77686_devs), NULL, 0);
+
+       if (ret < 0) {
+               dbg_info("%s : mfd_add_devices failed\n", __func__);
+               goto err_mfd;
+       }
+
+       pm_runtime_set_active(max77686->dev);
+       device_init_wakeup(max77686->dev, max77686->wakeup);
+
+       if (max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data) < 0) {
+               ret = -EIO;
+               dbg_info("%s : device not found on this channel\n", __func__);
+               goto err_mfd;
+       } else
+               dev_info(max77686->dev, "device found\n");
+
+       return ret;
+
+ err_mfd:
+       mfd_remove_devices(max77686->dev);
+       max77686_irq_exit(max77686);
+       i2c_unregister_device(max77686->rtc);
+ err:
+       kfree(max77686);
+       dev_err(max77686->dev, "device probe failed : %d\n", ret);
+       return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+       device_init_wakeup(max77686->dev, 0);
+       pm_runtime_set_suspended(max77686->dev);
+       mfd_remove_devices(max77686->dev);
+       max77686_irq_exit(max77686);
+       i2c_unregister_device(max77686->rtc);
+       kfree(max77686);
+       return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+       {"max77686", TYPE_MAX77686},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+static int max77686_suspend(struct device *dev)
+{
+       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(max77686->irq);
+
+       return 0;
+}
+
+static int max77686_resume(struct device *dev)
+{
+       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+       struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(max77686->irq);
+
+       max77686_irq_resume(max77686);
+       return 0;
+}
+
+const struct dev_pm_ops max77686_pm = {
+       .suspend = max77686_suspend,
+       .resume = max77686_resume,
+};
+
+static struct i2c_driver max77686_i2c_driver = {
+       .driver = {
+                  .name = "max77686",
+                  .owner = THIS_MODULE,
+                  .pm = &max77686_pm,
+                  .of_match_table = of_match_ptr(max77686_pmic_dt_match),
+                  },
+       .probe = max77686_i2c_probe,
+       .remove = max77686_i2c_remove,
+       .id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+       return i2c_add_driver(&max77686_i2c_driver);
+}
+
+subsys_initcall(max77686_i2c_init);
+
+static void __exit max77686_i2c_exit(void)
+{
+       i2c_del_driver(&max77686_i2c_driver);
+}
+
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
index 36db5a4..67162cc 100644 (file)
@@ -195,6 +195,15 @@ config REGULATOR_MAX8998
          via I2C bus. The provided regulator is suitable for S3C6410
          and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_MAX77686
+       tristate "Maxim 77686 regulator"
+       depends on MFD_MAX77686
+       help
+         This driver controls a Maxim 77686 voltage regulator via I2C
+         bus. The provided regulator is suitable for Exynos5 chips to
+         control VDD_ARM and VDD_INT voltages.It supports LDOs[1-26]
+         and BUCKs[1-9].
+
 config REGULATOR_PCAP
        tristate "Motorola PCAP2 regulator driver"
        depends on EZX_PCAP
index 94b5274..008931b 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
 obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
 obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
 obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
+obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
new file mode 100644 (file)
index 0000000..6409e82
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * max77686.c - Regulator driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+struct max77686_data {
+       struct device *dev;
+       struct max77686_dev *iodev;
+       int num_regulators;
+       struct regulator_dev **rdev;
+       int ramp_delay;         /* index of ramp_delay */
+
+       /*GPIO-DVS feature is not enabled with the
+        *current version of MAX77686 driver.*/
+};
+
+struct voltage_map_desc {
+       int min;
+       int max;
+       int step;
+       unsigned int n_bits;
+};
+
+/* Voltage maps in mV */
+static const struct voltage_map_desc ldo_voltage_map_desc = {
+       .min = 800,     .max = 3950,    .step = 50,     .n_bits = 6,
+};                             /* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 */
+
+static const struct voltage_map_desc ldo_low_voltage_map_desc = {
+       .min = 800,     .max = 2375,    .step = 25,     .n_bits = 6,
+};                             /* LDO1 ~ 2, 6 ~ 8, 15 */
+
+static const struct voltage_map_desc buck_dvs_voltage_map_desc = {
+       .min = 600000,  .max = 3787500, .step = 12500,  .n_bits = 8,
+};                             /* Buck2, 3, 4 (uV) */
+
+static const struct voltage_map_desc buck_voltage_map_desc = {
+       .min = 750,     .max = 3900,    .step = 50,     .n_bits = 6,
+};                             /* Buck1, 5 ~ 9 */
+
+static const struct voltage_map_desc *reg_voltage_map[] = {
+       [MAX77686_LDO1] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO2] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO3] = &ldo_voltage_map_desc,
+       [MAX77686_LDO4] = &ldo_voltage_map_desc,
+       [MAX77686_LDO5] = &ldo_voltage_map_desc,
+       [MAX77686_LDO6] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO7] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO8] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO9] = &ldo_voltage_map_desc,
+       [MAX77686_LDO10] = &ldo_voltage_map_desc,
+       [MAX77686_LDO11] = &ldo_voltage_map_desc,
+       [MAX77686_LDO12] = &ldo_voltage_map_desc,
+       [MAX77686_LDO13] = &ldo_voltage_map_desc,
+       [MAX77686_LDO14] = &ldo_voltage_map_desc,
+       [MAX77686_LDO15] = &ldo_low_voltage_map_desc,
+       [MAX77686_LDO16] = &ldo_voltage_map_desc,
+       [MAX77686_LDO17] = &ldo_voltage_map_desc,
+       [MAX77686_LDO18] = &ldo_voltage_map_desc,
+       [MAX77686_LDO19] = &ldo_voltage_map_desc,
+       [MAX77686_LDO20] = &ldo_voltage_map_desc,
+       [MAX77686_LDO21] = &ldo_voltage_map_desc,
+       [MAX77686_LDO22] = &ldo_voltage_map_desc,
+       [MAX77686_LDO23] = &ldo_voltage_map_desc,
+       [MAX77686_LDO24] = &ldo_voltage_map_desc,
+       [MAX77686_LDO25] = &ldo_voltage_map_desc,
+       [MAX77686_LDO26] = &ldo_voltage_map_desc,
+       [MAX77686_BUCK1] = &buck_voltage_map_desc,
+       [MAX77686_BUCK2] = &buck_dvs_voltage_map_desc,
+       [MAX77686_BUCK3] = &buck_dvs_voltage_map_desc,
+       [MAX77686_BUCK4] = &buck_dvs_voltage_map_desc,
+       [MAX77686_BUCK5] = &buck_voltage_map_desc,
+       [MAX77686_BUCK6] = &buck_voltage_map_desc,
+       [MAX77686_BUCK7] = &buck_voltage_map_desc,
+       [MAX77686_BUCK8] = &buck_voltage_map_desc,
+       [MAX77686_BUCK9] = &buck_voltage_map_desc,
+       [MAX77686_EN32KHZ_AP] = NULL,
+       [MAX77686_EN32KHZ_CP] = NULL,
+       [MAX77686_P32KH] = NULL,
+};
+
+static int max77686_get_voltage_unit(int rid)
+{
+       int unit = 0;
+
+       switch (rid) {
+       case MAX77686_BUCK2...MAX77686_BUCK4:
+               unit = 1;       /* BUCK2,3,4 is uV */
+               break;
+       default:
+               unit = 1000;
+               break;
+       }
+
+       return unit;
+}
+
+static int max77686_list_voltage(struct regulator_dev *rdev,
+                                unsigned int selector)
+{
+       const struct voltage_map_desc *desc;
+       int rid = rdev_get_id(rdev);
+       int val;
+
+       if (rid >= ARRAY_SIZE(reg_voltage_map) || rid < 0)
+               return -EINVAL;
+
+       desc = reg_voltage_map[rid];
+       if (desc == NULL)
+               return -EINVAL;
+
+       val = desc->min + desc->step * selector;
+       if (val > desc->max)
+               return -EINVAL;
+
+       return val * max77686_get_voltage_unit(rid);
+}
+
+static int max77686_get_enable_register(struct regulator_dev *rdev,
+                                       int *reg, int *mask, int *pattern)
+{
+       int rid = rdev_get_id(rdev);
+
+       switch (rid) {
+       case MAX77686_LDO1...MAX77686_LDO26:
+               *reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+               *mask = 0xC0;
+               *pattern = 0xC0;
+               break;
+       case MAX77686_BUCK1:
+               *reg = MAX77686_REG_BUCK1CTRL;
+               *mask = 0x03;
+               *pattern = 0x03;
+               break;
+       case MAX77686_BUCK2:
+               *reg = MAX77686_REG_BUCK2CTRL1;
+               *mask = 0x30;
+               *pattern = 0x10;
+               break;
+       case MAX77686_BUCK3:
+               *reg = MAX77686_REG_BUCK3CTRL1;
+               *mask = 0x30;
+               *pattern = 0x10;
+               break;
+       case MAX77686_BUCK4:
+               *reg = MAX77686_REG_BUCK4CTRL1;
+               *mask = 0x30;
+               *pattern = 0x10;
+               break;
+       case MAX77686_BUCK5...MAX77686_BUCK9:
+               *reg = MAX77686_REG_BUCK5CTRL + (rid - MAX77686_BUCK5) * 2;
+               *mask = 0x03;
+               *pattern = 0x03;
+               break;
+       case MAX77686_EN32KHZ_AP...MAX77686_P32KH:
+               *reg = MAX77686_REG_32KHZ_;
+               *mask = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+               *pattern = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+               break;
+       default:
+               /* Not controllable or not exists */
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max77686_reg_is_enabled(struct regulator_dev *rdev)
+{
+       struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max77686->iodev->i2c;
+       int ret, reg, mask, pattern;
+       u8 val;
+
+       ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+       if (ret)
+               return ret;
+
+       ret = max77686_read_reg(i2c, reg, &val);
+       if (ret)
+               return ret;
+
+       return (val & mask) == pattern;
+}
+
+static int max77686_reg_enable(struct regulator_dev *rdev)
+{
+       struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max77686->iodev->i2c;
+       int ret, reg, mask, pattern;
+
+       ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+       if (ret)
+               return ret;
+
+       return max77686_update_reg(i2c, reg, pattern, mask);
+}
+
+static int max77686_reg_disable(struct regulator_dev *rdev)
+{
+       struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max77686->iodev->i2c;
+       int ret, reg, mask, pattern;
+
+       ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+       if (ret)
+               return ret;
+
+       return max77686_update_reg(i2c, reg, ~mask, mask);
+}
+
+static int max77686_get_voltage_register(struct regulator_dev *rdev,
+                                        int *_reg, int *_shift, int *_mask)
+{
+       int rid = rdev_get_id(rdev);
+       int reg, shift = 0, mask = 0x3f;
+
+       switch (rid) {
+       case MAX77686_LDO1...MAX77686_LDO26:
+               reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+               break;
+       case MAX77686_BUCK1:
+               reg = MAX77686_REG_BUCK1OUT;
+               break;
+       case MAX77686_BUCK2:
+               reg = MAX77686_REG_BUCK2DVS1;
+               mask = 0xff;
+               break;
+       case MAX77686_BUCK3:
+               reg = MAX77686_REG_BUCK3DVS1;
+               mask = 0xff;
+               break;
+       case MAX77686_BUCK4:
+               reg = MAX77686_REG_BUCK4DVS1;
+               mask = 0xff;
+               break;
+       case MAX77686_BUCK5...MAX77686_BUCK9:
+               reg = MAX77686_REG_BUCK5OUT + (rid - MAX77686_BUCK5) * 2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *_reg = reg;
+       *_shift = shift;
+       *_mask = mask;
+
+       return 0;
+}
+
+static int max77686_get_voltage(struct regulator_dev *rdev)
+{
+       struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max77686->iodev->i2c;
+       int reg, shift, mask, ret;
+       u8 val;
+
+       ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+       if (ret)
+               return ret;
+
+       ret = max77686_read_reg(i2c, reg, &val);
+       if (ret)
+               return ret;
+
+       val >>= shift;
+       val &= mask;
+
+       return max77686_list_voltage(rdev, val);
+}
+
+static inline int max77686_get_voltage_proper_val(const struct voltage_map_desc
+                                                 *desc, int min_vol,
+                                                 int max_vol)
+{
+       int i = 0;
+
+       if (desc == NULL)
+               return -EINVAL;
+
+       if (max_vol < desc->min || min_vol > desc->max)
+               return -EINVAL;
+
+       while (desc->min + desc->step * i < min_vol &&
+              desc->min + desc->step * i < desc->max)
+               i++;
+
+       if (desc->min + desc->step * i > max_vol)
+               return -EINVAL;
+
+       if (i >= (1 << desc->n_bits))
+               return -EINVAL;
+
+       return i;
+}
+
+static int max77686_set_voltage(struct regulator_dev *rdev,
+                               int min_uV, int max_uV, unsigned *selector)
+{
+       struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max77686->iodev->i2c;
+       int min_vol = min_uV, max_vol = max_uV, unit = 0;
+       const struct voltage_map_desc *desc;
+       int rid = rdev_get_id(rdev);
+       int reg, shift = 0, mask, ret;
+       int i;
+       int ramp[] = {13, 27, 57, 100}; /* ramp_rate in mV/uS */
+       u8 org;
+
+       unit = max77686_get_voltage_unit(rid);
+       min_vol /= unit;
+       max_vol /= unit;
+
+       desc = reg_voltage_map[rid];
+
+       i = max77686_get_voltage_proper_val(desc, min_vol, max_vol);
+       if (i < 0)
+               return i;
+
+       ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+       if (ret)
+               return ret;
+
+       max77686_read_reg(i2c, reg, &org);
+       org = (org & mask) >> shift;
+
+       ret = max77686_update_reg(i2c, reg, i << shift, mask << shift);
+       *selector = i;
+
+       if (rid == MAX77686_BUCK2 || rid == MAX77686_BUCK3 ||
+           rid == MAX77686_BUCK4) {
+               /* If the voltage is increasing */
+               if (org < i)
+                       udelay(DIV_ROUND_UP(desc->step * (i - org),
+                                           ramp[max77686->ramp_delay]));
+       }
+
+       return ret;
+}
+
+static struct regulator_ops max77686_ops = {
+       .list_voltage = max77686_list_voltage,
+       .is_enabled = max77686_reg_is_enabled,
+       .enable = max77686_reg_enable,
+       .disable = max77686_reg_disable,
+       .get_voltage = max77686_get_voltage,
+       .set_voltage = max77686_set_voltage,
+       .set_suspend_enable = max77686_reg_enable,
+       .set_suspend_disable = max77686_reg_disable,
+};
+
+static struct regulator_ops max77686_fixedvolt_ops = {
+       .list_voltage = max77686_list_voltage,
+       .is_enabled = max77686_reg_is_enabled,
+       .enable = max77686_reg_enable,
+       .disable = max77686_reg_disable,
+       .set_suspend_enable = max77686_reg_enable,
+       .set_suspend_disable = max77686_reg_disable,
+};
+
+#define regulator_desc_ldo(num)                {       \
+       .name           = "LDO"#num,            \
+       .id             = MAX77686_LDO##num,    \
+       .ops            = &max77686_ops,        \
+       .type           = REGULATOR_VOLTAGE,    \
+       .owner          = THIS_MODULE,          \
+}
+#define regulator_desc_buck(num)               {       \
+       .name           = "BUCK"#num,           \
+       .id             = MAX77686_BUCK##num,   \
+       .ops            = &max77686_ops,        \
+       .type           = REGULATOR_VOLTAGE,    \
+       .owner          = THIS_MODULE,          \
+}
+
+static struct regulator_desc regulators[] = {
+       regulator_desc_ldo(1),
+       regulator_desc_ldo(2),
+       regulator_desc_ldo(3),
+       regulator_desc_ldo(4),
+       regulator_desc_ldo(5),
+       regulator_desc_ldo(6),
+       regulator_desc_ldo(7),
+       regulator_desc_ldo(8),
+       regulator_desc_ldo(9),
+       regulator_desc_ldo(10),
+       regulator_desc_ldo(11),
+       regulator_desc_ldo(12),
+       regulator_desc_ldo(13),
+       regulator_desc_ldo(14),
+       regulator_desc_ldo(15),
+       regulator_desc_ldo(16),
+       regulator_desc_ldo(17),
+       regulator_desc_ldo(18),
+       regulator_desc_ldo(19),
+       regulator_desc_ldo(20),
+       regulator_desc_ldo(21),
+       regulator_desc_ldo(22),
+       regulator_desc_ldo(23),
+       regulator_desc_ldo(24),
+       regulator_desc_ldo(25),
+       regulator_desc_ldo(26),
+       regulator_desc_buck(1),
+       regulator_desc_buck(2),
+       regulator_desc_buck(3),
+       regulator_desc_buck(4),
+       regulator_desc_buck(5),
+       regulator_desc_buck(6),
+       regulator_desc_buck(7),
+       regulator_desc_buck(8),
+       regulator_desc_buck(9),
+       {
+               .name = "EN32KHz_AP",
+               .id = MAX77686_EN32KHZ_AP,
+               .ops = &max77686_fixedvolt_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       }, {
+               .name = "EN32KHz_CP",
+               .id = MAX77686_EN32KHZ_CP,
+               .ops = &max77686_fixedvolt_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       }, {
+               .name = "ENP32KHz",
+               .id = MAX77686_P32KH,
+               .ops = &max77686_fixedvolt_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+};
+
+#ifdef CONFIG_OF
+static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
+                                       struct max77686_platform_data *pdata)
+{
+       struct device_node *pmic_np, *regulators_np, *reg_np;
+       struct max77686_regulator_data *rdata;
+       unsigned int i;
+
+       pmic_np = iodev->dev->of_node;
+       if (!pmic_np) {
+               dev_err(iodev->dev, "could not find pmic sub-node\n");
+               return -ENODEV;
+       }
+
+       regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators");
+       if (!regulators_np) {
+               dev_err(iodev->dev, "could not find regulators sub-node\n");
+               return -EINVAL;
+       }
+
+/* count the number of regulators to be supported in pmic */
+       pdata->num_regulators = 0;
+       for_each_child_of_node(regulators_np, reg_np)
+               pdata->num_regulators++;
+
+       rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) *
+                               pdata->num_regulators, GFP_KERNEL);
+       if (!rdata) {
+               dev_err(iodev->dev,
+                       "could not allocate memory for regulator data\n");
+               return -ENOMEM;
+       }
+
+       pdata->regulators = rdata;
+       for_each_child_of_node(regulators_np, reg_np) {
+               for (i = 0; i < ARRAY_SIZE(regulators); i++)
+                       if (!of_node_cmp(reg_np->name, regulators[i].name))
+                               break;
+
+               if (i == ARRAY_SIZE(regulators)) {
+                       dev_warn(iodev->dev,
+                               "No configuration data for regulator %s\n",
+                               reg_np->name);
+                       continue;
+               }
+
+               rdata->id = i;
+               rdata->initdata = of_get_regulator_init_data(
+                                               iodev->dev, reg_np);
+               rdata->reg_node = reg_np;
+               rdata++;
+       }
+
+       if (of_property_read_u32(pmic_np,
+               "max77686,buck_ramp_delay", &i))
+               pdata->ramp_delay = i & 0xff;
+
+       return 0;
+}
+#else
+static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev,
+                                       struct max8997_platform_data *pdata)
+{
+       return 0;
+}
+#endif /* CONFIG_OF */
+
+#define RAMP_VALUE (max77686->ramp_delay << 6)
+
+static __devinit int max77686_pmic_probe(struct platform_device *pdev)
+{
+       struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct max77686_platform_data *pdata = iodev->pdata;
+       struct regulator_dev **rdev;
+       struct max77686_data *max77686;
+       struct i2c_client *i2c = iodev->i2c;
+       int i, ret, size;
+
+       if (iodev->dev->of_node) {
+               ret = max77686_pmic_dt_parse_pdata(iodev, pdata);
+               if (ret)
+                       return ret;
+       }
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform data not found\n");
+               return -ENODEV;
+       }
+
+       max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL);
+       if (!max77686)
+               return -ENOMEM;
+
+       size = sizeof(struct regulator_dev *) * pdata->num_regulators;
+       max77686->rdev = kzalloc(size, GFP_KERNEL);
+       if (!max77686->rdev) {
+               kfree(max77686);
+               return -ENOMEM;
+       }
+
+       rdev = max77686->rdev;
+
+       max77686->dev = &pdev->dev;
+       max77686->iodev = iodev;
+       max77686->num_regulators = pdata->num_regulators;
+
+       if (pdata->ramp_delay) {
+               max77686->ramp_delay = pdata->ramp_delay;
+               max77686_update_reg(i2c, MAX77686_REG_BUCK2CTRL1,
+                       RAMP_VALUE, RAMP_MASK);
+               max77686_update_reg(i2c, MAX77686_REG_BUCK3CTRL1,
+                       RAMP_VALUE, RAMP_MASK);
+               max77686_update_reg(i2c, MAX77686_REG_BUCK4CTRL1,
+                       RAMP_VALUE, RAMP_MASK);
+       } else {
+               /* Default/Reset value is 27.5 mV/uS */
+               max77686->ramp_delay = MAX77686_RAMP_RATE_27MV;
+       }
+
+       platform_set_drvdata(pdev, max77686);
+
+       for (i = 0; i < pdata->num_regulators; i++) {
+               const struct voltage_map_desc *desc;
+               int id = pdata->regulators[i].id;
+
+               desc = reg_voltage_map[id];
+               if (desc)
+                       regulators[id].n_voltages =
+                           (desc->max - desc->min) / desc->step + 1;
+
+               rdev[i] = regulator_register(&regulators[id], max77686->dev,
+                                            pdata->regulators[i].initdata,
+                                            max77686, NULL);
+               if (IS_ERR(rdev[i])) {
+                       ret = PTR_ERR(rdev[i]);
+                       dev_err(max77686->dev,
+                               "regulator init failed for id : %d\n", id);
+                       rdev[i] = NULL;
+                       goto err;
+               }
+       }
+
+       return 0;
+ err:
+       for (i = 0; i < max77686->num_regulators; i++)
+               if (rdev[i])
+                       regulator_unregister(rdev[i]);
+
+       kfree(max77686->rdev);
+       kfree(max77686);
+
+       return ret;
+}
+
+static int __devexit max77686_pmic_remove(struct platform_device *pdev)
+{
+       struct max77686_data *max77686 = platform_get_drvdata(pdev);
+       struct regulator_dev **rdev = max77686->rdev;
+       int i;
+
+       for (i = 0; i < max77686->num_regulators; i++)
+               if (rdev[i])
+                       regulator_unregister(rdev[i]);
+
+       kfree(max77686->rdev);
+       kfree(max77686);
+
+       return 0;
+}
+
+static const struct platform_device_id max77686_pmic_id[] = {
+       {"max77686-pmic", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(platform, max77686_pmic_id);
+
+static struct platform_driver max77686_pmic_driver = {
+       .driver = {
+                  .name = "max77686-pmic",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = max77686_pmic_probe,
+       .remove = __devexit_p(max77686_pmic_remove),
+       .id_table = max77686_pmic_id,
+};
+
+static int __init max77686_pmic_init(void)
+{
+       return platform_driver_register(&max77686_pmic_driver);
+}
+
+subsys_initcall(max77686_pmic_init);
+
+static void __exit max77686_pmic_cleanup(void)
+{
+       platform_driver_unregister(&max77686_pmic_driver);
+}
+
+module_exit(max77686_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
index 514a691..04c6796 100644 (file)
@@ -19,6 +19,17 @@ config THERMAL_HWMON
        depends on HWMON=y || HWMON=THERMAL
        default y
 
+config CPU_THERMAL
+       bool "generic cpu cooling support"
+       depends on THERMAL && CPU_FREQ
+       help
+         This implements the generic cpu cooling mechanism through frequency
+         reduction, cpu hotplug and any other ways of reducing temperature. An
+         ACPI version of this already exists(drivers/acpi/processor_thermal.c).
+         This will be useful for platforms using the generic thermal interface
+         and not the ACPI interface.
+         If you want this support, you should say Y or M here.
+
 config SPEAR_THERMAL
        bool "SPEAr thermal sensor driver"
        depends on THERMAL
@@ -26,3 +37,12 @@ config SPEAR_THERMAL
        help
          Enable this to plug the SPEAr thermal sensor driver into the Linux
          thermal framework
+
+config EXYNOS_THERMAL
+       tristate "Temperature sensor on Samsung EXYNOS"
+       depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && THERMAL
+       help
+         If you say yes here you get support for TMU (Thermal Managment
+         Unit) on SAMSUNG EXYNOS series of SoC.
+         This driver can also be built as a module. If so, the module
+         will be called exynos4-tmu
index a9fff0b..4636e35 100644 (file)
@@ -3,4 +3,6 @@
 #
 
 obj-$(CONFIG_THERMAL)          += thermal_sys.o
-obj-$(CONFIG_SPEAR_THERMAL)            += spear_thermal.o
\ No newline at end of file
+obj-$(CONFIG_CPU_THERMAL)       += cpu_cooling.o
+obj-$(CONFIG_SPEAR_THERMAL)            += spear_thermal.o
+obj-$(CONFIG_EXYNOS_THERMAL)           += exynos_thermal.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
new file mode 100644 (file)
index 0000000..c40d9a0
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ *  linux/drivers/thermal/cpu_cooling.c
+ *
+ *  Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
+ *  Copyright (C) 2012  Amit Daniel <amit.kachhap@linaro.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *  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 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/cpu_cooling.h>
+
+/**
+ * struct cpufreq_cooling_device
+ * @id: unique integer value corresponding to each cpufreq_cooling_device
+ *     registered.
+ * @cool_dev: thermal_cooling_device pointer to keep track of the the
+ *     egistered cooling device.
+ * @tab_ptr: freq_clip_table table containing the maximum value of frequency to
+ *     be set for different cooling state.
+ * @tab_size: integer value representing a count of the above table.
+ * @cpufreq_state: integer value representing the current state of cpufreq
+ *     cooling devices.
+ * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
+ * @node: list_head to link all cpufreq_cooling_device together.
+ *
+ * This structure is required for keeping information of each
+ * cpufreq_cooling_device registered as a list whose head is represented by
+ * cooling_cpufreq_list. In order to prevent corruption of this list a
+ * mutex lock cooling_cpufreq_lock is used.
+ */
+struct cpufreq_cooling_device {
+       int id;
+       struct thermal_cooling_device *cool_dev;
+       struct freq_clip_table *tab_ptr;
+       unsigned int tab_size;
+       unsigned int cpufreq_state;
+       struct cpumask allowed_cpus;
+       struct list_head node;
+};
+static LIST_HEAD(cooling_cpufreq_list);
+static DEFINE_MUTEX(cooling_cpufreq_lock);
+static DEFINE_IDR(cpufreq_idr);
+
+/*per cpu variable to store the previous max frequency allowed*/
+static DEFINE_PER_CPU(unsigned int, max_policy_freq);
+
+/*notify_table passes value to the CPUFREQ_ADJUST callback function.*/
+#define NOTIFY_INVALID NULL
+static struct freq_clip_table *notify_table;
+
+/*Head of the blocking notifier chain to inform about frequency clamping*/
+static BLOCKING_NOTIFIER_HEAD(cputherm_state_notifier_list);
+
+/**
+ * get_idr - function to get a unique id.
+ * @idr: struct idr * handle used to create a id.
+ * @id: int * value generated by this function.
+ */
+static int get_idr(struct idr *idr, int *id)
+{
+       int err;
+again:
+       if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
+               return -ENOMEM;
+
+       mutex_lock(&cooling_cpufreq_lock);
+       err = idr_get_new(idr, NULL, id);
+       mutex_unlock(&cooling_cpufreq_lock);
+
+       if (unlikely(err == -EAGAIN))
+               goto again;
+       else if (unlikely(err))
+               return err;
+
+       *id = *id & MAX_ID_MASK;
+       return 0;
+}
+
+/**
+ * release_idr - function to free the unique id.
+ * @idr: struct idr * handle used for creating the id.
+ * @id: int value representing the unique id.
+ */
+static void release_idr(struct idr *idr, int id)
+{
+       mutex_lock(&cooling_cpufreq_lock);
+       idr_remove(idr, id);
+       mutex_unlock(&cooling_cpufreq_lock);
+}
+
+/**
+ * cputherm_register_notifier - Register a notifier with cpu cooling interface.
+ * @nb:        struct notifier_block * with callback info.
+ * @list: integer value for which notification is needed. possible values are
+ *     CPUFREQ_COOLING_START and CPUFREQ_COOLING_STOP.
+ *
+ * This exported function registers a driver with cpu cooling layer. The driver
+ * will be notified when any cpu cooling action is called.
+ */
+int cputherm_register_notifier(struct notifier_block *nb, unsigned int list)
+{
+       int ret = 0;
+
+       switch (list) {
+       case CPUFREQ_COOLING_START:
+       case CPUFREQ_COOLING_STOP:
+               ret = blocking_notifier_chain_register(
+                               &cputherm_state_notifier_list, nb);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+EXPORT_SYMBOL(cputherm_register_notifier);
+
+/**
+ * cputherm_unregister_notifier - Un-register a notifier.
+ * @nb:        struct notifier_block * with callback info.
+ * @list: integer value for which notification is needed. values possible are
+ *     CPUFREQ_COOLING_START or CPUFREQ_COOLING_STOP.
+ *
+ * This exported function un-registers a driver with cpu cooling layer.
+ */
+int cputherm_unregister_notifier(struct notifier_block *nb, unsigned int list)
+{
+       int ret = 0;
+
+       switch (list) {
+       case CPUFREQ_COOLING_START:
+       case CPUFREQ_COOLING_STOP:
+               ret = blocking_notifier_chain_unregister(
+                               &cputherm_state_notifier_list, nb);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+EXPORT_SYMBOL(cputherm_unregister_notifier);
+
+/*Below codes defines functions to be used for cpufreq as cooling device*/
+
+/**
+ * is_cpufreq_valid - function to check if a cpu has frequency transition policy.
+ * @cpu: cpu for which check is needed.
+ */
+static int is_cpufreq_valid(int cpu)
+{
+       struct cpufreq_policy policy;
+       return !cpufreq_get_policy(&policy, cpu);
+}
+
+/**
+ * cpufreq_apply_cooling - function to apply frequency clipping.
+ * @cpufreq_device: cpufreq_cooling_device pointer containing frequency
+ *     clipping data.
+ * @cooling_state: value of the cooling state.
+ */
+static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
+                               unsigned long cooling_state)
+{
+       unsigned int event, cpuid, state;
+       struct freq_clip_table *th_table, *table_ptr;
+       const struct cpumask *maskPtr = &cpufreq_device->allowed_cpus;
+       struct cpufreq_cooling_device *cpufreq_ptr;
+
+       if (cooling_state > cpufreq_device->tab_size)
+               return -EINVAL;
+
+       /*Check if the old cooling action is same as new cooling action*/
+       if (cpufreq_device->cpufreq_state == cooling_state)
+               return 0;
+
+       /*pass cooling table info to the cpufreq_thermal_notifier callback*/
+       notify_table = NOTIFY_INVALID;
+
+       if (cooling_state > 0) {
+               th_table = &(cpufreq_device->tab_ptr[cooling_state - 1]);
+               notify_table = th_table;
+       }
+
+       /*check if any lower clip frequency active in other cpufreq_device's*/
+       list_for_each_entry(cpufreq_ptr, &cooling_cpufreq_list, node) {
+
+               state = cpufreq_ptr->cpufreq_state;
+               if (state == 0 || cpufreq_ptr == cpufreq_device)
+                       continue;
+
+               if (!cpumask_equal(&cpufreq_ptr->allowed_cpus,
+                               &cpufreq_device->allowed_cpus))
+                       continue;
+
+               table_ptr = &(cpufreq_ptr->tab_ptr[state - 1]);
+               if (notify_table == NULL ||
+                               (table_ptr->freq_clip_max <
+                               notify_table->freq_clip_max))
+                       notify_table =  table_ptr;
+       }
+
+       cpufreq_device->cpufreq_state = cooling_state;
+
+       if (notify_table != NOTIFY_INVALID) {
+               event = CPUFREQ_COOLING_START;
+               maskPtr = notify_table->mask_val;
+       } else {
+               event = CPUFREQ_COOLING_STOP;
+       }
+
+       blocking_notifier_call_chain(&cputherm_state_notifier_list,
+                                               event, notify_table);
+
+       for_each_cpu(cpuid, maskPtr) {
+               if (is_cpufreq_valid(cpuid))
+                       cpufreq_update_policy(cpuid);
+       }
+
+       notify_table = NOTIFY_INVALID;
+
+       return 0;
+}
+
+/**
+ * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
+ * @nb:        struct notifier_block * with callback info.
+ * @event: value showing cpufreq event for which this function invoked.
+ * @data: callback-specific data
+ */
+static int cpufreq_thermal_notifier(struct notifier_block *nb,
+                                       unsigned long event, void *data)
+{
+       struct cpufreq_policy *policy = data;
+       unsigned long max_freq = 0;
+
+       if (event != CPUFREQ_ADJUST)
+               return 0;
+
+       if (notify_table != NOTIFY_INVALID) {
+               max_freq = notify_table->freq_clip_max;
+
+               if (!per_cpu(max_policy_freq, policy->cpu))
+                       per_cpu(max_policy_freq, policy->cpu) = policy->max;
+       } else {
+               if (per_cpu(max_policy_freq, policy->cpu)) {
+                       max_freq = per_cpu(max_policy_freq, policy->cpu);
+                       per_cpu(max_policy_freq, policy->cpu) = 0;
+               } else {
+                       max_freq = policy->max;
+               }
+       }
+
+       /* Never exceed user_policy.max*/
+       if (max_freq > policy->user_policy.max)
+               max_freq = policy->user_policy.max;
+
+       if (policy->max != max_freq)
+               cpufreq_verify_within_limits(policy, 0, max_freq);
+
+       return 0;
+}
+
+/*
+ * cpufreq cooling device callback functions are defined below
+ */
+
+/**
+ * cpufreq_get_max_state - callback function to get the max cooling state.
+ * @cdev: thermal cooling device pointer.
+ * @state: fill this variable with the max cooling state.
+ */
+static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       int ret = -EINVAL;
+       struct cpufreq_cooling_device *cpufreq_device;
+
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
+               if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
+                       *state = cpufreq_device->tab_size;
+                       ret = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&cooling_cpufreq_lock);
+       return ret;
+}
+
+/**
+ * cpufreq_get_cur_state - callback function to get the current cooling state.
+ * @cdev: thermal cooling device pointer.
+ * @state: fill this variable with the current cooling state.
+ */
+static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       int ret = -EINVAL;
+       struct cpufreq_cooling_device *cpufreq_device;
+
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
+               if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
+                       *state = cpufreq_device->cpufreq_state;
+                       ret = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&cooling_cpufreq_lock);
+       return ret;
+}
+
+/**
+ * cpufreq_set_cur_state - callback function to set the current cooling state.
+ * @cdev: thermal cooling device pointer.
+ * @state: set this variable to the current cooling state.
+ */
+static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long state)
+{
+       int ret = -EINVAL;
+       struct cpufreq_cooling_device *cpufreq_device;
+
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
+               if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
+                       ret = 0;
+                       break;
+               }
+       }
+       if (!ret)
+               ret = cpufreq_apply_cooling(cpufreq_device, state);
+
+       mutex_unlock(&cooling_cpufreq_lock);
+
+       return ret;
+}
+
+/*Bind cpufreq callbacks to thermal cooling device ops*/
+static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
+       .get_max_state = cpufreq_get_max_state,
+       .get_cur_state = cpufreq_get_cur_state,
+       .set_cur_state = cpufreq_set_cur_state,
+};
+
+/*Notifier for cpufreq policy change*/
+static struct notifier_block thermal_cpufreq_notifier_block = {
+       .notifier_call = cpufreq_thermal_notifier,
+};
+
+/**
+ * cpufreq_cooling_register - function to create cpufreq cooling device.
+ * @tab_ptr: table ptr containing the maximum value of frequency to be clipped
+ *     for each cooling state.
+ * @tab_size: count of entries in the above table.
+ *     happen.
+ */
+struct thermal_cooling_device *cpufreq_cooling_register(
+       struct freq_clip_table *tab_ptr, unsigned int tab_size)
+{
+       struct thermal_cooling_device *cool_dev;
+       struct cpufreq_cooling_device *cpufreq_dev = NULL;
+       struct freq_clip_table *clip_tab;
+       unsigned int cpufreq_dev_count = 0;
+       char dev_name[THERMAL_NAME_LENGTH];
+       int ret = 0, id = 0, i;
+
+       if (tab_ptr == NULL || tab_size == 0)
+               return ERR_PTR(-EINVAL);
+
+       list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node)
+               cpufreq_dev_count++;
+
+       cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
+                       GFP_KERNEL);
+       if (!cpufreq_dev)
+               return ERR_PTR(-ENOMEM);
+
+       /*Verify that all the entries of freq_clip_table are present*/
+       for (i = 0; i < tab_size; i++) {
+               clip_tab = ((struct freq_clip_table *)&tab_ptr[i]);
+               if (!clip_tab->freq_clip_max || !clip_tab->mask_val
+                                       || !clip_tab->temp_level) {
+                       kfree(cpufreq_dev);
+                       return ERR_PTR(-EINVAL);
+               }
+               /*
+                *Consolidate all the cpumask for all the individual entries
+                *of the trip table. This is useful in resetting all the
+                *clipped frequencies to the normal level for each cpufreq
+                *cooling device.
+                */
+               cpumask_or(&cpufreq_dev->allowed_cpus,
+                       &cpufreq_dev->allowed_cpus, clip_tab->mask_val);
+       }
+
+       cpufreq_dev->tab_ptr = tab_ptr;
+       cpufreq_dev->tab_size = tab_size;
+
+       ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
+       if (ret) {
+               kfree(cpufreq_dev);
+               return ERR_PTR(-EINVAL);
+       }
+
+       sprintf(dev_name, "thermal-cpufreq-%d", cpufreq_dev->id);
+
+       cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
+                                               &cpufreq_cooling_ops);
+       if (!cool_dev) {
+               release_idr(&cpufreq_idr, cpufreq_dev->id);
+               kfree(cpufreq_dev);
+               return ERR_PTR(-EINVAL);
+       }
+       cpufreq_dev->id = id;
+       cpufreq_dev->cool_dev = cool_dev;
+       mutex_lock(&cooling_cpufreq_lock);
+       list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list);
+
+       /*Register the notifier for first cpufreq cooling device*/
+       if (cpufreq_dev_count == 0)
+               cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
+                                               CPUFREQ_POLICY_NOTIFIER);
+
+       mutex_unlock(&cooling_cpufreq_lock);
+       return cool_dev;
+}
+EXPORT_SYMBOL(cpufreq_cooling_register);
+
+/**
+ * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
+ * @cdev: thermal cooling device pointer.
+ */
+void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
+{
+       struct cpufreq_cooling_device *cpufreq_dev = NULL;
+       unsigned int cpufreq_dev_count = 0;
+
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) {
+               if (cpufreq_dev && cpufreq_dev->cool_dev == cdev)
+                       break;
+               cpufreq_dev_count++;
+       }
+
+       if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) {
+               mutex_unlock(&cooling_cpufreq_lock);
+               return;
+       }
+
+       list_del(&cpufreq_dev->node);
+
+       /*Unregister the notifier for the last cpufreq cooling device*/
+       if (cpufreq_dev_count == 1) {
+               cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
+                                       CPUFREQ_POLICY_NOTIFIER);
+       }
+       mutex_unlock(&cooling_cpufreq_lock);
+       thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
+       release_idr(&cpufreq_idr, cpufreq_dev->id);
+       kfree(cpufreq_dev);
+}
+EXPORT_SYMBOL(cpufreq_cooling_unregister);
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644 (file)
index 0000000..e4e6759
--- /dev/null
@@ -0,0 +1,956 @@
+/*
+ * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_data/exynos_thermal.h>
+#include <linux/thermal.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#include <linux/of.h>
+
+#include <plat/cpu.h>
+
+/*Exynos generic registers*/
+#define EXYNOS_TMU_REG_TRIMINFO                0x0
+#define EXYNOS_TMU_REG_CONTROL         0x20
+#define EXYNOS_TMU_REG_STATUS          0x28
+#define EXYNOS_TMU_REG_CURRENT_TEMP    0x40
+#define EXYNOS_TMU_REG_INTEN           0x70
+#define EXYNOS_TMU_REG_INTSTAT         0x74
+#define EXYNOS_TMU_REG_INTCLEAR                0x78
+
+#define EXYNOS_TMU_TRIM_TEMP_MASK      0xff
+#define EXYNOS_TMU_GAIN_SHIFT          8
+#define EXYNOS_TMU_REF_VOLTAGE_SHIFT   24
+#define EXYNOS_TMU_CORE_ON             3
+#define EXYNOS_TMU_CORE_OFF            2
+#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET     50
+
+/*Exynos4 specific registers*/
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0    0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1    0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2    0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3    0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0     0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1     0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2     0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3     0x6C
+
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK   0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK   0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK   0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK   0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL       0x1111
+
+/*Exynos5 specific registers*/
+#define EXYNOS5_TMU_TRIMINFO_CON       0x14
+#define EXYNOS5_THD_TEMP_RISE          0x50
+#define EXYNOS5_THD_TEMP_FALL          0x54
+#define EXYNOS5_EMUL_CON               0x80
+
+#define EXYNOS5_TRIMINFO_RELOAD                0x1
+#define EXYNOS5_TMU_CLEAR_RISE_INT     0x111
+#define EXYNOS5_TMU_CLEAR_FALL_INT     (0x111 << 16)
+#define EXYNOS5_MUX_ADDR_VALUE         6
+#define EXYNOS5_MUX_ADDR_SHIFT         20
+#define EXYNOS5_TMU_TRIP_MODE_SHIFT    13
+
+#define EFUSE_MIN_VALUE 40
+#define EFUSE_MAX_VALUE 100
+
+/*In-kernel thermal framework related macros & definations*/
+#define SENSOR_NAME_LEN        16
+#define MAX_TRIP_COUNT 8
+#define MAX_COOLING_DEVICE 4
+
+#define ACTIVE_INTERVAL 500
+#define IDLE_INTERVAL 10000
+#define MCELSIUS       1000
+
+/* CPU Zone information */
+#define PANIC_ZONE      4
+#define WARN_ZONE       3
+#define MONITOR_ZONE    2
+#define SAFE_ZONE       1
+
+#define GET_ZONE(trip) (trip + 2)
+#define GET_TRIP(zone) (zone - 2)
+
+#define EXYNOS_ZONE_COUNT      3
+
+struct exynos_tmu_data {
+       struct exynos_tmu_platform_data *pdata;
+       struct resource *mem;
+       void __iomem *base;
+       int irq;
+       enum soc_type soc;
+       struct work_struct irq_work;
+       struct mutex lock;
+       struct clk *clk;
+       u8 temp_error1, temp_error2;
+};
+
+struct thermal_trip_point_conf {
+       int trip_val[MAX_TRIP_COUNT];
+       int trip_count;
+};
+
+struct thermal_cooling_conf {
+       struct freq_clip_table freq_data[MAX_TRIP_COUNT];
+       int freq_clip_count;
+};
+
+struct thermal_sensor_conf {
+       char name[SENSOR_NAME_LEN];
+       int (*read_temperature)(void *data);
+       struct thermal_trip_point_conf trip_data;
+       struct thermal_cooling_conf cooling_data;
+       void *private_data;
+};
+
+struct exynos_thermal_zone {
+       enum thermal_device_mode mode;
+       struct thermal_zone_device *therm_dev;
+       struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
+       unsigned int cool_dev_size;
+       struct platform_device *exynos4_dev;
+       struct thermal_sensor_conf *sensor_conf;
+};
+
+static struct exynos_thermal_zone *th_zone;
+static void exynos_unregister_thermal(void);
+static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
+
+/* Get mode callback functions for thermal zone */
+static int exynos_get_mode(struct thermal_zone_device *thermal,
+                       enum thermal_device_mode *mode)
+{
+       if (th_zone)
+               *mode = th_zone->mode;
+       return 0;
+}
+
+/* Set mode callback functions for thermal zone */
+static int exynos_set_mode(struct thermal_zone_device *thermal,
+                       enum thermal_device_mode mode)
+{
+       if (!th_zone->therm_dev) {
+               pr_notice("thermal zone not registered\n");
+               return 0;
+       }
+
+       mutex_lock(&th_zone->therm_dev->lock);
+
+       if (mode == THERMAL_DEVICE_ENABLED)
+               th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
+       else
+               th_zone->therm_dev->polling_delay = 0;
+
+       mutex_unlock(&th_zone->therm_dev->lock);
+
+       th_zone->mode = mode;
+       thermal_zone_device_update(th_zone->therm_dev);
+       pr_info("thermal polling set for duration=%d msec\n",
+                               th_zone->therm_dev->polling_delay);
+       return 0;
+}
+
+/*
+ * This function may be called from interrupt based temperature sensor
+ * when threshold is changed.
+ */
+static void exynos_report_trigger(void)
+{
+       unsigned int i;
+       char data[10];
+       char *envp[] = { data, NULL };
+
+       if (!th_zone || !th_zone->therm_dev)
+               return;
+
+       thermal_zone_device_update(th_zone->therm_dev);
+
+       mutex_lock(&th_zone->therm_dev->lock);
+       /* Find the level for which trip happened */
+       for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
+               if (th_zone->therm_dev->last_temperature <
+                       th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
+                       break;
+       }
+
+       if (th_zone->mode == THERMAL_DEVICE_ENABLED) {
+               if (i > 0)
+                       th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
+               else
+                       th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
+       }
+
+       snprintf(data, sizeof(data), "%u", i);
+       kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
+       mutex_unlock(&th_zone->therm_dev->lock);
+}
+
+/* Get trip type callback functions for thermal zone */
+static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
+                                enum thermal_trip_type *type)
+{
+       switch (GET_ZONE(trip)) {
+       case MONITOR_ZONE:
+       case WARN_ZONE:
+               *type = THERMAL_TRIP_ACTIVE;
+               break;
+       case PANIC_ZONE:
+               *type = THERMAL_TRIP_CRITICAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Get trip temperature callback functions for thermal zone */
+static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+                               unsigned long *temp)
+{
+       if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
+               return -EINVAL;
+
+       *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
+       /* convert the temperature into millicelsius */
+       *temp = *temp * MCELSIUS;
+
+       return 0;
+}
+
+/* Get critical temperature callback functions for thermal zone */
+static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
+                               unsigned long *temp)
+{
+       int ret;
+       /* Panic zone */
+       ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
+       return ret;
+}
+
+/* Bind callback functions for thermal zone */
+static int exynos_bind(struct thermal_zone_device *thermal,
+                       struct thermal_cooling_device *cdev)
+{
+       int ret = 0, i;
+
+       /* find the cooling device registered*/
+       for (i = 0; i < th_zone->cool_dev_size; i++)
+               if (cdev == th_zone->cool_dev[i])
+                       break;
+
+       /*No matching cooling device*/
+       if (i == th_zone->cool_dev_size)
+               return 0;
+
+       switch (GET_ZONE(i)) {
+       case MONITOR_ZONE:
+       case WARN_ZONE:
+               if (thermal_zone_bind_cooling_device(thermal, i, cdev)) {
+                       pr_err("error binding cooling dev inst 0\n");
+                       ret = -EINVAL;
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/* Unbind callback functions for thermal zone */
+static int exynos_unbind(struct thermal_zone_device *thermal,
+                       struct thermal_cooling_device *cdev)
+{
+       int ret = 0, i;
+
+       /* find the cooling device registered*/
+       for (i = 0; i < th_zone->cool_dev_size; i++)
+               if (cdev == th_zone->cool_dev[i])
+                       break;
+
+       /*No matching cooling device*/
+       if (i == th_zone->cool_dev_size)
+               return 0;
+
+       switch (GET_ZONE(i)) {
+       case MONITOR_ZONE:
+       case WARN_ZONE:
+               if (thermal_zone_unbind_cooling_device(thermal, i, cdev)) {
+                       pr_err("error unbinding cooling dev\n");
+                       ret = -EINVAL;
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/* Get temperature callback functions for thermal zone */
+static int exynos_get_temp(struct thermal_zone_device *thermal,
+                       unsigned long *temp)
+{
+       void *data;
+
+       if (!th_zone->sensor_conf) {
+               pr_info("Temperature sensor not initialised\n");
+               return -EINVAL;
+       }
+       data = th_zone->sensor_conf->private_data;
+       *temp = th_zone->sensor_conf->read_temperature(data);
+       /* convert the temperature into millicelsius */
+       *temp = *temp * MCELSIUS;
+       return 0;
+}
+
+/* Operation callback functions for thermal zone */
+static struct thermal_zone_device_ops const exynos_dev_ops = {
+       .bind = exynos_bind,
+       .unbind = exynos_unbind,
+       .get_temp = exynos_get_temp,
+       .get_mode = exynos_get_mode,
+       .set_mode = exynos_set_mode,
+       .get_trip_type = exynos_get_trip_type,
+       .get_trip_temp = exynos_get_trip_temp,
+       .get_crit_temp = exynos_get_crit_temp,
+};
+
+/* Register with the in-kernel thermal management */
+static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+       int ret, count, tab_size;
+       struct freq_clip_table *tab_ptr, *clip_data;
+
+       if (!sensor_conf || !sensor_conf->read_temperature) {
+               pr_err("Temperature sensor not initialised\n");
+               return -EINVAL;
+       }
+
+       th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
+       if (!th_zone)
+               return -ENOMEM;
+
+       th_zone->sensor_conf = sensor_conf;
+
+       tab_ptr = (struct freq_clip_table *)sensor_conf->cooling_data.freq_data;
+       tab_size = sensor_conf->cooling_data.freq_clip_count;
+
+       /* Register the cpufreq cooling device */
+       for (count = 0; count < tab_size; count++) {
+               clip_data = (struct freq_clip_table *)&(tab_ptr[count]);
+               clip_data->mask_val = cpumask_of(0);
+               th_zone->cool_dev[count] = cpufreq_cooling_register(
+                                               clip_data, 1);
+               if (IS_ERR(th_zone->cool_dev[count])) {
+                       pr_err("Failed to register cpufreq cooling device\n");
+                       ret = -EINVAL;
+                       th_zone->cool_dev_size = count;
+                       goto err_unregister;
+               }
+       }
+       th_zone->cool_dev_size = count;
+
+       th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+                       EXYNOS_ZONE_COUNT, NULL, &exynos_dev_ops, 0, 0, 0,
+                       IDLE_INTERVAL);
+
+       if (IS_ERR(th_zone->therm_dev)) {
+               pr_err("Failed to register thermal zone device\n");
+               ret = -EINVAL;
+               goto err_unregister;
+       }
+       th_zone->mode = THERMAL_DEVICE_ENABLED;
+
+       pr_info("Exynos: Kernel Thermal management registered\n");
+
+       return 0;
+
+err_unregister:
+       exynos_unregister_thermal();
+       return ret;
+}
+
+/* Un-Register with the in-kernel thermal management */
+static void exynos_unregister_thermal(void)
+{
+       int i;
+
+       for (i = 0; i < th_zone->cool_dev_size; i++) {
+               if (th_zone && th_zone->cool_dev[i])
+                       cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+       }
+
+       if (th_zone && th_zone->therm_dev)
+               thermal_zone_device_unregister(th_zone->therm_dev);
+
+       kfree(th_zone);
+
+       pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+
+/*
+ * TMU treats temperature as a mapped temperature code.
+ * The temperature is converted differently depending on the calibration type.
+ */
+static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
+{
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       int temp_code;
+
+       if (data->soc == SOC_ARCH_EXYNOS4)
+               /* temp should range between 25 and 125 */
+               if (temp < 25 || temp > 125) {
+                       temp_code = -EINVAL;
+                       goto out;
+               }
+
+       switch (pdata->cal_type) {
+       case TYPE_TWO_POINT_TRIMMING:
+               temp_code = (temp - 25) *
+                   (data->temp_error2 - data->temp_error1) /
+                   (85 - 25) + data->temp_error1;
+               break;
+       case TYPE_ONE_POINT_TRIMMING:
+               temp_code = temp + data->temp_error1 - 25;
+               break;
+       default:
+               temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
+               break;
+       }
+out:
+       return temp_code;
+}
+
+/*
+ * Calculate a temperature value from a temperature code.
+ * The unit of the temperature is degree Celsius.
+ */
+static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
+{
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       int temp;
+
+       if (data->soc == SOC_ARCH_EXYNOS4)
+               /* temp_code should range between 75 and 175 */
+               if (temp_code < 75 || temp_code > 175) {
+                       temp = -ENODATA;
+                       goto out;
+               }
+
+       switch (pdata->cal_type) {
+       case TYPE_TWO_POINT_TRIMMING:
+               temp = (temp_code - data->temp_error1) * (85 - 25) /
+                   (data->temp_error2 - data->temp_error1) + 25;
+               break;
+       case TYPE_ONE_POINT_TRIMMING:
+               temp = temp_code - data->temp_error1 + 25;
+               break;
+       default:
+               temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
+               break;
+       }
+out:
+       return temp;
+}
+
+static int exynos_tmu_initialize(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       unsigned int status, trim_info, rising_threshold;
+       int ret = 0, threshold_code;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+
+       status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+       if (!status) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (data->soc == SOC_ARCH_EXYNOS5) {
+               __raw_writel(EXYNOS5_TRIMINFO_RELOAD,
+                               data->base + EXYNOS5_TMU_TRIMINFO_CON);
+       }
+       /* Save trimming info in order to perform calibration */
+       trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+       data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
+       data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
+
+       if ((EFUSE_MIN_VALUE > data->temp_error1) ||
+                       (data->temp_error1 > EFUSE_MAX_VALUE) ||
+                       (data->temp_error2 != 0))
+               data->temp_error1 = pdata->efuse_value;
+
+       if (data->soc == SOC_ARCH_EXYNOS4) {
+               /* Write temperature code for threshold */
+               threshold_code = temp_to_code(data, pdata->threshold);
+               if (threshold_code < 0) {
+                       ret = threshold_code;
+                       goto out;
+               }
+               writeb(threshold_code,
+                       data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+               writeb(pdata->trigger_levels[0],
+                       data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+               writeb(pdata->trigger_levels[1],
+                       data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+               writeb(pdata->trigger_levels[2],
+                       data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+               writeb(pdata->trigger_levels[3],
+                       data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+               writel(EXYNOS4_TMU_INTCLEAR_VAL,
+                       data->base + EXYNOS_TMU_REG_INTCLEAR);
+       } else if (data->soc == SOC_ARCH_EXYNOS5) {
+               /* Write temperature code for threshold */
+               threshold_code = temp_to_code(data, pdata->trigger_levels[0]);
+               if (threshold_code < 0) {
+                       ret = threshold_code;
+                       goto out;
+               }
+               rising_threshold = threshold_code;
+               threshold_code = temp_to_code(data, pdata->trigger_levels[1]);
+               if (threshold_code < 0) {
+                       ret = threshold_code;
+                       goto out;
+               }
+               rising_threshold |= (threshold_code << 8);
+               threshold_code = temp_to_code(data, pdata->trigger_levels[2]);
+               if (threshold_code < 0) {
+                       ret = threshold_code;
+                       goto out;
+               }
+               rising_threshold |= (threshold_code << 16);
+
+               writel(rising_threshold,
+                               data->base + EXYNOS5_THD_TEMP_RISE);
+               writel(0, data->base + EXYNOS5_THD_TEMP_FALL);
+
+               writel(EXYNOS5_TMU_CLEAR_RISE_INT|EXYNOS5_TMU_CLEAR_FALL_INT,
+                               data->base + EXYNOS_TMU_REG_INTCLEAR);
+       }
+out:
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+
+       return ret;
+}
+
+static void exynos_tmu_control(struct platform_device *pdev, bool on)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       struct exynos_tmu_platform_data *pdata = data->pdata;
+       unsigned int con, interrupt_en;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+
+       con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
+               pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
+
+       if (data->soc == SOC_ARCH_EXYNOS5) {
+               con |= pdata->noise_cancel_mode << EXYNOS5_TMU_TRIP_MODE_SHIFT;
+               con |= (EXYNOS5_MUX_ADDR_VALUE << EXYNOS5_MUX_ADDR_SHIFT);
+       }
+
+       if (on) {
+               con |= EXYNOS_TMU_CORE_ON;
+               interrupt_en = pdata->trigger_level3_en << 12 |
+                       pdata->trigger_level2_en << 8 |
+                       pdata->trigger_level1_en << 4 |
+                       pdata->trigger_level0_en;
+       } else {
+               con |= EXYNOS_TMU_CORE_OFF;
+               interrupt_en = 0; /* Disable all interrupts */
+       }
+       writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
+       writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+}
+
+static int exynos_tmu_read(struct exynos_tmu_data *data)
+{
+       u8 temp_code;
+       int temp;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+
+       temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
+       temp = code_to_temp(data, temp_code);
+
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+
+       return temp;
+}
+
+static void exynos_tmu_work(struct work_struct *work)
+{
+       struct exynos_tmu_data *data = container_of(work,
+                       struct exynos_tmu_data, irq_work);
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+
+
+       if (data->soc == SOC_ARCH_EXYNOS5)
+               writel(EXYNOS5_TMU_CLEAR_RISE_INT,
+                               data->base + EXYNOS_TMU_REG_INTCLEAR);
+       else
+               writel(EXYNOS4_TMU_INTCLEAR_VAL,
+                               data->base + EXYNOS_TMU_REG_INTCLEAR);
+
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+       exynos_report_trigger();
+       enable_irq(data->irq);
+}
+
+static irqreturn_t exynos_tmu_irq(int irq, void *id)
+{
+       struct exynos_tmu_data *data = id;
+
+       disable_irq_nosync(irq);
+       schedule_work(&data->irq_work);
+
+       return IRQ_HANDLED;
+}
+static struct thermal_sensor_conf exynos_sensor_conf = {
+       .name                   = "exynos-therm",
+       .read_temperature       = (int (*)(void *))exynos_tmu_read,
+};
+
+#if defined(CONFIG_CPU_EXYNOS4210)
+static struct exynos_tmu_platform_data const exynos4_default_tmu_data = {
+       .threshold = 80,
+       .trigger_levels[0] = 5,
+       .trigger_levels[1] = 20,
+       .trigger_levels[2] = 30,
+       .trigger_level0_en = 1,
+       .trigger_level1_en = 1,
+       .trigger_level2_en = 1,
+       .trigger_level3_en = 0,
+       .gain = 15,
+       .reference_voltage = 7,
+       .cal_type = TYPE_ONE_POINT_TRIMMING,
+       .freq_tab[0] = {
+               .freq_clip_max = 800 * 1000,
+               .temp_level = 85,
+       },
+       .freq_tab[1] = {
+               .freq_clip_max = 200 * 1000,
+               .temp_level = 100,
+       },
+       .freq_tab_count = 2,
+       .type = SOC_ARCH_EXYNOS4,
+};
+#define EXYNOS4_TMU_DRV_DATA (&exynos4_default_tmu_data)
+#else
+#define EXYNOS4_TMU_DRV_DATA (NULL)
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250)
+static struct exynos_tmu_platform_data const exynos5_default_tmu_data = {
+       .trigger_levels[0] = 85,
+       .trigger_levels[1] = 103,
+       .trigger_levels[2] = 110,
+       .trigger_level0_en = 1,
+       .trigger_level1_en = 1,
+       .trigger_level2_en = 1,
+       .trigger_level3_en = 0,
+       .gain = 8,
+       .reference_voltage = 16,
+       .noise_cancel_mode = 4,
+       .cal_type = TYPE_ONE_POINT_TRIMMING,
+       .efuse_value = 55,
+       .freq_tab[0] = {
+               .freq_clip_max = 800 * 1000,
+               .temp_level = 85,
+       },
+       .freq_tab[1] = {
+               .freq_clip_max = 200 * 1000,
+               .temp_level = 103,
+       },
+       .freq_tab_count = 2,
+       .type = SOC_ARCH_EXYNOS5,
+};
+#define EXYNOS5_TMU_DRV_DATA (&exynos5_default_tmu_data)
+#else
+#define EXYNOS5_TMU_DRV_DATA (NULL)
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_tmu_match[] = {
+       {
+               .compatible = "samsung,exynos4-tmu",
+               .data = (void *)EXYNOS4_TMU_DRV_DATA,
+       },
+       {
+               .compatible = "samsung,exynos5-tmu",
+               .data = (void *)EXYNOS5_TMU_DRV_DATA,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_tmu_match);
+#else
+#define  exynos_tmu_match NULL
+#endif
+
+static struct platform_device_id exynos_tmu_driver_ids[] = {
+       {
+               .name           = "exynos4-tmu",
+               .driver_data    = (kernel_ulong_t)EXYNOS4_TMU_DRV_DATA,
+       },
+       {
+               .name           = "exynos5-tmu",
+               .driver_data    = (kernel_ulong_t)EXYNOS5_TMU_DRV_DATA,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(platform, exynos4_tmu_driver_ids);
+
+static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
+                       struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
+               if (!match)
+                       return NULL;
+               return (struct exynos_tmu_platform_data *) match->data;
+       }
+#endif
+       return (struct exynos_tmu_platform_data *)
+                       platform_get_device_id(pdev)->driver_data;
+}
+static int __devinit exynos_tmu_probe(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data;
+       struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
+       int ret, i;
+
+       if (!pdata)
+               pdata = exynos_get_driver_data(pdev);
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform init data supplied.\n");
+               return -ENODEV;
+       }
+       data = kzalloc(sizeof(struct exynos_tmu_data), GFP_KERNEL);
+       if (!data) {
+               dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+               return -ENOMEM;
+       }
+
+       data->irq = platform_get_irq(pdev, 0);
+       if (data->irq < 0) {
+               ret = data->irq;
+               dev_err(&pdev->dev, "Failed to get platform irq\n");
+               goto err_free;
+       }
+
+       INIT_WORK(&data->irq_work, exynos_tmu_work);
+
+       data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!data->mem) {
+               ret = -ENOENT;
+               dev_err(&pdev->dev, "Failed to get platform resource\n");
+               goto err_free;
+       }
+
+       data->mem = request_mem_region(data->mem->start,
+                       resource_size(data->mem), pdev->name);
+       if (!data->mem) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "Failed to request memory region\n");
+               goto err_free;
+       }
+
+       data->base = ioremap(data->mem->start, resource_size(data->mem));
+       if (!data->base) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "Failed to ioremap memory\n");
+               goto err_mem_region;
+       }
+
+       ret = request_irq(data->irq, exynos_tmu_irq,
+               IRQF_TRIGGER_RISING, "exynos-tmu", data);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+               goto err_io_remap;
+       }
+
+       data->clk = clk_get(NULL, "tmu_apbif");
+       if (IS_ERR(data->clk)) {
+               ret = PTR_ERR(data->clk);
+               dev_err(&pdev->dev, "Failed to get clock\n");
+               goto err_irq;
+       }
+
+       if (pdata->type == SOC_ARCH_EXYNOS5 ||
+                               pdata->type == SOC_ARCH_EXYNOS4)
+               data->soc = pdata->type;
+       else {
+               ret = -EINVAL;
+               dev_err(&pdev->dev, "Platform not supported\n");
+               goto err_clk;
+       }
+
+       data->pdata = pdata;
+       platform_set_drvdata(pdev, data);
+       mutex_init(&data->lock);
+
+       ret = exynos_tmu_initialize(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to initialize TMU\n");
+               goto err_clk;
+       }
+
+       exynos_tmu_control(pdev, true);
+
+       /*Register the sensor with thermal management interface*/
+       (&exynos_sensor_conf)->private_data = data;
+       exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
+                       pdata->trigger_level1_en + pdata->trigger_level2_en +
+                       pdata->trigger_level3_en;
+
+       for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
+               exynos_sensor_conf.trip_data.trip_val[i] =
+                       pdata->threshold + pdata->trigger_levels[i];
+
+       exynos_sensor_conf.cooling_data.freq_clip_count =
+                                               pdata->freq_tab_count;
+       for (i = 0; i < pdata->freq_tab_count; i++) {
+               exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
+                                       pdata->freq_tab[i].freq_clip_max;
+               exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
+                                       pdata->freq_tab[i].temp_level;
+       }
+
+       ret = exynos_register_thermal(&exynos_sensor_conf);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register thermal interface\n");
+               goto err_clk;
+       }
+       return 0;
+err_clk:
+       platform_set_drvdata(pdev, NULL);
+       clk_put(data->clk);
+err_irq:
+       free_irq(data->irq, data);
+err_io_remap:
+       iounmap(data->base);
+err_mem_region:
+       release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+       kfree(data);
+
+       return ret;
+}
+
+static int __devexit exynos_tmu_remove(struct platform_device *pdev)
+{
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+
+       exynos_tmu_control(pdev, false);
+
+       exynos_unregister_thermal();
+
+       clk_put(data->clk);
+
+       free_irq(data->irq, data);
+
+       iounmap(data->base);
+       release_mem_region(data->mem->start, resource_size(data->mem));
+
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       exynos_tmu_control(pdev, false);
+
+       return 0;
+}
+
+static int exynos_tmu_resume(struct platform_device *pdev)
+{
+       exynos_tmu_initialize(pdev);
+       exynos_tmu_control(pdev, true);
+
+       return 0;
+}
+#else
+#define exynos_tmu_suspend NULL
+#define exynos_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos_tmu_driver = {
+       .driver = {
+               .name   = "exynos-tmu",
+               .owner  = THIS_MODULE,
+               .of_match_table = exynos_tmu_match,
+       },
+       .probe = exynos_tmu_probe,
+       .remove = __devexit_p(exynos_tmu_remove),
+       .suspend = exynos_tmu_suspend,
+       .resume = exynos_tmu_resume,
+       .id_table = exynos_tmu_driver_ids,
+};
+
+module_platform_driver(exynos_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos-tmu");
index d8b0aee..88c27cf 100644 (file)
@@ -988,6 +988,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port,
        wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
        wr_regl(port, S3C2410_UFCON, cfg->ufcon);
 
+       wr_regl(port, S3C64XX_UINTM, 0xf);
+       wr_regl(port, S3C64XX_UINTP, 0xf);
+
        /* some delay is required after fifo reset */
        udelay(1);
 }
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
new file mode 100644 (file)
index 0000000..ed6c096
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  linux/include/linux/cpu_cooling.h
+ *
+ *  Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
+ *  Copyright (C) 2012  Amit Daniel <amit.kachhap@linaro.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *  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 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __CPU_COOLING_H__
+#define __CPU_COOLING_H__
+
+#include <linux/thermal.h>
+
+#define CPUFREQ_COOLING_START          0
+#define CPUFREQ_COOLING_STOP           1
+
+/**
+ * struct freq_clip_table
+ * @freq_clip_max: maximum frequency allowed for this cooling state.
+ * @temp_level: Temperature level at which the temperature clipping will
+ *     happen.
+ * @mask_val: cpumask of the allowed cpu's where the clipping will take place.
+ *
+ * This structure is required to be filled and passed to the
+ * cpufreq_cooling_unregister function.
+ */
+struct freq_clip_table {
+       unsigned int freq_clip_max;
+       unsigned int temp_level;
+       const struct cpumask *mask_val;
+};
+
+/**
+ * cputherm_register_notifier - Register a notifier with cpu cooling interface.
+ * @nb:        struct notifier_block * with callback info.
+ * @list: integer value for which notification is needed. possible values are
+ *     CPUFREQ_COOLING_TYPE and CPUHOTPLUG_COOLING_TYPE.
+ *
+ * This exported function registers a driver with cpu cooling layer. The driver
+ * will be notified when any cpu cooling action is called.
+ */
+int cputherm_register_notifier(struct notifier_block *nb, unsigned int list);
+
+/**
+ * cputherm_unregister_notifier - Un-register a notifier.
+ * @nb:        struct notifier_block * with callback info.
+ * @list: integer value for which notification is needed. values possible are
+ *     CPUFREQ_COOLING_TYPE.
+ *
+ * This exported function un-registers a driver with cpu cooling layer.
+ */
+int cputherm_unregister_notifier(struct notifier_block *nb, unsigned int list);
+
+#ifdef CONFIG_CPU_FREQ
+/**
+ * cpufreq_cooling_register - function to create cpufreq cooling device.
+ * @tab_ptr: table ptr containing the maximum value of frequency to be clipped
+ *     for each cooling state.
+ * @tab_size: count of entries in the above table.
+ * @mask_val: cpumask containing the allowed cpu's where frequency clipping can
+ *     happen.
+ */
+struct thermal_cooling_device *cpufreq_cooling_register(
+       struct freq_clip_table *tab_ptr, unsigned int tab_size);
+
+/**
+ * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
+ * @cdev: thermal cooling device pointer.
+ */
+void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
+#else /*!CONFIG_CPU_FREQ*/
+static inline struct thermal_cooling_device *cpufreq_cooling_register(
+       struct freq_clip_table *tab_ptr, unsigned int tab_size);
+{
+       return NULL;
+}
+static inline void cpufreq_cooling_unregister(
+               struct thermal_cooling_device *cdev)
+{
+       return;
+}
+#endif /*CONFIG_CPU_FREQ*/
+
+#endif /* __CPU_COOLING_H__ */
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h
new file mode 100644 (file)
index 0000000..560d0af
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * max77686.h - Voltage regulator driver for the Maxim 77686
+ *
+ *  Copyright (C) 2011 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __LINUX_MFD_MAX77686_PRIV_H
+#define __LINUX_MFD_MAX77686_PRIV_H
+
+#include <linux/i2c.h>
+
+#define MAX77686_REG_INVALID           (0xff)
+#define RAMP_MASK                      0xC0
+
+enum max77686_pmic_reg {
+       MAX77686_REG_DEVICE_ID          = 0x00,
+       MAX77686_REG_INTSRC             = 0x01,
+       MAX77686_REG_INT1               = 0x02,
+       MAX77686_REG_INT2               = 0x03,
+
+       MAX77686_REG_INT1MSK            = 0x04,
+       MAX77686_REG_INT2MSK            = 0x05,
+
+       MAX77686_REG_STATUS1            = 0x06,
+       MAX77686_REG_STATUS2            = 0x07,
+
+       MAX77686_REG_PWRON              = 0x08,
+       MAX77686_REG_ONOFF_DELAY        = 0x09,
+       MAX77686_REG_MRSTB              = 0x0A,
+       /* Reserved: 0x0B-0x0F */
+
+       MAX77686_REG_BUCK1CTRL          = 0x10,
+       MAX77686_REG_BUCK1OUT           = 0x11,
+       MAX77686_REG_BUCK2CTRL1         = 0x12,
+       MAX77686_REG_BUCK234FREQ        = 0x13,
+       MAX77686_REG_BUCK2DVS1          = 0x14,
+       MAX77686_REG_BUCK2DVS2          = 0x15,
+       MAX77686_REG_BUCK2DVS3          = 0x16,
+       MAX77686_REG_BUCK2DVS4          = 0x17,
+       MAX77686_REG_BUCK2DVS5          = 0x18,
+       MAX77686_REG_BUCK2DVS6          = 0x19,
+       MAX77686_REG_BUCK2DVS7          = 0x1A,
+       MAX77686_REG_BUCK2DVS8          = 0x1B,
+       MAX77686_REG_BUCK3CTRL1         = 0x1C,
+       /* Reserved: 0x1D */
+       MAX77686_REG_BUCK3DVS1          = 0x1E,
+       MAX77686_REG_BUCK3DVS2          = 0x1F,
+       MAX77686_REG_BUCK3DVS3          = 0x20,
+       MAX77686_REG_BUCK3DVS4          = 0x21,
+       MAX77686_REG_BUCK3DVS5          = 0x22,
+       MAX77686_REG_BUCK3DVS6          = 0x23,
+       MAX77686_REG_BUCK3DVS7          = 0x24,
+       MAX77686_REG_BUCK3DVS8          = 0x25,
+       MAX77686_REG_BUCK4CTRL1         = 0x26,
+       /* Reserved: 0x27 */
+       MAX77686_REG_BUCK4DVS1          = 0x28,
+       MAX77686_REG_BUCK4DVS2          = 0x29,
+       MAX77686_REG_BUCK4DVS3          = 0x2A,
+       MAX77686_REG_BUCK4DVS4          = 0x2B,
+       MAX77686_REG_BUCK4DVS5          = 0x2C,
+       MAX77686_REG_BUCK4DVS6          = 0x2D,
+       MAX77686_REG_BUCK4DVS7          = 0x2E,
+       MAX77686_REG_BUCK4DVS8          = 0x2F,
+       MAX77686_REG_BUCK5CTRL          = 0x30,
+       MAX77686_REG_BUCK5OUT           = 0x31,
+       MAX77686_REG_BUCK6CTRL          = 0x32,
+       MAX77686_REG_BUCK6OUT           = 0x33,
+       MAX77686_REG_BUCK7CTRL          = 0x34,
+       MAX77686_REG_BUCK7OUT           = 0x35,
+       MAX77686_REG_BUCK8CTRL          = 0x36,
+       MAX77686_REG_BUCK8OUT           = 0x37,
+       MAX77686_REG_BUCK9CTRL          = 0x38,
+       MAX77686_REG_BUCK9OUT           = 0x39,
+       /* Reserved: 0x3A-0x3F */
+
+       MAX77686_REG_LDO1CTRL1          = 0x40,
+       MAX77686_REG_LDO2CTRL1          = 0x41,
+       MAX77686_REG_LDO3CTRL1          = 0x42,
+       MAX77686_REG_LDO4CTRL1          = 0x43,
+       MAX77686_REG_LDO5CTRL1          = 0x44,
+       MAX77686_REG_LDO6CTRL1          = 0x45,
+       MAX77686_REG_LDO7CTRL1          = 0x46,
+       MAX77686_REG_LDO8CTRL1          = 0x47,
+       MAX77686_REG_LDO9CTRL1          = 0x48,
+       MAX77686_REG_LDO10CTRL1         = 0x49,
+       MAX77686_REG_LDO11CTRL1         = 0x4A,
+       MAX77686_REG_LDO12CTRL1         = 0x4B,
+       MAX77686_REG_LDO13CTRL1         = 0x4C,
+       MAX77686_REG_LDO14CTRL1         = 0x4D,
+       MAX77686_REG_LDO15CTRL1         = 0x4E,
+       MAX77686_REG_LDO16CTRL1         = 0x4F,
+       MAX77686_REG_LDO17CTRL1         = 0x50,
+       MAX77686_REG_LDO18CTRL1         = 0x51,
+       MAX77686_REG_LDO19CTRL1         = 0x52,
+       MAX77686_REG_LDO20CTRL1         = 0x53,
+       MAX77686_REG_LDO21CTRL1         = 0x54,
+       MAX77686_REG_LDO22CTRL1         = 0x55,
+       MAX77686_REG_LDO23CTRL1         = 0x56,
+       MAX77686_REG_LDO24CTRL1         = 0x57,
+       MAX77686_REG_LDO25CTRL1         = 0x58,
+       MAX77686_REG_LDO26CTRL1         = 0x59,
+       /* Reserved: 0x5A-0x5F */
+       MAX77686_REG_LDO1CTRL2          = 0x60,
+       MAX77686_REG_LDO2CTRL2          = 0x61,
+       MAX77686_REG_LDO3CTRL2          = 0x62,
+       MAX77686_REG_LDO4CTRL2          = 0x63,
+       MAX77686_REG_LDO5CTRL2          = 0x64,
+       MAX77686_REG_LDO6CTRL2          = 0x65,
+       MAX77686_REG_LDO7CTRL2          = 0x66,
+       MAX77686_REG_LDO8CTRL2          = 0x67,
+       MAX77686_REG_LDO9CTRL2          = 0x68,
+       MAX77686_REG_LDO10CTRL2         = 0x69,
+       MAX77686_REG_LDO11CTRL2         = 0x6A,
+       MAX77686_REG_LDO12CTRL2         = 0x6B,
+       MAX77686_REG_LDO13CTRL2         = 0x6C,
+       MAX77686_REG_LDO14CTRL2         = 0x6D,
+       MAX77686_REG_LDO15CTRL2         = 0x6E,
+       MAX77686_REG_LDO16CTRL2         = 0x6F,
+       MAX77686_REG_LDO17CTRL2         = 0x70,
+       MAX77686_REG_LDO18CTRL2         = 0x71,
+       MAX77686_REG_LDO19CTRL2         = 0x72,
+       MAX77686_REG_LDO20CTRL2         = 0x73,
+       MAX77686_REG_LDO21CTRL2         = 0x74,
+       MAX77686_REG_LDO22CTRL2         = 0x75,
+       MAX77686_REG_LDO23CTRL2         = 0x76,
+       MAX77686_REG_LDO24CTRL2         = 0x77,
+       MAX77686_REG_LDO25CTRL2         = 0x78,
+       MAX77686_REG_LDO26CTRL2         = 0x79,
+       /* Reserved: 0x7A-0x7D */
+
+       MAX77686_REG_BBAT_CHG           = 0x7E,
+       MAX77686_REG_32KHZ_             = 0x7F,
+
+       MAX77686_REG_PMIC_END           = 0x80,
+};
+
+enum max77686_rtc_reg {
+       MAX77686_RTC_INT                = 0x00,
+       MAX77686_RTC_INTM               = 0x01,
+       MAX77686_RTC_CONTROLM           = 0x02,
+       MAX77686_RTC_CONTROL            = 0x03,
+       MAX77686_RTC_UPDATE0            = 0x04,
+       /* Reserved: 0x5 */
+       MAX77686_WTSR_SMPL_CNTL         = 0x06,
+       MAX77686_RTC_SEC                = 0x07,
+       MAX77686_RTC_MIN                = 0x08,
+       MAX77686_RTC_HOUR               = 0x09,
+       MAX77686_RTC_WEEKDAY            = 0x0A,
+       MAX77686_RTC_MONTH              = 0x0B,
+       MAX77686_RTC_YEAR               = 0x0C,
+       MAX77686_RTC_DATE               = 0x0D,
+       MAX77686_ALARM1_SEC             = 0x0E,
+       MAX77686_ALARM1_MIN             = 0x0F,
+       MAX77686_ALARM1_HOUR            = 0x10,
+       MAX77686_ALARM1_WEEKDAY         = 0x11,
+       MAX77686_ALARM1_MONTH           = 0x12,
+       MAX77686_ALARM1_YEAR            = 0x13,
+       MAX77686_ALARM1_DATE            = 0x14,
+       MAX77686_ALARM2_SEC             = 0x15,
+       MAX77686_ALARM2_MIN             = 0x16,
+       MAX77686_ALARM2_HOUR            = 0x17,
+       MAX77686_ALARM2_WEEKDAY         = 0x18,
+       MAX77686_ALARM2_MONTH           = 0x19,
+       MAX77686_ALARM2_YEAR            = 0x1A,
+       MAX77686_ALARM2_DATE            = 0x1B,
+};
+
+#define MAX77686_IRQSRC_PMIC           (0)
+#define MAX77686_IRQSRC_RTC            (1 << 0)
+
+enum max77686_irq_source {
+       PMIC_INT1 = 0,
+       PMIC_INT2,
+       RTC_INT,
+
+       MAX77686_IRQ_GROUP_NR,
+};
+
+enum max77686_irq {
+       MAX77686_PMICIRQ_PWRONF,
+       MAX77686_PMICIRQ_PWRONR,
+       MAX77686_PMICIRQ_JIGONBF,
+       MAX77686_PMICIRQ_JIGONBR,
+       MAX77686_PMICIRQ_ACOKBF,
+       MAX77686_PMICIRQ_ACOKBR,
+       MAX77686_PMICIRQ_ONKEY1S,
+       MAX77686_PMICIRQ_MRSTB,
+
+       MAX77686_PMICIRQ_140C,
+       MAX77686_PMICIRQ_120C,
+
+       MAX77686_RTCIRQ_RTC60S,
+       MAX77686_RTCIRQ_RTCA1,
+       MAX77686_RTCIRQ_RTCA2,
+       MAX77686_RTCIRQ_SMPL,
+       MAX77686_RTCIRQ_RTC1S,
+       MAX77686_RTCIRQ_WTSR,
+
+       MAX77686_IRQ_NR,
+};
+
+struct max77686_dev {
+       struct device *dev;
+       struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
+       struct i2c_client *rtc; /* slave addr 0x0c */
+       struct mutex iolock;
+       int type;
+       int irq;
+       bool wakeup;
+       struct mutex irqlock;
+       int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
+       int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
+       struct irq_domain *irq_domain;
+       struct max77686_platform_data *pdata;
+};
+
+enum max77686_types {
+       TYPE_MAX77686,
+};
+
+extern int max77686_irq_init(struct max77686_dev *max77686);
+extern void max77686_irq_exit(struct max77686_dev *max77686);
+extern int max77686_irq_resume(struct max77686_dev *max77686);
+
+extern int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
+extern int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count,
+                               u8 *buf);
+extern int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
+extern int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count,
+                               u8 *buf);
+extern int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask);
+
+#endif /*  __LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
new file mode 100644 (file)
index 0000000..b959974
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * max77686.h - Driver for the Maxim 77686
+ *
+ *  Copyright (C) 2011 Samsung Electrnoics
+ *  Chiwoong Byun <woong.byun@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77686 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77686_H
+#define __LINUX_MFD_MAX77686_H
+
+#include <linux/regulator/consumer.h>
+
+/* MAX77686 regulator IDs */
+enum max77686_regulators {
+       MAX77686_LDO1 = 0,
+       MAX77686_LDO2,
+       MAX77686_LDO3,
+       MAX77686_LDO4,
+       MAX77686_LDO5,
+       MAX77686_LDO6,
+       MAX77686_LDO7,
+       MAX77686_LDO8,
+       MAX77686_LDO9,
+       MAX77686_LDO10,
+       MAX77686_LDO11,
+       MAX77686_LDO12,
+       MAX77686_LDO13,
+       MAX77686_LDO14,
+       MAX77686_LDO15,
+       MAX77686_LDO16,
+       MAX77686_LDO17,
+       MAX77686_LDO18,
+       MAX77686_LDO19,
+       MAX77686_LDO20,
+       MAX77686_LDO21,
+       MAX77686_LDO22,
+       MAX77686_LDO23,
+       MAX77686_LDO24,
+       MAX77686_LDO25,
+       MAX77686_LDO26,
+       MAX77686_BUCK1,
+       MAX77686_BUCK2,
+       MAX77686_BUCK3,
+       MAX77686_BUCK4,
+       MAX77686_BUCK5,
+       MAX77686_BUCK6,
+       MAX77686_BUCK7,
+       MAX77686_BUCK8,
+       MAX77686_BUCK9,
+       MAX77686_EN32KHZ_AP,
+       MAX77686_EN32KHZ_CP,
+       MAX77686_P32KH,
+
+       MAX77686_REG_MAX,
+};
+
+enum max77686_ramp_rate {
+       MAX77686_RAMP_RATE_13MV = 0,
+       MAX77686_RAMP_RATE_27MV,        /* default */
+       MAX77686_RAMP_RATE_55MV,
+       MAX77686_RAMP_RATE_100MV,
+};
+
+struct max77686_regulator_data {
+       int id;
+       struct regulator_init_data *initdata;
+       struct device_node *reg_node;
+};
+
+struct max77686_platform_data {
+       bool wakeup;
+       u8 ramp_delay;
+       struct max77686_regulator_data *regulators;
+       int num_regulators;
+       struct max77686_opmode_data *opmode_data;
+
+       /*
+        * GPIO-DVS feature is not enabled with the current version of
+        * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
+        * voltage at probe.
+        */
+};
+
+
+extern int max77686_debug_mask;                /* enables debug prints */
+
+enum {
+       MAX77686_DEBUG_INFO = 1 << 0,
+       MAX77686_DEBUG_MASK = 1 << 1,
+       MAX77686_DEBUG_INT = 1 << 2,
+};
+
+#ifndef CONFIG_DEBUG_MAX77686
+
+#define dbg_mask(fmt, ...) do { } while (0)
+#define dbg_info(fmt, ...) do { } while (0)
+#define dbg_int(fmt, ...) do { } while (0)
+
+#else
+
+#define dbg_mask(fmt, ...)                                     \
+do {                                                           \
+       if (max77686_debug_mask & MAX77686_DEBUG_MASK)          \
+               printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__);   \
+} while (0)
+
+#define dbg_info(fmt, ...)                                     \
+do {                                                           \
+       if (max77686_debug_mask & MAX77686_DEBUG_INFO)          \
+               printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__);   \
+} while (0)
+
+#define dbg_int(fmt, ...)                                      \
+do {                                                           \
+       if (max77686_debug_mask & MAX77686_DEBUG_INT)           \
+               printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__);   \
+} while (0)
+#endif /* DEBUG_MAX77686 */
+
+#endif /* __LINUX_MFD_MAX77686_H */
diff --git a/include/linux/platform_data/exynos4_tmu.h b/include/linux/platform_data/exynos4_tmu.h
deleted file mode 100644 (file)
index 39e038c..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * exynos4_tmu.h - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _LINUX_EXYNOS4_TMU_H
-#define _LINUX_EXYNOS4_TMU_H
-
-enum calibration_type {
-       TYPE_ONE_POINT_TRIMMING,
-       TYPE_TWO_POINT_TRIMMING,
-       TYPE_NONE,
-};
-
-/**
- * struct exynos4_tmu_platform_data
- * @threshold: basic temperature for generating interrupt
- *            25 <= threshold <= 125 [unit: degree Celsius]
- * @trigger_levels: array for each interrupt levels
- *     [unit: degree Celsius]
- *     0: temperature for trigger_level0 interrupt
- *        condition for trigger_level0 interrupt:
- *             current temperature > threshold + trigger_levels[0]
- *     1: temperature for trigger_level1 interrupt
- *        condition for trigger_level1 interrupt:
- *             current temperature > threshold + trigger_levels[1]
- *     2: temperature for trigger_level2 interrupt
- *        condition for trigger_level2 interrupt:
- *             current temperature > threshold + trigger_levels[2]
- *     3: temperature for trigger_level3 interrupt
- *        condition for trigger_level3 interrupt:
- *             current temperature > threshold + trigger_levels[3]
- * @trigger_level0_en:
- *     1 = enable trigger_level0 interrupt,
- *     0 = disable trigger_level0 interrupt
- * @trigger_level1_en:
- *     1 = enable trigger_level1 interrupt,
- *     0 = disable trigger_level1 interrupt
- * @trigger_level2_en:
- *     1 = enable trigger_level2 interrupt,
- *     0 = disable trigger_level2 interrupt
- * @trigger_level3_en:
- *     1 = enable trigger_level3 interrupt,
- *     0 = disable trigger_level3 interrupt
- * @gain: gain of amplifier in the positive-TC generator block
- *     0 <= gain <= 15
- * @reference_voltage: reference voltage of amplifier
- *     in the positive-TC generator block
- *     0 <= reference_voltage <= 31
- * @cal_type: calibration type for temperature
- *
- * This structure is required for configuration of exynos4_tmu driver.
- */
-struct exynos4_tmu_platform_data {
-       u8 threshold;
-       u8 trigger_levels[4];
-       bool trigger_level0_en;
-       bool trigger_level1_en;
-       bool trigger_level2_en;
-       bool trigger_level3_en;
-
-       u8 gain;
-       u8 reference_voltage;
-
-       enum calibration_type cal_type;
-};
-#endif /* _LINUX_EXYNOS4_TMU_H */
diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h
new file mode 100644 (file)
index 0000000..858eaca
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * exynos_thermal.h - Samsung EXYNOS TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_EXYNOS_THERMAL_H
+#define _LINUX_EXYNOS_THERMAL_H
+#include <linux/cpu_cooling.h>
+
+enum calibration_type {
+       TYPE_ONE_POINT_TRIMMING,
+       TYPE_TWO_POINT_TRIMMING,
+       TYPE_NONE,
+};
+
+enum soc_type {
+       SOC_ARCH_EXYNOS4 = 1,
+       SOC_ARCH_EXYNOS5,
+};
+/**
+ * struct exynos_tmu_platform_data
+ * @threshold: basic temperature for generating interrupt
+ *            25 <= threshold <= 125 [unit: degree Celsius]
+ * @trigger_levels: array for each interrupt levels
+ *     [unit: degree Celsius]
+ *     0: temperature for trigger_level0 interrupt
+ *        condition for trigger_level0 interrupt:
+ *             current temperature > threshold + trigger_levels[0]
+ *     1: temperature for trigger_level1 interrupt
+ *        condition for trigger_level1 interrupt:
+ *             current temperature > threshold + trigger_levels[1]
+ *     2: temperature for trigger_level2 interrupt
+ *        condition for trigger_level2 interrupt:
+ *             current temperature > threshold + trigger_levels[2]
+ *     3: temperature for trigger_level3 interrupt
+ *        condition for trigger_level3 interrupt:
+ *             current temperature > threshold + trigger_levels[3]
+ * @trigger_level0_en:
+ *     1 = enable trigger_level0 interrupt,
+ *     0 = disable trigger_level0 interrupt
+ * @trigger_level1_en:
+ *     1 = enable trigger_level1 interrupt,
+ *     0 = disable trigger_level1 interrupt
+ * @trigger_level2_en:
+ *     1 = enable trigger_level2 interrupt,
+ *     0 = disable trigger_level2 interrupt
+ * @trigger_level3_en:
+ *     1 = enable trigger_level3 interrupt,
+ *     0 = disable trigger_level3 interrupt
+ * @gain: gain of amplifier in the positive-TC generator block
+ *     0 <= gain <= 15
+ * @reference_voltage: reference voltage of amplifier
+ *     in the positive-TC generator block
+ *     0 <= reference_voltage <= 31
+ * @noise_cancel_mode: noise cancellation mode
+ *     000, 100, 101, 110 and 111 can be different modes
+ * @type: determines the type of SOC
+ * @efuse_value: platform defined fuse value
+ * @cal_type: calibration type for temperature
+ * @freq_clip_table: Table representing frequency reduction percentage.
+ * @freq_tab_count: Count of the above table as frequency reduction may
+ *     applicable to only some of the trigger levels.
+ *
+ * This structure is required for configuration of exynos_tmu driver.
+ */
+struct exynos_tmu_platform_data {
+       u8 threshold;
+       u8 trigger_levels[4];
+       bool trigger_level0_en;
+       bool trigger_level1_en;
+       bool trigger_level2_en;
+       bool trigger_level3_en;
+
+       u8 gain;
+       u8 reference_voltage;
+       u8 noise_cancel_mode;
+       u32 efuse_value;
+
+       enum calibration_type cal_type;
+       enum soc_type type;
+       struct freq_clip_table freq_tab[4];
+       unsigned int freq_tab_count;
+};
+#endif /* _LINUX_EXYNOS_THERMAL_H */