diff options
author | Mark A. Greer <mgreer@animalcreek.com> | 2012-12-21 10:04:04 -0700 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2013-01-05 21:43:56 +1100 |
commit | dfd061d5a8f5a6d89c77d23693a63038ff8cbcd8 (patch) | |
tree | b499446322453bbb204679c4fca33debbccb9cad | |
parent | 3b3f440023b3809c8eabec681768a4bcee15f2b4 (diff) |
crypto: omap-sham - Add code to use dmaengine API
Add code to use the new dmaengine API alongside
the existing DMA code that uses the private
OMAP DMA API. The API to use is chosen by
defining or undefining 'OMAP_SHAM_DMA_PRIVATE'.
This is a transitional change and the code that uses
the private DMA API will be removed in an upcoming
commit.
CC: Russell King <rmk+kernel@arm.linux.org.uk>
CC: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
Signed-off-by: Mark A. Greer <mgreer@animalcreek.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r-- | drivers/crypto/omap-sham.c | 154 |
1 files changed, 145 insertions, 9 deletions
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 9c3b096e15e..f54ceb8f5b2 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -13,6 +13,8 @@ * Some ideas are from old omap-sha1-md5.c driver. */ +#define OMAP_SHAM_DMA_PRIVATE + #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/err.h> @@ -27,6 +29,8 @@ #include <linux/platform_device.h> #include <linux/scatterlist.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/omap-dma.h> #include <linux/pm_runtime.h> #include <linux/delay.h> #include <linux/crypto.h> @@ -37,15 +41,15 @@ #include <crypto/hash.h> #include <crypto/internal/hash.h> -#include <linux/omap-dma.h> -#include <mach/irqs.h> - #define SHA_REG_DIGEST(x) (0x00 + ((x) * 0x04)) #define SHA_REG_DIN(x) (0x1C + ((x) * 0x04)) #define SHA1_MD5_BLOCK_SIZE SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 +#define DST_MAXBURST 16 +#define DMA_MIN (DST_MAXBURST * sizeof(u32)) + #define SHA_REG_DIGCNT 0x14 #define SHA_REG_CTRL 0x18 @@ -109,6 +113,9 @@ struct omap_sham_reqctx { /* walk state */ struct scatterlist *sg; +#ifndef OMAP_SHAM_DMA_PRIVATE + struct scatterlist sgl; +#endif unsigned int offset; /* offset in current sg */ unsigned int total; /* total request */ @@ -142,8 +149,12 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; +#ifdef OMAP_SHAM_DMA_PRIVATE int dma; int dma_lch; +#else + struct dma_chan *dma_lch; +#endif struct tasklet_struct done_task; unsigned long flags; @@ -313,15 +324,32 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, return -EINPROGRESS; } +#ifndef OMAP_SHAM_DMA_PRIVATE +static void omap_sham_dma_callback(void *param) +{ + struct omap_sham_dev *dd = param; + + set_bit(FLAGS_DMA_READY, &dd->flags); + tasklet_schedule(&dd->done_task); +} +#endif + static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, - size_t length, int final) + size_t length, int final, int is_sg) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE int len32; +#else + struct dma_async_tx_descriptor *tx; + struct dma_slave_config cfg; + int len32, ret; +#endif dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); +#ifdef OMAP_SHAM_DMA_PRIVATE len32 = DIV_ROUND_UP(length, sizeof(u32)); omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32, @@ -331,6 +359,50 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); +#else + memset(&cfg, 0, sizeof(cfg)); + + cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_maxburst = DST_MAXBURST; + + ret = dmaengine_slave_config(dd->dma_lch, &cfg); + if (ret) { + pr_err("omap-sham: can't configure dmaengine slave: %d\n", ret); + return ret; + } + + len32 = DIV_ROUND_UP(length, DMA_MIN) * DMA_MIN; + + if (is_sg) { + /* + * The SG entry passed in may not have the 'length' member + * set correctly so use a local SG entry (sgl) with the + * proper value for 'length' instead. If this is not done, + * the dmaengine may try to DMA the incorrect amount of data. + */ + sg_init_table(&ctx->sgl, 1); + ctx->sgl.page_link = ctx->sg->page_link; + ctx->sgl.offset = ctx->sg->offset; + sg_dma_len(&ctx->sgl) = len32; + sg_dma_address(&ctx->sgl) = sg_dma_address(ctx->sg); + + tx = dmaengine_prep_slave_sg(dd->dma_lch, &ctx->sgl, 1, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } else { + tx = dmaengine_prep_slave_single(dd->dma_lch, dma_addr, len32, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } + + if (!tx) { + dev_err(dd->dev, "prep_slave_sg/single() failed\n"); + return -EINVAL; + } + + tx->callback = omap_sham_dma_callback; + tx->callback_param = dd; +#endif + omap_sham_write_ctrl(dd, length, final, 1); ctx->digcnt += length; @@ -340,7 +412,12 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, set_bit(FLAGS_DMA_ACTIVE, &dd->flags); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_start_dma(dd->dma_lch); +#else + dmaengine_submit(tx); + dma_async_issue_pending(dd->dma_lch); +#endif return -EINPROGRESS; } @@ -387,6 +464,8 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, struct omap_sham_reqctx *ctx, size_t length, int final) { + int ret; + ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, ctx->dma_addr)) { @@ -396,8 +475,12 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, ctx->flags &= ~BIT(FLAGS_SG); - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final); + ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0); + if (ret) + dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen, + DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_dma_slow(struct omap_sham_dev *dd) @@ -432,6 +515,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); unsigned int length, final, tail; struct scatterlist *sg; + int ret; if (!ctx->total) return 0; @@ -439,6 +523,17 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) if (ctx->bufcnt || ctx->offset) return omap_sham_update_dma_slow(dd); +#ifndef OMAP_SHAM_DMA_PRIVATE + /* + * Don't use the sg interface when the transfer size is less + * than the number of elements in a DMA frame. Otherwise, + * the dmaengine infrastructure will calculate that it needs + * to transfer 0 frames which ultimately fails. + */ + if (ctx->total < (DST_MAXBURST * sizeof(u32))) + return omap_sham_update_dma_slow(dd); +#endif + dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n", ctx->digcnt, ctx->bufcnt, ctx->total); @@ -476,8 +571,11 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total; - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final); + ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1); + if (ret) + dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_cpu(struct omap_sham_dev *dd) @@ -496,7 +594,12 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_stop_dma(dd->dma_lch); +#else + dmaengine_terminate_all(dd->dma_lch); +#endif + if (ctx->flags & BIT(FLAGS_SG)) { dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); if (ctx->sg->length == ctx->offset) { @@ -583,7 +686,7 @@ static int omap_sham_final_req(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(req); int err = 0, use_dma = 1; - if (ctx->bufcnt <= 64) + if (ctx->bufcnt <= DMA_MIN) /* faster to handle last block with cpu */ use_dma = 0; @@ -699,6 +802,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (err) goto err1; +#ifdef OMAP_SHAM_DMA_PRIVATE omap_set_dma_dest_params(dd->dma_lch, 0, OMAP_DMA_AMODE_CONSTANT, dd->phys_base + SHA_REG_DIN(0), 0, 16); @@ -708,6 +812,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, omap_set_dma_src_burst_mode(dd->dma_lch, OMAP_DMA_DATA_BURST_4); +#endif if (ctx->digcnt) /* request has changed - restore hash */ @@ -1099,6 +1204,7 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef OMAP_SHAM_DMA_PRIVATE static void omap_sham_dma_callback(int lch, u16 ch_status, void *data) { struct omap_sham_dev *dd = data; @@ -1136,12 +1242,17 @@ static void omap_sham_dma_cleanup(struct omap_sham_dev *dd) dd->dma_lch = -1; } } +#endif static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; struct resource *res; +#ifndef OMAP_SHAM_DMA_PRIVATE + dma_cap_mask_t mask; + unsigned dma_chan; +#endif int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1176,7 +1287,11 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENODEV; goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE dd->dma = res->start; +#else + dma_chan = res->start; +#endif /* Get the IRQ */ dd->irq = platform_get_irq(pdev, 0); @@ -1193,9 +1308,22 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE err = omap_sham_dma_init(dd); if (err) goto dma_err; +#else + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dma_chan); + if (!dd->dma_lch) { + dev_err(dev, "unable to obtain RX DMA engine channel %u\n", + dma_chan); + err = -ENXIO; + goto dma_err; + } +#endif dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { @@ -1231,7 +1359,11 @@ err_algs: iounmap(dd->io_base); pm_runtime_disable(dev); io_err: +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif dma_err: if (dd->irq >= 0) free_irq(dd->irq, dd); @@ -1260,7 +1392,11 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif if (dd->irq >= 0) free_irq(dd->irq, dd); kfree(dd); |