Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 328627
b: refs/heads/master
c: 010b481
h: refs/heads/master
i:
  328625: d21179a
  328623: c934285
v: v3
  • Loading branch information
Marek Vasut authored and Mark Brown committed Sep 5, 2012
1 parent 3e31937 commit 400126b
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 54 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7d520d28dd5287d14b5ec6cf4405a1220ca57d42
refs/heads/master: 010b481834b2b60f7d8543263a63e69396019f7b
141 changes: 88 additions & 53 deletions trunk/drivers/spi/spi-mxs.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@

#define DRIVER_NAME "mxs-spi"

#define SSP_TIMEOUT 1000 /* 1000 ms */
/* Use 10S timeout for very long transfers, it should suffice. */
#define SSP_TIMEOUT 10000

#define SG_NUM 4
#define SG_MAXLEN 0xff00

struct mxs_spi {
Expand Down Expand Up @@ -219,61 +219,94 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,
int *first, int *last, int write)
{
struct mxs_ssp *ssp = &spi->ssp;
struct dma_async_tx_descriptor *desc;
struct scatterlist sg[SG_NUM];
struct dma_async_tx_descriptor *desc = NULL;
const bool vmalloced_buf = is_vmalloc_addr(buf);
const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN;
const int sgs = DIV_ROUND_UP(len, desc_len);
int sg_count;
uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);
int ret;

if (len > SG_NUM * SG_MAXLEN) {
dev_err(ssp->dev, "Data chunk too big for DMA\n");
int min, ret;
uint32_t ctrl0;
struct page *vm_page;
void *sg_buf;
struct {
uint32_t pio[4];
struct scatterlist sg;
} *dma_xfer;

if (!len)
return -EINVAL;
}

dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL);
if (!dma_xfer)
return -ENOMEM;

INIT_COMPLETION(spi->c);

ctrl0 = readl(ssp->base + HW_SSP_CTRL0);
ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);

if (*first)
pio |= BM_SSP_CTRL0_LOCK_CS;
if (*last)
pio |= BM_SSP_CTRL0_IGNORE_CRC;
ctrl0 |= BM_SSP_CTRL0_LOCK_CS;
if (!write)
pio |= BM_SSP_CTRL0_READ;

if (ssp->devid == IMX23_SSP)
pio |= len;
else
writel(len, ssp->base + HW_SSP_XFER_SIZE);

/* Queue the PIO register write transfer. */
desc = dmaengine_prep_slave_sg(ssp->dmach,
(struct scatterlist *)&pio,
1, DMA_TRANS_NONE, 0);
if (!desc) {
dev_err(ssp->dev,
"Failed to get PIO reg. write descriptor.\n");
return -EINVAL;
}
ctrl0 |= BM_SSP_CTRL0_READ;

/* Queue the DMA data transfer. */
sg_init_table(sg, (len / SG_MAXLEN) + 1);
sg_count = 0;
while (len) {
sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN));
len -= min(len, SG_MAXLEN);
buf += min(len, SG_MAXLEN);
}
dma_map_sg(ssp->dev, sg, sg_count,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count,
write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);

if (!desc) {
dev_err(ssp->dev,
"Failed to get DMA data write descriptor.\n");
ret = -EINVAL;
goto err;
for (sg_count = 0; sg_count < sgs; sg_count++) {
min = min(len, desc_len);

/* Prepare the transfer descriptor. */
if ((sg_count + 1 == sgs) && *last)
ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC;

if (ssp->devid == IMX23_SSP)
ctrl0 |= min;

dma_xfer[sg_count].pio[0] = ctrl0;
dma_xfer[sg_count].pio[3] = min;

if (vmalloced_buf) {
vm_page = vmalloc_to_page(buf);
if (!vm_page) {
ret = -ENOMEM;
goto err_vmalloc;
}
sg_buf = page_address(vm_page) +
((size_t)buf & ~PAGE_MASK);
} else {
sg_buf = buf;
}

sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min);
ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

len -= min;
buf += min;

/* Queue the PIO register write transfer. */
desc = dmaengine_prep_slave_sg(ssp->dmach,
(struct scatterlist *)dma_xfer[sg_count].pio,
(ssp->devid == IMX23_SSP) ? 1 : 4,
DMA_TRANS_NONE,
sg_count ? DMA_PREP_INTERRUPT : 0);
if (!desc) {
dev_err(ssp->dev,
"Failed to get PIO reg. write descriptor.\n");
ret = -EINVAL;
goto err_mapped;
}

desc = dmaengine_prep_slave_sg(ssp->dmach,
&dma_xfer[sg_count].sg, 1,
write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);

if (!desc) {
dev_err(ssp->dev,
"Failed to get DMA data write descriptor.\n");
ret = -EINVAL;
goto err_mapped;
}
}

/*
Expand All @@ -289,21 +322,23 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,

ret = wait_for_completion_timeout(&spi->c,
msecs_to_jiffies(SSP_TIMEOUT));

if (!ret) {
dev_err(ssp->dev, "DMA transfer timeout\n");
ret = -ETIMEDOUT;
goto err;
goto err_vmalloc;
}

ret = 0;

err:
for (--sg_count; sg_count >= 0; sg_count--) {
dma_unmap_sg(ssp->dev, &sg[sg_count], 1,
err_vmalloc:
while (--sg_count >= 0) {
err_mapped:
dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}

kfree(dma_xfer);

return ret;
}

Expand Down

0 comments on commit 400126b

Please sign in to comment.