aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-shmobile/Makefile1
-rw-r--r--arch/arm/mach-shmobile/include/mach/common.h1
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7779.h35
-rw-r--r--arch/arm/mach-shmobile/pm-r8a7779.c235
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7779.c8
5 files changed, 280 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index 6f6ef3ad4db..b0d0d4b1ccb 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_ARCH_R8A7740) += entry-intc.o
obj-$(CONFIG_SUSPEND) += suspend.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ARCH_SH7372) += pm-sh7372.o sleep-sh7372.o
+obj-$(CONFIG_ARCH_R8A7779) += pm-r8a7779.o
# Board objects
obj-$(CONFIG_MACH_G3EVM) += board-g3evm.o
diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h
index 304ac311533..44ce124bfde 100644
--- a/arch/arm/mach-shmobile/include/mach/common.h
+++ b/arch/arm/mach-shmobile/include/mach/common.h
@@ -66,5 +66,6 @@ extern void r8a7779_add_early_devices(void);
extern void r8a7779_add_standard_devices(void);
extern void r8a7779_clock_init(void);
extern void r8a7779_pinmux_init(void);
+extern void r8a7779_pm_init(void);
#endif /* __ARCH_MACH_COMMON_H */
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7779.h b/arch/arm/mach-shmobile/include/mach/r8a7779.h
index 60e101aaea9..e6a6166fdf2 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7779.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7779.h
@@ -1,6 +1,9 @@
#ifndef __ASM_R8A7779_H__
#define __ASM_R8A7779_H__
+#include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
+
/* Pin Function Controller:
* GPIO_FN_xx - GPIO used to select pin function
* GPIO_GP_x_x - GPIO mapped to real I/O pin on CPU
@@ -322,4 +325,36 @@ enum {
GPIO_FN_GPS_MAG, GPIO_FN_FCE, GPIO_FN_SCK4_B,
};
+struct platform_device;
+
+struct r8a7779_pm_ch {
+ unsigned long chan_offs;
+ unsigned int chan_bit;
+ unsigned int isr_bit;
+};
+
+struct r8a7779_pm_domain {
+ struct generic_pm_domain genpd;
+ struct r8a7779_pm_ch ch;
+};
+
+static inline struct r8a7779_pm_ch *to_r8a7779_ch(struct generic_pm_domain *d)
+{
+ return &container_of(d, struct r8a7779_pm_domain, genpd)->ch;
+}
+
+#ifdef CONFIG_PM
+extern struct r8a7779_pm_domain r8a7779_sh4a;
+extern struct r8a7779_pm_domain r8a7779_sgx;
+extern struct r8a7779_pm_domain r8a7779_vdp1;
+extern struct r8a7779_pm_domain r8a7779_impx3;
+
+extern void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd);
+extern void r8a7779_add_device_to_domain(struct r8a7779_pm_domain *r8a7779_pd,
+ struct platform_device *pdev);
+#else
+#define r8a7779_init_pm_domain(pd) do { } while (0)
+#define r8a7779_add_device_to_domain(pd, pdev) do { } while (0)
+#endif /* CONFIG_PM */
+
#endif /* __ASM_R8A7779_H__ */
diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c
new file mode 100644
index 00000000000..d9c56fe067a
--- /dev/null
+++ b/arch/arm/mach-shmobile/pm-r8a7779.c
@@ -0,0 +1,235 @@
+/*
+ * r8a7779 Power management support
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Copyright (C) 2011 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/err.h>
+#include <linux/pm_clock.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/console.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <mach/common.h>
+#include <mach/r8a7779.h>
+
+static void __iomem *r8a7779_sysc_base;
+
+/* SYSC */
+#define SYSCSR 0x00
+#define SYSCISR 0x04
+#define SYSCISCR 0x08
+#define SYSCIER 0x0c
+#define SYSCIMR 0x10
+#define PWRSR0 0x40
+#define PWRSR1 0x80
+#define PWRSR2 0xc0
+#define PWRSR3 0x100
+#define PWRSR4 0x140
+
+#define PWRSR_OFFS 0x00
+#define PWROFFCR_OFFS 0x04
+#define PWRONCR_OFFS 0x0c
+#define PWRER_OFFS 0x14
+
+#define SYSCSR_RETRIES 100
+#define SYSCSR_DELAY_US 1
+
+#define SYSCISR_RETRIES 1000
+#define SYSCISR_DELAY_US 1
+
+#ifdef CONFIG_PM
+
+static int r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch *r8a7779_ch,
+ int sr_bit, int reg_offs)
+{
+ int k;
+
+ for (k = 0; k < SYSCSR_RETRIES; k++) {
+ if (ioread32(r8a7779_sysc_base + SYSCSR) & (1 << sr_bit))
+ break;
+ udelay(SYSCSR_DELAY_US);
+ }
+
+ if (k == SYSCSR_RETRIES)
+ return -EAGAIN;
+
+ iowrite32(1 << r8a7779_ch->chan_bit,
+ r8a7779_sysc_base + r8a7779_ch->chan_offs + reg_offs);
+
+ return 0;
+}
+
+static int r8a7779_sysc_pwr_off(struct r8a7779_pm_ch *r8a7779_ch)
+{
+ return r8a7779_sysc_pwr_on_off(r8a7779_ch, 0, PWROFFCR_OFFS);
+}
+
+static int r8a7779_sysc_pwr_on(struct r8a7779_pm_ch *r8a7779_ch)
+{
+ return r8a7779_sysc_pwr_on_off(r8a7779_ch, 1, PWRONCR_OFFS);
+}
+
+static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
+ int (*on_off_fn)(struct r8a7779_pm_ch *))
+{
+ unsigned int isr_mask = 1 << r8a7779_ch->isr_bit;
+ unsigned int chan_mask = 1 << r8a7779_ch->chan_bit;
+ unsigned int status;
+ int ret = 0;
+ int k;
+
+ iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
+
+ do {
+ ret = on_off_fn(r8a7779_ch);
+ if (ret)
+ goto out;
+
+ status = ioread32(r8a7779_sysc_base +
+ r8a7779_ch->chan_offs + PWRER_OFFS);
+ } while (status & chan_mask);
+
+ for (k = 0; k < SYSCISR_RETRIES; k++) {
+ if (ioread32(r8a7779_sysc_base + SYSCISR) & isr_mask)
+ break;
+ udelay(SYSCISR_DELAY_US);
+ }
+
+ if (k == SYSCISR_RETRIES)
+ ret = -EIO;
+
+ iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
+
+ out:
+ pr_debug("r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d\n",
+ r8a7779_ch->isr_bit, ioread32(r8a7779_sysc_base + PWRSR0),
+ ioread32(r8a7779_sysc_base + PWRSR1),
+ ioread32(r8a7779_sysc_base + PWRSR2),
+ ioread32(r8a7779_sysc_base + PWRSR3),
+ ioread32(r8a7779_sysc_base + PWRSR4), ret);
+ return ret;
+}
+
+static int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
+{
+ return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_off);
+}
+
+static int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
+{
+ return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_on);
+}
+
+static void __init r8a7779_sysc_init(void)
+{
+ r8a7779_sysc_base = ioremap_nocache(0xffd85000, PAGE_SIZE);
+ if (!r8a7779_sysc_base)
+ panic("unable to ioremap r8a7779 SYSC hardware block\n");
+
+ /* enable all interrupt sources, but do not use interrupt handler */
+ iowrite32(0x0131000e, r8a7779_sysc_base + SYSCIER);
+ iowrite32(0, r8a7779_sysc_base + SYSCIMR);
+}
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+ return r8a7779_sysc_power_down(to_r8a7779_ch(genpd));
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+ return r8a7779_sysc_power_up(to_r8a7779_ch(genpd));
+}
+
+static bool pd_is_off(struct generic_pm_domain *genpd)
+{
+ struct r8a7779_pm_ch *r8a7779_ch = to_r8a7779_ch(genpd);
+ unsigned int st;
+
+ st = ioread32(r8a7779_sysc_base + r8a7779_ch->chan_offs + PWRSR_OFFS);
+ if (st & (1 << r8a7779_ch->chan_bit))
+ return true;
+
+ return false;
+}
+
+static bool pd_active_wakeup(struct device *dev)
+{
+ return true;
+}
+
+void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd)
+{
+ struct generic_pm_domain *genpd = &r8a7779_pd->genpd;
+
+ pm_genpd_init(genpd, NULL, false);
+ genpd->dev_ops.stop = pm_clk_suspend;
+ genpd->dev_ops.start = pm_clk_resume;
+ genpd->dev_ops.active_wakeup = pd_active_wakeup;
+ genpd->dev_irq_safe = true;
+ genpd->power_off = pd_power_down;
+ genpd->power_on = pd_power_up;
+
+ if (pd_is_off(&r8a7779_pd->genpd))
+ pd_power_up(&r8a7779_pd->genpd);
+}
+
+void r8a7779_add_device_to_domain(struct r8a7779_pm_domain *r8a7779_pd,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ pm_genpd_add_device(&r8a7779_pd->genpd, dev);
+ if (pm_clk_no_clocks(dev))
+ pm_clk_add(dev, NULL);
+}
+
+struct r8a7779_pm_domain r8a7779_sh4a = {
+ .ch = {
+ .chan_offs = 0x80, /* PWRSR1 .. PWRER1 */
+ .isr_bit = 16, /* SH4A */
+ }
+};
+
+struct r8a7779_pm_domain r8a7779_sgx = {
+ .ch = {
+ .chan_offs = 0xc0, /* PWRSR2 .. PWRER2 */
+ .isr_bit = 20, /* SGX */
+ }
+};
+
+struct r8a7779_pm_domain r8a7779_vdp1 = {
+ .ch = {
+ .chan_offs = 0x100, /* PWRSR3 .. PWRER3 */
+ .isr_bit = 21, /* VDP */
+ }
+};
+
+struct r8a7779_pm_domain r8a7779_impx3 = {
+ .ch = {
+ .chan_offs = 0x140, /* PWRSR4 .. PWRER4 */
+ .isr_bit = 24, /* IMP */
+ }
+};
+
+#else /* CONFIG_PM */
+
+static inline void r8a7779_sysc_init(void) {}
+
+#endif /* CONFIG_PM */
+
+void __init r8a7779_pm_init(void)
+{
+ r8a7779_sysc_init();
+}
diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c
index b48a4b049c3..4725663bd03 100644
--- a/arch/arm/mach-shmobile/setup-r8a7779.c
+++ b/arch/arm/mach-shmobile/setup-r8a7779.c
@@ -30,6 +30,7 @@
#include <linux/sh_timer.h>
#include <mach/hardware.h>
#include <mach/r8a7779.h>
+#include <mach/common.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -218,6 +219,13 @@ static struct platform_device *r8a7779_late_devices[] __initdata = {
void __init r8a7779_add_standard_devices(void)
{
+ r8a7779_pm_init();
+
+ r8a7779_init_pm_domain(&r8a7779_sh4a);
+ r8a7779_init_pm_domain(&r8a7779_sgx);
+ r8a7779_init_pm_domain(&r8a7779_vdp1);
+ r8a7779_init_pm_domain(&r8a7779_impx3);
+
platform_add_devices(r8a7779_early_devices,
ARRAY_SIZE(r8a7779_early_devices));
platform_add_devices(r8a7779_late_devices,