Skip to content

Commit c52d82f

Browse files
claudiubezneavarshini-rajendran
authored andcommitted
ARM: at91: PM: implement selection of LPM
The LPM shutdown controller output could signal the transition to PM state for different devices connected on board. On different boards LPM could be connected to different devices (e.g. on SAMA7G5-EK REV4 the LPM is connected to on main crystal oscillator, KSZ8081 PHY and to MCP16502 PMIC). Toggling LPM on BSR PM mode is done unconditionally and it helps PMIC to transition to a power saving mode. Toggling LPM on ULP0 and ULP1 should be done conditionally based on user defined wakeup sources, available wakeup source for PM mode and connections to SHDWC's LPM pin. On ULP0 any device could act as wakeup sources. On ULP1 only some of the on SoC controllers could act as wakeup sources. For this the architecture specific PM code parses board specific LPM devices, check them against possible wakeup source (in case of ULP1) and tells assembly code to act properly on SHDWC's LPM pin. Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
1 parent 8b1f64f commit c52d82f

4 files changed

Lines changed: 145 additions & 9 deletions

File tree

arch/arm/mach-at91/pm.c

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct at91_pm_quirks {
116116
* @config_shdwc_ws: wakeup sources configuration function for SHDWC
117117
* @config_pmc_ws: wakeup srouces configuration function for PMC
118118
* @ws_ids: wakup sources of_device_id array
119+
* @shdwc_np: pointer to shdwc node
119120
* @bu: backup unit mapped data (for backup mode)
120121
* @quirks: PM quirks
121122
* @data: PM data to be used on last phase of suspend
@@ -126,6 +127,7 @@ struct at91_soc_pm {
126127
int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity);
127128
int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity);
128129
const struct of_device_id *ws_ids;
130+
struct device_node *shdwc_np;
129131
struct at91_pm_bu *bu;
130132
struct at91_pm_quirks quirks;
131133
struct at91_pm_data data;
@@ -233,6 +235,84 @@ static const struct of_device_id sama7g5_ws_ids[] = {
233235
{ /* sentinel */ }
234236
};
235237

238+
static int at91_pm_device_in_list(const struct platform_device *pdev,
239+
const struct of_device_id *ids)
240+
{
241+
struct platform_device *local_pdev;
242+
const struct of_device_id *match;
243+
struct device_node *np;
244+
int in_list = 0;
245+
246+
for_each_matching_node_and_match(np, ids, &match) {
247+
local_pdev = of_find_device_by_node(np);
248+
if (!local_pdev)
249+
continue;
250+
251+
if (pdev == local_pdev)
252+
in_list = 1;
253+
254+
put_device(&local_pdev->dev);
255+
if (in_list)
256+
return in_list;
257+
}
258+
259+
return in_list;
260+
}
261+
262+
static int at91_pm_prepare_lpm(unsigned int pm_mode)
263+
{
264+
struct platform_device *pdev;
265+
int ndevices, i, ret;
266+
struct of_phandle_args lpmspec;
267+
268+
if ((pm_mode != AT91_PM_ULP0 && pm_mode != AT91_PM_ULP1) ||
269+
!soc_pm.shdwc_np)
270+
return 0;
271+
272+
ndevices = of_count_phandle_with_args(soc_pm.shdwc_np,
273+
"microchip,lpm-connection", 0);
274+
if (ndevices < 0)
275+
return 0;
276+
277+
soc_pm.data.lpm = 1;
278+
for (i = 0; i < ndevices; i++) {
279+
ret = of_parse_phandle_with_args(soc_pm.shdwc_np,
280+
"microchip,lpm-connection",
281+
NULL, i, &lpmspec);
282+
if (ret < 0) {
283+
if (ret == -ENOENT) {
284+
continue;
285+
} else {
286+
soc_pm.data.lpm = 0;
287+
return ret;
288+
}
289+
}
290+
291+
pdev = of_find_device_by_node(lpmspec.np);
292+
if (!pdev)
293+
continue;
294+
295+
if (device_may_wakeup(&pdev->dev)) {
296+
if (pm_mode == AT91_PM_ULP1) {
297+
/*
298+
* ULP1 wake-up sources are limited. Ignore it if not
299+
* in soc_pm.ws_ids.
300+
*/
301+
if (at91_pm_device_in_list(pdev, soc_pm.ws_ids))
302+
soc_pm.data.lpm = 0;
303+
} else {
304+
soc_pm.data.lpm = 0;
305+
}
306+
}
307+
308+
put_device(&pdev->dev);
309+
if (!soc_pm.data.lpm)
310+
break;
311+
}
312+
313+
return 0;
314+
}
315+
236316
static int at91_pm_config_ws(unsigned int pm_mode, bool set)
237317
{
238318
const struct wakeup_source_info *wsi;
@@ -471,10 +551,17 @@ static int at91_pm_begin(suspend_state_t state)
471551
soc_pm.data.mode = -1;
472552
}
473553

474-
ret = at91_pm_config_ws(soc_pm.data.mode, true);
554+
ret = at91_pm_prepare_lpm(soc_pm.data.mode);
475555
if (ret)
476556
return ret;
477557

558+
ret = at91_pm_config_ws(soc_pm.data.mode, true);
559+
if (ret) {
560+
/* Revert LPM if any. */
561+
soc_pm.data.lpm = 0;
562+
return ret;
563+
}
564+
478565
if (soc_pm.data.mode == AT91_PM_BACKUP)
479566
soc_pm.bu->suspended = 1;
480567
else if (soc_pm.bu)
@@ -1237,7 +1324,11 @@ static void __init at91_pm_modes_init(const u32 *maps, int len)
12371324
AT91_PM_REPLACE_MODES(maps, SHDWC);
12381325
} else {
12391326
soc_pm.data.shdwc = of_iomap(np, 0);
1240-
of_node_put(np);
1327+
/*
1328+
* np is used further on suspend/resume path so we skip the
1329+
* of_node_put(np) here.
1330+
*/
1331+
soc_pm.shdwc_np = np;
12411332
}
12421333
}
12431334

@@ -1609,7 +1700,8 @@ void __init sama7_pm_init(void)
16091700
AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP,
16101701
};
16111702
static const u32 iomaps[] __initconst = {
1612-
[AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU),
1703+
[AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU) |
1704+
AT91_PM_IOMAP(SHDWC),
16131705
[AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) |
16141706
AT91_PM_IOMAP(SHDWC) |
16151707
AT91_PM_IOMAP(ETHC),

arch/arm/mach-at91/pm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct at91_pm_data {
3939
unsigned int suspend_mode;
4040
unsigned int pmc_mckr_offset;
4141
unsigned int pmc_version;
42+
unsigned int lpm;
4243
};
4344
#endif
4445

arch/arm/mach-at91/pm_data-offsets.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ int main(void)
1818
pmc_mckr_offset));
1919
DEFINE(PM_DATA_PMC_VERSION, offsetof(struct at91_pm_data,
2020
pmc_version));
21+
DEFINE(PM_DATA_LPM, offsetof(struct at91_pm_data, lpm));
2122

2223
return 0;
2324
}

arch/arm/mach-at91/pm_suspend.S

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,30 @@ lp_done_\ena:
110110
#endif
111111
.endm
112112

113-
.macro at91_backup_set_lpm reg
113+
/*
114+
* Set LPM
115+
* @ena: 0 - disable LPM
116+
* 1 - enable LPM
117+
*
118+
* Side effects: overwrites r7, r8, r9
119+
*/
120+
.macro at91_set_lpm ena
114121
#ifdef CONFIG_SOC_SAMA7
115-
orr \reg, \reg, #0x200000
122+
ldr r7, .lpm
123+
cmp r7, #1
124+
bne 21f
125+
ldr r7, .shdwc
126+
cmp r7, #0
127+
beq 21f
128+
mov r8, #0xA5000000
129+
add r8, #0x200000
130+
mov r9, #\ena
131+
cmp r9, #1
132+
beq 20f
133+
add r8, #0x200000
134+
20:
135+
str r8, [r7]
136+
21:
116137
#endif
117138
.endm
118139

@@ -502,7 +523,7 @@ sr_dis_exit:
502523
ldr tmp1, [pmc, #AT91_PMC_SR]
503524
str tmp1, .saved_osc_status
504525
tst tmp1, #AT91_PMC_MOSCRCS
505-
bne 1f
526+
bne 7f
506527

507528
/* Turn off RC oscillator */
508529
ldr tmp1, [pmc, #AT91_CKGR_MOR]
@@ -516,6 +537,9 @@ sr_dis_exit:
516537
tst tmp1, #AT91_PMC_MOSCRCS
517538
bne 2b
518539

540+
/* Enable LPM. */
541+
7: at91_set_lpm 1
542+
519543
/* Wait for interrupt */
520544
1: at91_cpu_idle
521545

@@ -533,8 +557,10 @@ sr_dis_exit:
533557
wait_mckrdy tmp3
534558
b 6f
535559

536-
5: /* Restore RC oscillator state */
537-
ldr tmp1, .saved_osc_status
560+
5: at91_set_lpm 0
561+
562+
/* Restore RC oscillator state */
563+
8: ldr tmp1, .saved_osc_status
538564
tst tmp1, #AT91_PMC_MOSCRCS
539565
beq 4f
540566

@@ -611,6 +637,9 @@ sr_dis_exit:
611637

612638
wait_mckrdy tmp3
613639

640+
/* Enable LPM */
641+
at91_set_lpm 1
642+
614643
/* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
615644
ldr tmp1, [pmc, #AT91_CKGR_MOR]
616645
orr tmp1, tmp1, #AT91_PMC_WAITMODE
@@ -624,6 +653,9 @@ sr_dis_exit:
624653

625654
wait_mckrdy tmp3
626655

656+
/* Disable LPM. */
657+
at91_set_lpm 0
658+
627659
/* Enable the crystal oscillator */
628660
ldr tmp1, [pmc, #AT91_CKGR_MOR]
629661
orr tmp1, tmp1, #AT91_PMC_MOSCEN
@@ -1014,7 +1046,9 @@ ulp_exit:
10141046
ldr r0, .shdwc
10151047
mov tmp1, #0xA5000000
10161048
add tmp1, tmp1, #0x1
1017-
at91_backup_set_lpm tmp1
1049+
#ifdef CONFIG_SOC_SAMA7
1050+
orr tmp1, tmp1, #0x200000
1051+
#endif
10181052
str tmp1, [r0, #0]
10191053
.endm
10201054

@@ -1045,6 +1079,10 @@ ENTRY(at91_pm_suspend_in_sram)
10451079
str tmp1, .memtype
10461080
ldr tmp1, [r0, #PM_DATA_MODE]
10471081
str tmp1, .pm_mode
1082+
#ifdef CONFIG_SOC_SAMA7
1083+
ldr tmp1, [r0, #PM_DATA_LPM]
1084+
str tmp1, .lpm
1085+
#endif
10481086

10491087
/*
10501088
* ldrne below are here to preload their address in the TLB as access
@@ -1132,6 +1170,10 @@ ENDPROC(at91_pm_suspend_in_sram)
11321170
.word 0
11331171
.pmc_version:
11341172
.word 0
1173+
#ifdef CONFIG_SOC_SAMA7
1174+
.lpm:
1175+
.word 0
1176+
#endif
11351177
.saved_mckr:
11361178
.word 0
11371179
.saved_pllar:

0 commit comments

Comments
 (0)