summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos')
-rw-r--r--arch/arm/mach-exynos/clock-exynos4.c5
-rw-r--r--arch/arm/mach-exynos/common.c60
-rw-r--r--arch/arm/mach-exynos/cpuidle.c151
-rw-r--r--arch/arm/mach-exynos/include/mach/map.h7
-rw-r--r--arch/arm/mach-exynos/include/mach/pmu.h2
-rw-r--r--arch/arm/mach-exynos/mct.c21
-rw-r--r--arch/arm/mach-exynos/pm.c15
7 files changed, 221 insertions, 40 deletions
diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c
index 6504d8b1f8e..df54c2a9222 100644
--- a/arch/arm/mach-exynos/clock-exynos4.c
+++ b/arch/arm/mach-exynos/clock-exynos4.c
@@ -471,6 +471,11 @@ static struct clk exynos4_init_clocks_off[] = {
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 5),
}, {
+ .name = "jpeg",
+ .id = 0,
+ .enable = exynos4_clk_ip_cam_ctrl,
+ .ctrlbit = (1 << 6),
+ }, {
.name = "fimc",
.devname = "exynos4-fimc.0",
.enable = exynos4_clk_ip_cam_ctrl,
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index cbbaca54966..66742e91464 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -26,10 +26,12 @@
#include <asm/hardware/gic.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
+#include <asm/cacheflush.h>
#include <mach/regs-irq.h>
#include <mach/regs-pmu.h>
#include <mach/regs-gpio.h>
+#include <mach/pmu.h>
#include <plat/cpu.h>
#include <plat/clock.h>
@@ -45,6 +47,8 @@
#include <plat/regs-serial.h>
#include "common.h"
+#define L2_AUX_VAL 0x7C470001
+#define L2_AUX_MASK 0xC200ffff
static const char name_exynos4210[] = "EXYNOS4210";
static const char name_exynos4212[] = "EXYNOS4212";
@@ -189,7 +193,12 @@ static struct map_desc exynos4_iodesc[] __initdata = {
}, {
.virtual = (unsigned long)S5P_VA_DMC0,
.pfn = __phys_to_pfn(EXYNOS4_PA_DMC0),
- .length = SZ_4K,
+ .length = SZ_64K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5P_VA_DMC1,
+ .pfn = __phys_to_pfn(EXYNOS4_PA_DMC1),
+ .length = SZ_64K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_USB_HSPHY,
@@ -592,23 +601,48 @@ static int __init exynos4_l2x0_cache_init(void)
if (soc_is_exynos5250())
return 0;
- /* TAG, Data Latency Control: 2cycle */
- __raw_writel(0x110, S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL);
+ int ret;
+ ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK);
+ if (!ret) {
+ l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs);
+ clean_dcache_area(&l2x0_regs_phys, sizeof(unsigned long));
+ return 0;
+ }
+
+ if (!(__raw_readl(S5P_VA_L2CC + L2X0_CTRL) & 0x1)) {
+ l2x0_saved_regs.phy_base = EXYNOS4_PA_L2CC;
+ /* TAG, Data Latency Control: 2 cycles */
+ l2x0_saved_regs.tag_latency = 0x110;
- if (soc_is_exynos4210())
- __raw_writel(0x110, S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL);
- else if (soc_is_exynos4212() || soc_is_exynos4412())
- __raw_writel(0x120, S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL);
+ if (soc_is_exynos4212() || soc_is_exynos4412())
+ l2x0_saved_regs.data_latency = 0x120;
+ else
+ l2x0_saved_regs.data_latency = 0x110;
+
+ l2x0_saved_regs.prefetch_ctrl = 0x30000007;
+ l2x0_saved_regs.pwr_ctrl =
+ (L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN);
- /* L2X0 Prefetch Control */
- __raw_writel(0x30000007, S5P_VA_L2CC + L2X0_PREFETCH_CTRL);
+ l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs);
- /* L2X0 Power Control */
- __raw_writel(L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN,
- S5P_VA_L2CC + L2X0_POWER_CTRL);
+ __raw_writel(l2x0_saved_regs.tag_latency,
+ S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL);
+ __raw_writel(l2x0_saved_regs.data_latency,
+ S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL);
- l2x0_init(S5P_VA_L2CC, 0x7C470001, 0xC200ffff);
+ /* L2X0 Prefetch Control */
+ __raw_writel(l2x0_saved_regs.prefetch_ctrl,
+ S5P_VA_L2CC + L2X0_PREFETCH_CTRL);
+
+ /* L2X0 Power Control */
+ __raw_writel(l2x0_saved_regs.pwr_ctrl,
+ S5P_VA_L2CC + L2X0_POWER_CTRL);
+
+ clean_dcache_area(&l2x0_regs_phys, sizeof(unsigned long));
+ clean_dcache_area(&l2x0_saved_regs, sizeof(struct l2x0_regs));
+ }
+ l2x0_init(S5P_VA_L2CC, L2_AUX_VAL, L2_AUX_MASK);
return 0;
}
early_initcall(exynos4_l2x0_cache_init);
diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c
index 4ebb382c597..33ab4e7558a 100644
--- a/arch/arm/mach-exynos/cpuidle.c
+++ b/arch/arm/mach-exynos/cpuidle.c
@@ -11,25 +11,53 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/time.h>
#include <asm/proc-fns.h>
+#include <asm/smp_scu.h>
+#include <asm/suspend.h>
+#include <asm/unified.h>
+#include <mach/regs-pmu.h>
+#include <mach/pmu.h>
+
+#include <plat/cpu.h>
+
+#define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \
+ S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
+ (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0))
+#define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \
+ S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
+ (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1))
+
+#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[] = {
+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 = "IDLE",
+ .name = "C0",
.desc = "ARM clock gating(WFI)",
},
+ [1] = {
+ .enter = exynos4_enter_lowpower,
+ .exit_latency = 300,
+ .target_residency = 100000,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "C1",
+ .desc = "ARM power down",
+ },
};
static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device);
@@ -39,9 +67,102 @@ static struct cpuidle_driver exynos4_idle_driver = {
.owner = THIS_MODULE,
};
+/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
+static void exynos4_set_wakeupmask(void)
+{
+ __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
+}
+
+static unsigned int g_pwr_ctrl, g_diag_reg;
+
+static void save_cpu_arch_register(void)
+{
+ /*read power control register*/
+ asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc");
+ /*read diagnostic register*/
+ asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc");
+ return;
+}
+
+static void restore_cpu_arch_register(void)
+{
+ /*write power control register*/
+ asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc");
+ /*write diagnostic register*/
+ asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc");
+ return;
+}
+
+static int idle_finisher(unsigned long flags)
+{
+ cpu_do_idle();
+ return 1;
+}
+
+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 */
+ exynos4_sys_powerdown_conf(SYS_AFTR);
+
+ __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR);
+ __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
+
+ save_cpu_arch_register();
+
+ /* 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);
+
+ cpu_pm_enter();
+ cpu_suspend(0, idle_finisher);
+
+#ifdef CONFIG_SMP
+ scu_enable(S5P_VA_SCU);
+#endif
+ cpu_pm_exit();
+
+ restore_cpu_arch_register();
+
+ /*
+ * If PMU failed while entering sleep mode, WFI will be
+ * ignored by PMU and then exiting cpu_do_idle().
+ * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
+ * in this situation.
+ */
+ tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
+ if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
+ tmp |= S5P_CENTRAL_LOWPWR_CFG;
+ __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
+ }
+
+ /* 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)
+ int index)
{
struct timeval before, after;
int idle_time;
@@ -60,6 +181,22 @@ static int exynos4_enter_idle(struct cpuidle_device *dev,
return index;
}
+static int exynos4_enter_lowpower(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ int new_index = index;
+
+ /* This mode only can be entered when other core's are offline */
+ if (num_online_cpus() > 1)
+ new_index = drv->safe_state_index;
+
+ if (new_index == 0)
+ return exynos4_enter_idle(dev, drv, new_index);
+ else
+ return exynos4_enter_core0_aftr(dev, drv, new_index);
+}
+
static int __init exynos4_init_cpuidle(void)
{
int i, max_cpuidle_state, cpu_id;
@@ -74,19 +211,25 @@ static int __init exynos4_init_cpuidle(void)
memcpy(&drv->states[i], &exynos4_cpuidle_set[i],
sizeof(struct cpuidle_state));
}
+ drv->safe_state_index = 0;
cpuidle_register_driver(&exynos4_idle_driver);
for_each_cpu(cpu_id, cpu_online_mask) {
device = &per_cpu(exynos4_cpuidle_device, cpu_id);
device->cpu = cpu_id;
- device->state_count = drv->state_count;
+ if (cpu_id == 0)
+ device->state_count = (sizeof(exynos4_cpuidle_set) /
+ sizeof(struct cpuidle_state));
+ else
+ device->state_count = 1; /* Support IDLE only */
if (cpuidle_register_device(device)) {
printk(KERN_ERR "CPUidle register device failed\n,");
return -EIO;
}
}
+
return 0;
}
device_initcall(exynos4_init_cpuidle);
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index bf90bb0ab2b..188d87d6ec4 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -32,6 +32,10 @@
#define EXYNOS4_PA_FIMC2 0x11820000
#define EXYNOS4_PA_FIMC3 0x11830000
+#define EXYNOS4_PA_JPEG 0x11840000
+
+#define EXYNOS4_PA_G2D 0x12800000
+
#define EXYNOS4_PA_I2S0 0x03830000
#define EXYNOS4_PA_I2S1 0xE3100000
#define EXYNOS4_PA_I2S2 0xE2A00000
@@ -67,6 +71,7 @@
#define EXYNOS4_PA_KEYPAD 0x100A0000
#define EXYNOS4_PA_DMC0 0x10400000
+#define EXYNOS4_PA_DMC1 0x10410000
#define EXYNOS4_PA_COMBINER 0x10440000
#define EXYNOS5_PA_COMBINER 0x10440000
@@ -179,6 +184,8 @@
#define S5P_PA_FIMC1 EXYNOS4_PA_FIMC1
#define S5P_PA_FIMC2 EXYNOS4_PA_FIMC2
#define S5P_PA_FIMC3 EXYNOS4_PA_FIMC3
+#define S5P_PA_JPEG EXYNOS4_PA_JPEG
+#define S5P_PA_G2D EXYNOS4_PA_G2D
#define S5P_PA_FIMD0 EXYNOS4_PA_FIMD0
#define S5P_PA_HDMI EXYNOS4_PA_HDMI
#define S5P_PA_IIC_HDMIPHY EXYNOS4_PA_IIC_HDMIPHY
diff --git a/arch/arm/mach-exynos/include/mach/pmu.h b/arch/arm/mach-exynos/include/mach/pmu.h
index 632dd563013..e76b7faba66 100644
--- a/arch/arm/mach-exynos/include/mach/pmu.h
+++ b/arch/arm/mach-exynos/include/mach/pmu.h
@@ -22,11 +22,13 @@ enum sys_powerdown {
NUM_SYS_POWERDOWN,
};
+extern unsigned long l2x0_regs_phys;
struct exynos4_pmu_conf {
void __iomem *reg;
unsigned int val[NUM_SYS_POWERDOWN];
};
extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode);
+extern void s3c_cpu_resume(void);
#endif /* __ASM_ARCH_PMU_H */
diff --git a/arch/arm/mach-exynos/mct.c b/arch/arm/mach-exynos/mct.c
index 1016515dc9a..cae3e2dae2e 100644
--- a/arch/arm/mach-exynos/mct.c
+++ b/arch/arm/mach-exynos/mct.c
@@ -29,12 +29,13 @@
#include <mach/regs-mct.h>
#include <asm/mach/time.h>
+#define TICK_BASE_CNT 1
+
enum {
MCT_INT_SPI,
MCT_INT_PPI
};
-static unsigned long clk_cnt_per_tick;
static unsigned long clk_rate;
static unsigned int mct_int_type;
@@ -205,11 +206,14 @@ static int exynos4_comp_set_next_event(unsigned long cycles,
static void exynos4_comp_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
+ unsigned long cycles_per_jiffy;
exynos4_mct_comp0_stop();
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- exynos4_mct_comp0_start(mode, clk_cnt_per_tick);
+ cycles_per_jiffy =
+ (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
+ exynos4_mct_comp0_start(mode, cycles_per_jiffy);
break;
case CLOCK_EVT_MODE_ONESHOT:
@@ -248,9 +252,7 @@ static struct irqaction mct_comp_event_irq = {
static void exynos4_clockevent_init(void)
{
- clk_cnt_per_tick = clk_rate / 2 / HZ;
-
- clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
+ clockevents_calc_mult_shift(&mct_comp_device, clk_rate, 5);
mct_comp_device.max_delta_ns =
clockevent_delta2ns(0xffffffff, &mct_comp_device);
mct_comp_device.min_delta_ns =
@@ -317,12 +319,15 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
+ unsigned long cycles_per_jiffy;
exynos4_mct_tick_stop(mevt);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- exynos4_mct_tick_start(clk_cnt_per_tick, mevt);
+ cycles_per_jiffy =
+ (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
+ exynos4_mct_tick_start(cycles_per_jiffy, mevt);
break;
case CLOCK_EVT_MODE_ONESHOT:
@@ -396,7 +401,7 @@ static void exynos4_mct_tick_init(struct clock_event_device *evt)
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 450;
- clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
+ clockevents_calc_mult_shift(evt, clk_rate / (TICK_BASE_CNT + 1), 5);
evt->max_delta_ns =
clockevent_delta2ns(0x7fffffff, evt);
evt->min_delta_ns =
@@ -404,7 +409,7 @@ static void exynos4_mct_tick_init(struct clock_event_device *evt)
clockevents_register_device(evt);
- exynos4_mct_write(0x1, mevt->base + MCT_L_TCNTB_OFFSET);
+ exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
if (mct_int_type == MCT_INT_SPI) {
if (cpu == 0) {
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index f105bd2b676..428cfeb5772 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -155,13 +155,6 @@ static struct sleep_save exynos4_core_save[] = {
SAVE_ITEM(S5P_SROM_BC3),
};
-static struct sleep_save exynos4_l2cc_save[] = {
- SAVE_ITEM(S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL),
- SAVE_ITEM(S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL),
- SAVE_ITEM(S5P_VA_L2CC + L2X0_PREFETCH_CTRL),
- SAVE_ITEM(S5P_VA_L2CC + L2X0_POWER_CTRL),
- SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL),
-};
/* For Cortex-A9 Diagnostic and Power control register */
static unsigned int save_arm_register[2];
@@ -182,7 +175,6 @@ static void exynos4_pm_prepare(void)
u32 tmp;
s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
- s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_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));
@@ -388,13 +380,6 @@ static void exynos4_pm_resume(void)
scu_enable(S5P_VA_SCU);
#endif
-#ifdef CONFIG_CACHE_L2X0
- s3c_pm_do_restore_core(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save));
- outer_inv_all();
- /* enable L2X0*/
- writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL);
-#endif
-
early_wakeup:
return;
}