1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
From bb35d670afd2f3501de36c158e9842817ce013b8 Mon Sep 17 00:00:00 2001
From: Raghav Dogra <raghav@freescale.com>
Date: Fri, 15 Jan 2016 17:10:09 +0530
Subject: [PATCH 44/70] drivers/memory: Add deep sleep support for IFC
Add support of suspend, resume function to support deep sleep.
Also make sure of SRAM initialization during resume.
Signed-off-by: Raghav Dogra <raghav@freescale.com>
---
drivers/memory/fsl_ifc.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fsl_ifc.h | 6 ++
2 files changed, 169 insertions(+)
--- a/drivers/memory/fsl_ifc.c
+++ b/drivers/memory/fsl_ifc.c
@@ -24,6 +24,7 @@
#include <linux/compiler.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/io.h>
@@ -35,6 +36,8 @@
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
+#define FSL_IFC_V1_3_0 0x01030000
+#define IFC_TIMEOUT_MSECS 100000 /* 100ms */
/*
* convert_ifc_address - convert the base address
@@ -309,6 +312,161 @@ err:
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+/* save ifc registers */
+static int fsl_ifc_suspend(struct device *dev)
+{
+ struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(dev);
+ struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+ __be32 nand_evter_intr_en, cm_evter_intr_en, nor_evter_intr_en,
+ gpcm_evter_intr_en;
+
+ ctrl->saved_regs = kzalloc(sizeof(struct fsl_ifc_regs), GFP_KERNEL);
+ if (!ctrl->saved_regs)
+ return -ENOMEM;
+
+ cm_evter_intr_en = ifc_in32(&ifc->cm_evter_intr_en);
+ nand_evter_intr_en = ifc_in32(&ifc->ifc_nand.nand_evter_intr_en);
+ nor_evter_intr_en = ifc_in32(&ifc->ifc_nor.nor_evter_intr_en);
+ gpcm_evter_intr_en = ifc_in32(&ifc->ifc_gpcm.gpcm_evter_intr_en);
+
+/* IFC interrupts disabled */
+
+ ifc_out32(0x0, &ifc->cm_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_nor.nor_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_gpcm.gpcm_evter_intr_en);
+
+ memcpy_fromio(ctrl->saved_regs, ifc, sizeof(struct fsl_ifc_regs));
+
+/* save the interrupt values */
+ ctrl->saved_regs->cm_evter_intr_en = cm_evter_intr_en;
+ ctrl->saved_regs->ifc_nand.nand_evter_intr_en = nand_evter_intr_en;
+ ctrl->saved_regs->ifc_nor.nor_evter_intr_en = nor_evter_intr_en;
+ ctrl->saved_regs->ifc_gpcm.gpcm_evter_intr_en = gpcm_evter_intr_en;
+
+ return 0;
+}
+
+/* restore ifc registers */
+static int fsl_ifc_resume(struct device *dev)
+{
+ struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(dev);
+ struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+ struct fsl_ifc_regs *savd_regs = ctrl->saved_regs;
+ uint32_t ver = 0, ncfgr, status, ifc_bank, i;
+
+/*
+ * IFC interrupts disabled
+ */
+ ifc_out32(0x0, &ifc->cm_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_nor.nor_evter_intr_en);
+ ifc_out32(0x0, &ifc->ifc_gpcm.gpcm_evter_intr_en);
+
+
+ if (ctrl->saved_regs) {
+ for (ifc_bank = 0; ifc_bank < FSL_IFC_BANK_COUNT; ifc_bank++) {
+ ifc_out32(savd_regs->cspr_cs[ifc_bank].cspr_ext,
+ &ifc->cspr_cs[ifc_bank].cspr_ext);
+ ifc_out32(savd_regs->cspr_cs[ifc_bank].cspr,
+ &ifc->cspr_cs[ifc_bank].cspr);
+ ifc_out32(savd_regs->amask_cs[ifc_bank].amask,
+ &ifc->amask_cs[ifc_bank].amask);
+ ifc_out32(savd_regs->csor_cs[ifc_bank].csor_ext,
+ &ifc->csor_cs[ifc_bank].csor_ext);
+ ifc_out32(savd_regs->csor_cs[ifc_bank].csor,
+ &ifc->csor_cs[ifc_bank].csor);
+ for (i = 0; i < 4; i++) {
+ ifc_out32(savd_regs->ftim_cs[ifc_bank].ftim[i],
+ &ifc->ftim_cs[ifc_bank].ftim[i]);
+ }
+ }
+ ifc_out32(savd_regs->ifc_gcr, &ifc->ifc_gcr);
+ ifc_out32(savd_regs->cm_evter_en, &ifc->cm_evter_en);
+
+/*
+* IFC controller NAND machine registers
+*/
+ ifc_out32(savd_regs->ifc_nand.ncfgr, &ifc->ifc_nand.ncfgr);
+ ifc_out32(savd_regs->ifc_nand.nand_fcr0,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(savd_regs->ifc_nand.nand_fcr1,
+ &ifc->ifc_nand.nand_fcr1);
+ ifc_out32(savd_regs->ifc_nand.row0, &ifc->ifc_nand.row0);
+ ifc_out32(savd_regs->ifc_nand.row1, &ifc->ifc_nand.row1);
+ ifc_out32(savd_regs->ifc_nand.col0, &ifc->ifc_nand.col0);
+ ifc_out32(savd_regs->ifc_nand.col1, &ifc->ifc_nand.col1);
+ ifc_out32(savd_regs->ifc_nand.row2, &ifc->ifc_nand.row2);
+ ifc_out32(savd_regs->ifc_nand.col2, &ifc->ifc_nand.col2);
+ ifc_out32(savd_regs->ifc_nand.row3, &ifc->ifc_nand.row3);
+ ifc_out32(savd_regs->ifc_nand.col3, &ifc->ifc_nand.col3);
+ ifc_out32(savd_regs->ifc_nand.nand_fbcr,
+ &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(savd_regs->ifc_nand.nand_fir0,
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(savd_regs->ifc_nand.nand_fir1,
+ &ifc->ifc_nand.nand_fir1);
+ ifc_out32(savd_regs->ifc_nand.nand_fir2,
+ &ifc->ifc_nand.nand_fir2);
+ ifc_out32(savd_regs->ifc_nand.nand_csel,
+ &ifc->ifc_nand.nand_csel);
+ ifc_out32(savd_regs->ifc_nand.nandseq_strt,
+ &ifc->ifc_nand.nandseq_strt);
+ ifc_out32(savd_regs->ifc_nand.nand_evter_en,
+ &ifc->ifc_nand.nand_evter_en);
+ ifc_out32(savd_regs->ifc_nand.nanndcr, &ifc->ifc_nand.nanndcr);
+
+/*
+* IFC controller NOR machine registers
+*/
+ ifc_out32(savd_regs->ifc_nor.nor_evter_en,
+ &ifc->ifc_nor.nor_evter_en);
+ ifc_out32(savd_regs->ifc_nor.norcr, &ifc->ifc_nor.norcr);
+
+/*
+ * IFC controller GPCM Machine registers
+ */
+ ifc_out32(savd_regs->ifc_gpcm.gpcm_evter_en,
+ &ifc->ifc_gpcm.gpcm_evter_en);
+
+
+
+/*
+ * IFC interrupts enabled
+ */
+ ifc_out32(ctrl->saved_regs->cm_evter_intr_en, &ifc->cm_evter_intr_en);
+ ifc_out32(ctrl->saved_regs->ifc_nand.nand_evter_intr_en,
+ &ifc->ifc_nand.nand_evter_intr_en);
+ ifc_out32(ctrl->saved_regs->ifc_nor.nor_evter_intr_en,
+ &ifc->ifc_nor.nor_evter_intr_en);
+ ifc_out32(ctrl->saved_regs->ifc_gpcm.gpcm_evter_intr_en,
+ &ifc->ifc_gpcm.gpcm_evter_intr_en);
+
+ kfree(ctrl->saved_regs);
+ ctrl->saved_regs = NULL;
+ }
+
+ ver = ifc_in32(&ctrl->regs->ifc_rev);
+ ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr);
+ if (ver >= FSL_IFC_V1_3_0) {
+
+ ifc_out32(ncfgr | IFC_NAND_SRAM_INIT_EN,
+ &ifc->ifc_nand.ncfgr);
+ /* wait for SRAM_INIT bit to be clear or timeout */
+ status = spin_event_timeout(
+ !(ifc_in32(&ifc->ifc_nand.ncfgr)
+ & IFC_NAND_SRAM_INIT_EN),
+ IFC_TIMEOUT_MSECS, 0);
+
+ if (!status)
+ dev_err(ctrl->dev, "Timeout waiting for IFC SRAM INIT");
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
static const struct of_device_id fsl_ifc_match[] = {
{
.compatible = "fsl,ifc",
@@ -316,10 +474,15 @@ static const struct of_device_id fsl_ifc
{},
};
+static const struct dev_pm_ops ifc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_ifc_suspend, fsl_ifc_resume)
+};
+
static struct platform_driver fsl_ifc_ctrl_driver = {
.driver = {
.name = "fsl-ifc",
.of_match_table = fsl_ifc_match,
+ .pm = &ifc_pm_ops,
},
.probe = fsl_ifc_ctrl_probe,
.remove = fsl_ifc_ctrl_remove,
--- a/include/linux/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -270,6 +270,8 @@
*/
/* Auto Boot Mode */
#define IFC_NAND_NCFGR_BOOT 0x80000000
+/* SRAM INIT EN */
+#define IFC_NAND_SRAM_INIT_EN 0x20000000
/* Addressing Mode-ROW0+n/COL0 */
#define IFC_NAND_NCFGR_ADDR_MODE_RC0 0x00000000
/* Addressing Mode-ROW0+n/COL0+n */
@@ -842,6 +844,10 @@ struct fsl_ifc_ctrl {
u32 nand_stat;
wait_queue_head_t nand_wait;
bool little_endian;
+#ifdef CONFIG_PM_SLEEP
+ /*save regs when system goes to deep sleep*/
+ struct fsl_ifc_regs *saved_regs;
+#endif
};
extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
|