Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/avr32/mach-at32ap/pm.c
10817 views
1
/*
2
* AVR32 AP Power Management
3
*
4
* Copyright (C) 2008 Atmel Corporation
5
*
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* version 2 as published by the Free Software Foundation.
9
*/
10
#include <linux/io.h>
11
#include <linux/suspend.h>
12
#include <linux/vmalloc.h>
13
14
#include <asm/cacheflush.h>
15
#include <asm/sysreg.h>
16
17
#include <mach/chip.h>
18
#include <mach/pm.h>
19
#include <mach/sram.h>
20
21
#include "sdramc.h"
22
23
#define SRAM_PAGE_FLAGS (SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1) \
24
| SYSREG_BF(AP, 3) | SYSREG_BIT(G))
25
26
27
static unsigned long pm_sram_start;
28
static size_t pm_sram_size;
29
static struct vm_struct *pm_sram_area;
30
31
static void (*avr32_pm_enter_standby)(unsigned long sdramc_base);
32
static void (*avr32_pm_enter_str)(unsigned long sdramc_base);
33
34
/*
35
* Must be called with interrupts disabled. Exceptions will be masked
36
* on return (i.e. all exceptions will be "unrecoverable".)
37
*/
38
static void *avr32_pm_map_sram(void)
39
{
40
unsigned long vaddr;
41
unsigned long page_addr;
42
u32 tlbehi;
43
u32 mmucr;
44
45
vaddr = (unsigned long)pm_sram_area->addr;
46
page_addr = pm_sram_start & PAGE_MASK;
47
48
/*
49
* Mask exceptions and grab the first TLB entry. We won't be
50
* needing it while sleeping.
51
*/
52
asm volatile("ssrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
53
54
mmucr = sysreg_read(MMUCR);
55
tlbehi = sysreg_read(TLBEHI);
56
sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
57
58
tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
59
tlbehi |= vaddr & PAGE_MASK;
60
tlbehi |= SYSREG_BIT(TLBEHI_V);
61
62
sysreg_write(TLBELO, page_addr | SRAM_PAGE_FLAGS);
63
sysreg_write(TLBEHI, tlbehi);
64
__builtin_tlbw();
65
66
return (void *)(vaddr + pm_sram_start - page_addr);
67
}
68
69
/*
70
* Must be called with interrupts disabled. Exceptions will be
71
* unmasked on return.
72
*/
73
static void avr32_pm_unmap_sram(void)
74
{
75
u32 mmucr;
76
u32 tlbehi;
77
u32 tlbarlo;
78
79
/* Going to update TLB entry at index 0 */
80
mmucr = sysreg_read(MMUCR);
81
tlbehi = sysreg_read(TLBEHI);
82
sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
83
84
/* Clear the "valid" bit */
85
tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
86
sysreg_write(TLBEHI, tlbehi);
87
88
/* Mark it as "not accessed" */
89
tlbarlo = sysreg_read(TLBARLO);
90
sysreg_write(TLBARLO, tlbarlo | 0x80000000U);
91
92
/* Update the TLB */
93
__builtin_tlbw();
94
95
/* Unmask exceptions */
96
asm volatile("csrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
97
}
98
99
static int avr32_pm_valid_state(suspend_state_t state)
100
{
101
switch (state) {
102
case PM_SUSPEND_ON:
103
case PM_SUSPEND_STANDBY:
104
case PM_SUSPEND_MEM:
105
return 1;
106
107
default:
108
return 0;
109
}
110
}
111
112
static int avr32_pm_enter(suspend_state_t state)
113
{
114
u32 lpr_saved;
115
u32 evba_saved;
116
void *sram;
117
118
switch (state) {
119
case PM_SUSPEND_STANDBY:
120
sram = avr32_pm_map_sram();
121
122
/* Switch to in-sram exception handlers */
123
evba_saved = sysreg_read(EVBA);
124
sysreg_write(EVBA, (unsigned long)sram);
125
126
/*
127
* Save the LPR register so that we can re-enable
128
* SDRAM Low Power mode on resume.
129
*/
130
lpr_saved = sdramc_readl(LPR);
131
pr_debug("%s: Entering standby...\n", __func__);
132
avr32_pm_enter_standby(SDRAMC_BASE);
133
sdramc_writel(LPR, lpr_saved);
134
135
/* Switch back to regular exception handlers */
136
sysreg_write(EVBA, evba_saved);
137
138
avr32_pm_unmap_sram();
139
break;
140
141
case PM_SUSPEND_MEM:
142
sram = avr32_pm_map_sram();
143
144
/* Switch to in-sram exception handlers */
145
evba_saved = sysreg_read(EVBA);
146
sysreg_write(EVBA, (unsigned long)sram);
147
148
/*
149
* Save the LPR register so that we can re-enable
150
* SDRAM Low Power mode on resume.
151
*/
152
lpr_saved = sdramc_readl(LPR);
153
pr_debug("%s: Entering suspend-to-ram...\n", __func__);
154
avr32_pm_enter_str(SDRAMC_BASE);
155
sdramc_writel(LPR, lpr_saved);
156
157
/* Switch back to regular exception handlers */
158
sysreg_write(EVBA, evba_saved);
159
160
avr32_pm_unmap_sram();
161
break;
162
163
case PM_SUSPEND_ON:
164
pr_debug("%s: Entering idle...\n", __func__);
165
cpu_enter_idle();
166
break;
167
168
default:
169
pr_debug("%s: Invalid suspend state %d\n", __func__, state);
170
goto out;
171
}
172
173
pr_debug("%s: wakeup\n", __func__);
174
175
out:
176
return 0;
177
}
178
179
static const struct platform_suspend_ops avr32_pm_ops = {
180
.valid = avr32_pm_valid_state,
181
.enter = avr32_pm_enter,
182
};
183
184
static unsigned long avr32_pm_offset(void *symbol)
185
{
186
extern u8 pm_exception[];
187
188
return (unsigned long)symbol - (unsigned long)pm_exception;
189
}
190
191
static int __init avr32_pm_init(void)
192
{
193
extern u8 pm_exception[];
194
extern u8 pm_irq0[];
195
extern u8 pm_standby[];
196
extern u8 pm_suspend_to_ram[];
197
extern u8 pm_sram_end[];
198
void *dst;
199
200
/*
201
* To keep things simple, we depend on not needing more than a
202
* single page.
203
*/
204
pm_sram_size = avr32_pm_offset(pm_sram_end);
205
if (pm_sram_size > PAGE_SIZE)
206
goto err;
207
208
pm_sram_start = sram_alloc(pm_sram_size);
209
if (!pm_sram_start)
210
goto err_alloc_sram;
211
212
/* Grab a virtual area we can use later on. */
213
pm_sram_area = get_vm_area(pm_sram_size, VM_IOREMAP);
214
if (!pm_sram_area)
215
goto err_vm_area;
216
pm_sram_area->phys_addr = pm_sram_start;
217
218
local_irq_disable();
219
dst = avr32_pm_map_sram();
220
memcpy(dst, pm_exception, pm_sram_size);
221
flush_dcache_region(dst, pm_sram_size);
222
invalidate_icache_region(dst, pm_sram_size);
223
avr32_pm_unmap_sram();
224
local_irq_enable();
225
226
avr32_pm_enter_standby = dst + avr32_pm_offset(pm_standby);
227
avr32_pm_enter_str = dst + avr32_pm_offset(pm_suspend_to_ram);
228
intc_set_suspend_handler(avr32_pm_offset(pm_irq0));
229
230
suspend_set_ops(&avr32_pm_ops);
231
232
printk("AVR32 AP Power Management enabled\n");
233
234
return 0;
235
236
err_vm_area:
237
sram_free(pm_sram_start, pm_sram_size);
238
err_alloc_sram:
239
err:
240
pr_err("AVR32 Power Management initialization failed\n");
241
return -ENOMEM;
242
}
243
arch_initcall(avr32_pm_init);
244
245