Hi,
The patch series purpose is to disable the DDR controller's clock
and DDR system clock during the suspending to achive the lower power consumption.
Wenyou Yang (2):
pm: at91: add the PM initialization for the sama5d3 and sama5d4
pm: at91: add support to disable/enable the DDR clock
arch/arm/mach-at91/generic.h | 4 ++
arch/arm/mach-at91/include/mach/sama5d3.h | 1 +
arch/arm/mach-at91/include/mach/sama5d4.h | 5 ++
arch/arm/mach-at91/pm.c | 25 +++++++++
arch/arm/mach-at91/pm.h | 9 ++++
arch/arm/mach-at91/pm_suspend.S | 83 +++++++++++++++++++++++++++++
arch/arm/mach-at91/sama5.c | 10 +++-
include/linux/clk/at91_pmc.h | 1 +
8 files changed, 136 insertions(+), 2 deletions(-)
--
1.7.9.5
To get the mpddrc peripheral id from the PM initialization,
which will be used to disable mpddrc'c clock during suspending.
Signed-off-by: Wenyou Yang <[email protected]>
---
arch/arm/mach-at91/generic.h | 4 ++++
arch/arm/mach-at91/include/mach/sama5d3.h | 1 +
arch/arm/mach-at91/include/mach/sama5d4.h | 5 +++++
arch/arm/mach-at91/pm.c | 21 +++++++++++++++++++++
arch/arm/mach-at91/sama5.c | 10 ++++++++--
5 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index a6e726a..fa70811 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -34,11 +34,15 @@ extern void __init at91rm9200_pm_init(void);
extern void __init at91sam9260_pm_init(void);
extern void __init at91sam9g45_pm_init(void);
extern void __init at91sam9x5_pm_init(void);
+extern void __init sam5d3_pm_init(void);
+extern void __init sam5d4_pm_init(void);
#else
void __init at91rm9200_pm_init(void) { }
void __init at91sam9260_pm_init(void) { }
void __init at91sam9g45_pm_init(void) { }
void __init at91sam9x5_pm_init(void) { }
+void __init sam5d3_pm_init(void) {}
+void __init sam5d4_pm_init(void) {}
#endif
#endif /* _AT91_GENERIC_H */
diff --git a/arch/arm/mach-at91/include/mach/sama5d3.h b/arch/arm/mach-at91/include/mach/sama5d3.h
index 25613d8..5a383d8 100644
--- a/arch/arm/mach-at91/include/mach/sama5d3.h
+++ b/arch/arm/mach-at91/include/mach/sama5d3.h
@@ -63,6 +63,7 @@
#define SAMA5D3_ID_TDES 44 /* Triple Data Encryption Standard */
#define SAMA5D3_ID_TRNG 45 /* True Random Generator Number */
#define SAMA5D3_ID_IRQ0 47 /* Advanced Interrupt Controller (IRQ0) */
+#define SAMA5D3_ID_MPDDRC 49 /* MPDDR controller */
/*
* User Peripheral physical base addresses.
diff --git a/arch/arm/mach-at91/include/mach/sama5d4.h b/arch/arm/mach-at91/include/mach/sama5d4.h
index f256a45..e45f1b8 100644
--- a/arch/arm/mach-at91/include/mach/sama5d4.h
+++ b/arch/arm/mach-at91/include/mach/sama5d4.h
@@ -14,6 +14,11 @@
#define SAMA5D4_H
/*
+ * Peripheral identifiers/interrupts.
+ */
+#define SAMA5D4_ID_MPDDRC 16 /* MPDDR controller */
+
+/*
* User Peripheral physical base addresses.
*/
#define SAMA5D4_BASE_USART3 0xfc00c000 /* (USART3 non-secure) Base Address */
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 2574bad..7acb40e 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -41,6 +41,8 @@
static struct {
unsigned long uhp_udp_mask;
int memctrl;
+ unsigned int ddrc_pid;
+ bool is_sama5d4;
} at91_pm_data;
void __iomem *at91_ramc_base[2];
@@ -360,3 +362,22 @@ void __init at91sam9x5_pm_init(void)
at91_pm_data.memctrl = AT91_MEMCTRL_DDRSDR;
return at91_pm_init();
}
+
+void __init sam5d3_pm_init(void)
+{
+ at91_dt_ramc();
+ at91_pm_data.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP;
+ at91_pm_data.memctrl = AT91_MEMCTRL_DDRSDR;
+ at91_pm_data.ddrc_pid = SAMA5D3_ID_MPDDRC;
+ return at91_pm_init();
+}
+
+void __init sam5d4_pm_init(void)
+{
+ at91_dt_ramc();
+ at91_pm_data.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP;
+ at91_pm_data.memctrl = AT91_MEMCTRL_DDRSDR;
+ at91_pm_data.ddrc_pid = SAMA5D4_ID_MPDDRC;
+ at91_pm_data.is_sama5d4 = true;
+ return at91_pm_init();
+}
diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c
index 228814a..4338d43 100644
--- a/arch/arm/mach-at91/sama5.c
+++ b/arch/arm/mach-at91/sama5.c
@@ -31,7 +31,7 @@
static void __init sama5_dt_device_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
- at91sam9x5_pm_init();
+ sam5d3_pm_init();
}
static const char *sama5_dt_board_compat[] __initconst = {
@@ -84,10 +84,16 @@ static const char *sama5_alt_dt_board_compat[] __initconst = {
NULL
};
+static void __init sama5d4_dt_device_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ sam5d4_pm_init();
+}
+
DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
/* Maintainer: Atmel */
.map_io = sama5_alt_map_io,
- .init_machine = sama5_dt_device_init,
+ .init_machine = sama5d4_dt_device_init,
.dt_compat = sama5_alt_dt_board_compat,
.l2c_aux_mask = ~0UL,
MACHINE_END
--
1.7.9.5
To get the lower power consumption, disable the MPDDR controller's clock
and the DDR system clock before going to suspend, enable these clocks
after resuming.
For the SAM5D4, postpone disabling the clocks, instead of the DDR entering
the self-fresh mode immediately.
Signed-off-by: Wenyou Yang <[email protected]>
---
arch/arm/mach-at91/pm.c | 4 ++
arch/arm/mach-at91/pm.h | 9 +++++
arch/arm/mach-at91/pm_suspend.S | 83 +++++++++++++++++++++++++++++++++++++++
include/linux/clk/at91_pmc.h | 1 +
4 files changed, 97 insertions(+)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 7acb40e..7af032d 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -148,6 +148,10 @@ static void at91_pm_suspend(suspend_state_t state)
pm_data |= (state == PM_SUSPEND_MEM) ?
AT91_PM_MODE(AT91_PM_SLOW_CLOCK) : 0;
+ pm_data |= AT91_PM_DDRC_PID(at91_pm_data.ddrc_pid);
+ pm_data |= at91_pm_data.is_sama5d4 ?
+ AT91_PM_IS_SAMA5D4(AT91_PM_SAMA5D4_BIT) : 0;
+
flush_cache_all();
outer_disable();
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index d7def22..8e4f863 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -22,4 +22,13 @@
#define AT91_PM_SLOW_CLOCK 0x01
+#define AT91_PM_DDRC_PID_MASK 0xff
+#define AT91_PM_DDRC_PID_OFFSET 8
+#define AT91_PM_DDRC_PID(x) (((x) & AT91_PM_DDRC_PID_MASK) << AT91_PM_DDRC_PID_OFFSET)
+
+#define AT91_PM_SAMA5D4_MASK 0x01
+#define AT91_PM_SAMA5D4_OFFSET 24
+#define AT91_PM_IS_SAMA5D4(x) (((x) & AT91_PM_SAMA5D4_MASK) << AT91_PM_SAMA5D4_OFFSET)
+#define AT91_PM_SAMA5D4_BIT 0x01
+
#endif
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
index 5347ad4..4c774de 100644
--- a/arch/arm/mach-at91/pm_suspend.S
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -20,6 +20,9 @@
#define SRAMC_SELF_FRESH_ACTIVE 0x01
#define SRAMC_SELF_FRESH_EXIT 0x00
+#define DDR_CLOCK_ENABLE 0x01
+#define DDR_CLOCK_DISABLE 0x00
+
pmc .req r0
tmp1 .req r4
tmp2 .req r5
@@ -99,10 +102,28 @@ ENTRY(at91_pm_suspend_in_sram)
and r0, r0, #AT91_PM_MODE_MASK
str r0, .pm_mode
+ lsr r0, r3, #AT91_PM_DDRC_PID_OFFSET
+ and r0, r0, #AT91_PM_DDRC_PID_MASK
+ str r0, .ddrc_pid
+
+ lsr r0, r3, #AT91_PM_SAMA5D4_MASK
+ and r0, r0, #AT91_PM_SAMA5D4_OFFSET
+ str r0, .sama5d4_bit
+
/* Active the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_ACTIVE
bl at91_sramc_self_refresh
+ /* If the cpu isn't sama5d4, disable the ddr clock here */
+ ldr r0, .sama5d4_bit
+ tst r0, #AT91_PM_SAMA5D4_BIT
+ bne skip_ddr_clock
+
+ /* Disable DDR clock */
+ mov r0, #DDR_CLOCK_DISABLE
+ bl at91_ddr_clock
+
+skip_ddr_clock:
ldr r0, .pm_mode
tst r0, #AT91_PM_SLOW_CLOCK
beq skip_disable_main_clock
@@ -135,6 +156,15 @@ ENTRY(at91_pm_suspend_in_sram)
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
+ /* If the cpu is the sama5d4, disable the ddr clock here */
+ ldr r0, .sama5d4_bit
+ tst r0, #AT91_PM_SAMA5D4_BIT
+ beq skip_disable_main_clock
+
+ /* Disable DDR clock */
+ mov r0, #DDR_CLOCK_DISABLE
+ bl at91_ddr_clock
+
skip_disable_main_clock:
ldr pmc, .pmc_base
@@ -176,6 +206,10 @@ skip_disable_main_clock:
wait_mckrdy
skip_enable_main_clock:
+ /* Enable DDR clock */
+ mov r0, #DDR_CLOCK_ENABLE
+ bl at91_ddr_clock
+
/* Exit the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_EXIT
bl at91_sramc_self_refresh
@@ -310,6 +344,51 @@ exit_sramc_sf:
mov pc, lr
ENDPROC(at91_sramc_self_refresh)
+/*
+ * void at91_ddr_clock(unsigned char is_enable)
+ *
+ * @input param
+ * @r0: 0x01 - enable DDR clock
+ * 0x00 - disable DDR clock
+ * register usage:
+ * @r1: ddrc peripheral id
+ * @r2: base address of the pmc
+ */
+ENTRY(at91_ddr_clock)
+ ldr r1, .ddrc_pid
+ ldr r2, .pmc_base
+
+ /* DDRC peripheral clock */
+ cmp r1, #0
+ beq ddr_sys_clk
+
+ and r1, r1, #AT91_PMC_PCR_PID
+ orr r1, r1, #AT91_PMC_PCR_CMD
+
+ tst r0, #DDR_CLOCK_ENABLE
+ beq ddrc_clk
+ orr r1, r1, #AT91_PMC_PCR_EN
+
+ddrc_clk:
+ str r1, [r2, #AT91_PMC_PCR]
+
+ddr_sys_clk:
+ /* DDR system clock */
+ mov r3, #AT91_PMC_SYS_DDR
+
+ tst r0, #DDR_CLOCK_ENABLE
+ beq disable_sys_clk
+
+ str r3, [r2, #AT91_PMC_SCER]
+ b exit_ddr_clock
+
+disable_sys_clk:
+ str r3, [r2, #AT91_PMC_SCDR]
+
+exit_ddr_clock:
+ mov pc, lr
+ENDPROC(at91_ddr_clock)
+
.pmc_base:
.word 0
.sramc_base:
@@ -320,6 +399,10 @@ ENDPROC(at91_sramc_self_refresh)
.word 0
.pm_mode:
.word 0
+.ddrc_pid:
+ .word 0
+.sama5d4_bit:
+ .word 0
.saved_mckr:
.word 0
.saved_pllar:
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index c8e3b3d..10d2913 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -36,6 +36,7 @@ extern void __iomem *at91_pmc_base;
#define AT91RM9200_PMC_UDP (1 << 1) /* USB Devcice Port Clock [AT91RM9200 only] */
#define AT91RM9200_PMC_MCKUDP (1 << 2) /* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */
#define AT91RM9200_PMC_UHP (1 << 4) /* USB Host Port Clock [AT91RM9200 only] */
+#define AT91_PMC_SYS_DDR (1 << 2) /* DDR clock[some SAM9 and SAMA5D only] */
#define AT91SAM926x_PMC_UHP (1 << 6) /* USB Host Port Clock [AT91SAM926x only] */
#define AT91SAM926x_PMC_UDP (1 << 7) /* USB Devcice Port Clock [AT91SAM926x only] */
#define AT91_PMC_PCK0 (1 << 8) /* Programmable Clock 0 */
--
1.7.9.5