* [PATCH 1/4] ARM: at91: pm: Use ULP0 naming instead of slow clock
2018-07-17 8:26 [PATCH 0/4] rework ULP1 patches Claudiu Beznea
@ 2018-07-17 8:26 ` Claudiu Beznea
2018-07-17 8:26 ` [PATCH 2/4] ARM: at91: pm: Add ULP1 mode support Claudiu Beznea
` (3 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 8:26 UTC (permalink / raw)
To: nicolas.ferre, alexandre.belloni, linux, mturquette, sboyd
Cc: linux-arm-kernel, linux-kernel, linux-clk, Claudiu Beznea
Switch to use ULP0 naming instead of slow clock naming for power modes, to
be as closed as possible to datasheet. This commit does the necessary
renaming and macro addition to be as close as possible to the namings
from [1].
[1] https://lore.kernel.org/lkml/1470650705-31418-3-git-send-email-wenyou.yang@atmel.com
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
arch/arm/mach-at91/pm.c | 18 +++++++++---------
arch/arm/mach-at91/pm.h | 3 ++-
arch/arm/mach-at91/pm_suspend.S | 12 ++++++------
3 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 849014c01cf4..d43f00a715d7 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -40,15 +40,15 @@ extern void at91_pinctrl_gpio_resume(void);
#endif
static const match_table_t pm_modes __initconst = {
- { 0, "standby" },
- { AT91_PM_SLOW_CLOCK, "ulp0" },
+ { AT91_PM_STANDBY, "standby" },
+ { AT91_PM_ULP0, "ulp0" },
{ AT91_PM_BACKUP, "backup" },
{ -1, NULL },
};
static struct at91_pm_data pm_data = {
- .standby_mode = 0,
- .suspend_mode = AT91_PM_SLOW_CLOCK,
+ .standby_mode = AT91_PM_STANDBY,
+ .suspend_mode = AT91_PM_ULP0,
};
#define at91_ramc_read(id, field) \
@@ -145,7 +145,7 @@ static int at91_pm_verify_clocks(void)
*/
int at91_suspend_entering_slow_clock(void)
{
- return (pm_data.mode >= AT91_PM_SLOW_CLOCK);
+ return (pm_data.mode >= AT91_PM_ULP0);
}
EXPORT_SYMBOL(at91_suspend_entering_slow_clock);
@@ -186,7 +186,7 @@ static void at91_pm_suspend(suspend_state_t state)
* event sources; and reduces DRAM power. But otherwise it's identical to
* PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks.
*
- * AT91_PM_SLOW_CLOCK is like STANDBY plus slow clock mode, so drivers must
+ * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must
* suspend more deeply, the master clock switches to the clk32k and turns off
* the main oscillator
*
@@ -204,7 +204,7 @@ static int at91_pm_enter(suspend_state_t state)
/*
* Ensure that clocks are in a valid state.
*/
- if ((pm_data.mode >= AT91_PM_SLOW_CLOCK) &&
+ if (pm_data.mode >= AT91_PM_ULP0 &&
!at91_pm_verify_clocks())
goto error;
@@ -546,9 +546,9 @@ static void __init at91_pm_backup_init(void)
pm_data.sfrbu = NULL;
if (pm_data.standby_mode == AT91_PM_BACKUP)
- pm_data.standby_mode = AT91_PM_SLOW_CLOCK;
+ pm_data.standby_mode = AT91_PM_ULP0;
if (pm_data.suspend_mode == AT91_PM_BACKUP)
- pm_data.suspend_mode = AT91_PM_SLOW_CLOCK;
+ pm_data.suspend_mode = AT91_PM_ULP0;
}
struct pmc_info {
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index f95d31496f08..c44eaf17db86 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -21,7 +21,8 @@
#define AT91_MEMCTRL_SDRAMC 1
#define AT91_MEMCTRL_DDRSDR 2
-#define AT91_PM_SLOW_CLOCK 0x01
+#define AT91_PM_STANDBY 0x00
+#define AT91_PM_ULP0 0x01
#define AT91_PM_BACKUP 0x02
#ifndef __ASSEMBLY__
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
index daca91feea6a..821322d1a64d 100644
--- a/arch/arm/mach-at91/pm_suspend.S
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -112,8 +112,8 @@ ENTRY(at91_pm_suspend_in_sram)
bl at91_sramc_self_refresh
ldr r0, .pm_mode
- cmp r0, #AT91_PM_SLOW_CLOCK
- beq slow_clock
+ cmp r0, #AT91_PM_ULP0
+ beq ulp0_mode
cmp r0, #AT91_PM_BACKUP
beq backup_mode
@@ -122,8 +122,8 @@ ENTRY(at91_pm_suspend_in_sram)
at91_cpu_idle
b exit_suspend
-slow_clock:
- bl at91_slowck_mode
+ulp0_mode:
+ bl at91_ulp0_mode
b exit_suspend
backup_mode:
bl at91_backup_mode
@@ -151,7 +151,7 @@ ENTRY(at91_backup_mode)
str tmp1, [r0, #0]
ENDPROC(at91_backup_mode)
-ENTRY(at91_slowck_mode)
+ENTRY(at91_ulp0_mode)
ldr pmc, .pmc_base
/* Save Master clock setting */
@@ -212,7 +212,7 @@ ENTRY(at91_slowck_mode)
wait_mckrdy
mov pc, lr
-ENDPROC(at91_slowck_mode)
+ENDPROC(at91_ulp0_mode)
/*
* void at91_sramc_self_refresh(unsigned int is_active)
--
2.7.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/4] ARM: at91: pm: Add ULP1 mode support
2018-07-17 8:26 [PATCH 0/4] rework ULP1 patches Claudiu Beznea
2018-07-17 8:26 ` [PATCH 1/4] ARM: at91: pm: Use ULP0 naming instead of slow clock Claudiu Beznea
@ 2018-07-17 8:26 ` Claudiu Beznea
2018-07-17 8:26 ` [PATCH 3/4] ARM: at91: pm: add PMC fast startup registers defines Claudiu Beznea
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 8:26 UTC (permalink / raw)
To: nicolas.ferre, alexandre.belloni, linux, mturquette, sboyd
Cc: linux-arm-kernel, linux-kernel, linux-clk, Wenyou Yang,
Ludovic Desroches, Claudiu Beznea
From: Wenyou Yang <wenyou.yang@atmel.com>
In the ULP1 mode, in order to achieve the lowest power consumption
with the system in retention mode and be able to resume on the wake
up events, all the clocks are shut off, inclusive the embedded 12MHz
RC oscillator, and the number of wake up sources is limited as well.
When the wake up event is asserted, the embedded 12MHz RC oscillator
restarts automatically.
The ULP1 (Ultra Low-power mode 1) is introduced by SAMA5D2.
The previous size of pm_suspend.o was 2148 bytes. With the addition of
ULP1 mode the new size of pm_suspend.o raised at 2456 bytes.
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
[claudiu.beznea@microchip.com: aligned with 4.18-rc1]
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
arch/arm/mach-at91/pm.c | 1 +
arch/arm/mach-at91/pm.h | 3 +-
arch/arm/mach-at91/pm_suspend.S | 142 ++++++++++++++++++++++++++++++++++------
include/linux/clk/at91_pmc.h | 2 +
4 files changed, 127 insertions(+), 21 deletions(-)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index d43f00a715d7..099d8094018c 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -42,6 +42,7 @@ extern void at91_pinctrl_gpio_resume(void);
static const match_table_t pm_modes __initconst = {
{ AT91_PM_STANDBY, "standby" },
{ AT91_PM_ULP0, "ulp0" },
+ { AT91_PM_ULP1, "ulp1" },
{ AT91_PM_BACKUP, "backup" },
{ -1, NULL },
};
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index c44eaf17db86..9bd4e6ca672a 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -23,7 +23,8 @@
#define AT91_PM_STANDBY 0x00
#define AT91_PM_ULP0 0x01
-#define AT91_PM_BACKUP 0x02
+#define AT91_PM_ULP1 0x02
+#define AT91_PM_BACKUP 0x03
#ifndef __ASSEMBLY__
struct at91_pm_data {
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
index 821322d1a64d..a7c6ae13c945 100644
--- a/arch/arm/mach-at91/pm_suspend.S
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -42,6 +42,15 @@ tmp2 .req r5
.endm
/*
+ * Wait for main oscillator selection is done
+ */
+ .macro wait_moscsels
+1: ldr tmp1, [pmc, #AT91_PMC_SR]
+ tst tmp1, #AT91_PMC_MOSCSELS
+ beq 1b
+ .endm
+
+/*
* Wait until PLLA has locked.
*/
.macro wait_pllalock
@@ -112,19 +121,20 @@ ENTRY(at91_pm_suspend_in_sram)
bl at91_sramc_self_refresh
ldr r0, .pm_mode
- cmp r0, #AT91_PM_ULP0
- beq ulp0_mode
+ cmp r0, #AT91_PM_STANDBY
+ beq standby
cmp r0, #AT91_PM_BACKUP
beq backup_mode
+ bl at91_ulp_mode
+ b exit_suspend
+
+standby:
/* Wait for interrupt */
ldr pmc, .pmc_base
at91_cpu_idle
b exit_suspend
-ulp0_mode:
- bl at91_ulp0_mode
- b exit_suspend
backup_mode:
bl at91_backup_mode
b exit_suspend
@@ -151,7 +161,102 @@ ENTRY(at91_backup_mode)
str tmp1, [r0, #0]
ENDPROC(at91_backup_mode)
-ENTRY(at91_ulp0_mode)
+.macro at91_pm_ulp0_mode
+ ldr pmc, .pmc_base
+
+ /* Turn off the crystal oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ bic tmp1, tmp1, #AT91_PMC_MOSCEN
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ /* Wait for interrupt */
+ at91_cpu_idle
+
+ /* Turn on the crystal oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ orr tmp1, tmp1, #AT91_PMC_MOSCEN
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ wait_moscrdy
+.endm
+
+/**
+ * Note: This procedure only applies on the platform which uses
+ * the external crystal oscillator as a main clock source.
+ */
+.macro at91_pm_ulp1_mode
+ ldr pmc, .pmc_base
+
+ /* Switch the main clock source to 12-MHz RC oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ bic tmp1, tmp1, #AT91_PMC_MOSCSEL
+ bic tmp1, tmp1, #AT91_PMC_KEY_MASK
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ wait_moscsels
+
+ /* Disable the crystal oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ bic tmp1, tmp1, #AT91_PMC_MOSCEN
+ bic tmp1, tmp1, #AT91_PMC_KEY_MASK
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ /* Switch the master clock source to main clock */
+ ldr tmp1, [pmc, #AT91_PMC_MCKR]
+ bic tmp1, tmp1, #AT91_PMC_CSS
+ orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
+ str tmp1, [pmc, #AT91_PMC_MCKR]
+
+ wait_mckrdy
+
+ /* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ orr tmp1, tmp1, #AT91_PMC_WAITMODE
+ bic tmp1, tmp1, #AT91_PMC_KEY_MASK
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ wait_mckrdy
+
+ /* Enable the crystal oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ orr tmp1, tmp1, #AT91_PMC_MOSCEN
+ bic tmp1, tmp1, #AT91_PMC_KEY_MASK
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ wait_moscrdy
+
+ /* Switch the master clock source to slow clock */
+ ldr tmp1, [pmc, #AT91_PMC_MCKR]
+ bic tmp1, tmp1, #AT91_PMC_CSS
+ str tmp1, [pmc, #AT91_PMC_MCKR]
+
+ wait_mckrdy
+
+ /* Switch main clock source to crystal oscillator */
+ ldr tmp1, [pmc, #AT91_CKGR_MOR]
+ orr tmp1, tmp1, #AT91_PMC_MOSCSEL
+ bic tmp1, tmp1, #AT91_PMC_KEY_MASK
+ orr tmp1, tmp1, #AT91_PMC_KEY
+ str tmp1, [pmc, #AT91_CKGR_MOR]
+
+ wait_moscsels
+
+ /* Switch the master clock source to main clock */
+ ldr tmp1, [pmc, #AT91_PMC_MCKR]
+ bic tmp1, tmp1, #AT91_PMC_CSS
+ orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
+ str tmp1, [pmc, #AT91_PMC_MCKR]
+
+ wait_mckrdy
+.endm
+
+ENTRY(at91_ulp_mode)
ldr pmc, .pmc_base
/* Save Master clock setting */
@@ -174,22 +279,19 @@ ENTRY(at91_ulp0_mode)
orr tmp1, tmp1, #(1 << 29) /* bit 29 always set */
str tmp1, [pmc, #AT91_CKGR_PLLAR]
- /* Turn off the main oscillator */
- ldr tmp1, [pmc, #AT91_CKGR_MOR]
- bic tmp1, tmp1, #AT91_PMC_MOSCEN
- orr tmp1, tmp1, #AT91_PMC_KEY
- str tmp1, [pmc, #AT91_CKGR_MOR]
+ ldr r0, .pm_mode
+ cmp r0, #AT91_PM_ULP1
+ beq ulp1_mode
- /* Wait for interrupt */
- at91_cpu_idle
+ at91_pm_ulp0_mode
+ b ulp_exit
- /* Turn on the main oscillator */
- ldr tmp1, [pmc, #AT91_CKGR_MOR]
- orr tmp1, tmp1, #AT91_PMC_MOSCEN
- orr tmp1, tmp1, #AT91_PMC_KEY
- str tmp1, [pmc, #AT91_CKGR_MOR]
+ulp1_mode:
+ at91_pm_ulp1_mode
+ b ulp_exit
- wait_moscrdy
+ulp_exit:
+ ldr pmc, .pmc_base
/* Restore PLLA setting */
ldr tmp1, .saved_pllar
@@ -212,7 +314,7 @@ ENTRY(at91_ulp0_mode)
wait_mckrdy
mov pc, lr
-ENDPROC(at91_ulp0_mode)
+ENDPROC(at91_ulp_mode)
/*
* void at91_sramc_self_refresh(unsigned int is_active)
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 6aca5ce8a99a..4ea2cbf9b50d 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -47,8 +47,10 @@
#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
#define AT91_PMC_MOSCEN (1 << 0) /* Main Oscillator Enable */
#define AT91_PMC_OSCBYPASS (1 << 1) /* Oscillator Bypass */
+#define AT91_PMC_WAITMODE (1 << 2) /* Wait Mode Command */
#define AT91_PMC_MOSCRCEN (1 << 3) /* Main On-Chip RC Oscillator Enable [some SAM9] */
#define AT91_PMC_OSCOUNT (0xff << 8) /* Main Oscillator Start-up Time */
+#define AT91_PMC_KEY_MASK (0xff << 16)
#define AT91_PMC_KEY (0x37 << 16) /* MOR Writing Key */
#define AT91_PMC_MOSCSEL (1 << 24) /* Main Oscillator Selection [some SAM9] */
#define AT91_PMC_CFDEN (1 << 25) /* Clock Failure Detector Enable [some SAM9] */
--
2.7.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/4] ARM: at91: pm: add PMC fast startup registers defines
2018-07-17 8:26 [PATCH 0/4] rework ULP1 patches Claudiu Beznea
2018-07-17 8:26 ` [PATCH 1/4] ARM: at91: pm: Use ULP0 naming instead of slow clock Claudiu Beznea
2018-07-17 8:26 ` [PATCH 2/4] ARM: at91: pm: Add ULP1 mode support Claudiu Beznea
@ 2018-07-17 8:26 ` Claudiu Beznea
2018-07-17 8:26 ` [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode Claudiu Beznea
2018-07-17 19:16 ` [PATCH 0/4] rework ULP1 patches Alexandre Belloni
4 siblings, 0 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 8:26 UTC (permalink / raw)
To: nicolas.ferre, alexandre.belloni, linux, mturquette, sboyd
Cc: linux-arm-kernel, linux-kernel, linux-clk, Claudiu Beznea
Add PMC fast startup registers defines.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
include/linux/clk/at91_pmc.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 4ea2cbf9b50d..931ab05f771d 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -157,6 +157,19 @@
#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
+#define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */
+#define AT91_PMC_FSTT(n) BIT(n)
+#define AT91_PMC_RTCAL BIT(17) /* RTC Alarm Enable */
+#define AT91_PMC_USBAL BIT(18) /* USB Resume Enable */
+#define AT91_PMC_SDMMC_CD BIT(19) /* SDMMC Card Detect Enable */
+#define AT91_PMC_LPM BIT(20) /* Low-power Mode */
+#define AT91_PMC_RXLP_MCE BIT(24) /* Backup UART Receive Enable */
+#define AT91_PMC_ACC_CE BIT(25) /* ACC Enable */
+
+#define AT91_PMC_FSPR 0x74 /* Fast Startup Polarity Reg */
+
+#define AT91_PMC_FS_INPUT_MASK 0x7ff
+
#define AT91_PMC_PLLICPR 0x80 /* PLL Charge Pump Current Register */
#define AT91_PMC_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */
--
2.7.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode
2018-07-17 8:26 [PATCH 0/4] rework ULP1 patches Claudiu Beznea
` (2 preceding siblings ...)
2018-07-17 8:26 ` [PATCH 3/4] ARM: at91: pm: add PMC fast startup registers defines Claudiu Beznea
@ 2018-07-17 8:26 ` Claudiu Beznea
2018-07-17 10:45 ` Alexandre Belloni
2018-07-17 11:06 ` [RESEND PATCH] " Claudiu Beznea
2018-07-17 19:16 ` [PATCH 0/4] rework ULP1 patches Alexandre Belloni
4 siblings, 2 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 8:26 UTC (permalink / raw)
To: nicolas.ferre, alexandre.belloni, linux, mturquette, sboyd
Cc: linux-arm-kernel, linux-kernel, linux-clk, Claudiu Beznea
Since for ULP1 PM mode of SAMA5D2 the wakeup sources are limited and
well known add a method to check if these wakeup sources are defined by
user (either via DT or filesystem). In case there are no wakeup sources
defined for ULP1 the PM suspend will fail, otherwise these will be
configured in fast startup registers of PMC. Since wakeup sources of
ULP1 need also to be configured in SHDWC registers the code was a bit
changed to map the SHDWC also in case ULP1 is requested by user (this
was done in the initialization phase). In case the ULP1 initialization
fails the ULP0 mode is used (this mode was also used in case backup mode
initialization failed).
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
arch/arm/mach-at91/pm.c | 165 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 143 insertions(+), 22 deletions(-)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 099d8094018c..c8ef696b83b6 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -80,6 +80,87 @@ static struct at91_pm_bu {
phys_addr_t resume;
} *pm_bu;
+struct wakeup_source_info {
+ unsigned int pmc_fsmr_bit;
+ unsigned int shdwc_mr_bit;
+ bool set_polarity;
+};
+
+static const struct wakeup_source_info ws_info[] = {
+ { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true },
+ { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) },
+ { .pmc_fsmr_bit = AT91_PMC_USBAL },
+ { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
+};
+
+static const struct of_device_id sama5d2_ws_ids[] = {
+ { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] },
+ { .compatible = "atmel,at91rm9200-rtc", .data = &ws_info[1] },
+ { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] },
+ { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] },
+ { .compatible = "usb-ohci", .data = &ws_info[2] },
+ { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] },
+ { .compatible = "usb-ehci", .data = &ws_info[2] },
+ { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] },
+ { /* sentinel */ }
+};
+
+static int at91_pm_config_ws(unsigned int pm_mode, bool set)
+{
+ const struct wakeup_source_info *wsi;
+ const struct of_device_id *match;
+ struct platform_device *pdev;
+ struct device_node *np;
+ unsigned int mode = 0, polarity = 0, val = 0;
+
+ if (pm_mode != AT91_PM_ULP1)
+ return 0;
+
+ if (!pm_data.pmc || !pm_data.shdwc)
+ return -EPERM;
+
+ if (!set) {
+ writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+ return 0;
+ }
+
+ /* SHDWC.WUIR */
+ val = readl(pm_data.shdwc + 0x0c);
+ mode |= (val & 0x3ff);
+ polarity |= ((val >> 16) & 0x3ff);
+
+ /* SHDWC.MR */
+ val = readl(pm_data.shdwc + 0x04);
+
+ /* Loop through defined wakeup sources. */
+ for_each_matching_node_and_match(np, sama5d2_ws_ids, &match) {
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ continue;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ wsi = match->data;
+
+ /* Check if enabled on SHDWC. */
+ if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
+ continue;
+
+ mode |= wsi->pmc_fsmr_bit;
+ if (wsi->set_polarity)
+ polarity |= wsi->pmc_fsmr_bit;
+ }
+ }
+
+ if (mode) {
+ writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+ writel(polarity, pm_data.pmc + AT91_PMC_FSPR);
+ } else {
+ pr_err("AT91: PM: no ULP1 wakeup sources found!");
+ }
+
+ return mode ? 0 : -EPERM;
+}
+
/*
* Called after processes are frozen, but before we shutdown devices.
*/
@@ -98,7 +179,7 @@ static int at91_pm_begin(suspend_state_t state)
pm_data.mode = -1;
}
- return 0;
+ return at91_pm_config_ws(pm_data.mode, true);
}
/*
@@ -234,6 +315,7 @@ static int at91_pm_enter(suspend_state_t state)
*/
static void at91_pm_end(void)
{
+ at91_pm_config_ws(pm_data.mode, false);
}
@@ -479,31 +561,28 @@ static void __init at91_pm_sram_init(void)
&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
}
-static void __init at91_pm_backup_init(void)
+static bool __init at91_is_pm_mode_active(int pm_mode)
+{
+ return (pm_data.standby_mode == pm_mode ||
+ pm_data.suspend_mode == pm_mode);
+}
+
+static int __init at91_pm_backup_init(void)
{
struct gen_pool *sram_pool;
struct device_node *np;
struct platform_device *pdev = NULL;
+ int ret = -ENODEV;
- if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
- (pm_data.suspend_mode != AT91_PM_BACKUP))
- return;
+ if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
+ return 0;
pm_bu = NULL;
- np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
- if (!np) {
- pr_warn("%s: failed to find shdwc!\n", __func__);
- return;
- }
-
- pm_data.shdwc = of_iomap(np, 0);
- of_node_put(np);
-
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
if (!np) {
pr_warn("%s: failed to find sfrbu!\n", __func__);
- goto sfrbu_fail;
+ return ret;
}
pm_data.sfrbu = of_iomap(np, 0);
@@ -530,6 +609,7 @@ static void __init at91_pm_backup_init(void)
pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
if (!pm_bu) {
pr_warn("%s: unable to alloc securam!\n", __func__);
+ ret = -ENOMEM;
goto securam_fail;
}
@@ -537,21 +617,62 @@ static void __init at91_pm_backup_init(void)
pm_bu->canary = __pa_symbol(&canary);
pm_bu->resume = __pa_symbol(cpu_resume);
- return;
+ return 0;
-sfrbu_fail:
- iounmap(pm_data.shdwc);
- pm_data.shdwc = NULL;
securam_fail:
iounmap(pm_data.sfrbu);
pm_data.sfrbu = NULL;
+ return ret;
+}
- if (pm_data.standby_mode == AT91_PM_BACKUP)
+static void __init at91_pm_use_default_mode(int pm_mode)
+{
+ if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP)
+ return;
+
+ if (pm_data.standby_mode == pm_mode)
pm_data.standby_mode = AT91_PM_ULP0;
- if (pm_data.suspend_mode == AT91_PM_BACKUP)
+ if (pm_data.suspend_mode == pm_mode)
pm_data.suspend_mode = AT91_PM_ULP0;
}
+static void __init at91_pm_modes_init(void)
+{
+ struct device_node *np;
+ int ret;
+
+ if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
+ !at91_is_pm_mode_active(AT91_PM_ULP1))
+ return;
+
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
+ if (!np) {
+ pr_warn("%s: failed to find shdwc!\n", __func__);
+ goto ulp1_default;
+ }
+
+ pm_data.shdwc = of_iomap(np, 0);
+ of_node_put(np);
+
+ ret = at91_pm_backup_init();
+ if (ret) {
+ if (!at91_is_pm_mode_active(AT91_PM_ULP1))
+ goto unmap;
+ else
+ goto backup_default;
+ }
+
+ return;
+
+unmap:
+ iounmap(pm_data.shdwc);
+ pm_data.shdwc = NULL;
+ulp1_default:
+ at91_pm_use_default_mode(AT91_PM_ULP1);
+backup_default:
+ at91_pm_use_default_mode(AT91_PM_BACKUP);
+}
+
struct pmc_info {
unsigned long uhp_udp_mask;
};
@@ -645,7 +766,7 @@ void __init sama5d2_pm_init(void)
if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
return;
- at91_pm_backup_init();
+ at91_pm_modes_init();
sama5_pm_init();
}
--
2.7.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode
2018-07-17 8:26 ` [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode Claudiu Beznea
@ 2018-07-17 10:45 ` Alexandre Belloni
2018-07-17 10:49 ` Claudiu Beznea
2018-07-17 11:06 ` [RESEND PATCH] " Claudiu Beznea
1 sibling, 1 reply; 9+ messages in thread
From: Alexandre Belloni @ 2018-07-17 10:45 UTC (permalink / raw)
To: Claudiu Beznea
Cc: nicolas.ferre, linux, mturquette, sboyd, linux-arm-kernel,
linux-kernel, linux-clk
Hi,
On 17/07/2018 11:26:57+0300, Claudiu Beznea wrote:
> Since for ULP1 PM mode of SAMA5D2 the wakeup sources are limited and
> well known add a method to check if these wakeup sources are defined by
> user (either via DT or filesystem). In case there are no wakeup sources
> defined for ULP1 the PM suspend will fail, otherwise these will be
> configured in fast startup registers of PMC. Since wakeup sources of
> ULP1 need also to be configured in SHDWC registers the code was a bit
> changed to map the SHDWC also in case ULP1 is requested by user (this
> was done in the initialization phase). In case the ULP1 initialization
> fails the ULP0 mode is used (this mode was also used in case backup mode
> initialization failed).
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
> arch/arm/mach-at91/pm.c | 165 +++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 143 insertions(+), 22 deletions(-)
>
> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> index 099d8094018c..c8ef696b83b6 100644
> --- a/arch/arm/mach-at91/pm.c
> +++ b/arch/arm/mach-at91/pm.c
> @@ -80,6 +80,87 @@ static struct at91_pm_bu {
> phys_addr_t resume;
> } *pm_bu;
>
> +struct wakeup_source_info {
> + unsigned int pmc_fsmr_bit;
> + unsigned int shdwc_mr_bit;
> + bool set_polarity;
> +};
> +
> +static const struct wakeup_source_info ws_info[] = {
> + { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true },
> + { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) },
> + { .pmc_fsmr_bit = AT91_PMC_USBAL },
> + { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
> +};
> +
> +static const struct of_device_id sama5d2_ws_ids[] = {
> + { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] },
> + { .compatible = "atmel,at91rm9200-rtc", .data = &ws_info[1] },
> + { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] },
> + { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] },
> + { .compatible = "usb-ohci", .data = &ws_info[2] },
> + { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] },
> + { .compatible = "usb-ehci", .data = &ws_info[2] },
> + { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] },
> + { /* sentinel */ }
> +};
> +
> +static int at91_pm_config_ws(unsigned int pm_mode, bool set)
> +{
> + const struct wakeup_source_info *wsi;
> + const struct of_device_id *match;
> + struct platform_device *pdev;
> + struct device_node *np;
> + unsigned int mode = 0, polarity = 0, val = 0;
> +
> + if (pm_mode != AT91_PM_ULP1)
> + return 0;
> +
> + if (!pm_data.pmc || !pm_data.shdwc)
> + return -EPERM;
> +
> + if (!set) {
> + writel(mode, pm_data.pmc + AT91_PMC_FSMR);
> + return 0;
> + }
> +
> + /* SHDWC.WUIR */
> + val = readl(pm_data.shdwc + 0x0c);
> + mode |= (val & 0x3ff);
> + polarity |= ((val >> 16) & 0x3ff);
> +
> + /* SHDWC.MR */
> + val = readl(pm_data.shdwc + 0x04);
> +
> + /* Loop through defined wakeup sources. */
> + for_each_matching_node_and_match(np, sama5d2_ws_ids, &match) {
> + pdev = of_find_device_by_node(np);
of_find_device_by_node takes a reference to the embedded struct
device...
> + if (!pdev)
> + continue;
> +
> + if (device_may_wakeup(&pdev->dev)) {
> + wsi = match->data;
> +
> + /* Check if enabled on SHDWC. */
> + if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
> + continue;
> +
> + mode |= wsi->pmc_fsmr_bit;
> + if (wsi->set_polarity)
> + polarity |= wsi->pmc_fsmr_bit;
> + }
So you need to drop it here.
Can you do that and resend, just that patch? thanks.
> + }
> +
> + if (mode) {
> + writel(mode, pm_data.pmc + AT91_PMC_FSMR);
> + writel(polarity, pm_data.pmc + AT91_PMC_FSPR);
> + } else {
> + pr_err("AT91: PM: no ULP1 wakeup sources found!");
> + }
> +
> + return mode ? 0 : -EPERM;
> +}
> +
> /*
> * Called after processes are frozen, but before we shutdown devices.
> */
> @@ -98,7 +179,7 @@ static int at91_pm_begin(suspend_state_t state)
> pm_data.mode = -1;
> }
>
> - return 0;
> + return at91_pm_config_ws(pm_data.mode, true);
> }
>
> /*
> @@ -234,6 +315,7 @@ static int at91_pm_enter(suspend_state_t state)
> */
> static void at91_pm_end(void)
> {
> + at91_pm_config_ws(pm_data.mode, false);
> }
>
>
> @@ -479,31 +561,28 @@ static void __init at91_pm_sram_init(void)
> &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
> }
>
> -static void __init at91_pm_backup_init(void)
> +static bool __init at91_is_pm_mode_active(int pm_mode)
> +{
> + return (pm_data.standby_mode == pm_mode ||
> + pm_data.suspend_mode == pm_mode);
> +}
> +
> +static int __init at91_pm_backup_init(void)
> {
> struct gen_pool *sram_pool;
> struct device_node *np;
> struct platform_device *pdev = NULL;
> + int ret = -ENODEV;
>
> - if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
> - (pm_data.suspend_mode != AT91_PM_BACKUP))
> - return;
> + if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
> + return 0;
>
> pm_bu = NULL;
>
> - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
> - if (!np) {
> - pr_warn("%s: failed to find shdwc!\n", __func__);
> - return;
> - }
> -
> - pm_data.shdwc = of_iomap(np, 0);
> - of_node_put(np);
> -
> np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
> if (!np) {
> pr_warn("%s: failed to find sfrbu!\n", __func__);
> - goto sfrbu_fail;
> + return ret;
> }
>
> pm_data.sfrbu = of_iomap(np, 0);
> @@ -530,6 +609,7 @@ static void __init at91_pm_backup_init(void)
> pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
> if (!pm_bu) {
> pr_warn("%s: unable to alloc securam!\n", __func__);
> + ret = -ENOMEM;
> goto securam_fail;
> }
>
> @@ -537,21 +617,62 @@ static void __init at91_pm_backup_init(void)
> pm_bu->canary = __pa_symbol(&canary);
> pm_bu->resume = __pa_symbol(cpu_resume);
>
> - return;
> + return 0;
>
> -sfrbu_fail:
> - iounmap(pm_data.shdwc);
> - pm_data.shdwc = NULL;
> securam_fail:
> iounmap(pm_data.sfrbu);
> pm_data.sfrbu = NULL;
> + return ret;
> +}
>
> - if (pm_data.standby_mode == AT91_PM_BACKUP)
> +static void __init at91_pm_use_default_mode(int pm_mode)
> +{
> + if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP)
> + return;
> +
> + if (pm_data.standby_mode == pm_mode)
> pm_data.standby_mode = AT91_PM_ULP0;
> - if (pm_data.suspend_mode == AT91_PM_BACKUP)
> + if (pm_data.suspend_mode == pm_mode)
> pm_data.suspend_mode = AT91_PM_ULP0;
> }
>
> +static void __init at91_pm_modes_init(void)
> +{
> + struct device_node *np;
> + int ret;
> +
> + if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
> + !at91_is_pm_mode_active(AT91_PM_ULP1))
> + return;
> +
> + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
> + if (!np) {
> + pr_warn("%s: failed to find shdwc!\n", __func__);
> + goto ulp1_default;
> + }
> +
> + pm_data.shdwc = of_iomap(np, 0);
> + of_node_put(np);
> +
> + ret = at91_pm_backup_init();
> + if (ret) {
> + if (!at91_is_pm_mode_active(AT91_PM_ULP1))
> + goto unmap;
> + else
> + goto backup_default;
> + }
> +
> + return;
> +
> +unmap:
> + iounmap(pm_data.shdwc);
> + pm_data.shdwc = NULL;
> +ulp1_default:
> + at91_pm_use_default_mode(AT91_PM_ULP1);
> +backup_default:
> + at91_pm_use_default_mode(AT91_PM_BACKUP);
> +}
> +
> struct pmc_info {
> unsigned long uhp_udp_mask;
> };
> @@ -645,7 +766,7 @@ void __init sama5d2_pm_init(void)
> if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
> return;
>
> - at91_pm_backup_init();
> + at91_pm_modes_init();
> sama5_pm_init();
> }
>
> --
> 2.7.4
>
--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode
2018-07-17 10:45 ` Alexandre Belloni
@ 2018-07-17 10:49 ` Claudiu Beznea
0 siblings, 0 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 10:49 UTC (permalink / raw)
To: Alexandre Belloni
Cc: sboyd, mturquette, linux-kernel, linux, linux-clk, linux-arm-kernel
On 17.07.2018 13:45, Alexandre Belloni wrote:
> Hi,
>
> On 17/07/2018 11:26:57+0300, Claudiu Beznea wrote:
>> Since for ULP1 PM mode of SAMA5D2 the wakeup sources are limited and
>> well known add a method to check if these wakeup sources are defined by
>> user (either via DT or filesystem). In case there are no wakeup sources
>> defined for ULP1 the PM suspend will fail, otherwise these will be
>> configured in fast startup registers of PMC. Since wakeup sources of
>> ULP1 need also to be configured in SHDWC registers the code was a bit
>> changed to map the SHDWC also in case ULP1 is requested by user (this
>> was done in the initialization phase). In case the ULP1 initialization
>> fails the ULP0 mode is used (this mode was also used in case backup mode
>> initialization failed).
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>> arch/arm/mach-at91/pm.c | 165 +++++++++++++++++++++++++++++++++++++++++-------
>> 1 file changed, 143 insertions(+), 22 deletions(-)
>>
>> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
>> index 099d8094018c..c8ef696b83b6 100644
>> --- a/arch/arm/mach-at91/pm.c
>> +++ b/arch/arm/mach-at91/pm.c
>> @@ -80,6 +80,87 @@ static struct at91_pm_bu {
>> phys_addr_t resume;
>> } *pm_bu;
>>
>> +struct wakeup_source_info {
>> + unsigned int pmc_fsmr_bit;
>> + unsigned int shdwc_mr_bit;
>> + bool set_polarity;
>> +};
>> +
>> +static const struct wakeup_source_info ws_info[] = {
>> + { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true },
>> + { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) },
>> + { .pmc_fsmr_bit = AT91_PMC_USBAL },
>> + { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
>> +};
>> +
>> +static const struct of_device_id sama5d2_ws_ids[] = {
>> + { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] },
>> + { .compatible = "atmel,at91rm9200-rtc", .data = &ws_info[1] },
>> + { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] },
>> + { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] },
>> + { .compatible = "usb-ohci", .data = &ws_info[2] },
>> + { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] },
>> + { .compatible = "usb-ehci", .data = &ws_info[2] },
>> + { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] },
>> + { /* sentinel */ }
>> +};
>> +
>> +static int at91_pm_config_ws(unsigned int pm_mode, bool set)
>> +{
>> + const struct wakeup_source_info *wsi;
>> + const struct of_device_id *match;
>> + struct platform_device *pdev;
>> + struct device_node *np;
>> + unsigned int mode = 0, polarity = 0, val = 0;
>> +
>> + if (pm_mode != AT91_PM_ULP1)
>> + return 0;
>> +
>> + if (!pm_data.pmc || !pm_data.shdwc)
>> + return -EPERM;
>> +
>> + if (!set) {
>> + writel(mode, pm_data.pmc + AT91_PMC_FSMR);
>> + return 0;
>> + }
>> +
>> + /* SHDWC.WUIR */
>> + val = readl(pm_data.shdwc + 0x0c);
>> + mode |= (val & 0x3ff);
>> + polarity |= ((val >> 16) & 0x3ff);
>> +
>> + /* SHDWC.MR */
>> + val = readl(pm_data.shdwc + 0x04);
>> +
>> + /* Loop through defined wakeup sources. */
>> + for_each_matching_node_and_match(np, sama5d2_ws_ids, &match) {
>> + pdev = of_find_device_by_node(np);
>
> of_find_device_by_node takes a reference to the embedded struct
> device...
>
Yes, thank you!
>> + if (!pdev)
>> + continue;
>> +
>> + if (device_may_wakeup(&pdev->dev)) {
>> + wsi = match->data;
>> +
>> + /* Check if enabled on SHDWC. */
>> + if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
>> + continue;
>> +
>> + mode |= wsi->pmc_fsmr_bit;
>> + if (wsi->set_polarity)
>> + polarity |= wsi->pmc_fsmr_bit;
>> + }
>
> So you need to drop it here.
>
> Can you do that and resend, just that patch? thanks.
>
>> + }
>> +
>> + if (mode) {
>> + writel(mode, pm_data.pmc + AT91_PMC_FSMR);
>> + writel(polarity, pm_data.pmc + AT91_PMC_FSPR);
>> + } else {
>> + pr_err("AT91: PM: no ULP1 wakeup sources found!");
>> + }
>> +
>> + return mode ? 0 : -EPERM;
>> +}
>> +
>> /*
>> * Called after processes are frozen, but before we shutdown devices.
>> */
>> @@ -98,7 +179,7 @@ static int at91_pm_begin(suspend_state_t state)
>> pm_data.mode = -1;
>> }
>>
>> - return 0;
>> + return at91_pm_config_ws(pm_data.mode, true);
>> }
>>
>> /*
>> @@ -234,6 +315,7 @@ static int at91_pm_enter(suspend_state_t state)
>> */
>> static void at91_pm_end(void)
>> {
>> + at91_pm_config_ws(pm_data.mode, false);
>> }
>>
>>
>> @@ -479,31 +561,28 @@ static void __init at91_pm_sram_init(void)
>> &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
>> }
>>
>> -static void __init at91_pm_backup_init(void)
>> +static bool __init at91_is_pm_mode_active(int pm_mode)
>> +{
>> + return (pm_data.standby_mode == pm_mode ||
>> + pm_data.suspend_mode == pm_mode);
>> +}
>> +
>> +static int __init at91_pm_backup_init(void)
>> {
>> struct gen_pool *sram_pool;
>> struct device_node *np;
>> struct platform_device *pdev = NULL;
>> + int ret = -ENODEV;
>>
>> - if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
>> - (pm_data.suspend_mode != AT91_PM_BACKUP))
>> - return;
>> + if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
>> + return 0;
>>
>> pm_bu = NULL;
>>
>> - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
>> - if (!np) {
>> - pr_warn("%s: failed to find shdwc!\n", __func__);
>> - return;
>> - }
>> -
>> - pm_data.shdwc = of_iomap(np, 0);
>> - of_node_put(np);
>> -
>> np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
>> if (!np) {
>> pr_warn("%s: failed to find sfrbu!\n", __func__);
>> - goto sfrbu_fail;
>> + return ret;
>> }
>>
>> pm_data.sfrbu = of_iomap(np, 0);
>> @@ -530,6 +609,7 @@ static void __init at91_pm_backup_init(void)
>> pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
>> if (!pm_bu) {
>> pr_warn("%s: unable to alloc securam!\n", __func__);
>> + ret = -ENOMEM;
>> goto securam_fail;
>> }
>>
>> @@ -537,21 +617,62 @@ static void __init at91_pm_backup_init(void)
>> pm_bu->canary = __pa_symbol(&canary);
>> pm_bu->resume = __pa_symbol(cpu_resume);
>>
>> - return;
>> + return 0;
>>
>> -sfrbu_fail:
>> - iounmap(pm_data.shdwc);
>> - pm_data.shdwc = NULL;
>> securam_fail:
>> iounmap(pm_data.sfrbu);
>> pm_data.sfrbu = NULL;
>> + return ret;
>> +}
>>
>> - if (pm_data.standby_mode == AT91_PM_BACKUP)
>> +static void __init at91_pm_use_default_mode(int pm_mode)
>> +{
>> + if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP)
>> + return;
>> +
>> + if (pm_data.standby_mode == pm_mode)
>> pm_data.standby_mode = AT91_PM_ULP0;
>> - if (pm_data.suspend_mode == AT91_PM_BACKUP)
>> + if (pm_data.suspend_mode == pm_mode)
>> pm_data.suspend_mode = AT91_PM_ULP0;
>> }
>>
>> +static void __init at91_pm_modes_init(void)
>> +{
>> + struct device_node *np;
>> + int ret;
>> +
>> + if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
>> + !at91_is_pm_mode_active(AT91_PM_ULP1))
>> + return;
>> +
>> + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
>> + if (!np) {
>> + pr_warn("%s: failed to find shdwc!\n", __func__);
>> + goto ulp1_default;
>> + }
>> +
>> + pm_data.shdwc = of_iomap(np, 0);
>> + of_node_put(np);
>> +
>> + ret = at91_pm_backup_init();
>> + if (ret) {
>> + if (!at91_is_pm_mode_active(AT91_PM_ULP1))
>> + goto unmap;
>> + else
>> + goto backup_default;
>> + }
>> +
>> + return;
>> +
>> +unmap:
>> + iounmap(pm_data.shdwc);
>> + pm_data.shdwc = NULL;
>> +ulp1_default:
>> + at91_pm_use_default_mode(AT91_PM_ULP1);
>> +backup_default:
>> + at91_pm_use_default_mode(AT91_PM_BACKUP);
>> +}
>> +
>> struct pmc_info {
>> unsigned long uhp_udp_mask;
>> };
>> @@ -645,7 +766,7 @@ void __init sama5d2_pm_init(void)
>> if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
>> return;
>>
>> - at91_pm_backup_init();
>> + at91_pm_modes_init();
>> sama5_pm_init();
>> }
>>
>> --
>> 2.7.4
>>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RESEND PATCH] ARM: at91: pm: configure wakeup sources for ULP1 mode
2018-07-17 8:26 ` [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode Claudiu Beznea
2018-07-17 10:45 ` Alexandre Belloni
@ 2018-07-17 11:06 ` Claudiu Beznea
1 sibling, 0 replies; 9+ messages in thread
From: Claudiu Beznea @ 2018-07-17 11:06 UTC (permalink / raw)
To: nicolas.ferre, alexandre.belloni, mturquette, sboyd
Cc: linux, linux-arm-kernel, linux-kernel, linux-clk, Claudiu Beznea
Since for ULP1 PM mode of SAMA5D2 the wakeup sources are limited and
well known add a method to check if these wakeup sources are defined by
user (either via DT or filesystem). In case there are no wakeup sources
defined for ULP1 the PM suspend will fail, otherwise these will be
configured in fast startup registers of PMC. Since wakeup sources of
ULP1 need also to be configured in SHDWC registers the code was a bit
changed to map the SHDWC also in case ULP1 is requested by user (this
was done in the initialization phase). In case the ULP1 initialization
fails the ULP0 mode is used (this mode was also used in case backup mode
initialization failed).
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
arch/arm/mach-at91/pm.c | 168 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 146 insertions(+), 22 deletions(-)
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 099d8094018c..e8f3d0f97e61 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -80,6 +80,90 @@ static struct at91_pm_bu {
phys_addr_t resume;
} *pm_bu;
+struct wakeup_source_info {
+ unsigned int pmc_fsmr_bit;
+ unsigned int shdwc_mr_bit;
+ bool set_polarity;
+};
+
+static const struct wakeup_source_info ws_info[] = {
+ { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true },
+ { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) },
+ { .pmc_fsmr_bit = AT91_PMC_USBAL },
+ { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
+};
+
+static const struct of_device_id sama5d2_ws_ids[] = {
+ { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] },
+ { .compatible = "atmel,at91rm9200-rtc", .data = &ws_info[1] },
+ { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] },
+ { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] },
+ { .compatible = "usb-ohci", .data = &ws_info[2] },
+ { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] },
+ { .compatible = "usb-ehci", .data = &ws_info[2] },
+ { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] },
+ { /* sentinel */ }
+};
+
+static int at91_pm_config_ws(unsigned int pm_mode, bool set)
+{
+ const struct wakeup_source_info *wsi;
+ const struct of_device_id *match;
+ struct platform_device *pdev;
+ struct device_node *np;
+ unsigned int mode = 0, polarity = 0, val = 0;
+
+ if (pm_mode != AT91_PM_ULP1)
+ return 0;
+
+ if (!pm_data.pmc || !pm_data.shdwc)
+ return -EPERM;
+
+ if (!set) {
+ writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+ return 0;
+ }
+
+ /* SHDWC.WUIR */
+ val = readl(pm_data.shdwc + 0x0c);
+ mode |= (val & 0x3ff);
+ polarity |= ((val >> 16) & 0x3ff);
+
+ /* SHDWC.MR */
+ val = readl(pm_data.shdwc + 0x04);
+
+ /* Loop through defined wakeup sources. */
+ for_each_matching_node_and_match(np, sama5d2_ws_ids, &match) {
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ continue;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ wsi = match->data;
+
+ /* Check if enabled on SHDWC. */
+ if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
+ goto put_node;
+
+ mode |= wsi->pmc_fsmr_bit;
+ if (wsi->set_polarity)
+ polarity |= wsi->pmc_fsmr_bit;
+ }
+
+put_node:
+ of_node_put(np);
+ }
+
+ if (mode) {
+ writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+ writel(polarity, pm_data.pmc + AT91_PMC_FSPR);
+ } else {
+ pr_err("AT91: PM: no ULP1 wakeup sources found!");
+ }
+
+ return mode ? 0 : -EPERM;
+}
+
/*
* Called after processes are frozen, but before we shutdown devices.
*/
@@ -98,7 +182,7 @@ static int at91_pm_begin(suspend_state_t state)
pm_data.mode = -1;
}
- return 0;
+ return at91_pm_config_ws(pm_data.mode, true);
}
/*
@@ -234,6 +318,7 @@ static int at91_pm_enter(suspend_state_t state)
*/
static void at91_pm_end(void)
{
+ at91_pm_config_ws(pm_data.mode, false);
}
@@ -479,31 +564,28 @@ static void __init at91_pm_sram_init(void)
&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
}
-static void __init at91_pm_backup_init(void)
+static bool __init at91_is_pm_mode_active(int pm_mode)
+{
+ return (pm_data.standby_mode == pm_mode ||
+ pm_data.suspend_mode == pm_mode);
+}
+
+static int __init at91_pm_backup_init(void)
{
struct gen_pool *sram_pool;
struct device_node *np;
struct platform_device *pdev = NULL;
+ int ret = -ENODEV;
- if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
- (pm_data.suspend_mode != AT91_PM_BACKUP))
- return;
+ if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
+ return 0;
pm_bu = NULL;
- np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
- if (!np) {
- pr_warn("%s: failed to find shdwc!\n", __func__);
- return;
- }
-
- pm_data.shdwc = of_iomap(np, 0);
- of_node_put(np);
-
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
if (!np) {
pr_warn("%s: failed to find sfrbu!\n", __func__);
- goto sfrbu_fail;
+ return ret;
}
pm_data.sfrbu = of_iomap(np, 0);
@@ -530,6 +612,7 @@ static void __init at91_pm_backup_init(void)
pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
if (!pm_bu) {
pr_warn("%s: unable to alloc securam!\n", __func__);
+ ret = -ENOMEM;
goto securam_fail;
}
@@ -537,21 +620,62 @@ static void __init at91_pm_backup_init(void)
pm_bu->canary = __pa_symbol(&canary);
pm_bu->resume = __pa_symbol(cpu_resume);
- return;
+ return 0;
-sfrbu_fail:
- iounmap(pm_data.shdwc);
- pm_data.shdwc = NULL;
securam_fail:
iounmap(pm_data.sfrbu);
pm_data.sfrbu = NULL;
+ return ret;
+}
- if (pm_data.standby_mode == AT91_PM_BACKUP)
+static void __init at91_pm_use_default_mode(int pm_mode)
+{
+ if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP)
+ return;
+
+ if (pm_data.standby_mode == pm_mode)
pm_data.standby_mode = AT91_PM_ULP0;
- if (pm_data.suspend_mode == AT91_PM_BACKUP)
+ if (pm_data.suspend_mode == pm_mode)
pm_data.suspend_mode = AT91_PM_ULP0;
}
+static void __init at91_pm_modes_init(void)
+{
+ struct device_node *np;
+ int ret;
+
+ if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
+ !at91_is_pm_mode_active(AT91_PM_ULP1))
+ return;
+
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
+ if (!np) {
+ pr_warn("%s: failed to find shdwc!\n", __func__);
+ goto ulp1_default;
+ }
+
+ pm_data.shdwc = of_iomap(np, 0);
+ of_node_put(np);
+
+ ret = at91_pm_backup_init();
+ if (ret) {
+ if (!at91_is_pm_mode_active(AT91_PM_ULP1))
+ goto unmap;
+ else
+ goto backup_default;
+ }
+
+ return;
+
+unmap:
+ iounmap(pm_data.shdwc);
+ pm_data.shdwc = NULL;
+ulp1_default:
+ at91_pm_use_default_mode(AT91_PM_ULP1);
+backup_default:
+ at91_pm_use_default_mode(AT91_PM_BACKUP);
+}
+
struct pmc_info {
unsigned long uhp_udp_mask;
};
@@ -645,7 +769,7 @@ void __init sama5d2_pm_init(void)
if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
return;
- at91_pm_backup_init();
+ at91_pm_modes_init();
sama5_pm_init();
}
--
2.7.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 0/4] rework ULP1 patches
2018-07-17 8:26 [PATCH 0/4] rework ULP1 patches Claudiu Beznea
` (3 preceding siblings ...)
2018-07-17 8:26 ` [PATCH 4/4] ARM: at91: pm: configure wakeup sources for ULP1 mode Claudiu Beznea
@ 2018-07-17 19:16 ` Alexandre Belloni
4 siblings, 0 replies; 9+ messages in thread
From: Alexandre Belloni @ 2018-07-17 19:16 UTC (permalink / raw)
To: Claudiu Beznea
Cc: nicolas.ferre, linux, mturquette, sboyd, linux-arm-kernel,
linux-kernel, linux-clk
On 17/07/2018 11:26:53+0300, Claudiu Beznea wrote:
> Hi,
>
> Based on the discussion at [1] and private discussion I had with Alexandre
> Belloni on IRC, I reworked ULP1 patches to remove device tree bindings
> proposed in [1].
>
> The implementation from this patches (mostly patch 4/4) is using
> device_may_wakeup() API that current Linux code provides and the fact that
> for the ULP1 the wakeup souces are known and well defined.
> Basically, this rework (mostly patch 4/4) loops through well defined ULP1
> wakeup sources and check if those wakeup sources were configured to wake
> up the system (vid DT or sysfs) and, based on that (and in case of RTC,
> based on SHDWC.MR), set the PMC fast startup mode and polarity registers.
> In [1] PMC fast startup mode and polarity registers were configured by
> introducing new DT bindings.
>
> Since with these patches SHDWC registers are used in ULP1 wakeup sources
> configuration the patch 4/4 changed a bit the PM initialization phase for
> SAMA5D2 in order to have mapped also the SHDWC in case ULP1 needs to be
> initialized. In case ULP1 configuration fails the ULP0 mode is used (same
> approach as in backup mode case).
>
> I measure the delay introduced in suspend sequence by using an oscilloscope
> connected to a GPIO and calling gpio_direction_output(PIN, 0) at the beginning
> of at91_pm_config_ws() and gpio_direction_output(PIN, 1) at the end of
> at91_pm_config_ws(). With these I measureed on oscilloscope the difference
> b/w the front changes. The measured time was *1.56ms*. Keep in mind that
> gpio_direction_output() will call, in the end,
> atmel_gpio_direction_output() which access 4 registers to toggle the PIN
> (in at91_pm_config_ws() there are also 4 peripheral register accesses).
>
> Besides this rework, I took the chance and I appended few other patches to
> this series, based on Wenyou Yang's previous work, related to ULP modes.
>
> Patch 1/4: replaces slow clock with ULP0
> Patch 2/4: is the Wenyou's patch wich adds ULP1 support
> Patch 3/4: is defining PMC fast startup registers
>
> The patches were based on v4.18-rc1.
>
> [1] https://patchwork.kernel.org/patch/8595091/
>
> Claudiu Beznea (3):
> ARM: at91: pm: Use ULP0 naming instead of slow clock
> ARM: at91: pm: add PMC fast startup registers defines
> ARM: at91: pm: configure wakeup sources for ULP1 mode
>
> Wenyou Yang (1):
> ARM: at91: pm: Add ULP1 mode support
>
> arch/arm/mach-at91/pm.c | 184 +++++++++++++++++++++++++++++++++-------
> arch/arm/mach-at91/pm.h | 6 +-
> arch/arm/mach-at91/pm_suspend.S | 142 ++++++++++++++++++++++++++-----
> include/linux/clk/at91_pmc.h | 15 ++++
> 4 files changed, 294 insertions(+), 53 deletions(-)
>
Applied, thanks.
--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 9+ messages in thread