Skip to content

Commit 851b250

Browse files
committed
mmc: dw_mmc: handle data blocks > than 4kB if IDMAC is used
As per DW MobileStorage databook "each descriptor can transfer up to 4kB of data in chained mode", moreover buffer size that is put in "des1" is limited to 13 bits, i.e. for example on attempt to IDMAC_SET_BUFFER1_SIZE(desc, 8192) size value that's effectively written will be 0. On the platform with 8kB PAGE_SIZE I see dw_mmc gets data blocks in SG-list of 8kB size and that leads to unpredictable behavior of the SD/MMC controller. In particular on write to FAT partition of SD-card the controller will stuck in the middle of DMA transaction. Solution to the problem is simple - we need to pass large (> 4kB) data buffers to the controller via multiple descriptors. And that's what that change does. What's interesting I did try original driver on same platform but configured with 4kB PAGE_SIZE and may confirm that data blocks passed in SG-list to dw_mmc never exeed 4kB limit - that explains why nobody ever faced a problem I did. Signed-off-by: Alexey Brodkin <[email protected]> Cc: [email protected]
1 parent f2105b2 commit 851b250

File tree

1 file changed

+36
-14
lines changed

1 file changed

+36
-14
lines changed

drivers/mmc/host/dw_mmc.c

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ struct idmac_desc {
8080

8181
u32 des3; /* buffer 2 physical address */
8282
};
83+
84+
/* Each descriptor can transfer up to 4KB of data in chained mode */
85+
#define DW_MCI_DESC_DATA_LENGTH 0x1000
8386
#endif /* CONFIG_MMC_DW_IDMAC */
8487

8588
static bool dw_mci_reset(struct dw_mci *host);
@@ -414,30 +417,49 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
414417
unsigned int sg_len)
415418
{
416419
int i;
417-
struct idmac_desc *desc = host->sg_cpu;
420+
unsigned int desc_len;
421+
struct idmac_desc *desc_first, *desc_last, *desc;
422+
423+
desc_first = desc_last = desc = host->sg_cpu;
418424

419-
for (i = 0; i < sg_len; i++, desc++) {
425+
for (i = 0; i < sg_len; i++) {
420426
unsigned int length = sg_dma_len(&data->sg[i]);
421427
u32 mem_addr = sg_dma_address(&data->sg[i]);
422428

423-
/* Set the OWN bit and disable interrupts for this descriptor */
424-
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
429+
for ( ; length ; desc++) {
430+
/* Make sure we're not putting too much of data */
431+
desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ?
432+
length : DW_MCI_DESC_DATA_LENGTH;
433+
434+
length -= desc_len;
425435

426-
/* Buffer length */
427-
IDMAC_SET_BUFFER1_SIZE(desc, length);
436+
/*
437+
* Set the OWN bit and disable interrupts
438+
* for this descriptor
439+
*/
440+
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
441+
IDMAC_DES0_CH;
428442

429-
/* Physical address to DMA to/from */
430-
desc->des2 = mem_addr;
443+
/* Buffer length */
444+
IDMAC_SET_BUFFER1_SIZE(desc, desc_len);
445+
446+
/* Physical address to DMA to/from */
447+
desc->des2 = mem_addr;
448+
449+
/* Update physical address for the next descriptor */
450+
mem_addr += desc_len;
451+
452+
/* Save pointer to the last descriptor */
453+
desc_last = desc;
454+
}
431455
}
432456

433457
/* Set first descriptor */
434-
desc = host->sg_cpu;
435-
desc->des0 |= IDMAC_DES0_FD;
458+
desc_first->des0 |= IDMAC_DES0_FD;
436459

437460
/* Set last descriptor */
438-
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
439-
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
440-
desc->des0 |= IDMAC_DES0_LD;
461+
desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
462+
desc_last->des0 |= IDMAC_DES0_LD;
441463

442464
wmb();
443465
}
@@ -2274,7 +2296,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
22742296
mmc->max_segs = host->ring_size;
22752297
mmc->max_blk_size = 65536;
22762298
mmc->max_blk_count = host->ring_size;
2277-
mmc->max_seg_size = 0x1000;
2299+
mmc->max_seg_size = DW_MCI_DESC_DATA_LENGTH;
22782300
mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
22792301
#else
22802302
mmc->max_segs = 64;

0 commit comments

Comments
 (0)