diff mbox

[05/11] ARM: shmobile: r8a7793: add power management support

Message ID 1425661798-10487-6-git-send-email-ulrich.hecht+renesas@gmail.com (mailing list archive)
State Deferred
Delegated to: Simon Horman
Headers show

Commit Message

Ulrich Hecht March 6, 2015, 5:09 p.m. UTC
Not used yet because it depends on SMP.

Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Signed-off-by: Khiem Nguyen <khiem.nguyen.xt@renesas.com>
Signed-off-by: Kouei Abe <kouei.abe.cp@renesas.com>
[uli: moved reset handling from SMP code, minor adjustments]
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
---
 arch/arm/mach-shmobile/Makefile     |   2 +-
 arch/arm/mach-shmobile/pm-r8a7793.c | 273 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-shmobile/r8a7793.h    |   6 +
 3 files changed, 280 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-shmobile/pm-r8a7793.c
 create mode 100644 arch/arm/mach-shmobile/r8a7793.h

Comments

Simon Horman March 7, 2015, 12:09 a.m. UTC | #1
Hi Ulrich,

On Fri, Mar 06, 2015 at 06:09:52PM +0100, Ulrich Hecht wrote:
> Not used yet because it depends on SMP.
> 
> Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
> Signed-off-by: Khiem Nguyen <khiem.nguyen.xt@renesas.com>
> Signed-off-by: Kouei Abe <kouei.abe.cp@renesas.com>
> [uli: moved reset handling from SMP code, minor adjustments]
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
> ---

As I mentioned elsewhere there seems to be some scope for sharing code
across SoCs.

More comments below...

>  arch/arm/mach-shmobile/Makefile     |   2 +-
>  arch/arm/mach-shmobile/pm-r8a7793.c | 273 ++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-shmobile/r8a7793.h    |   6 +
>  3 files changed, 280 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-shmobile/pm-r8a7793.c
>  create mode 100644 arch/arm/mach-shmobile/r8a7793.h

[snip]

> diff --git a/arch/arm/mach-shmobile/pm-r8a7793.c b/arch/arm/mach-shmobile/pm-r8a7793.c
> new file mode 100644
> index 0000000..a59ac8d
> --- /dev/null
> +++ b/arch/arm/mach-shmobile/pm-r8a7793.c
> @@ -0,0 +1,273 @@
> +/*
> + * r8a7793 Power management support
> + *
> + * Copyright (C) 2014  Renesas Electronics Corporation
> + * Copyright (C) 2015  Ulrich Hecht
> + *
> + * 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/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/notifier.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/pm_clock.h>
> +#include <linux/pm_domain.h>
> +#include <linux/smp.h>
> +#include <asm/io.h>
> +#include "common.h"
> +#include "pm-rcar.h"
> +#include "r8a7793.h"
> +
> +#define RST		0xe6160000
> +#define CA15BAR		0x0020
> +#define CA15RESCNT	0x0040
> +#define RAM		0xe63c0000
> +
> +/* SYSC */
> +#define SYSCIER 0x0c
> +#define SYSCIMR 0x10
> +
> +struct r8a7793_pm_domain {
> +	struct generic_pm_domain genpd;
> +	struct rcar_sysc_ch ch;
> +};
> +
> +static inline struct rcar_sysc_ch *to_r8a7793_ch(struct generic_pm_domain *d)
> +{
> +	return &container_of(d, struct r8a7793_pm_domain, genpd)->ch;
> +}
> +
> +#if defined(CONFIG_PM) || defined(CONFIG_SMP)
> +
> +static void __init r8a7793_sysc_init(void)
> +{
> +	void __iomem *base = rcar_sysc_init(0xe6180000);
> +
> +	/* enable all interrupt sources, but do not use interrupt handler */
> +	iowrite32(0x0131000e, base + SYSCIER);
> +	iowrite32(0, base + SYSCIMR);

There was some discussion of the value to write to SYSCIER in
response to "ARM: shmobile: r8a7794 SYSC setup code":

  >> Is it intentional that 0x00310060 differs from
  >> the value of 0x0131000e used on the r8a7790 and r8a7791?
  >
  > Yes. According to R-CAR Gen2 HW manual, interrupt bits are
  > different in each SoC. Therefore, I think the setting value
  > of SYSCIER should be set to a different value in each SoC.
  >
  > *Setting value of SYSCIER
  >  r8a7790 : 0x013111EF
  >  r8a7791 : 0x00111003
  >  r8a7794 : 0x00310060
  >
  > I don't know why the value of 0x0131000e is used on the r8a7790
  > and the r8a7791. (maybe wrong value)

  This value was most probably just copied from pm-r8a7779.c
  R-Car H1 has 4 four ARM cores, but there's no interrupt bit for ARM0,
  hence the 0xe value for the lowest nibble.

With the above in mind it seems likely to me that the use of
0x0131000e for the r8a7793 is incorrect. From my brief reading of the
above and the documentation it seems to me that as per the r8a7791,
the value should be 0x00111003.

[snip]
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Geert Uytterhoeven March 8, 2015, 10:07 a.m. UTC | #2
On Fri, Mar 6, 2015 at 6:09 PM, Ulrich Hecht
<ulrich.hecht+renesas@gmail.com> wrote:
> +#ifdef CONFIG_PM
> +
> +#define CPG_BASE 0xe6150000
> +#define CPG_LEN 0x1000

[...]

> +static void r8a7793_init_pm_domain(struct r8a7793_pm_domain *r8a7793_pd)
> +{
> +       struct generic_pm_domain *genpd = &r8a7793_pd->genpd;
> +
> +       pm_genpd_init(genpd, NULL, true);
> +       genpd->dev_ops.stop = pm_clk_suspend;
> +       genpd->dev_ops.start = pm_clk_resume;
> +       genpd->dev_ops.active_wakeup = pd_active_wakeup;
> +       genpd->power_off = pd_power_down;
> +       genpd->power_on = pd_power_up;
> +
> +       bus_register_notifier(&platform_bus_type, &platform_nb);
> +}
> +
> +static struct r8a7793_pm_domain r8a7793_pm_domains[] = {
> +       {
> +               .genpd.name = "pvrsrvkm",
> +               .ch = {
> +                       .chan_offs = 0xc0,      /* PWRSR2 .. PWRER2 */
> +                       .isr_bit = 20,          /* SGX */
> +               },
> +       },
> +};
> +
> +void __init r8a7793_init_pm_domains(void)
> +{
> +       int j;
> +
> +       for (j = 0; j < ARRAY_SIZE(r8a7793_pm_domains); j++)
> +               r8a7793_init_pm_domain(&r8a7793_pm_domains[j]);
> +}

[...]

> +static struct notifier_block platform_nb = {
> +       .notifier_call = r8a7793_pm_notifier_call,
> +};

This PM domain implementation definitely needs to be converted to DT before
it can be accepted upstream.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index 399342e..a104a24 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -13,7 +13,7 @@  obj-$(CONFIG_ARCH_R8A7778)	+= setup-r8a7778.o
 obj-$(CONFIG_ARCH_R8A7779)	+= setup-r8a7779.o pm-r8a7779.o
 obj-$(CONFIG_ARCH_R8A7790)	+= setup-r8a7790.o pm-r8a7790.o
 obj-$(CONFIG_ARCH_R8A7791)	+= setup-r8a7791.o pm-r8a7791.o
-obj-$(CONFIG_ARCH_R8A7793)	+= setup-r8a7793.o
+obj-$(CONFIG_ARCH_R8A7793)	+= setup-r8a7793.o pm-r8a7793.o
 obj-$(CONFIG_ARCH_R8A7794)	+= setup-r8a7794.o
 obj-$(CONFIG_ARCH_EMEV2)	+= setup-emev2.o
 obj-$(CONFIG_ARCH_R7S72100)	+= setup-r7s72100.o
diff --git a/arch/arm/mach-shmobile/pm-r8a7793.c b/arch/arm/mach-shmobile/pm-r8a7793.c
new file mode 100644
index 0000000..a59ac8d
--- /dev/null
+++ b/arch/arm/mach-shmobile/pm-r8a7793.c
@@ -0,0 +1,273 @@ 
+/*
+ * r8a7793 Power management support
+ *
+ * Copyright (C) 2014  Renesas Electronics Corporation
+ * Copyright (C) 2015  Ulrich Hecht
+ *
+ * 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/kernel.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_domain.h>
+#include <linux/smp.h>
+#include <asm/io.h>
+#include "common.h"
+#include "pm-rcar.h"
+#include "r8a7793.h"
+
+#define RST		0xe6160000
+#define CA15BAR		0x0020
+#define CA15RESCNT	0x0040
+#define RAM		0xe63c0000
+
+/* SYSC */
+#define SYSCIER 0x0c
+#define SYSCIMR 0x10
+
+struct r8a7793_pm_domain {
+	struct generic_pm_domain genpd;
+	struct rcar_sysc_ch ch;
+};
+
+static inline struct rcar_sysc_ch *to_r8a7793_ch(struct generic_pm_domain *d)
+{
+	return &container_of(d, struct r8a7793_pm_domain, genpd)->ch;
+}
+
+#if defined(CONFIG_PM) || defined(CONFIG_SMP)
+
+static void __init r8a7793_sysc_init(void)
+{
+	void __iomem *base = rcar_sysc_init(0xe6180000);
+
+	/* enable all interrupt sources, but do not use interrupt handler */
+	iowrite32(0x0131000e, base + SYSCIER);
+	iowrite32(0, base + SYSCIMR);
+}
+
+#else /* CONFIG_PM || CONFIG_SMP */
+
+static inline void r8a7793_sysc_init(void) {}
+
+#endif /* CONFIG_PM || CONFIG_SMP */
+
+#ifdef CONFIG_PM
+
+#define CPG_BASE 0xe6150000
+#define CPG_LEN 0x1000
+
+/* Software Reset */
+#define SRCR0		0x00a0
+#define SRCR1		0x00a8
+#define SRCR2		0x00b0
+#define SRCR3		0x00b8
+#define SRCR4		0x00bc
+#define SRCR5		0x00c4
+#define SRCR6		0x01c8
+#define SRCR7		0x01cc
+#define SRCR8		0x0920
+#define SRCR9		0x0924
+#define SRCR10		0x0928
+#define SRCR11		0x092c
+#define SRSTCLR0	0x0940
+#define SRSTCLR1	0x0944
+#define SRSTCLR2	0x0948
+#define SRSTCLR3	0x094c
+#define SRSTCLR4	0x0950
+#define SRSTCLR5	0x0954
+#define SRSTCLR6	0x0958
+#define SRSTCLR7	0x095c
+#define SRSTCLR8	0x0960
+#define SRSTCLR9	0x0964
+#define SRSTCLR10	0x0968
+#define SRSTCLR11	0x096c
+
+#define SRST_REG(n)	{ .srcr = SRCR##n, .srstclr = SRSTCLR##n, }
+
+static struct software_reset_reg {
+	u16	srcr;
+	u16	srstclr;
+} r8a7793_reset_regs[] = {
+	[0] = SRST_REG(0),
+	[1] = SRST_REG(1),
+	[2] = SRST_REG(2),
+	[3] = SRST_REG(3),
+	[4] = SRST_REG(4),
+	[5] = SRST_REG(5),
+	[6] = SRST_REG(6),
+	[7] = SRST_REG(7),
+	[8] = SRST_REG(8),
+	[9] = SRST_REG(9),
+	[10] = SRST_REG(10),
+	[11] = SRST_REG(11),
+};
+
+static DEFINE_SPINLOCK(r8a7793_reset_lock);
+
+void r8a7793_module_reset(unsigned int n, u32 bits, int usecs)
+{
+	void __iomem *cpg_base;
+	unsigned long flags;
+	u32 srcr;
+
+	if (n >= ARRAY_SIZE(r8a7793_reset_regs)) {
+		pr_err("SRCR%u is not available\n", n);
+		return;
+	}
+
+	/* give a short delay for at least one RCLK cycle */
+	if (usecs <= 0)
+		usecs = 50;
+
+	cpg_base = ioremap(CPG_BASE, CPG_LEN);
+
+	spin_lock_irqsave(&r8a7793_reset_lock, flags);
+	srcr = readl_relaxed(cpg_base + r8a7793_reset_regs[n].srcr);
+	writel_relaxed(srcr | bits, cpg_base + r8a7793_reset_regs[n].srcr);
+	readl_relaxed(cpg_base + r8a7793_reset_regs[n].srcr); /* sync */
+	spin_unlock_irqrestore(&r8a7793_reset_lock, flags);
+
+	udelay(usecs);
+
+	writel_relaxed(bits, cpg_base + r8a7793_reset_regs[n].srstclr);
+	readl_relaxed(cpg_base + r8a7793_reset_regs[n].srstclr); /* sync */
+
+	iounmap(cpg_base);
+}
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct rcar_sysc_ch *r8a7793_ch = to_r8a7793_ch(genpd);
+	int ret;
+
+	ret =  rcar_sysc_power_down(to_r8a7793_ch(genpd));
+
+	if (r8a7793_ch->chan_offs == 0xc0) {
+		/*
+		 * Issue software reset to 3DG functional blocks right after
+		 * the SGX power shut-off to avoid a hardware lock-up issue
+		 * triggered when we bring the SGX power up next time.
+		 */
+		r8a7793_module_reset(1, BIT(12), 2); /* DVFS */
+		r8a7793_module_reset(8, BIT(0), 2); /* CONST */
+	}
+
+	return ret;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	return rcar_sysc_power_up(to_r8a7793_ch(genpd));
+}
+
+static bool pd_active_wakeup(struct device *dev)
+{
+	return true;
+}
+
+static struct notifier_block platform_nb;
+
+static void r8a7793_init_pm_domain(struct r8a7793_pm_domain *r8a7793_pd)
+{
+	struct generic_pm_domain *genpd = &r8a7793_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, true);
+	genpd->dev_ops.stop = pm_clk_suspend;
+	genpd->dev_ops.start = pm_clk_resume;
+	genpd->dev_ops.active_wakeup = pd_active_wakeup;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+
+	bus_register_notifier(&platform_bus_type, &platform_nb);
+}
+
+static struct r8a7793_pm_domain r8a7793_pm_domains[] = {
+	{
+		.genpd.name = "pvrsrvkm",
+		.ch = {
+			.chan_offs = 0xc0,	/* PWRSR2 .. PWRER2 */
+			.isr_bit = 20,		/* SGX */
+		},
+	},
+};
+
+void __init r8a7793_init_pm_domains(void)
+{
+	int j;
+
+	for (j = 0; j < ARRAY_SIZE(r8a7793_pm_domains); j++)
+		r8a7793_init_pm_domain(&r8a7793_pm_domains[j]);
+}
+
+static int r8a7793_pm_notifier_call(struct notifier_block *nb,
+				    unsigned long event, void *data)
+{
+	struct device *dev = data;
+	struct r8a7793_pm_domain *pd;
+	int j;
+
+	switch (event) {
+	case BUS_NOTIFY_BIND_DRIVER:
+		for (j = 0; j < ARRAY_SIZE(r8a7793_pm_domains); j++) {
+			pd = &r8a7793_pm_domains[j];
+			if (!strcmp(pd->genpd.name, dev_name(dev))) {
+				pm_genpd_add_device(&pd->genpd, dev);
+				if (pm_clk_no_clocks(dev))
+					pm_clk_add(dev, NULL);
+			}
+		}
+		break;
+
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		for (j = 0; j < ARRAY_SIZE(r8a7793_pm_domains); j++) {
+			pd = &r8a7793_pm_domains[j];
+			if (!strcmp(pd->genpd.name, dev_name(dev)))
+				pm_genpd_remove_device(&pd->genpd, dev);
+
+		}
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block platform_nb = {
+	.notifier_call = r8a7793_pm_notifier_call,
+};
+
+#endif /* CONFIG_PM */
+
+void __init r8a7793_pm_init(void)
+{
+	void __iomem *p;
+	u32 bar;
+	static int once;
+
+	if (once++)
+		return;
+
+	/* RAM for jump stub, because BAR requires 256KB aligned address */
+	p = ioremap_nocache(RAM, shmobile_boot_size);
+	memcpy_toio(p, shmobile_boot_vector, shmobile_boot_size);
+	iounmap(p);
+
+	/* setup reset vectors */
+	p = ioremap_nocache(RST, 0x63);
+	bar = (RAM >> 8) & 0xfffffc00;
+	writel_relaxed(bar, p + CA15BAR);
+	writel_relaxed(bar | 0x10, p + CA15BAR);
+
+	/* enable clocks to all CPUs */
+	writel_relaxed((readl_relaxed(p + CA15RESCNT) & ~0x0f) | 0xa5a50000,
+		       p + CA15RESCNT);
+	iounmap(p);
+
+	r8a7793_sysc_init();
+	shmobile_smp_apmu_suspend_init();
+}
diff --git a/arch/arm/mach-shmobile/r8a7793.h b/arch/arm/mach-shmobile/r8a7793.h
new file mode 100644
index 0000000..78894c3
--- /dev/null
+++ b/arch/arm/mach-shmobile/r8a7793.h
@@ -0,0 +1,6 @@ 
+#ifndef __ASM_R8A7793_H__
+#define __ASM_R8A7793_H__
+
+void r8a7793_pm_init(void);
+
+#endif /* __ASM_R8A7793_H__ */