From: David Brownell <[email protected]>
Minor MMC cleanup, mostly for DMA:
- Tighten some parts of scatterlist handling:
* switch to a NR_SG parameter (like other drivers), and use
it to disable some code;
* make max_seg_size reflect the EDMA limit (usually 2 MBytes)
not the controller's multiblock request limit (64x bigger);
* use standard scatterlist accessors.
- Use more section annotations to shrink runtime driver footprint.
- Fix whitespace glitches and remove an unused struct member, both
left over from the "rip out 1/4..." patch.
- Move some bits of code earlier, to simplify later patches.
- Fix a few messages to terminate properly, with just a newline.
- Report -EIO for DMA errors.
There should be at most very minor functional changes here.
Signed-off-by: David Brownell <[email protected]>
---
drivers/mmc/host/davinci_mmc.c | 163 ++++++++++++++++++++++-----------------
1 file changed, 94 insertions(+), 69 deletions(-)
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -156,15 +156,43 @@
#define DAVINCI_MMC_CMDTYPE_BCR 1
#define DAVINCI_MMC_CMDTYPE_AC 2
#define DAVINCI_MMC_CMDTYPE_ADTC 3
-#define EDMA_MAX_LOGICAL_CHA_ALLOWED 1
/* MMCSD Init clock in Hz in opendain mode */
#define MMCSD_INIT_CLOCK 200000
#define DRIVER_NAME "davinci_mmc"
+/*
+ * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
+ * and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
+ * for drivers with max_hw_segs == 1, making the segments bigger (64KB)
+ * than the page or two that's otherwise typical.
+ *
+ * FIXME make NR_SG = 16 behave to get the same throughput boost from
+ * EDMA transfer linkage (for read *AND* write) but without extra page
+ * copies. The existing code hard-wires a *single* transfer link, so
+ * it will behave poorly on typical segments (one or two pages); and
+ * looks rather dubious.
+ */
+#define MAX_CCNT ((1 << 16) - 1)
+
+#ifdef CONFIG_MMC_BLOCK_BOUNCE
+#define NR_SG 1
+#else
+#define NR_SG 2
+#endif
+
+static unsigned rw_threshold = 32;
+module_param(rw_threshold, uint, S_IRUGO);
+MODULE_PARM_DESC(rw_threshold,
+ "Read/Write threshold, can be 16/32. Default = 32");
+
+static unsigned __initdata use_dma = 1;
+module_param(use_dma, uint, 0);
+MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
+
struct edma_ch_mmcsd {
unsigned char cnt_chanel;
- unsigned int chanel_num[EDMA_MAX_LOGICAL_CHA_ALLOWED];
+ unsigned int chanel_num[NR_SG - 1];
};
struct mmc_davinci_host {
@@ -197,9 +225,6 @@ struct mmc_davinci_host {
unsigned int option_read;
unsigned int option_write;
-
- /* data structure to queue one request */
- struct mmc_request *que_mmc_request;
};
enum mmcsdevent {
@@ -214,14 +239,6 @@ enum mmcsdevent {
MMCSD_EVENT_BLOCK_XFERRED = (1 << 0)
};
-static unsigned rw_threshold = 32;
-module_param(rw_threshold, uint, S_IRUGO);
-MODULE_PARM_DESC(rw_threshold,
- "Read/Write threshold, can be 16/32. Default = 32");
-
-static unsigned use_dma = 1;
-module_param(use_dma, uint, S_IRUGO);
-MODULE_PARM_DESC(use_dma, "Wether to use DMA or not. Default = 1");
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
@@ -285,7 +302,7 @@ static void mmc_davinci_sg_to_buf(struct
struct scatterlist *sg;
sg = host->data->sg + host->sg_idx;
- host->buffer_bytes_left = sg->length;
+ host->buffer_bytes_left = sg_dma_len(sg);
host->buffer = sg_virt(sg);
if (host->buffer_bytes_left > host->bytes_left)
host->buffer_bytes_left = host->bytes_left;
@@ -479,9 +496,17 @@ static void davinci_abort_dma(struct mmc
static void mmc_davinci_dma_cb(int lch, u16 ch_status, void *data)
{
if (DMA_COMPLETE != ch_status) {
- struct mmc_davinci_host *host = (struct mmc_davinci_host *)data;
- dev_warn(mmc_dev(host->mmc), "[DMA FAILED]");
+ struct mmc_davinci_host *host = data;
+
+ /* Currently means: DMA Event Missed, or "null" transfer
+ * request was seen. In the future, TC errors (like bad
+ * addresses) might be presented too.
+ */
+ dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
+ (host->data->flags & MMC_DATA_WRITE)
+ ? "write" : "read");
davinci_abort_dma(host);
+ host->data->error = -EIO;
}
}
@@ -505,7 +530,7 @@ static int mmc_davinci_send_dma_request(
unsigned int count;
int num_frames, frame;
-#define MAX_C_CNT 64000
+#define MAX_C_CNT MAX_CCNT
frame = data->blksz;
count = sg_dma_len(sg);
@@ -612,7 +637,7 @@ static int mmc_davinci_send_dma_request(
}
}
- if (host->sg_len > 1) {
+ if (NR_SG > 1 && host->sg_len > 1) {
davinci_get_dma_params(sync_dev, &temp);
temp.opt &= ~TCINTEN;
davinci_set_dma_params(sync_dev, &temp);
@@ -672,36 +697,35 @@ static int mmc_davinci_start_dma_transfe
struct mmc_data *data = host->data;
int mask = rw_threshold - 1;
- host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE
: DMA_FROM_DEVICE));
/* Decide if we can use DMA */
for (i = 0; i < host->sg_len; i++) {
- if (data->sg[i].length & mask) {
- use_dma = 0;
- break;
+ if (sg_dma_len(data->sg + i) & mask) {
+ dma_unmap_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ return -1;
}
}
- if (!use_dma) {
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
- (data->flags & MMC_DATA_WRITE)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
- return -1;
- }
-
host->do_dma = 1;
-
mmc_davinci_send_dma_request(host, req);
return 0;
}
-static int davinci_release_dma_channels(struct mmc_davinci_host *host)
+static void __init_or_module
+davinci_release_dma_channels(struct mmc_davinci_host *host)
{
+ if (!host->use_dma)
+ return;
+
davinci_free_dma(host->txdma);
davinci_free_dma(host->rxdma);
@@ -709,8 +733,6 @@ static int davinci_release_dma_channels(
davinci_free_dma(host->edma_ch_details.chanel_num[0]);
host->edma_ch_details.cnt_chanel = 0;
}
-
- return 0;
}
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
@@ -740,6 +762,13 @@ static int __init davinci_acquire_dma_ch
host->edma_ch_details.cnt_chanel = 0;
+ /* REVISIT the NR_SG > 1 code all looks rather dubious...
+ * example, the comment below about single block mode for
+ * writes is quite obviously wrong.
+ */
+ if (NR_SG == 1)
+ return 0;
+
/* currently data Writes are done using single block mode,
* so no DMA slave write channel is required for now */
@@ -786,7 +815,7 @@ mmc_davinci_prepare_data(struct mmc_davi
dev_dbg(mmc_dev(host->mmc),
"MMCSD : Data xfer (%s %s), "
- "DTO %d cycles + %d ns, %d blocks of %d bytes\r\n",
+ "DTO %d cycles + %d ns, %d blocks of %d bytes\n",
(req->data->flags & MMC_DATA_STREAM) ? "stream" : "block",
(req->data->flags & MMC_DATA_WRITE) ? "write" : "read",
req->data->timeout_clks, req->data->timeout_ns,
@@ -830,15 +859,12 @@ mmc_davinci_prepare_data(struct mmc_davi
host->bytes_left = req->data->blocks * req->data->blksz;
- if ((host->use_dma == 1) &&
- ((host->bytes_left & (rw_threshold - 1)) == 0) &&
- (mmc_davinci_start_dma_transfer(host, req) == 0)) {
+ if (host->use_dma && (host->bytes_left & (rw_threshold - 1)) == 0
+ && mmc_davinci_start_dma_transfer(host, req) == 0) {
host->buffer = NULL;
host->bytes_left = 0;
} else {
/* Revert to CPU Copy */
-
- host->do_dma = 0;
mmc_davinci_sg_to_buf(host);
}
}
@@ -898,7 +924,7 @@ static void mmc_davinci_set_ios(struct m
cpu_arm_clk = host->mmc_input_clk;
dev_dbg(mmc_dev(host->mmc),
- "clock %dHz busmode %d powermode %d Vdd %04x\r\n",
+ "clock %dHz busmode %d powermode %d Vdd %04x\n",
ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd);
if (ios->bus_width == MMC_BUS_WIDTH_4) {
@@ -1138,21 +1164,21 @@ static irqreturn_t mmc_davinci_irq(int i
struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
unsigned int status;
- if (host->cmd == NULL && host->data == NULL) {
- status = readl(host->base + DAVINCI_MMCST0);
- dev_dbg(mmc_dev(host->mmc),
- "Spurious interrupt 0x%04x\n", status);
- /* Disable the interrupt from mmcsd */
- writel(0, host->base + DAVINCI_MMCIM);
- return IRQ_HANDLED;
- }
+ if (host->cmd == NULL && host->data == NULL) {
+ status = readl(host->base + DAVINCI_MMCST0);
+ dev_dbg(mmc_dev(host->mmc),
+ "Spurious interrupt 0x%04x\n", status);
+ /* Disable the interrupt from mmcsd */
+ writel(0, host->base + DAVINCI_MMCIM);
+ return IRQ_HANDLED;
+ }
+
do {
status = readl(host->base + DAVINCI_MMCST0);
if (status == 0)
break;
-
- if (handle_core_command(host, status))
- break;
+ if (handle_core_command(host, status))
+ break;
} while (1);
return IRQ_HANDLED;
}
@@ -1184,7 +1210,7 @@ static struct mmc_host_ops mmc_davinci_o
.get_ro = mmc_davinci_get_ro,
};
-static void init_mmcsd_host(struct mmc_davinci_host *host)
+static void __init init_mmcsd_host(struct mmc_davinci_host *host)
{
/* DAT line portion is diabled and in reset state */
writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
@@ -1267,6 +1293,12 @@ static int __init davinci_mmcsd_probe(st
init_mmcsd_host(host);
+ host->use_dma = use_dma;
+ host->irq = irq;
+
+ if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
+ host->use_dma = 0;
+
/* REVISIT: someday, support IRQ-driven card detection. */
mmc->caps |= MMC_CAP_NEEDS_POLL;
@@ -1279,17 +1311,17 @@ static int __init davinci_mmcsd_probe(st
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-#ifdef CONFIG_MMC_BLOCK_BOUNCE
- mmc->max_phys_segs = 1;
- mmc->max_hw_segs = 1;
-#else
- mmc->max_phys_segs = 2;
- mmc->max_hw_segs = 2;
-#endif
+ /* with no iommu coalescing pages, each phys_seg is a hw_seg */
+ mmc->max_hw_segs = NR_SG;
+ mmc->max_phys_segs = mmc->max_hw_segs;
+
+ /* EDMA limit per hw segment (one or two MBytes) */
+ mmc->max_seg_size = MAX_CCNT * rw_threshold;
+
+ /* MMC/SD controller limits for multiblock requests */
mmc->max_blk_size = 4095; /* BLEN is 11 bits */
mmc->max_blk_count = 65535; /* NBLK is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
- mmc->max_seg_size = mmc->max_req_size;
dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
@@ -1297,12 +1329,6 @@ static int __init davinci_mmcsd_probe(st
dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
- host->use_dma = use_dma;
- host->irq = irq;
-
- if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
- host->use_dma = 0;
-
platform_set_drvdata(pdev, host);
ret = mmc_add_host(mmc);
@@ -1323,8 +1349,7 @@ static int __init davinci_mmcsd_probe(st
out:
if (host) {
- if (host->edma_ch_details.cnt_chanel)
- davinci_release_dma_channels(host);
+ davinci_release_dma_channels(host);
if (host->clk) {
clk_disable(host->clk);
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source