Skip to content

Commit edc7121

Browse files
committed
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 737b0e5 commit edc7121

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
@@ -65,6 +65,7 @@ struct at91_pm_sfrbu_regs {
6565
* @config_shdwc_ws: wakeup sources configuration function for SHDWC
6666
* @config_pmc_ws: wakeup srouces configuration function for PMC
6767
* @ws_ids: wakup sources of_device_id array
68+
* @shdwc_np: pointer to shdwc node
6869
* @data: PM data to be used on last phase of suspend
6970
* @sfrbu_regs: SFRBU registers mapping
7071
* @bu: backup unit mapped data (for backup mode)
@@ -74,6 +75,7 @@ struct at91_soc_pm {
7475
int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity);
7576
int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity);
7677
const struct of_device_id *ws_ids;
78+
struct device_node *shdwc_np;
7779
struct at91_pm_bu *bu;
7880
struct at91_pm_data data;
7981
struct at91_pm_sfrbu_regs sfrbu_regs;
@@ -178,6 +180,84 @@ static const struct of_device_id sama7g5_ws_ids[] = {
178180
{ /* sentinel */ }
179181
};
180182

183+
static int at91_pm_device_in_list(const struct platform_device *pdev,
184+
const struct of_device_id *ids)
185+
{
186+
struct platform_device *local_pdev;
187+
const struct of_device_id *match;
188+
struct device_node *np;
189+
int in_list = 0;
190+
191+
for_each_matching_node_and_match(np, ids, &match) {
192+
local_pdev = of_find_device_by_node(np);
193+
if (!local_pdev)
194+
continue;
195+
196+
if (pdev == local_pdev)
197+
in_list = 1;
198+
199+
put_device(&local_pdev->dev);
200+
if (in_list)
201+
return in_list;
202+
}
203+
204+
return in_list;
205+
}
206+
207+
static int at91_pm_prepare_lpm(unsigned int pm_mode)
208+
{
209+
struct platform_device *pdev;
210+
int ndevices, i, ret;
211+
struct of_phandle_args lpmspec;
212+
213+
if ((pm_mode != AT91_PM_ULP0 && pm_mode != AT91_PM_ULP1) ||
214+
!soc_pm.shdwc_np)
215+
return 0;
216+
217+
ndevices = of_count_phandle_with_args(soc_pm.shdwc_np,
218+
"microchip,lpm-connection", 0);
219+
if (ndevices < 0)
220+
return 0;
221+
222+
soc_pm.data.lpm = 1;
223+
for (i = 0; i < ndevices; i++) {
224+
ret = of_parse_phandle_with_args(soc_pm.shdwc_np,
225+
"microchip,lpm-connection",
226+
NULL, i, &lpmspec);
227+
if (ret < 0) {
228+
if (ret == -ENOENT) {
229+
continue;
230+
} else {
231+
soc_pm.data.lpm = 0;
232+
return ret;
233+
}
234+
}
235+
236+
pdev = of_find_device_by_node(lpmspec.np);
237+
if (!pdev)
238+
continue;
239+
240+
if (device_may_wakeup(&pdev->dev)) {
241+
if (pm_mode == AT91_PM_ULP1) {
242+
/*
243+
* ULP1 wake-up sources are limited. Ignore it if not
244+
* in soc_pm.ws_ids.
245+
*/
246+
if (at91_pm_device_in_list(pdev, soc_pm.ws_ids))
247+
soc_pm.data.lpm = 0;
248+
} else {
249+
soc_pm.data.lpm = 0;
250+
}
251+
}
252+
253+
put_device(&pdev->dev);
254+
if (!soc_pm.data.lpm)
255+
break;
256+
}
257+
258+
return 0;
259+
}
260+
181261
static int at91_pm_config_ws(unsigned int pm_mode, bool set)
182262
{
183263
const struct wakeup_source_info *wsi;
@@ -283,10 +363,17 @@ static int at91_pm_begin(suspend_state_t state)
283363
soc_pm.data.mode = -1;
284364
}
285365

286-
ret = at91_pm_config_ws(soc_pm.data.mode, true);
366+
ret = at91_pm_prepare_lpm(soc_pm.data.mode);
287367
if (ret)
288368
return ret;
289369

370+
ret = at91_pm_config_ws(soc_pm.data.mode, true);
371+
if (ret) {
372+
/* Revert LPM if any. */
373+
soc_pm.data.lpm = 0;
374+
return ret;
375+
}
376+
290377
if (soc_pm.data.mode == AT91_PM_BACKUP)
291378
soc_pm.bu->suspended = 1;
292379
else if (soc_pm.bu)
@@ -892,7 +979,11 @@ static void __init at91_pm_modes_init(const u32 *maps, int len)
892979
soc_pm.data.suspend_mode = mode;
893980
} else {
894981
soc_pm.data.shdwc = of_iomap(np, 0);
895-
of_node_put(np);
982+
/*
983+
* np is used further on suspend/resume path so we skip the
984+
* of_node_put(np) here.
985+
*/
986+
soc_pm.shdwc_np = np;
896987
}
897988
}
898989

@@ -1210,7 +1301,8 @@ void __init sama7_pm_init(void)
12101301
AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP,
12111302
};
12121303
static const u32 iomaps[] __initconst = {
1213-
[AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU),
1304+
[AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU) |
1305+
AT91_PM_IOMAP(SHDWC),
12141306
[AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) |
12151307
AT91_PM_IOMAP(SHDWC),
12161308
[AT91_PM_BACKUP] = AT91_PM_IOMAP(SFRBU) |

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
@@ -106,9 +106,30 @@ lp_done_\ena:
106106
#endif
107107
.endm
108108

109-
.macro at91_backup_set_lpm reg
109+
/*
110+
* Set LPM
111+
* @ena: 0 - disable LPM
112+
* 1 - enable LPM
113+
*
114+
* Side effects: overwrites r7, r8, r9
115+
*/
116+
.macro at91_set_lpm ena
110117
#ifdef CONFIG_SOC_SAMA7
111-
orr \reg, \reg, #0x200000
118+
ldr r7, .lpm
119+
cmp r7, #1
120+
bne 21f
121+
ldr r7, .shdwc
122+
cmp r7, #0
123+
beq 21f
124+
mov r8, #0xA5000000
125+
add r8, #0x200000
126+
mov r9, #\ena
127+
cmp r9, #1
128+
beq 20f
129+
add r8, #0x200000
130+
20:
131+
str r8, [r7]
132+
21:
112133
#endif
113134
.endm
114135

@@ -483,7 +504,7 @@ sr_dis_exit:
483504
ldr tmp1, [pmc, #AT91_PMC_SR]
484505
str tmp1, .saved_osc_status
485506
tst tmp1, #AT91_PMC_MOSCRCS
486-
bne 1f
507+
bne 7f
487508

488509
/* Turn off RC oscillator */
489510
ldr tmp1, [pmc, #AT91_CKGR_MOR]
@@ -497,6 +518,9 @@ sr_dis_exit:
497518
tst tmp1, #AT91_PMC_MOSCRCS
498519
bne 2b
499520

521+
/* Enable LPM. */
522+
7: at91_set_lpm 1
523+
500524
/* Wait for interrupt */
501525
1: at91_cpu_idle
502526

@@ -514,8 +538,10 @@ sr_dis_exit:
514538
wait_mckrdy tmp3
515539
b 6f
516540

517-
5: /* Restore RC oscillator state */
518-
ldr tmp1, .saved_osc_status
541+
5: at91_set_lpm 0
542+
543+
/* Restore RC oscillator state */
544+
8: ldr tmp1, .saved_osc_status
519545
tst tmp1, #AT91_PMC_MOSCRCS
520546
beq 4f
521547

@@ -592,6 +618,9 @@ sr_dis_exit:
592618

593619
wait_mckrdy tmp3
594620

621+
/* Enable LPM */
622+
at91_set_lpm 1
623+
595624
/* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
596625
ldr tmp1, [pmc, #AT91_CKGR_MOR]
597626
orr tmp1, tmp1, #AT91_PMC_WAITMODE
@@ -605,6 +634,9 @@ sr_dis_exit:
605634

606635
wait_mckrdy tmp3
607636

637+
/* Disable LPM. */
638+
at91_set_lpm 0
639+
608640
/* Enable the crystal oscillator */
609641
ldr tmp1, [pmc, #AT91_CKGR_MOR]
610642
orr tmp1, tmp1, #AT91_PMC_MOSCEN
@@ -995,7 +1027,9 @@ ulp_exit:
9951027
ldr r0, .shdwc
9961028
mov tmp1, #0xA5000000
9971029
add tmp1, tmp1, #0x1
998-
at91_backup_set_lpm tmp1
1030+
#ifdef CONFIG_SOC_SAMA7
1031+
orr tmp1, tmp1, #0x200000
1032+
#endif
9991033
str tmp1, [r0, #0]
10001034
.endm
10011035

@@ -1026,6 +1060,10 @@ ENTRY(at91_pm_suspend_in_sram)
10261060
str tmp1, .memtype
10271061
ldr tmp1, [r0, #PM_DATA_MODE]
10281062
str tmp1, .pm_mode
1063+
#ifdef CONFIG_SOC_SAMA7
1064+
ldr tmp1, [r0, #PM_DATA_LPM]
1065+
str tmp1, .lpm
1066+
#endif
10291067

10301068
/*
10311069
* ldrne below are here to preload their address in the TLB as access
@@ -1113,6 +1151,10 @@ ENDPROC(at91_pm_suspend_in_sram)
11131151
.word 0
11141152
.pmc_version:
11151153
.word 0
1154+
#ifdef CONFIG_SOC_SAMA7
1155+
.lpm:
1156+
.word 0
1157+
#endif
11161158
.saved_mckr:
11171159
.word 0
11181160
.saved_pllar:

0 commit comments

Comments
 (0)