diff options
author | Heiner Kallweit <hkallweit1@gmail.com> | 2017-04-07 15:22:44 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2017-04-24 15:42:21 -0400 |
commit | 79ed05e329c34c68ab2a87f729e666fcf208fdad (patch) | |
tree | c3fdf8c4648f371abf3eb7edb4eab654bf64f685 | |
parent | 0b6ed71c3e95abf6cd1dbfc53919defdce3449f5 (diff) |
mmc: meson-gx: add support for descriptor chain mode
So far a bounce buffer is used to serialize the scatterlist(s).
This overhead can be avoided by switching to descriptor chain mode.
As result the performance is drastically improved. On a Odroid-C2 with
a 128 GB eMMC module raw reads reach 140 MB/s.
Prerequisite for descriptor chain mode is that all scatterlist buffers
are 8 byte aligned for 64-bit DMA. That's not always the case, at least
the brcmfmac SDIO WiFi driver is known to cause problems.
Therefore, for each request, check upfront whether all scatterlist
buffers are 8 byte aligned and fall back to bounce buffer mode if
that's not the case.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r-- | drivers/mmc/host/meson-gx-mmc.c | 172 |
1 files changed, 156 insertions, 16 deletions
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index eb5ee68dbd46..1842ed341af1 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c | |||
@@ -111,6 +111,11 @@ | |||
111 | #define SD_EMMC_CMD_TIMEOUT 1024 /* in ms */ | 111 | #define SD_EMMC_CMD_TIMEOUT 1024 /* in ms */ |
112 | #define SD_EMMC_CMD_TIMEOUT_DATA 4096 /* in ms */ | 112 | #define SD_EMMC_CMD_TIMEOUT_DATA 4096 /* in ms */ |
113 | #define SD_EMMC_CFG_CMD_GAP 16 /* in clock cycles */ | 113 | #define SD_EMMC_CFG_CMD_GAP 16 /* in clock cycles */ |
114 | #define SD_EMMC_DESC_BUF_LEN PAGE_SIZE | ||
115 | |||
116 | #define SD_EMMC_PRE_REQ_DONE BIT(0) | ||
117 | #define SD_EMMC_DESC_CHAIN_MODE BIT(1) | ||
118 | |||
114 | #define MUX_CLK_NUM_PARENTS 2 | 119 | #define MUX_CLK_NUM_PARENTS 2 |
115 | 120 | ||
116 | struct meson_tuning_params { | 121 | struct meson_tuning_params { |
@@ -119,6 +124,13 @@ struct meson_tuning_params { | |||
119 | u8 rx_phase; | 124 | u8 rx_phase; |
120 | }; | 125 | }; |
121 | 126 | ||
127 | struct sd_emmc_desc { | ||
128 | u32 cmd_cfg; | ||
129 | u32 cmd_arg; | ||
130 | u32 cmd_data; | ||
131 | u32 cmd_resp; | ||
132 | }; | ||
133 | |||
122 | struct meson_host { | 134 | struct meson_host { |
123 | struct device *dev; | 135 | struct device *dev; |
124 | struct mmc_host *mmc; | 136 | struct mmc_host *mmc; |
@@ -137,18 +149,13 @@ struct meson_host { | |||
137 | unsigned int bounce_buf_size; | 149 | unsigned int bounce_buf_size; |
138 | void *bounce_buf; | 150 | void *bounce_buf; |
139 | dma_addr_t bounce_dma_addr; | 151 | dma_addr_t bounce_dma_addr; |
152 | struct sd_emmc_desc *descs; | ||
153 | dma_addr_t descs_dma_addr; | ||
140 | 154 | ||
141 | struct meson_tuning_params tp; | 155 | struct meson_tuning_params tp; |
142 | bool vqmmc_enabled; | 156 | bool vqmmc_enabled; |
143 | }; | 157 | }; |
144 | 158 | ||
145 | struct sd_emmc_desc { | ||
146 | u32 cmd_cfg; | ||
147 | u32 cmd_arg; | ||
148 | u32 cmd_data; | ||
149 | u32 cmd_resp; | ||
150 | }; | ||
151 | |||
152 | #define CMD_CFG_LENGTH_MASK GENMASK(8, 0) | 159 | #define CMD_CFG_LENGTH_MASK GENMASK(8, 0) |
153 | #define CMD_CFG_BLOCK_MODE BIT(9) | 160 | #define CMD_CFG_BLOCK_MODE BIT(9) |
154 | #define CMD_CFG_R1B BIT(10) | 161 | #define CMD_CFG_R1B BIT(10) |
@@ -195,6 +202,66 @@ static struct mmc_command *meson_mmc_get_next_command(struct mmc_command *cmd) | |||
195 | return NULL; | 202 | return NULL; |
196 | } | 203 | } |
197 | 204 | ||
205 | static void meson_mmc_get_transfer_mode(struct mmc_host *mmc, | ||
206 | struct mmc_request *mrq) | ||
207 | { | ||
208 | struct mmc_data *data = mrq->data; | ||
209 | struct scatterlist *sg; | ||
210 | int i; | ||
211 | bool use_desc_chain_mode = true; | ||
212 | |||
213 | for_each_sg(data->sg, sg, data->sg_len, i) | ||
214 | /* check for 8 byte alignment */ | ||
215 | if (sg->offset & 7) { | ||
216 | WARN_ONCE(1, "unaligned scatterlist buffer\n"); | ||
217 | use_desc_chain_mode = false; | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | if (use_desc_chain_mode) | ||
222 | data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE; | ||
223 | } | ||
224 | |||
225 | static inline bool meson_mmc_desc_chain_mode(const struct mmc_data *data) | ||
226 | { | ||
227 | return data->host_cookie & SD_EMMC_DESC_CHAIN_MODE; | ||
228 | } | ||
229 | |||
230 | static inline bool meson_mmc_bounce_buf_read(const struct mmc_data *data) | ||
231 | { | ||
232 | return data && data->flags & MMC_DATA_READ && | ||
233 | !meson_mmc_desc_chain_mode(data); | ||
234 | } | ||
235 | |||
236 | static void meson_mmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) | ||
237 | { | ||
238 | struct mmc_data *data = mrq->data; | ||
239 | |||
240 | if (!data) | ||
241 | return; | ||
242 | |||
243 | meson_mmc_get_transfer_mode(mmc, mrq); | ||
244 | data->host_cookie |= SD_EMMC_PRE_REQ_DONE; | ||
245 | |||
246 | if (!meson_mmc_desc_chain_mode(data)) | ||
247 | return; | ||
248 | |||
249 | data->sg_count = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, | ||
250 | mmc_get_dma_dir(data)); | ||
251 | if (!data->sg_count) | ||
252 | dev_err(mmc_dev(mmc), "dma_map_sg failed"); | ||
253 | } | ||
254 | |||
255 | static void meson_mmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, | ||
256 | int err) | ||
257 | { | ||
258 | struct mmc_data *data = mrq->data; | ||
259 | |||
260 | if (data && meson_mmc_desc_chain_mode(data) && data->sg_count) | ||
261 | dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, | ||
262 | mmc_get_dma_dir(data)); | ||
263 | } | ||
264 | |||
198 | static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate) | 265 | static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate) |
199 | { | 266 | { |
200 | struct mmc_host *mmc = host->mmc; | 267 | struct mmc_host *mmc = host->mmc; |
@@ -509,6 +576,44 @@ static void meson_mmc_set_response_bits(struct mmc_command *cmd, u32 *cmd_cfg) | |||
509 | } | 576 | } |
510 | } | 577 | } |
511 | 578 | ||
579 | static void meson_mmc_desc_chain_transfer(struct mmc_host *mmc, u32 cmd_cfg) | ||
580 | { | ||
581 | struct meson_host *host = mmc_priv(mmc); | ||
582 | struct sd_emmc_desc *desc = host->descs; | ||
583 | struct mmc_data *data = host->cmd->data; | ||
584 | struct scatterlist *sg; | ||
585 | u32 start; | ||
586 | int i; | ||
587 | |||
588 | if (data->flags & MMC_DATA_WRITE) | ||
589 | cmd_cfg |= CMD_CFG_DATA_WR; | ||
590 | |||
591 | if (data->blocks > 1) { | ||
592 | cmd_cfg |= CMD_CFG_BLOCK_MODE; | ||
593 | meson_mmc_set_blksz(mmc, data->blksz); | ||
594 | } | ||
595 | |||
596 | for_each_sg(data->sg, sg, data->sg_count, i) { | ||
597 | unsigned int len = sg_dma_len(sg); | ||
598 | |||
599 | if (data->blocks > 1) | ||
600 | len /= data->blksz; | ||
601 | |||
602 | desc[i].cmd_cfg = cmd_cfg; | ||
603 | desc[i].cmd_cfg |= FIELD_PREP(CMD_CFG_LENGTH_MASK, len); | ||
604 | if (i > 0) | ||
605 | desc[i].cmd_cfg |= CMD_CFG_NO_CMD; | ||
606 | desc[i].cmd_arg = host->cmd->arg; | ||
607 | desc[i].cmd_resp = 0; | ||
608 | desc[i].cmd_data = sg_dma_address(sg); | ||
609 | } | ||
610 | desc[data->sg_count - 1].cmd_cfg |= CMD_CFG_END_OF_CHAIN; | ||
611 | |||
612 | dma_wmb(); /* ensure descriptor is written before kicked */ | ||
613 | start = host->descs_dma_addr | START_DESC_BUSY; | ||
614 | writel(start, host->regs + SD_EMMC_START); | ||
615 | } | ||
616 | |||
512 | static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | 617 | static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) |
513 | { | 618 | { |
514 | struct meson_host *host = mmc_priv(mmc); | 619 | struct meson_host *host = mmc_priv(mmc); |
@@ -519,6 +624,8 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | |||
519 | /* Setup descriptors */ | 624 | /* Setup descriptors */ |
520 | dma_rmb(); | 625 | dma_rmb(); |
521 | 626 | ||
627 | host->cmd = cmd; | ||
628 | |||
522 | cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode); | 629 | cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode); |
523 | cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */ | 630 | cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */ |
524 | 631 | ||
@@ -526,10 +633,16 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | |||
526 | 633 | ||
527 | /* data? */ | 634 | /* data? */ |
528 | if (data) { | 635 | if (data) { |
636 | data->bytes_xfered = 0; | ||
529 | cmd_cfg |= CMD_CFG_DATA_IO; | 637 | cmd_cfg |= CMD_CFG_DATA_IO; |
530 | cmd_cfg |= FIELD_PREP(CMD_CFG_TIMEOUT_MASK, | 638 | cmd_cfg |= FIELD_PREP(CMD_CFG_TIMEOUT_MASK, |
531 | ilog2(meson_mmc_get_timeout_msecs(data))); | 639 | ilog2(meson_mmc_get_timeout_msecs(data))); |
532 | 640 | ||
641 | if (meson_mmc_desc_chain_mode(data)) { | ||
642 | meson_mmc_desc_chain_transfer(mmc, cmd_cfg); | ||
643 | return; | ||
644 | } | ||
645 | |||
533 | if (data->blocks > 1) { | 646 | if (data->blocks > 1) { |
534 | cmd_cfg |= CMD_CFG_BLOCK_MODE; | 647 | cmd_cfg |= CMD_CFG_BLOCK_MODE; |
535 | cmd_cfg |= FIELD_PREP(CMD_CFG_LENGTH_MASK, | 648 | cmd_cfg |= FIELD_PREP(CMD_CFG_LENGTH_MASK, |
@@ -539,7 +652,6 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | |||
539 | cmd_cfg |= FIELD_PREP(CMD_CFG_LENGTH_MASK, data->blksz); | 652 | cmd_cfg |= FIELD_PREP(CMD_CFG_LENGTH_MASK, data->blksz); |
540 | } | 653 | } |
541 | 654 | ||
542 | data->bytes_xfered = 0; | ||
543 | xfer_bytes = data->blksz * data->blocks; | 655 | xfer_bytes = data->blksz * data->blocks; |
544 | if (data->flags & MMC_DATA_WRITE) { | 656 | if (data->flags & MMC_DATA_WRITE) { |
545 | cmd_cfg |= CMD_CFG_DATA_WR; | 657 | cmd_cfg |= CMD_CFG_DATA_WR; |
@@ -555,8 +667,6 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | |||
555 | ilog2(SD_EMMC_CMD_TIMEOUT)); | 667 | ilog2(SD_EMMC_CMD_TIMEOUT)); |
556 | } | 668 | } |
557 | 669 | ||
558 | host->cmd = cmd; | ||
559 | |||
560 | /* Last descriptor */ | 670 | /* Last descriptor */ |
561 | cmd_cfg |= CMD_CFG_END_OF_CHAIN; | 671 | cmd_cfg |= CMD_CFG_END_OF_CHAIN; |
562 | writel(cmd_cfg, host->regs + SD_EMMC_CMD_CFG); | 672 | writel(cmd_cfg, host->regs + SD_EMMC_CMD_CFG); |
@@ -569,14 +679,25 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) | |||
569 | static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) | 679 | static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) |
570 | { | 680 | { |
571 | struct meson_host *host = mmc_priv(mmc); | 681 | struct meson_host *host = mmc_priv(mmc); |
682 | bool needs_pre_post_req = mrq->data && | ||
683 | !(mrq->data->host_cookie & SD_EMMC_PRE_REQ_DONE); | ||
684 | |||
685 | if (needs_pre_post_req) { | ||
686 | meson_mmc_get_transfer_mode(mmc, mrq); | ||
687 | if (!meson_mmc_desc_chain_mode(mrq->data)) | ||
688 | needs_pre_post_req = false; | ||
689 | } | ||
690 | |||
691 | if (needs_pre_post_req) | ||
692 | meson_mmc_pre_req(mmc, mrq); | ||
572 | 693 | ||
573 | /* Stop execution */ | 694 | /* Stop execution */ |
574 | writel(0, host->regs + SD_EMMC_START); | 695 | writel(0, host->regs + SD_EMMC_START); |
575 | 696 | ||
576 | if (mrq->sbc) | 697 | meson_mmc_start_cmd(mmc, mrq->sbc ?: mrq->cmd); |
577 | meson_mmc_start_cmd(mmc, mrq->sbc); | 698 | |
578 | else | 699 | if (needs_pre_post_req) |
579 | meson_mmc_start_cmd(mmc, mrq->cmd); | 700 | meson_mmc_post_req(mmc, mrq, 0); |
580 | } | 701 | } |
581 | 702 | ||
582 | static void meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd) | 703 | static void meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd) |
@@ -654,7 +775,9 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id) | |||
654 | if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS)) { | 775 | if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS)) { |
655 | if (data && !cmd->error) | 776 | if (data && !cmd->error) |
656 | data->bytes_xfered = data->blksz * data->blocks; | 777 | data->bytes_xfered = data->blksz * data->blocks; |
657 | ret = IRQ_WAKE_THREAD; | 778 | if (meson_mmc_bounce_buf_read(data) || |
779 | meson_mmc_get_next_command(cmd)) | ||
780 | ret = IRQ_WAKE_THREAD; | ||
658 | } else { | 781 | } else { |
659 | dev_warn(host->dev, "Unknown IRQ! status=0x%04x: MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n", | 782 | dev_warn(host->dev, "Unknown IRQ! status=0x%04x: MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n", |
660 | status, cmd->opcode, cmd->arg, | 783 | status, cmd->opcode, cmd->arg, |
@@ -691,7 +814,7 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) | |||
691 | return IRQ_NONE; | 814 | return IRQ_NONE; |
692 | 815 | ||
693 | data = cmd->data; | 816 | data = cmd->data; |
694 | if (data && data->flags & MMC_DATA_READ) { | 817 | if (meson_mmc_bounce_buf_read(data)) { |
695 | xfer_bytes = data->blksz * data->blocks; | 818 | xfer_bytes = data->blksz * data->blocks; |
696 | WARN_ON(xfer_bytes > host->bounce_buf_size); | 819 | WARN_ON(xfer_bytes > host->bounce_buf_size); |
697 | sg_copy_from_buffer(data->sg, data->sg_len, | 820 | sg_copy_from_buffer(data->sg, data->sg_len, |
@@ -760,6 +883,8 @@ static const struct mmc_host_ops meson_mmc_ops = { | |||
760 | .request = meson_mmc_request, | 883 | .request = meson_mmc_request, |
761 | .set_ios = meson_mmc_set_ios, | 884 | .set_ios = meson_mmc_set_ios, |
762 | .get_cd = meson_mmc_get_cd, | 885 | .get_cd = meson_mmc_get_cd, |
886 | .pre_req = meson_mmc_pre_req, | ||
887 | .post_req = meson_mmc_post_req, | ||
763 | .execute_tuning = meson_mmc_execute_tuning, | 888 | .execute_tuning = meson_mmc_execute_tuning, |
764 | }; | 889 | }; |
765 | 890 | ||
@@ -845,6 +970,8 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
845 | mmc->caps |= MMC_CAP_CMD23; | 970 | mmc->caps |= MMC_CAP_CMD23; |
846 | mmc->max_blk_count = CMD_CFG_LENGTH_MASK; | 971 | mmc->max_blk_count = CMD_CFG_LENGTH_MASK; |
847 | mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; | 972 | mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; |
973 | mmc->max_segs = SD_EMMC_DESC_BUF_LEN / sizeof(struct sd_emmc_desc); | ||
974 | mmc->max_seg_size = mmc->max_req_size; | ||
848 | 975 | ||
849 | /* data bounce buffer */ | 976 | /* data bounce buffer */ |
850 | host->bounce_buf_size = mmc->max_req_size; | 977 | host->bounce_buf_size = mmc->max_req_size; |
@@ -857,11 +984,22 @@ static int meson_mmc_probe(struct platform_device *pdev) | |||
857 | goto err_div_clk; | 984 | goto err_div_clk; |
858 | } | 985 | } |
859 | 986 | ||
987 | host->descs = dma_alloc_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, | ||
988 | &host->descs_dma_addr, GFP_KERNEL); | ||
989 | if (!host->descs) { | ||
990 | dev_err(host->dev, "Allocating descriptor DMA buffer failed\n"); | ||
991 | ret = -ENOMEM; | ||
992 | goto err_bounce_buf; | ||
993 | } | ||
994 | |||
860 | mmc->ops = &meson_mmc_ops; | 995 | mmc->ops = &meson_mmc_ops; |
861 | mmc_add_host(mmc); | 996 | mmc_add_host(mmc); |
862 | 997 | ||
863 | return 0; | 998 | return 0; |
864 | 999 | ||
1000 | err_bounce_buf: | ||
1001 | dma_free_coherent(host->dev, host->bounce_buf_size, | ||
1002 | host->bounce_buf, host->bounce_dma_addr); | ||
865 | err_div_clk: | 1003 | err_div_clk: |
866 | clk_disable_unprepare(host->cfg_div_clk); | 1004 | clk_disable_unprepare(host->cfg_div_clk); |
867 | err_core_clk: | 1005 | err_core_clk: |
@@ -880,6 +1018,8 @@ static int meson_mmc_remove(struct platform_device *pdev) | |||
880 | /* disable interrupts */ | 1018 | /* disable interrupts */ |
881 | writel(0, host->regs + SD_EMMC_IRQ_EN); | 1019 | writel(0, host->regs + SD_EMMC_IRQ_EN); |
882 | 1020 | ||
1021 | dma_free_coherent(host->dev, SD_EMMC_DESC_BUF_LEN, | ||
1022 | host->descs, host->descs_dma_addr); | ||
883 | dma_free_coherent(host->dev, host->bounce_buf_size, | 1023 | dma_free_coherent(host->dev, host->bounce_buf_size, |
884 | host->bounce_buf, host->bounce_dma_addr); | 1024 | host->bounce_buf, host->bounce_dma_addr); |
885 | 1025 | ||