History

mmc: alcor: work with multiple-entry sglists

DMA on this hardware is limited to dealing with a 4096 bytes at a time. Previously, the driver was set up accordingly to request single-page DMA buffers, however that had the effect of generating a large number of small MMC requests for data I/O. Improve the driver to accept multi-entry scatter-gather lists. The size of each entry is already capped to 4096 bytes (AU6601_MAX_DMA_BLOCK_SIZE), matching the hardware requirements. Existing driver code already iterates through remaining sglist entries after each DMA transfer is complete. Also add some comments to help clarify the situation, and clear up some of the confusion I had regarding DMA vs PIO. Testing with dd, this increases write performance from 2mb/sec to 10mb/sec, and increases read performance from 4mb/sec to 14mb/sec.

Daniel Drake committed 7 years ago

Commit c671b6d1 parent f19337d2 files · +37 −19Browse files

Unified · Split

2 files
  1. drivers/mmc/host/alcor.c +36 −18
  2. include/linux/alcor_pci.h +1 −1
drivers/mmc/host/alcor.c+36−18
@@ -117,6 +117,9 @@ static void alcor_reset(struct alcor_sdmmc_host *host, u8 val) · 116 unmodified lines
117117 dev_err(host->dev, "%s: timeout\n", __func__);
118118 }
119119
120+/*
121+ * Perform DMA I/O of a single page.
122+ */
120123 static void alcor_data_set_dma(struct alcor_sdmmc_host *host)
121124 {
122125 struct alcor_pci_priv *priv = host->alcor_pci;
@@ -153,12 +156,26 @@ static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host) · 30 unmodified lines
153156 ctrl |= AU6601_DATA_WRITE;
154157
155158 if (data->host_cookie == COOKIE_MAPPED) {
159+ /*
160+ * For DMA transfers, this function is called just once,
161+ * at the start of the operation. The hardware can only
162+ * perform DMA I/O on a single page at a time, so here
163+ * we kick off the transfer with the first page, and expect
164+ * subsequent pages to be transferred upon IRQ events
165+ * indicating that the single-page DMA was completed.
166+ */
156167 alcor_data_set_dma(host);
157168 ctrl |= AU6601_DATA_DMA_MODE;
158169 host->dma_on = 1;
159170 alcor_write32(priv, data->sg_count * 0x1000,
160171 AU6601_REG_BLOCK_SIZE);
161172 } else {
173+ /*
174+ * For PIO transfers, we break down each operation
175+ * into several sector-sized transfers. When one sector has
176+ * complete, the IRQ handler will call this function again
177+ * to kick off the transfer of the next sector.
178+ */
162179 alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE);
163180 }
164181
@@ -776,8 +793,12 @@ static void alcor_pre_req(struct mmc_host *mmc, · 611 unmodified lines
776793 return;
777794 /*
778795 * We don't do DMA on "complex" transfers, i.e. with
779− * non-word-aligned buffers or lengths. Also, we don't bother
780− * with all the DMA setup overhead for short transfers.
796+ * non-word-aligned buffers or lengths. A future improvement
797+ * could be made to use temporary DMA bounce-buffers when these
798+ * requirements are not met.
799+ *
800+ * Also, we don't bother with all the DMA setup overhead for
801+ * short transfers.
781802 */
782803 if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE)
783804 return;
@@ -788,6 +809,8 @@ static void alcor_pre_req(struct mmc_host *mmc, · 4 unmodified lines
788809 for_each_sg(data->sg, sg, data->sg_len, i) {
789810 if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE)
790811 return;
812+ if (sg->offset != 0)
813+ return;
791814 }
792815
793816 /* This data might be unmapped at this time */
@@ -1038,26 +1061,21 @@ static void alcor_init_mmc(struct alcor_sdmmc_host *host) · 244 unmodified lines
10381061 mmc->ops = &alcor_sdc_ops;
10391062
10401063 /* The hardware does DMA data transfer of 4096 bytes to/from a single
1041− * buffer address. Scatterlists are not supported, but upon DMA
1042− * completion (signalled via IRQ), the original vendor driver does
1043− * then immediately set up another DMA transfer of the next 4096
1044− * bytes.
1045− *
1046− * This means that we need to handle the I/O in 4096 byte chunks.
1047− * Lacking a way to limit the sglist entries to 4096 bytes, we instead
1048− * impose that only one segment is provided, with maximum size 4096,
1049− * which also happens to be the minimum size. This means that the
1050− * single-entry sglist handled by this driver can be handed directly
1051− * to the hardware, nice and simple.
1064+ * buffer address. Scatterlists are not supported at the hardware
1065+ * level, however we can work with them at the driver level,
1066+ * provided that each segment is exactly 4096 bytes in size.
1067+ * Upon DMA completion of a single segment (signalled via IRQ), we
1068+ * immediately proceed to transfer the next segment from the
1069+ * scatterlist.
10521070 *
1053− * Unfortunately though, that means we only do 4096 bytes I/O per
1054− * MMC command. A future improvement would be to make the driver
1055− * accept sg lists and entries of any size, and simply iterate
1056− * through them 4096 bytes at a time.
1071+ * The overall request is limited to 240 sectors, matching the
1072+ * original vendor driver.
10571073 */
10581074 mmc->max_segs = AU6601_MAX_DMA_SEGMENTS;
10591075 mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE;
1060− mmc->max_req_size = mmc->max_seg_size;
1076+ mmc->max_blk_count = 240;
1077+ mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
1078+ dma_set_max_seg_size(host->dev, mmc->max_seg_size);
10611079 }
10621080
10631081 static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
include/linux/alcor_pci.h+1−1
@@ -23,7 +23,7 @@ · 22 unmodified lines
2323 #define AU6601_BASE_CLOCK 31000000
2424 #define AU6601_MIN_CLOCK 150000
2525 #define AU6601_MAX_CLOCK 208000000
26−#define AU6601_MAX_DMA_SEGMENTS 1
26+#define AU6601_MAX_DMA_SEGMENTS 64
2727 #define AU6601_MAX_PIO_SEGMENTS 1
2828 #define AU6601_MAX_DMA_BLOCK_SIZE 0x1000
2929 #define AU6601_MAX_PIO_BLOCK_SIZE 0x200

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t