| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 27aa9f97887f599267c345075e61979de785c770 Mon Sep 17 00:00:00 2001 |
| 2 | From: Peng Ma <peng.ma@nxp.com> |
| 3 | Date: Thu, 11 Oct 2018 16:49:41 +0800 |
| 4 | Subject: [PATCH] dma: caam: add dma memcpy driver |
| 5 | MIME-Version: 1.0 |
| 6 | Content-Type: text/plain; charset=UTF-8 |
| 7 | Content-Transfer-Encoding: 8bit |
| 8 | |
| 9 | This module introduces a memcpy DMA driver based on the DMA capabilities |
| 10 | of the CAAM hardware block. CAAM DMA is a platform driver that is only |
| 11 | probed if the device is defined in the device tree. The driver creates |
| 12 | a DMA channel for each JR of the CAAM. This introduces a dependency on |
| 13 | the JR driver. Therefore a defering mechanism was used to ensure that |
| 14 | the CAAM DMA driver is probed only after the JR driver. |
| 15 | |
| 16 | Signed-off-by: Radu Alexe <radu.alexe@nxp.com> |
| 17 | Signed-off-by: Tudor Ambarus <tudor-dan.ambarus@nxp.com> |
| 18 | Signed-off-by: Rajiv Vishwakarma <rajiv.vishwakarma@nxp.com> |
| 19 | Signed-off-by: Horia Geantă <horia.geanta@nxp.com> |
| 20 | [rebase] |
| 21 | Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> |
| 22 | --- |
| 23 | drivers/dma/Kconfig | 19 +- |
| 24 | drivers/dma/Makefile | 1 + |
| 25 | drivers/dma/caam_dma.c | 462 +++++++++++++++++++++++++++++++++++++++++++++++++ |
| 26 | 3 files changed, 481 insertions(+), 1 deletion(-) |
| 27 | create mode 100644 drivers/dma/caam_dma.c |
| 28 | |
| 29 | --- a/drivers/dma/Kconfig |
| 30 | +++ b/drivers/dma/Kconfig |
| 31 | @@ -132,6 +132,24 @@ config COH901318 |
| 32 | help |
| 33 | Enable support for ST-Ericsson COH 901 318 DMA. |
| 34 | |
| 35 | +config CRYPTO_DEV_FSL_CAAM_DMA |
| 36 | + tristate "CAAM DMA engine support" |
| 37 | + depends on CRYPTO_DEV_FSL_CAAM_JR |
| 38 | + default n |
| 39 | + select DMA_ENGINE |
| 40 | + select ASYNC_CORE |
| 41 | + select ASYNC_TX_ENABLE_CHANNEL_SWITCH |
| 42 | + help |
| 43 | + Selecting this will offload the DMA operations for users of |
| 44 | + the scatter gather memcopy API to the CAAM via job rings. The |
| 45 | + CAAM is a hardware module that provides hardware acceleration to |
| 46 | + cryptographic operations. It has a built-in DMA controller that can |
| 47 | + be programmed to read/write cryptographic data. This module defines |
| 48 | + a DMA driver that uses the DMA capabilities of the CAAM. |
| 49 | + |
| 50 | + To compile this as a module, choose M here: the module |
| 51 | + will be called caam_dma. |
| 52 | + |
| 53 | config DMA_BCM2835 |
| 54 | tristate "BCM2835 DMA engine support" |
| 55 | depends on ARCH_BCM2835 |
| 56 | @@ -663,7 +681,6 @@ config ZX_DMA |
| 57 | help |
| 58 | Support the DMA engine for ZTE ZX family platform devices. |
| 59 | |
| 60 | - |
| 61 | # driver files |
| 62 | source "drivers/dma/bestcomm/Kconfig" |
| 63 | |
| 64 | --- a/drivers/dma/Makefile |
| 65 | +++ b/drivers/dma/Makefile |
| 66 | @@ -77,6 +77,7 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o |
| 67 | obj-$(CONFIG_ZX_DMA) += zx_dma.o |
| 68 | obj-$(CONFIG_ST_FDMA) += st_fdma.o |
| 69 | obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/ |
| 70 | +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_DMA) += caam_dma.o |
| 71 | |
| 72 | obj-y += mediatek/ |
| 73 | obj-y += qcom/ |
| 74 | --- /dev/null |
| 75 | +++ b/drivers/dma/caam_dma.c |
| 76 | @@ -0,0 +1,462 @@ |
| 77 | +/* |
| 78 | + * caam support for SG DMA |
| 79 | + * |
| 80 | + * Copyright 2016 Freescale Semiconductor, Inc |
| 81 | + * Copyright 2017 NXP |
| 82 | + * |
| 83 | + * Redistribution and use in source and binary forms, with or without |
| 84 | + * modification, are permitted provided that the following conditions are met: |
| 85 | + * * Redistributions of source code must retain the above copyright |
| 86 | + * notice, this list of conditions and the following disclaimer. |
| 87 | + * * Redistributions in binary form must reproduce the above copyright |
| 88 | + * notice, this list of conditions and the following disclaimer in the |
| 89 | + * documentation and/or other materials provided with the distribution. |
| 90 | + * * Neither the names of the above-listed copyright holders nor the |
| 91 | + * names of any contributors may be used to endorse or promote products |
| 92 | + * derived from this software without specific prior written permission. |
| 93 | + * |
| 94 | + * |
| 95 | + * ALTERNATIVELY, this software may be distributed under the terms of the |
| 96 | + * GNU General Public License ("GPL") as published by the Free Software |
| 97 | + * Foundation, either version 2 of that License or (at your option) any |
| 98 | + * later version. |
| 99 | + * |
| 100 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 101 | + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 102 | + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 103 | + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE |
| 104 | + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 105 | + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 106 | + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 107 | + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 108 | + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 109 | + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 110 | + * POSSIBILITY OF SUCH DAMAGE. |
| 111 | + */ |
| 112 | + |
| 113 | +#include <linux/dma-mapping.h> |
| 114 | +#include <linux/dmaengine.h> |
| 115 | +#include <linux/module.h> |
| 116 | +#include <linux/platform_device.h> |
| 117 | +#include <linux/slab.h> |
| 118 | + |
| 119 | +#include "dmaengine.h" |
| 120 | + |
| 121 | +#include "../crypto/caam/regs.h" |
| 122 | +#include "../crypto/caam/jr.h" |
| 123 | +#include "../crypto/caam/error.h" |
| 124 | +#include "../crypto/caam/desc_constr.h" |
| 125 | + |
| 126 | +#define DESC_DMA_MEMCPY_LEN ((CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN) / \ |
| 127 | + CAAM_CMD_SZ) |
| 128 | + |
| 129 | +/* |
| 130 | + * This is max chunk size of a DMA transfer. If a buffer is larger than this |
| 131 | + * value it is internally broken into chunks of max CAAM_DMA_CHUNK_SIZE bytes |
| 132 | + * and for each chunk a DMA transfer request is issued. |
| 133 | + * This value is the largest number on 16 bits that is a multiple of 256 bytes |
| 134 | + * (the largest configurable CAAM DMA burst size). |
| 135 | + */ |
| 136 | +#define CAAM_DMA_CHUNK_SIZE 65280 |
| 137 | + |
| 138 | +struct caam_dma_sh_desc { |
| 139 | + u32 desc[DESC_DMA_MEMCPY_LEN] ____cacheline_aligned; |
| 140 | + dma_addr_t desc_dma; |
| 141 | +}; |
| 142 | + |
| 143 | +/* caam dma extended descriptor */ |
| 144 | +struct caam_dma_edesc { |
| 145 | + struct dma_async_tx_descriptor async_tx; |
| 146 | + struct list_head node; |
| 147 | + struct caam_dma_ctx *ctx; |
| 148 | + dma_addr_t src_dma; |
| 149 | + dma_addr_t dst_dma; |
| 150 | + unsigned int src_len; |
| 151 | + unsigned int dst_len; |
| 152 | + u32 jd[] ____cacheline_aligned; |
| 153 | +}; |
| 154 | + |
| 155 | +/* |
| 156 | + * caam_dma_ctx - per jr/channel context |
| 157 | + * @chan: dma channel used by async_tx API |
| 158 | + * @node: list_head used to attach to the global dma_ctx_list |
| 159 | + * @jrdev: Job Ring device |
| 160 | + * @pending_q: queue of pending (submitted, but not enqueued) jobs |
| 161 | + * @done_not_acked: jobs that have been completed by jr, but maybe not acked |
| 162 | + * @edesc_lock: protects extended descriptor |
| 163 | + */ |
| 164 | +struct caam_dma_ctx { |
| 165 | + struct dma_chan chan; |
| 166 | + struct list_head node; |
| 167 | + struct device *jrdev; |
| 168 | + struct list_head pending_q; |
| 169 | + struct list_head done_not_acked; |
| 170 | + spinlock_t edesc_lock; |
| 171 | +}; |
| 172 | + |
| 173 | +static struct dma_device *dma_dev; |
| 174 | +static struct caam_dma_sh_desc *dma_sh_desc; |
| 175 | +static LIST_HEAD(dma_ctx_list); |
| 176 | + |
| 177 | +static dma_cookie_t caam_dma_tx_submit(struct dma_async_tx_descriptor *tx) |
| 178 | +{ |
| 179 | + struct caam_dma_edesc *edesc = NULL; |
| 180 | + struct caam_dma_ctx *ctx = NULL; |
| 181 | + dma_cookie_t cookie; |
| 182 | + |
| 183 | + edesc = container_of(tx, struct caam_dma_edesc, async_tx); |
| 184 | + ctx = container_of(tx->chan, struct caam_dma_ctx, chan); |
| 185 | + |
| 186 | + spin_lock_bh(&ctx->edesc_lock); |
| 187 | + |
| 188 | + cookie = dma_cookie_assign(tx); |
| 189 | + list_add_tail(&edesc->node, &ctx->pending_q); |
| 190 | + |
| 191 | + spin_unlock_bh(&ctx->edesc_lock); |
| 192 | + |
| 193 | + return cookie; |
| 194 | +} |
| 195 | + |
| 196 | +static void caam_jr_chan_free_edesc(struct caam_dma_edesc *edesc) |
| 197 | +{ |
| 198 | + struct caam_dma_ctx *ctx = edesc->ctx; |
| 199 | + struct caam_dma_edesc *_edesc = NULL; |
| 200 | + |
| 201 | + spin_lock_bh(&ctx->edesc_lock); |
| 202 | + |
| 203 | + list_add_tail(&edesc->node, &ctx->done_not_acked); |
| 204 | + list_for_each_entry_safe(edesc, _edesc, &ctx->done_not_acked, node) { |
| 205 | + if (async_tx_test_ack(&edesc->async_tx)) { |
| 206 | + list_del(&edesc->node); |
| 207 | + kfree(edesc); |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + spin_unlock_bh(&ctx->edesc_lock); |
| 212 | +} |
| 213 | + |
| 214 | +static void caam_dma_done(struct device *dev, u32 *hwdesc, u32 err, |
| 215 | + void *context) |
| 216 | +{ |
| 217 | + struct caam_dma_edesc *edesc = context; |
| 218 | + struct caam_dma_ctx *ctx = edesc->ctx; |
| 219 | + dma_async_tx_callback callback; |
| 220 | + void *callback_param; |
| 221 | + |
| 222 | + if (err) |
| 223 | + caam_jr_strstatus(ctx->jrdev, err); |
| 224 | + |
| 225 | + dma_run_dependencies(&edesc->async_tx); |
| 226 | + |
| 227 | + spin_lock_bh(&ctx->edesc_lock); |
| 228 | + dma_cookie_complete(&edesc->async_tx); |
| 229 | + spin_unlock_bh(&ctx->edesc_lock); |
| 230 | + |
| 231 | + callback = edesc->async_tx.callback; |
| 232 | + callback_param = edesc->async_tx.callback_param; |
| 233 | + |
| 234 | + dma_descriptor_unmap(&edesc->async_tx); |
| 235 | + |
| 236 | + caam_jr_chan_free_edesc(edesc); |
| 237 | + |
| 238 | + if (callback) |
| 239 | + callback(callback_param); |
| 240 | +} |
| 241 | + |
| 242 | +static void caam_dma_memcpy_init_job_desc(struct caam_dma_edesc *edesc) |
| 243 | +{ |
| 244 | + u32 *jd = edesc->jd; |
| 245 | + u32 *sh_desc = dma_sh_desc->desc; |
| 246 | + dma_addr_t desc_dma = dma_sh_desc->desc_dma; |
| 247 | + |
| 248 | + /* init the job descriptor */ |
| 249 | + init_job_desc_shared(jd, desc_dma, desc_len(sh_desc), HDR_REVERSE); |
| 250 | + |
| 251 | + /* set SEQIN PTR */ |
| 252 | + append_seq_in_ptr(jd, edesc->src_dma, edesc->src_len, 0); |
| 253 | + |
| 254 | + /* set SEQOUT PTR */ |
| 255 | + append_seq_out_ptr(jd, edesc->dst_dma, edesc->dst_len, 0); |
| 256 | + |
| 257 | + print_hex_dump_debug("caam dma desc@" __stringify(__LINE__) ": ", |
| 258 | + DUMP_PREFIX_ADDRESS, 16, 4, jd, desc_bytes(jd), 1); |
| 259 | +} |
| 260 | + |
| 261 | +static struct dma_async_tx_descriptor * |
| 262 | +caam_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, |
| 263 | + size_t len, unsigned long flags) |
| 264 | +{ |
| 265 | + struct caam_dma_edesc *edesc; |
| 266 | + struct caam_dma_ctx *ctx = container_of(chan, struct caam_dma_ctx, |
| 267 | + chan); |
| 268 | + |
| 269 | + edesc = kzalloc(sizeof(*edesc) + DESC_JOB_IO_LEN, GFP_DMA | GFP_NOWAIT); |
| 270 | + if (!edesc) |
| 271 | + return ERR_PTR(-ENOMEM); |
| 272 | + |
| 273 | + dma_async_tx_descriptor_init(&edesc->async_tx, chan); |
| 274 | + edesc->async_tx.tx_submit = caam_dma_tx_submit; |
| 275 | + edesc->async_tx.flags = flags; |
| 276 | + edesc->async_tx.cookie = -EBUSY; |
| 277 | + |
| 278 | + edesc->src_dma = src; |
| 279 | + edesc->src_len = len; |
| 280 | + edesc->dst_dma = dst; |
| 281 | + edesc->dst_len = len; |
| 282 | + edesc->ctx = ctx; |
| 283 | + |
| 284 | + caam_dma_memcpy_init_job_desc(edesc); |
| 285 | + |
| 286 | + return &edesc->async_tx; |
| 287 | +} |
| 288 | + |
| 289 | +/* This function can be called in an interrupt context */ |
| 290 | +static void caam_dma_issue_pending(struct dma_chan *chan) |
| 291 | +{ |
| 292 | + struct caam_dma_ctx *ctx = container_of(chan, struct caam_dma_ctx, |
| 293 | + chan); |
| 294 | + struct caam_dma_edesc *edesc, *_edesc; |
| 295 | + |
| 296 | + spin_lock_bh(&ctx->edesc_lock); |
| 297 | + list_for_each_entry_safe(edesc, _edesc, &ctx->pending_q, node) { |
| 298 | + if (caam_jr_enqueue(ctx->jrdev, edesc->jd, |
| 299 | + caam_dma_done, edesc) < 0) |
| 300 | + break; |
| 301 | + list_del(&edesc->node); |
| 302 | + } |
| 303 | + spin_unlock_bh(&ctx->edesc_lock); |
| 304 | +} |
| 305 | + |
| 306 | +static void caam_dma_free_chan_resources(struct dma_chan *chan) |
| 307 | +{ |
| 308 | + struct caam_dma_ctx *ctx = container_of(chan, struct caam_dma_ctx, |
| 309 | + chan); |
| 310 | + struct caam_dma_edesc *edesc, *_edesc; |
| 311 | + |
| 312 | + spin_lock_bh(&ctx->edesc_lock); |
| 313 | + list_for_each_entry_safe(edesc, _edesc, &ctx->pending_q, node) { |
| 314 | + list_del(&edesc->node); |
| 315 | + kfree(edesc); |
| 316 | + } |
| 317 | + list_for_each_entry_safe(edesc, _edesc, &ctx->done_not_acked, node) { |
| 318 | + list_del(&edesc->node); |
| 319 | + kfree(edesc); |
| 320 | + } |
| 321 | + spin_unlock_bh(&ctx->edesc_lock); |
| 322 | +} |
| 323 | + |
| 324 | +static int caam_dma_jr_chan_bind(void) |
| 325 | +{ |
| 326 | + struct device *jrdev; |
| 327 | + struct caam_dma_ctx *ctx; |
| 328 | + int bonds = 0; |
| 329 | + int i; |
| 330 | + |
| 331 | + for (i = 0; i < caam_jr_driver_probed(); i++) { |
| 332 | + jrdev = caam_jridx_alloc(i); |
| 333 | + if (IS_ERR(jrdev)) { |
| 334 | + pr_err("job ring device %d allocation failed\n", i); |
| 335 | + continue; |
| 336 | + } |
| 337 | + |
| 338 | + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
| 339 | + if (!ctx) { |
| 340 | + caam_jr_free(jrdev); |
| 341 | + continue; |
| 342 | + } |
| 343 | + |
| 344 | + ctx->chan.device = dma_dev; |
| 345 | + ctx->chan.private = ctx; |
| 346 | + |
| 347 | + ctx->jrdev = jrdev; |
| 348 | + |
| 349 | + INIT_LIST_HEAD(&ctx->pending_q); |
| 350 | + INIT_LIST_HEAD(&ctx->done_not_acked); |
| 351 | + INIT_LIST_HEAD(&ctx->node); |
| 352 | + spin_lock_init(&ctx->edesc_lock); |
| 353 | + |
| 354 | + dma_cookie_init(&ctx->chan); |
| 355 | + |
| 356 | + /* add the context of this channel to the context list */ |
| 357 | + list_add_tail(&ctx->node, &dma_ctx_list); |
| 358 | + |
| 359 | + /* add this channel to the device chan list */ |
| 360 | + list_add_tail(&ctx->chan.device_node, &dma_dev->channels); |
| 361 | + |
| 362 | + bonds++; |
| 363 | + } |
| 364 | + |
| 365 | + return bonds; |
| 366 | +} |
| 367 | + |
| 368 | +static inline void caam_jr_dma_free(struct dma_chan *chan) |
| 369 | +{ |
| 370 | + struct caam_dma_ctx *ctx = container_of(chan, struct caam_dma_ctx, |
| 371 | + chan); |
| 372 | + |
| 373 | + list_del(&ctx->node); |
| 374 | + list_del(&chan->device_node); |
| 375 | + caam_jr_free(ctx->jrdev); |
| 376 | + kfree(ctx); |
| 377 | +} |
| 378 | + |
| 379 | +static void set_caam_dma_desc(u32 *desc) |
| 380 | +{ |
| 381 | + u32 *jmp_cmd; |
| 382 | + |
| 383 | + /* dma shared descriptor */ |
| 384 | + init_sh_desc(desc, HDR_SHARE_NEVER | (1 << HDR_START_IDX_SHIFT)); |
| 385 | + |
| 386 | + /* REG1 = CAAM_DMA_CHUNK_SIZE */ |
| 387 | + append_math_add_imm_u32(desc, REG1, ZERO, IMM, CAAM_DMA_CHUNK_SIZE); |
| 388 | + |
| 389 | + /* REG0 = SEQINLEN - CAAM_DMA_CHUNK_SIZE */ |
| 390 | + append_math_sub_imm_u32(desc, REG0, SEQINLEN, IMM, CAAM_DMA_CHUNK_SIZE); |
| 391 | + |
| 392 | + /* |
| 393 | + * if (REG0 > 0) |
| 394 | + * jmp to LABEL1 |
| 395 | + */ |
| 396 | + jmp_cmd = append_jump(desc, JUMP_TEST_INVALL | JUMP_COND_MATH_N | |
| 397 | + JUMP_COND_MATH_Z); |
| 398 | + |
| 399 | + /* REG1 = SEQINLEN */ |
| 400 | + append_math_sub(desc, REG1, SEQINLEN, ZERO, CAAM_CMD_SZ); |
| 401 | + |
| 402 | + /* LABEL1 */ |
| 403 | + set_jump_tgt_here(desc, jmp_cmd); |
| 404 | + |
| 405 | + /* VARSEQINLEN = REG1 */ |
| 406 | + append_math_add(desc, VARSEQINLEN, REG1, ZERO, CAAM_CMD_SZ); |
| 407 | + |
| 408 | + /* VARSEQOUTLEN = REG1 */ |
| 409 | + append_math_add(desc, VARSEQOUTLEN, REG1, ZERO, CAAM_CMD_SZ); |
| 410 | + |
| 411 | + /* do FIFO STORE */ |
| 412 | + append_seq_fifo_store(desc, 0, FIFOST_TYPE_METADATA | LDST_VLF); |
| 413 | + |
| 414 | + /* do FIFO LOAD */ |
| 415 | + append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | |
| 416 | + FIFOLD_TYPE_IFIFO | LDST_VLF); |
| 417 | + |
| 418 | + /* |
| 419 | + * if (REG0 > 0) |
| 420 | + * jmp 0xF8 (after shared desc header) |
| 421 | + */ |
| 422 | + append_jump(desc, JUMP_TEST_INVALL | JUMP_COND_MATH_N | |
| 423 | + JUMP_COND_MATH_Z | 0xF8); |
| 424 | + |
| 425 | + print_hex_dump_debug("caam dma shdesc@" __stringify(__LINE__) ": ", |
| 426 | + DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), |
| 427 | + 1); |
| 428 | +} |
| 429 | + |
| 430 | +static int caam_dma_probe(struct platform_device *pdev) |
| 431 | +{ |
| 432 | + struct device *dev = &pdev->dev; |
| 433 | + struct device *ctrldev = dev->parent; |
| 434 | + struct dma_chan *chan, *_chan; |
| 435 | + u32 *sh_desc; |
| 436 | + int err = -ENOMEM; |
| 437 | + int bonds; |
| 438 | + |
| 439 | + if (!caam_jr_driver_probed()) { |
| 440 | + dev_info(dev, "Defer probing after JR driver probing\n"); |
| 441 | + return -EPROBE_DEFER; |
| 442 | + } |
| 443 | + |
| 444 | + dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL); |
| 445 | + if (!dma_dev) |
| 446 | + return -ENOMEM; |
| 447 | + |
| 448 | + dma_sh_desc = kzalloc(sizeof(*dma_sh_desc), GFP_KERNEL | GFP_DMA); |
| 449 | + if (!dma_sh_desc) |
| 450 | + goto desc_err; |
| 451 | + |
| 452 | + sh_desc = dma_sh_desc->desc; |
| 453 | + set_caam_dma_desc(sh_desc); |
| 454 | + dma_sh_desc->desc_dma = dma_map_single(ctrldev, sh_desc, |
| 455 | + desc_bytes(sh_desc), |
| 456 | + DMA_TO_DEVICE); |
| 457 | + if (dma_mapping_error(ctrldev, dma_sh_desc->desc_dma)) { |
| 458 | + dev_err(dev, "unable to map dma descriptor\n"); |
| 459 | + goto map_err; |
| 460 | + } |
| 461 | + |
| 462 | + INIT_LIST_HEAD(&dma_dev->channels); |
| 463 | + |
| 464 | + bonds = caam_dma_jr_chan_bind(); |
| 465 | + if (!bonds) { |
| 466 | + err = -ENODEV; |
| 467 | + goto jr_bind_err; |
| 468 | + } |
| 469 | + |
| 470 | + dma_dev->dev = dev; |
| 471 | + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; |
| 472 | + dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); |
| 473 | + dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask); |
| 474 | + dma_dev->device_tx_status = dma_cookie_status; |
| 475 | + dma_dev->device_issue_pending = caam_dma_issue_pending; |
| 476 | + dma_dev->device_prep_dma_memcpy = caam_dma_prep_memcpy; |
| 477 | + dma_dev->device_free_chan_resources = caam_dma_free_chan_resources; |
| 478 | + |
| 479 | + err = dma_async_device_register(dma_dev); |
| 480 | + if (err) { |
| 481 | + dev_err(dev, "Failed to register CAAM DMA engine\n"); |
| 482 | + goto jr_bind_err; |
| 483 | + } |
| 484 | + |
| 485 | + dev_info(dev, "caam dma support with %d job rings\n", bonds); |
| 486 | + |
| 487 | + return err; |
| 488 | + |
| 489 | +jr_bind_err: |
| 490 | + list_for_each_entry_safe(chan, _chan, &dma_dev->channels, device_node) |
| 491 | + caam_jr_dma_free(chan); |
| 492 | + |
| 493 | + dma_unmap_single(ctrldev, dma_sh_desc->desc_dma, desc_bytes(sh_desc), |
| 494 | + DMA_TO_DEVICE); |
| 495 | +map_err: |
| 496 | + kfree(dma_sh_desc); |
| 497 | +desc_err: |
| 498 | + kfree(dma_dev); |
| 499 | + return err; |
| 500 | +} |
| 501 | + |
| 502 | +static int caam_dma_remove(struct platform_device *pdev) |
| 503 | +{ |
| 504 | + struct device *dev = &pdev->dev; |
| 505 | + struct device *ctrldev = dev->parent; |
| 506 | + struct caam_dma_ctx *ctx, *_ctx; |
| 507 | + |
| 508 | + dma_async_device_unregister(dma_dev); |
| 509 | + |
| 510 | + list_for_each_entry_safe(ctx, _ctx, &dma_ctx_list, node) { |
| 511 | + list_del(&ctx->node); |
| 512 | + caam_jr_free(ctx->jrdev); |
| 513 | + kfree(ctx); |
| 514 | + } |
| 515 | + |
| 516 | + dma_unmap_single(ctrldev, dma_sh_desc->desc_dma, |
| 517 | + desc_bytes(dma_sh_desc->desc), DMA_TO_DEVICE); |
| 518 | + |
| 519 | + kfree(dma_sh_desc); |
| 520 | + kfree(dma_dev); |
| 521 | + |
| 522 | + dev_info(dev, "caam dma support disabled\n"); |
| 523 | + return 0; |
| 524 | +} |
| 525 | + |
| 526 | +static struct platform_driver caam_dma_driver = { |
| 527 | + .driver = { |
| 528 | + .name = "caam-dma", |
| 529 | + }, |
| 530 | + .probe = caam_dma_probe, |
| 531 | + .remove = caam_dma_remove, |
| 532 | +}; |
| 533 | +module_platform_driver(caam_dma_driver); |
| 534 | + |
| 535 | +MODULE_LICENSE("Dual BSD/GPL"); |
| 536 | +MODULE_DESCRIPTION("NXP CAAM support for DMA engine"); |
| 537 | +MODULE_AUTHOR("NXP Semiconductors"); |
| 538 | +MODULE_ALIAS("platform:caam-dma"); |