aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2006-07-02 11:50:59 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-07-02 11:50:59 -0400
commita406f5a3b68ee1db2306a2ba1c9b00dbd3505d05 (patch)
tree47434d59bab5732b7154522e14923c591c666360
parent3192a28f7d34ea8f1d0fef8ca5bc0314b5b5bb19 (diff)
[MMC] Fix sdhci PIO routines
The sdhci controllers operate with blocks, not bytes. The PIO routines must therefore make sure that the minimum unit transfered is a complete block. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--drivers/mmc/sdhci.c158
-rw-r--r--drivers/mmc/sdhci.h6
2 files changed, 101 insertions, 63 deletions
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index b9aa60aed7f0..8e480140cd28 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -8,12 +8,6 @@
8 * published by the Free Software Foundation. 8 * published by the Free Software Foundation.
9 */ 9 */
10 10
11 /*
12 * Note that PIO transfer is rather crappy atm. The buffer full/empty
13 * interrupts aren't reliable so we currently transfer the entire buffer
14 * directly. Patches to solve the problem are welcome.
15 */
16
17#include <linux/delay.h> 11#include <linux/delay.h>
18#include <linux/highmem.h> 12#include <linux/highmem.h>
19#include <linux/pci.h> 13#include <linux/pci.h>
@@ -128,7 +122,7 @@ static void sdhci_init(struct sdhci_host *host)
128 SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | 122 SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
129 SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | 123 SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
130 SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | 124 SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
131 SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | 125 SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
132 SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; 126 SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE;
133 127
134 writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); 128 writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
@@ -189,79 +183,96 @@ static inline int sdhci_next_sg(struct sdhci_host* host)
189 return host->num_sg; 183 return host->num_sg;
190} 184}
191 185
192static void sdhci_transfer_pio(struct sdhci_host *host) 186static void sdhci_read_block_pio(struct sdhci_host *host)
193{ 187{
188 int blksize, chunk_remain;
189 u32 data;
194 char *buffer; 190 char *buffer;
195 u32 mask; 191 int size;
196 int bytes, size;
197 unsigned long max_jiffies;
198 192
199 BUG_ON(!host->data); 193 DBG("PIO reading\n");
200 194
201 if (host->num_sg == 0) 195 blksize = host->data->blksz;
202 return; 196 chunk_remain = 0;
203 197 data = 0;
204 bytes = 0;
205 if (host->data->flags & MMC_DATA_READ)
206 mask = SDHCI_DATA_AVAILABLE;
207 else
208 mask = SDHCI_SPACE_AVAILABLE;
209 198
210 buffer = sdhci_kmap_sg(host) + host->offset; 199 buffer = sdhci_kmap_sg(host) + host->offset;
211 200
212 /* Transfer shouldn't take more than 5 s */ 201 while (blksize) {
213 max_jiffies = jiffies + HZ * 5; 202 if (chunk_remain == 0) {
203 data = readl(host->ioaddr + SDHCI_BUFFER);
204 chunk_remain = min(blksize, 4);
205 }
214 206
215 while (host->size > 0) { 207 size = min(host->size, host->remain);
216 if (time_after(jiffies, max_jiffies)) { 208 size = min(size, chunk_remain);
217 printk(KERN_ERR "%s: PIO transfer stalled. "
218 "Please report this to "
219 BUGMAIL ".\n", mmc_hostname(host->mmc));
220 sdhci_dumpregs(host);
221 209
222 sdhci_kunmap_sg(host); 210 chunk_remain -= size;
211 blksize -= size;
212 host->offset += size;
213 host->remain -= size;
214 host->size -= size;
215 while (size) {
216 *buffer = data & 0xFF;
217 buffer++;
218 data >>= 8;
219 size--;
220 }
223 221
224 host->data->error = MMC_ERR_FAILED; 222 if (host->remain == 0) {
225 sdhci_finish_data(host); 223 sdhci_kunmap_sg(host);
226 return; 224 if (sdhci_next_sg(host) == 0) {
225 BUG_ON(blksize != 0);
226 return;
227 }
228 buffer = sdhci_kmap_sg(host);
227 } 229 }
230 }
228 231
229 if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask)) 232 sdhci_kunmap_sg(host);
230 continue; 233}
231 234
232 size = min(host->size, host->remain); 235static void sdhci_write_block_pio(struct sdhci_host *host)
236{
237 int blksize, chunk_remain;
238 u32 data;
239 char *buffer;
240 int bytes, size;
233 241
234 if (size >= 4) { 242 DBG("PIO writing\n");
235 if (host->data->flags & MMC_DATA_READ) 243
236 *(u32*)buffer = readl(host->ioaddr + SDHCI_BUFFER); 244 blksize = host->data->blksz;
237 else 245 chunk_remain = 4;
238 writel(*(u32*)buffer, host->ioaddr + SDHCI_BUFFER); 246 data = 0;
239 size = 4; 247
240 } else if (size >= 2) { 248 bytes = 0;
241 if (host->data->flags & MMC_DATA_READ) 249 buffer = sdhci_kmap_sg(host) + host->offset;
242 *(u16*)buffer = readw(host->ioaddr + SDHCI_BUFFER); 250
243 else 251 while (blksize) {
244 writew(*(u16*)buffer, host->ioaddr + SDHCI_BUFFER); 252 size = min(host->size, host->remain);
245 size = 2; 253 size = min(size, chunk_remain);
246 } else {
247 if (host->data->flags & MMC_DATA_READ)
248 *(u8*)buffer = readb(host->ioaddr + SDHCI_BUFFER);
249 else
250 writeb(*(u8*)buffer, host->ioaddr + SDHCI_BUFFER);
251 size = 1;
252 }
253 254
254 buffer += size; 255 chunk_remain -= size;
256 blksize -= size;
255 host->offset += size; 257 host->offset += size;
256 host->remain -= size; 258 host->remain -= size;
257
258 bytes += size;
259 host->size -= size; 259 host->size -= size;
260 while (size) {
261 data >>= 8;
262 data |= (u32)*buffer << 24;
263 buffer++;
264 size--;
265 }
266
267 if (chunk_remain == 0) {
268 writel(data, host->ioaddr + SDHCI_BUFFER);
269 chunk_remain = min(blksize, 4);
270 }
260 271
261 if (host->remain == 0) { 272 if (host->remain == 0) {
262 sdhci_kunmap_sg(host); 273 sdhci_kunmap_sg(host);
263 if (sdhci_next_sg(host) == 0) { 274 if (sdhci_next_sg(host) == 0) {
264 DBG("PIO transfer: %d bytes\n", bytes); 275 BUG_ON(blksize != 0);
265 return; 276 return;
266 } 277 }
267 buffer = sdhci_kmap_sg(host); 278 buffer = sdhci_kmap_sg(host);
@@ -269,8 +280,35 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
269 } 280 }
270 281
271 sdhci_kunmap_sg(host); 282 sdhci_kunmap_sg(host);
283}
284
285static void sdhci_transfer_pio(struct sdhci_host *host)
286{
287 u32 mask;
288
289 BUG_ON(!host->data);
290
291 if (host->size == 0)
292 return;
293
294 if (host->data->flags & MMC_DATA_READ)
295 mask = SDHCI_DATA_AVAILABLE;
296 else
297 mask = SDHCI_SPACE_AVAILABLE;
298
299 while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
300 if (host->data->flags & MMC_DATA_READ)
301 sdhci_read_block_pio(host);
302 else
303 sdhci_write_block_pio(host);
304
305 if (host->size == 0)
306 break;
307
308 BUG_ON(host->num_sg == 0);
309 }
272 310
273 DBG("PIO transfer: %d bytes\n", bytes); 311 DBG("PIO transfer complete.\n");
274} 312}
275 313
276static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) 314static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
@@ -863,7 +901,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
863 if (host->data->error != MMC_ERR_NONE) 901 if (host->data->error != MMC_ERR_NONE)
864 sdhci_finish_data(host); 902 sdhci_finish_data(host);
865 else { 903 else {
866 if (intmask & (SDHCI_INT_BUF_FULL | SDHCI_INT_BUF_EMPTY)) 904 if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
867 sdhci_transfer_pio(host); 905 sdhci_transfer_pio(host);
868 906
869 if (intmask & SDHCI_INT_DATA_END) 907 if (intmask & SDHCI_INT_DATA_END)
diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h
index a8f45215ef3a..f8df28f8d6dd 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -95,8 +95,8 @@
95#define SDHCI_INT_RESPONSE 0x00000001 95#define SDHCI_INT_RESPONSE 0x00000001
96#define SDHCI_INT_DATA_END 0x00000002 96#define SDHCI_INT_DATA_END 0x00000002
97#define SDHCI_INT_DMA_END 0x00000008 97#define SDHCI_INT_DMA_END 0x00000008
98#define SDHCI_INT_BUF_EMPTY 0x00000010 98#define SDHCI_INT_SPACE_AVAIL 0x00000010
99#define SDHCI_INT_BUF_FULL 0x00000020 99#define SDHCI_INT_DATA_AVAIL 0x00000020
100#define SDHCI_INT_CARD_INSERT 0x00000040 100#define SDHCI_INT_CARD_INSERT 0x00000040
101#define SDHCI_INT_CARD_REMOVE 0x00000080 101#define SDHCI_INT_CARD_REMOVE 0x00000080
102#define SDHCI_INT_CARD_INT 0x00000100 102#define SDHCI_INT_CARD_INT 0x00000100
@@ -116,7 +116,7 @@
116#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ 116#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
117 SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) 117 SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
118#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ 118#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
119 SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | \ 119 SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
120 SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ 120 SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
121 SDHCI_INT_DATA_END_BIT) 121 SDHCI_INT_DATA_END_BIT)
122 122