[glsdk/meta-ti-glsdk.git] / recipes-kernel / linux / linux-ti33x-psp-3.2 / 3.2.7 / 0027-mmc-dw_mmc-Fix-PIO-mode-with-support-of-highmem.patch
1 From 639e92e01ab0ae188751ea83327e4720d37909c4 Mon Sep 17 00:00:00 2001
2 From: Seungwon Jeon <tgih.jun@samsung.com>
3 Date: Thu, 9 Feb 2012 14:32:43 +0900
4 Subject: [PATCH 27/30] mmc: dw_mmc: Fix PIO mode with support of highmem
6 commit f9c2a0dc42a6938ff2a80e55ca2bbd1d5581c72e upstream.
8 Current PIO mode makes a kernel crash with CONFIG_HIGHMEM.
9 Highmem pages have a NULL from sg_virt(sg).
10 This patch fixes the following problem.
12 Unable to handle kernel NULL pointer dereference at virtual address 00000000
13 pgd = c0004000
14 [00000000] *pgd=00000000
15 Internal error: Oops: 817 [#1] PREEMPT SMP
16 Modules linked in:
17 CPU: 0 Not tainted (3.0.15-01423-gdbf465f #589)
18 PC is at dw_mci_pull_data32+0x4c/0x9c
19 LR is at dw_mci_read_data_pio+0x54/0x1f0
20 pc : [<c0358824>] lr : [<c035988c>] psr: 20000193
21 sp : c0619d48 ip : c0619d70 fp : c0619d6c
22 r10: 00000000 r9 : 00000002 r8 : 00001000
23 r7 : 00000200 r6 : 00000000 r5 : e1dd3100 r4 : 00000000
24 r3 : 65622023 r2 : 0000007f r1 : eeb96000 r0 : e1dd3100
25 Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment
26 xkernel
27 Control: 10c5387d Table: 61e2004a DAC: 00000015
28 Process swapper (pid: 0, stack limit = 0xc06182f0)
29 Stack: (0xc0619d48 to 0xc061a000)
30 9d40: e1dd3100 e1a4f000 00000000 e1dd3100 e1a4f000 00000200
31 9d60: c0619da4 c0619d70 c035988c c03587e4 c0619d9c e18158f4 e1dd3100 e1dd3100
32 9d80: 00000020 00000000 00000000 00000020 c06e8a84 00000000 c0619e04 c0619da8
33 9da0: c0359b24 c0359844 e18158f4 e1dd3164 e1dd3168 e1dd3150 3d02fc79 e1dd3154
34 9dc0: e1dd3178 00000000 00000020 00000000 e1dd3150 00000000 c10dd7e8 e1a84900
35 9de0: c061e7cc 00000000 00000000 0000008d c06e8a84 c061e780 c0619e4c c0619e08
36 9e00: c00c4738 c0359a34 3d02fc79 00000000 c0619e4c c05a1698 c05a1670 c05a165c
37 9e20: c04de8b0 c061e780 c061e7cc e1a84900 ffffed68 0000008d c0618000 00000000
38 9e40: c0619e6c c0619e50 c00c48b4 c00c46c8 c061e780 c00423ac c061e7cc ffffed68
39 9e60: c0619e8c c0619e70 c00c7358 c00c487c 0000008d ffffee38 c0618000 ffffed68
40 9e80: c0619ea4 c0619e90 c00c4258 c00c72b0 c00423ac ffffee38 c0619ecc c0619ea8
41 9ea0: c004241c c00c4234 ffffffff f8810000 0000006d 00000002 00000001 7fffffff
42 9ec0: c0619f44 c0619ed0 c0048bc0 c00423c4 220ae7a9 00000000 386f0d30 0005d3a4
43 9ee0: c00423ac c10dd0b8 c06f2cd8 c0618000 c0594778 c003a674 7fffffff c0619f44
44 9f00: 386f0d30 c0619f18 c00a6f94 c005be3c 80000013 ffffffff 386f0d30 0005d3a4
45 9f20: 386f0d30 0005d2d1 c10dd0a8 c10dd0b8 c06f2cd8 c0618000 c0619f74 c0619f48
46 9f40: c0345858 c005be00 c00a2440 c0618000 c0618000 c00410d8 c06c1944 c00410fc
47 9f60: c0594778 c003a674 c0619f9c c0619f78 c004a7e8 c03457b4 c0618000 c06c18f8
48 9f80: 00000000 c0039c70 c06c18d4 c003a674 c0619fb4 c0619fa0 c04ceafc c004a714
49 9fa0: c06287b4 c06c18f8 c0619ff4 c0619fb8 c0008b68 c04cea68 c0008578 00000000
50 9fc0: 00000000 c003a674 00000000 10c5387d c0628658 c003aa78 c062f1c4 4000406a
51 9fe0: 413fc090 00000000 00000000 c0619ff8 40008044 c0008858 00000000 00000000
52 Backtrace:
53 [<c03587d8>] (dw_mci_pull_data32+0x0/0x9c) from [<c035988c>] (dw_mci_read_data_pio+0x54/0x1f0)
54 r6:00000200 r5:e1a4f000 r4:e1dd3100
55 [<c0359838>] (dw_mci_read_data_pio+0x0/0x1f0) from [<c0359b24>] (dw_mci_interrupt+0xfc/0x4a4)
56 [<c0359a28>] (dw_mci_interrupt+0x0/0x4a4) from [<c00c4738>] (handle_irq_event_percpu+0x7c/0x1b4)
57 [<c00c46bc>] (handle_irq_event_percpu+0x0/0x1b4) from [<c00c48b4>] (handle_irq_event+0x44/0x64)
58 [<c00c4870>] (handle_irq_event+0x0/0x64) from [<c00c7358>] (handle_fasteoi_irq+0xb4/0x124)
59 r7:ffffed68 r6:c061e7cc r5:c00423ac r4:c061e780
60 [<c00c72a4>] (handle_fasteoi_irq+0x0/0x124) from [<c00c4258>] (generic_handle_irq+0x30/0x38)
61 r7:ffffed68 r6:c0618000 r5:ffffee38 r4:0000008d
62 [<c00c4228>] (generic_handle_irq+0x0/0x38) from [<c004241c>] (asm_do_IRQ+0x64/0xe0)
63 r5:ffffee38 r4:c00423ac
64 [<c00423b8>] (asm_do_IRQ+0x0/0xe0) from [<c0048bc0>] (__irq_svc+0x80/0x14c)
65 Exception stack(0xc0619ed0 to 0xc0619f18)
67 Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
68 Acked-by: Will Newton <will.newton@imgtec.com>
69 Signed-off-by: Chris Ball <cjb@laptop.org>
70 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
71 ---
72 drivers/mmc/host/dw_mmc.c | 144 +++++++++++++++++++++++---------------------
73 include/linux/mmc/dw_mmc.h | 6 +-
74 2 files changed, 79 insertions(+), 71 deletions(-)
76 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
77 index 3aaeb08..baf3d42 100644
78 --- a/drivers/mmc/host/dw_mmc.c
79 +++ b/drivers/mmc/host/dw_mmc.c
80 @@ -22,7 +22,6 @@
81 #include <linux/ioport.h>
82 #include <linux/module.h>
83 #include <linux/platform_device.h>
84 -#include <linux/scatterlist.h>
85 #include <linux/seq_file.h>
86 #include <linux/slab.h>
87 #include <linux/stat.h>
88 @@ -502,8 +501,14 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
89 host->dir_status = DW_MCI_SEND_STATUS;
91 if (dw_mci_submit_data_dma(host, data)) {
92 + int flags = SG_MITER_ATOMIC;
93 + if (host->data->flags & MMC_DATA_READ)
94 + flags |= SG_MITER_TO_SG;
95 + else
96 + flags |= SG_MITER_FROM_SG;
97 +
98 + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
99 host->sg = data->sg;
100 - host->pio_offset = 0;
101 host->part_buf_start = 0;
102 host->part_buf_count = 0;
104 @@ -953,6 +958,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
105 * generates a block interrupt, hence setting
106 * the scatter-gather pointer to NULL.
107 */
108 + sg_miter_stop(&host->sg_miter);
109 host->sg = NULL;
110 ctrl = mci_readl(host, CTRL);
111 ctrl |= SDMMC_CTRL_FIFO_RESET;
112 @@ -1286,54 +1292,44 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
114 static void dw_mci_read_data_pio(struct dw_mci *host)
115 {
116 - struct scatterlist *sg = host->sg;
117 - void *buf = sg_virt(sg);
118 - unsigned int offset = host->pio_offset;
119 + struct sg_mapping_iter *sg_miter = &host->sg_miter;
120 + void *buf;
121 + unsigned int offset;
122 struct mmc_data *data = host->data;
123 int shift = host->data_shift;
124 u32 status;
125 unsigned int nbytes = 0, len;
126 + unsigned int remain, fcnt;
128 do {
129 - len = host->part_buf_count +
130 - (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
131 - if (offset + len <= sg->length) {
132 + if (!sg_miter_next(sg_miter))
133 + goto done;
134 +
135 + host->sg = sg_miter->__sg;
136 + buf = sg_miter->addr;
137 + remain = sg_miter->length;
138 + offset = 0;
139 +
140 + do {
141 + fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS))
142 + << shift) + host->part_buf_count;
143 + len = min(remain, fcnt);
144 + if (!len)
145 + break;
146 dw_mci_pull_data(host, (void *)(buf + offset), len);
147 -
148 offset += len;
149 nbytes += len;
150 -
151 - if (offset == sg->length) {
152 - flush_dcache_page(sg_page(sg));
153 - host->sg = sg = sg_next(sg);
154 - if (!sg)
155 - goto done;
156 -
157 - offset = 0;
158 - buf = sg_virt(sg);
159 - }
160 - } else {
161 - unsigned int remaining = sg->length - offset;
162 - dw_mci_pull_data(host, (void *)(buf + offset),
163 - remaining);
164 - nbytes += remaining;
165 -
166 - flush_dcache_page(sg_page(sg));
167 - host->sg = sg = sg_next(sg);
168 - if (!sg)
169 - goto done;
170 -
171 - offset = len - remaining;
172 - buf = sg_virt(sg);
173 - dw_mci_pull_data(host, buf, offset);
174 - nbytes += offset;
175 - }
176 + remain -= len;
177 + } while (remain);
178 + sg_miter->consumed = offset;
180 status = mci_readl(host, MINTSTS);
181 mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
182 if (status & DW_MCI_DATA_ERROR_FLAGS) {
183 host->data_status = status;
184 data->bytes_xfered += nbytes;
185 + sg_miter_stop(sg_miter);
186 + host->sg = NULL;
187 smp_wmb();
189 set_bit(EVENT_DATA_ERROR, &host->pending_events);
190 @@ -1342,65 +1338,66 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
191 return;
192 }
193 } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
194 - host->pio_offset = offset;
195 data->bytes_xfered += nbytes;
196 +
197 + if (!remain) {
198 + if (!sg_miter_next(sg_miter))
199 + goto done;
200 + sg_miter->consumed = 0;
201 + }
202 + sg_miter_stop(sg_miter);
203 return;
205 done:
206 data->bytes_xfered += nbytes;
207 + sg_miter_stop(sg_miter);
208 + host->sg = NULL;
209 smp_wmb();
210 set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
211 }
213 static void dw_mci_write_data_pio(struct dw_mci *host)
214 {
215 - struct scatterlist *sg = host->sg;
216 - void *buf = sg_virt(sg);
217 - unsigned int offset = host->pio_offset;
218 + struct sg_mapping_iter *sg_miter = &host->sg_miter;
219 + void *buf;
220 + unsigned int offset;
221 struct mmc_data *data = host->data;
222 int shift = host->data_shift;
223 u32 status;
224 unsigned int nbytes = 0, len;
225 + unsigned int fifo_depth = host->fifo_depth;
226 + unsigned int remain, fcnt;
228 do {
229 - len = ((host->fifo_depth -
230 - SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift)
231 - - host->part_buf_count;
232 - if (offset + len <= sg->length) {
233 + if (!sg_miter_next(sg_miter))
234 + goto done;
235 +
236 + host->sg = sg_miter->__sg;
237 + buf = sg_miter->addr;
238 + remain = sg_miter->length;
239 + offset = 0;
240 +
241 + do {
242 + fcnt = ((fifo_depth -
243 + SDMMC_GET_FCNT(mci_readl(host, STATUS)))
244 + << shift) - host->part_buf_count;
245 + len = min(remain, fcnt);
246 + if (!len)
247 + break;
248 host->push_data(host, (void *)(buf + offset), len);
249 -
250 offset += len;
251 nbytes += len;
252 - if (offset == sg->length) {
253 - host->sg = sg = sg_next(sg);
254 - if (!sg)
255 - goto done;
256 -
257 - offset = 0;
258 - buf = sg_virt(sg);
259 - }
260 - } else {
261 - unsigned int remaining = sg->length - offset;
262 -
263 - host->push_data(host, (void *)(buf + offset),
264 - remaining);
265 - nbytes += remaining;
266 -
267 - host->sg = sg = sg_next(sg);
268 - if (!sg)
269 - goto done;
270 -
271 - offset = len - remaining;
272 - buf = sg_virt(sg);
273 - host->push_data(host, (void *)buf, offset);
274 - nbytes += offset;
275 - }
276 + remain -= len;
277 + } while (remain);
278 + sg_miter->consumed = offset;
280 status = mci_readl(host, MINTSTS);
281 mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
282 if (status & DW_MCI_DATA_ERROR_FLAGS) {
283 host->data_status = status;
284 data->bytes_xfered += nbytes;
285 + sg_miter_stop(sg_miter);
286 + host->sg = NULL;
288 smp_wmb();
290 @@ -1410,12 +1407,20 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
291 return;
292 }
293 } while (status & SDMMC_INT_TXDR); /* if TXDR write again */
294 - host->pio_offset = offset;
295 data->bytes_xfered += nbytes;
296 +
297 + if (!remain) {
298 + if (!sg_miter_next(sg_miter))
299 + goto done;
300 + sg_miter->consumed = 0;
301 + }
302 + sg_miter_stop(sg_miter);
303 return;
305 done:
306 data->bytes_xfered += nbytes;
307 + sg_miter_stop(sg_miter);
308 + host->sg = NULL;
309 smp_wmb();
310 set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
311 }
312 @@ -1618,6 +1623,7 @@ static void dw_mci_work_routine_card(struct work_struct *work)
313 * block interrupt, hence setting the
314 * scatter-gather pointer to NULL.
315 */
316 + sg_miter_stop(&host->sg_miter);
317 host->sg = NULL;
319 ctrl = mci_readl(host, CTRL);
320 diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
321 index 6dc9b80..107fcb3 100644
322 --- a/include/linux/mmc/dw_mmc.h
323 +++ b/include/linux/mmc/dw_mmc.h
324 @@ -14,6 +14,8 @@
325 #ifndef LINUX_MMC_DW_MMC_H
326 #define LINUX_MMC_DW_MMC_H
328 +#include <linux/scatterlist.h>
329 +
330 #define MAX_MCI_SLOTS 2
332 enum dw_mci_state {
333 @@ -40,7 +42,7 @@ struct mmc_data;
334 * @lock: Spinlock protecting the queue and associated data.
335 * @regs: Pointer to MMIO registers.
336 * @sg: Scatterlist entry currently being processed by PIO code, if any.
337 - * @pio_offset: Offset into the current scatterlist entry.
338 + * @sg_miter: PIO mapping scatterlist iterator.
339 * @cur_slot: The slot which is currently using the controller.
340 * @mrq: The request currently being processed on @cur_slot,
341 * or NULL if the controller is idle.
342 @@ -115,7 +117,7 @@ struct dw_mci {
343 void __iomem *regs;
345 struct scatterlist *sg;
346 - unsigned int pio_offset;
347 + struct sg_mapping_iter sg_miter;
349 struct dw_mci_slot *cur_slot;
350 struct mmc_request *mrq;
351 --
352 1.7.9.4