1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
- */
- #include <linux/debugfs.h>
- #include <linux/export.h>
- #include <linux/delay.h>
- #include <linux/kernel.h>
- #include <linux/msm_ipa.h>
- #include <linux/mutex.h>
- #include <linux/ipa.h>
- #include "linux/msm_gsi.h"
- #include <linux/dmapool.h>
- #include "ipa_i.h"
- #define IPA_DMA_POLLING_MIN_SLEEP_RX 1010
- #define IPA_DMA_POLLING_MAX_SLEEP_RX 1050
- #define IPA_DMA_SYS_DESC_MAX_FIFO_SZ 0x7FF8
- #define IPA_DMA_MAX_PKT_SZ 0xFFFF
- #define IPA_DMA_DUMMY_BUFF_SZ 8
- #define IPA_DMA_PREFETCH_WA_THRESHOLD 9
- #define IPADMA_DRV_NAME "ipa_dma"
- #define IPADMA_DBG(fmt, args...) \
- do { \
- pr_debug(IPADMA_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
- IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPADMA_DBG_LOW(fmt, args...) \
- do { \
- pr_debug(IPADMA_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPADMA_ERR(fmt, args...) \
- do { \
- pr_err(IPADMA_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
- IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPADMA_FUNC_ENTRY() \
- IPADMA_DBG_LOW("ENTRY\n")
- #define IPADMA_FUNC_EXIT() \
- IPADMA_DBG_LOW("EXIT\n")
- #ifdef CONFIG_DEBUG_FS
- #define IPADMA_MAX_MSG_LEN 1024
- static char dbg_buff[IPADMA_MAX_MSG_LEN];
- static void ipa3_dma_debugfs_init(void);
- static void ipa3_dma_debugfs_destroy(void);
- #else
- static void ipa3_dma_debugfs_init(void) {}
- static void ipa3_dma_debugfs_destroy(void) {}
- #endif
- /**
- * struct ipa3_dma_ctx -IPADMA driver context information
- * @enable_ref_cnt: ipa dma enable reference count
- * @destroy_pending: destroy ipa_dma after handling all pending memcpy
- * @ipa_dma_xfer_wrapper_cache: cache of ipa3_dma_xfer_wrapper structs
- * @sync_lock: lock for synchronisation in sync_memcpy
- * @async_lock: lock for synchronisation in async_memcpy
- * @enable_lock: lock for is_enabled
- * @pending_lock: lock for synchronize is_enable and pending_cnt
- * @done: no pending works-ipadma can be destroyed
- * @ipa_dma_sync_prod_hdl: handle of sync memcpy producer
- * @ipa_dma_async_prod_hdl:handle of async memcpy producer
- * @ipa_dma_sync_cons_hdl: handle of sync memcpy consumer
- * @sync_memcpy_pending_cnt: number of pending sync memcopy operations
- * @async_memcpy_pending_cnt: number of pending async memcopy operations
- * @uc_memcpy_pending_cnt: number of pending uc memcopy operations
- * @total_sync_memcpy: total number of sync memcpy (statistics)
- * @total_async_memcpy: total number of async memcpy (statistics)
- * @total_uc_memcpy: total number of uc memcpy (statistics)
- */
- struct ipa3_dma_ctx {
- unsigned int enable_ref_cnt;
- bool destroy_pending;
- struct kmem_cache *ipa_dma_xfer_wrapper_cache;
- struct mutex sync_lock;
- spinlock_t async_lock;
- struct mutex enable_lock;
- spinlock_t pending_lock;
- struct completion done;
- u32 ipa_dma_sync_prod_hdl;
- u32 ipa_dma_async_prod_hdl;
- u32 ipa_dma_sync_cons_hdl;
- u32 ipa_dma_async_cons_hdl;
- atomic_t sync_memcpy_pending_cnt;
- atomic_t async_memcpy_pending_cnt;
- atomic_t uc_memcpy_pending_cnt;
- atomic_t total_sync_memcpy;
- atomic_t total_async_memcpy;
- atomic_t total_uc_memcpy;
- struct ipa_mem_buffer ipa_dma_dummy_src_sync;
- struct ipa_mem_buffer ipa_dma_dummy_dst_sync;
- struct ipa_mem_buffer ipa_dma_dummy_src_async;
- struct ipa_mem_buffer ipa_dma_dummy_dst_async;
- };
- static struct ipa3_dma_ctx *ipa3_dma_ctx;
- /**
- * struct ipa3_dma_init_refcnt_ctrl -IPADMA driver init control information
- * @ref_cnt: reference count for initialization operations
- * @lock: lock for the reference count
- */
- struct ipa3_dma_init_refcnt_ctrl {
- unsigned int ref_cnt;
- struct mutex lock;
- };
- static struct ipa3_dma_init_refcnt_ctrl *ipa3_dma_init_refcnt_ctrl;
- /**
- * ipa3_dma_setup() - One time setup for IPA DMA
- *
- * This function should be called once to setup ipa dma
- * by creating the init reference count controller
- *
- * Return codes: 0: success
- * Negative value: failure
- */
- int ipa3_dma_setup(void)
- {
- IPADMA_FUNC_ENTRY();
- if (ipa3_dma_init_refcnt_ctrl) {
- IPADMA_ERR("Setup already done\n");
- return -EFAULT;
- }
- ipa3_dma_init_refcnt_ctrl =
- kzalloc(sizeof(*(ipa3_dma_init_refcnt_ctrl)), GFP_KERNEL);
- if (!ipa3_dma_init_refcnt_ctrl) {
- IPADMA_ERR("kzalloc error.\n");
- return -ENOMEM;
- }
- mutex_init(&ipa3_dma_init_refcnt_ctrl->lock);
- IPADMA_FUNC_EXIT();
- return 0;
- }
- /**
- * ipa3_dma_shutdown() - Clear setup operations.
- *
- * Cleanup for the setup function.
- * Should be called during IPA driver unloading.
- * It assumes all ipa_dma operations are done and ipa_dma is destroyed.
- *
- * Return codes: None.
- */
- void ipa3_dma_shutdown(void)
- {
- IPADMA_FUNC_ENTRY();
- if (!ipa3_dma_init_refcnt_ctrl)
- return;
- kfree(ipa3_dma_init_refcnt_ctrl);
- ipa3_dma_init_refcnt_ctrl = NULL;
- IPADMA_FUNC_EXIT();
- }
- /**
- * ipa3_dma_init() -Initialize IPADMA.
- *
- * This function initialize all IPADMA internal data and connect in dma:
- * MEMCPY_DMA_SYNC_PROD ->MEMCPY_DMA_SYNC_CONS
- * MEMCPY_DMA_ASYNC_PROD->MEMCPY_DMA_SYNC_CONS
- *
- * Can be executed several times (re-entrant)
- *
- * Return codes: 0: success
- * -EFAULT: Mismatch between context existence and init ref_cnt
- * -EINVAL: IPA driver is not initialized
- * -ENOMEM: allocating memory error
- * -EPERM: pipe connection failed
- */
- int ipa3_dma_init(void)
- {
- struct ipa3_dma_ctx *ipa_dma_ctx_t;
- struct ipa_sys_connect_params sys_in;
- int res = 0;
- int sync_sz;
- int async_sz;
- IPADMA_FUNC_ENTRY();
- if (!ipa3_dma_init_refcnt_ctrl) {
- IPADMA_ERR("Setup isn't done yet!\n");
- return -EINVAL;
- }
- mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
- if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 0) {
- IPADMA_DBG("Already initialized refcnt=%d\n",
- ipa3_dma_init_refcnt_ctrl->ref_cnt);
- if (!ipa3_dma_ctx) {
- IPADMA_ERR("Context missing. refcnt=%d\n",
- ipa3_dma_init_refcnt_ctrl->ref_cnt);
- res = -EFAULT;
- } else {
- ipa3_dma_init_refcnt_ctrl->ref_cnt++;
- }
- goto init_unlock;
- }
- if (ipa3_dma_ctx) {
- IPADMA_ERR("Context already exist\n");
- res = -EFAULT;
- goto init_unlock;
- }
- if (!ipa3_is_ready()) {
- IPADMA_ERR("IPA is not ready yet\n");
- res = -EINVAL;
- goto init_unlock;
- }
- ipa_dma_ctx_t = kzalloc(sizeof(*(ipa3_dma_ctx)), GFP_KERNEL);
- if (!ipa_dma_ctx_t) {
- res = -ENOMEM;
- goto init_unlock;
- }
- ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache =
- kmem_cache_create("IPA DMA XFER WRAPPER",
- sizeof(struct ipa3_dma_xfer_wrapper), 0, 0, NULL);
- if (!ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache) {
- IPAERR(":failed to create ipa dma xfer wrapper cache.\n");
- res = -ENOMEM;
- goto fail_mem_ctrl;
- }
- mutex_init(&ipa_dma_ctx_t->enable_lock);
- spin_lock_init(&ipa_dma_ctx_t->async_lock);
- mutex_init(&ipa_dma_ctx_t->sync_lock);
- spin_lock_init(&ipa_dma_ctx_t->pending_lock);
- init_completion(&ipa_dma_ctx_t->done);
- ipa_dma_ctx_t->enable_ref_cnt = 0;
- ipa_dma_ctx_t->destroy_pending = false;
- atomic_set(&ipa_dma_ctx_t->async_memcpy_pending_cnt, 0);
- atomic_set(&ipa_dma_ctx_t->sync_memcpy_pending_cnt, 0);
- atomic_set(&ipa_dma_ctx_t->uc_memcpy_pending_cnt, 0);
- atomic_set(&ipa_dma_ctx_t->total_async_memcpy, 0);
- atomic_set(&ipa_dma_ctx_t->total_sync_memcpy, 0);
- atomic_set(&ipa_dma_ctx_t->total_uc_memcpy, 0);
- sync_sz = IPA_SYS_DESC_FIFO_SZ;
- async_sz = IPA_DMA_SYS_DESC_MAX_FIFO_SZ;
- /*
- * for ipav3.5 we need to double the rings and allocate dummy buffers
- * in order to apply the prefetch WA
- */
- if (ipa_get_hw_type() == IPA_HW_v3_5) {
- sync_sz *= 2;
- async_sz *= 2;
- ipa_dma_ctx_t->ipa_dma_dummy_src_sync.base =
- dma_alloc_coherent(ipa3_ctx->pdev,
- IPA_DMA_DUMMY_BUFF_SZ * 4,
- &ipa_dma_ctx_t->ipa_dma_dummy_src_sync.phys_base,
- GFP_KERNEL);
- if (!ipa_dma_ctx_t->ipa_dma_dummy_src_sync.base) {
- IPAERR("DMA alloc fail %d bytes for prefetch WA\n",
- IPA_DMA_DUMMY_BUFF_SZ);
- res = -ENOMEM;
- goto fail_alloc_dummy;
- }
- ipa_dma_ctx_t->ipa_dma_dummy_dst_sync.base =
- ipa_dma_ctx_t->ipa_dma_dummy_src_sync.base +
- IPA_DMA_DUMMY_BUFF_SZ;
- ipa_dma_ctx_t->ipa_dma_dummy_dst_sync.phys_base =
- ipa_dma_ctx_t->ipa_dma_dummy_src_sync.phys_base +
- IPA_DMA_DUMMY_BUFF_SZ;
- ipa_dma_ctx_t->ipa_dma_dummy_src_async.base =
- ipa_dma_ctx_t->ipa_dma_dummy_dst_sync.base +
- IPA_DMA_DUMMY_BUFF_SZ;
- ipa_dma_ctx_t->ipa_dma_dummy_src_async.phys_base =
- ipa_dma_ctx_t->ipa_dma_dummy_dst_sync.phys_base +
- IPA_DMA_DUMMY_BUFF_SZ;
- ipa_dma_ctx_t->ipa_dma_dummy_dst_async.base =
- ipa_dma_ctx_t->ipa_dma_dummy_src_async.base +
- IPA_DMA_DUMMY_BUFF_SZ;
- ipa_dma_ctx_t->ipa_dma_dummy_dst_async.phys_base =
- ipa_dma_ctx_t->ipa_dma_dummy_src_async.phys_base +
- IPA_DMA_DUMMY_BUFF_SZ;
- }
- /* IPADMA SYNC PROD-source for sync memcpy */
- memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
- sys_in.client = IPA_CLIENT_MEMCPY_DMA_SYNC_PROD;
- sys_in.desc_fifo_sz = sync_sz;
- sys_in.ipa_ep_cfg.mode.mode = IPA_DMA;
- sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_MEMCPY_DMA_SYNC_CONS;
- sys_in.skip_ep_cfg = false;
- if (ipa3_setup_sys_pipe(&sys_in,
- &ipa_dma_ctx_t->ipa_dma_sync_prod_hdl)) {
- IPADMA_ERR(":setup sync prod pipe failed\n");
- res = -EPERM;
- goto fail_sync_prod;
- }
- /* IPADMA SYNC CONS-destination for sync memcpy */
- memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
- sys_in.client = IPA_CLIENT_MEMCPY_DMA_SYNC_CONS;
- sys_in.desc_fifo_sz = sync_sz;
- sys_in.skip_ep_cfg = false;
- sys_in.ipa_ep_cfg.mode.mode = IPA_BASIC;
- sys_in.notify = NULL;
- sys_in.priv = NULL;
- if (ipa3_setup_sys_pipe(&sys_in,
- &ipa_dma_ctx_t->ipa_dma_sync_cons_hdl)) {
- IPADMA_ERR(":setup sync cons pipe failed.\n");
- res = -EPERM;
- goto fail_sync_cons;
- }
- IPADMA_DBG("SYNC MEMCPY pipes are connected\n");
- /* IPADMA ASYNC PROD-source for sync memcpy */
- memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
- sys_in.client = IPA_CLIENT_MEMCPY_DMA_ASYNC_PROD;
- sys_in.desc_fifo_sz = async_sz;
- sys_in.ipa_ep_cfg.mode.mode = IPA_DMA;
- sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS;
- sys_in.skip_ep_cfg = false;
- sys_in.notify = NULL;
- if (ipa3_setup_sys_pipe(&sys_in,
- &ipa_dma_ctx_t->ipa_dma_async_prod_hdl)) {
- IPADMA_ERR(":setup async prod pipe failed.\n");
- res = -EPERM;
- goto fail_async_prod;
- }
- /* IPADMA ASYNC CONS-destination for sync memcpy */
- memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
- sys_in.client = IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS;
- sys_in.desc_fifo_sz = async_sz;
- sys_in.skip_ep_cfg = false;
- sys_in.ipa_ep_cfg.mode.mode = IPA_BASIC;
- sys_in.notify = ipa3_dma_async_memcpy_notify_cb;
- sys_in.priv = NULL;
- if (ipa3_setup_sys_pipe(&sys_in,
- &ipa_dma_ctx_t->ipa_dma_async_cons_hdl)) {
- IPADMA_ERR(":setup async cons pipe failed.\n");
- res = -EPERM;
- goto fail_async_cons;
- }
- ipa3_dma_debugfs_init();
- ipa3_dma_ctx = ipa_dma_ctx_t;
- ipa3_dma_init_refcnt_ctrl->ref_cnt = 1;
- IPADMA_DBG("ASYNC MEMCPY pipes are connected\n");
- IPADMA_FUNC_EXIT();
- goto init_unlock;
- fail_async_cons:
- ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_async_prod_hdl);
- fail_async_prod:
- ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_sync_cons_hdl);
- fail_sync_cons:
- ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_sync_prod_hdl);
- fail_sync_prod:
- dma_free_coherent(ipa3_ctx->pdev, IPA_DMA_DUMMY_BUFF_SZ * 4,
- ipa_dma_ctx_t->ipa_dma_dummy_src_sync.base,
- ipa_dma_ctx_t->ipa_dma_dummy_src_sync.phys_base);
- fail_alloc_dummy:
- kmem_cache_destroy(ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache);
- fail_mem_ctrl:
- kfree(ipa_dma_ctx_t);
- ipa3_dma_ctx = NULL;
- init_unlock:
- mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
- return res;
- }
- /**
- * ipa3_dma_enable() -Vote for IPA clocks.
- *
- * Can be executed several times (re-entrant)
- *
- *Return codes: 0: success
- * -EINVAL: IPADMA is not initialized
- */
- int ipa3_dma_enable(void)
- {
- IPADMA_FUNC_ENTRY();
- if ((ipa3_dma_ctx == NULL) ||
- (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
- IPADMA_ERR("IPADMA isn't initialized, can't enable\n");
- return -EINVAL;
- }
- mutex_lock(&ipa3_dma_ctx->enable_lock);
- if (ipa3_dma_ctx->enable_ref_cnt > 0) {
- IPADMA_ERR("Already enabled refcnt=%d\n",
- ipa3_dma_ctx->enable_ref_cnt);
- ipa3_dma_ctx->enable_ref_cnt++;
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
- return 0;
- }
- IPA_ACTIVE_CLIENTS_INC_SPECIAL("DMA");
- ipa3_dma_ctx->enable_ref_cnt = 1;
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
- IPADMA_FUNC_EXIT();
- return 0;
- }
- static bool ipa3_dma_work_pending(void)
- {
- if (atomic_read(&ipa3_dma_ctx->sync_memcpy_pending_cnt)) {
- IPADMA_DBG("pending sync\n");
- return true;
- }
- if (atomic_read(&ipa3_dma_ctx->async_memcpy_pending_cnt)) {
- IPADMA_DBG("pending async\n");
- return true;
- }
- if (atomic_read(&ipa3_dma_ctx->uc_memcpy_pending_cnt)) {
- IPADMA_DBG("pending uc\n");
- return true;
- }
- IPADMA_DBG_LOW("no pending work\n");
- return false;
- }
- /**
- * ipa3_dma_disable()- Unvote for IPA clocks.
- *
- * enter to power save mode.
- *
- * Return codes: 0: success
- * -EINVAL: IPADMA is not initialized
- * -EPERM: Operation not permitted as ipa_dma is already
- * diabled
- * -EFAULT: can not disable ipa_dma as there are pending
- * memcopy works
- */
- int ipa3_dma_disable(void)
- {
- unsigned long flags;
- int res = 0;
- bool dec_clks = false;
- IPADMA_FUNC_ENTRY();
- if ((ipa3_dma_ctx == NULL) ||
- (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
- IPADMA_ERR("IPADMA isn't initialized, can't disable\n");
- return -EINVAL;
- }
- mutex_lock(&ipa3_dma_ctx->enable_lock);
- spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (ipa3_dma_ctx->enable_ref_cnt > 1) {
- IPADMA_DBG("Multiple enablement done. refcnt=%d\n",
- ipa3_dma_ctx->enable_ref_cnt);
- ipa3_dma_ctx->enable_ref_cnt--;
- goto completed;
- }
- if (ipa3_dma_ctx->enable_ref_cnt == 0) {
- IPADMA_ERR("Already disabled\n");
- res = -EPERM;
- goto completed;
- }
- if (ipa3_dma_work_pending()) {
- IPADMA_ERR("There is pending work, can't disable.\n");
- res = -EFAULT;
- goto completed;
- }
- ipa3_dma_ctx->enable_ref_cnt = 0;
- dec_clks = true;
- IPADMA_FUNC_EXIT();
- completed:
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- if (dec_clks)
- IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA");
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
- return res;
- }
- /**
- * ipa3_dma_sync_memcpy()- Perform synchronous memcpy using IPA.
- *
- * @dest: physical address to store the copied data.
- * @src: physical address of the source data to copy.
- * @len: number of bytes to copy.
- *
- * Return codes: 0: success
- * -EINVAL: invalid params
- * -EPERM: operation not permitted as ipa_dma isn't enable or
- * initialized
- * -gsi_status : on GSI failures
- * -EFAULT: other
- */
- int ipa3_dma_sync_memcpy(u64 dest, u64 src, int len)
- {
- int ep_idx;
- int res;
- int i = 0;
- struct ipa3_sys_context *cons_sys;
- struct ipa3_sys_context *prod_sys;
- struct ipa3_dma_xfer_wrapper *xfer_descr = NULL;
- struct ipa3_dma_xfer_wrapper *head_descr = NULL;
- struct gsi_xfer_elem prod_xfer_elem;
- struct gsi_xfer_elem cons_xfer_elem;
- struct gsi_chan_xfer_notify gsi_notify;
- unsigned long flags;
- bool stop_polling = false;
- bool prefetch_wa = false;
- IPADMA_FUNC_ENTRY();
- IPADMA_DBG_LOW("dest = 0x%llx, src = 0x%llx, len = %d\n",
- dest, src, len);
- if (ipa3_dma_ctx == NULL) {
- IPADMA_ERR("IPADMA isn't initialized, can't memcpy\n");
- return -EPERM;
- }
- if ((max(src, dest) - min(src, dest)) < len) {
- IPADMA_ERR("invalid addresses - overlapping buffers\n");
- return -EINVAL;
- }
- if (len > IPA_DMA_MAX_PKT_SZ || len <= 0) {
- IPADMA_ERR("invalid len, %d\n", len);
- return -EINVAL;
- }
- spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->enable_ref_cnt) {
- IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- return -EPERM;
- }
- atomic_inc(&ipa3_dma_ctx->sync_memcpy_pending_cnt);
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_SYNC_CONS);
- if (-1 == ep_idx) {
- IPADMA_ERR("Client %u is not mapped\n",
- IPA_CLIENT_MEMCPY_DMA_SYNC_CONS);
- return -EFAULT;
- }
- cons_sys = ipa3_ctx->ep[ep_idx].sys;
- ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_SYNC_PROD);
- if (-1 == ep_idx) {
- IPADMA_ERR("Client %u is not mapped\n",
- IPA_CLIENT_MEMCPY_DMA_SYNC_PROD);
- return -EFAULT;
- }
- prod_sys = ipa3_ctx->ep[ep_idx].sys;
- xfer_descr = kmem_cache_zalloc(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache,
- GFP_KERNEL);
- if (!xfer_descr) {
- IPADMA_ERR("failed to alloc xfer descr wrapper\n");
- res = -ENOMEM;
- goto fail_mem_alloc;
- }
- xfer_descr->phys_addr_dest = dest;
- xfer_descr->phys_addr_src = src;
- xfer_descr->len = len;
- init_completion(&xfer_descr->xfer_done);
- mutex_lock(&ipa3_dma_ctx->sync_lock);
- list_add_tail(&xfer_descr->link, &cons_sys->head_desc_list);
- cons_sys->len++;
- cons_xfer_elem.addr = dest;
- cons_xfer_elem.len = len;
- cons_xfer_elem.type = GSI_XFER_ELEM_DATA;
- cons_xfer_elem.flags = GSI_XFER_FLAG_EOT;
- prod_xfer_elem.addr = src;
- prod_xfer_elem.len = len;
- prod_xfer_elem.type = GSI_XFER_ELEM_DATA;
- prod_xfer_elem.xfer_user_data = NULL;
- /*
- * when copy is less than 9B we need to chain another dummy
- * copy so the total size will be larger (for ipav3.5)
- * for the consumer we have to prepare an additional credit
- */
- prefetch_wa = ((ipa_get_hw_type() == IPA_HW_v3_5) &&
- len < IPA_DMA_PREFETCH_WA_THRESHOLD);
- if (prefetch_wa) {
- cons_xfer_elem.xfer_user_data = NULL;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &cons_xfer_elem, false);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer dest descr res:%d\n",
- res);
- goto fail_send;
- }
- cons_xfer_elem.addr =
- ipa3_dma_ctx->ipa_dma_dummy_dst_sync.phys_base;
- cons_xfer_elem.len = IPA_DMA_DUMMY_BUFF_SZ;
- cons_xfer_elem.type = GSI_XFER_ELEM_DATA;
- cons_xfer_elem.flags = GSI_XFER_FLAG_EOT;
- cons_xfer_elem.xfer_user_data = xfer_descr;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &cons_xfer_elem, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer dummy dest descr res:%d\n",
- res);
- goto fail_send;
- }
- prod_xfer_elem.flags = GSI_XFER_FLAG_CHAIN;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &prod_xfer_elem, false);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer src descr res:%d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- prod_xfer_elem.addr =
- ipa3_dma_ctx->ipa_dma_dummy_src_sync.phys_base;
- prod_xfer_elem.len = IPA_DMA_DUMMY_BUFF_SZ;
- prod_xfer_elem.type = GSI_XFER_ELEM_DATA;
- prod_xfer_elem.flags = GSI_XFER_FLAG_EOT;
- prod_xfer_elem.xfer_user_data = NULL;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &prod_xfer_elem, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer dummy src descr res:%d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- } else {
- cons_xfer_elem.xfer_user_data = xfer_descr;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &cons_xfer_elem, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer dest descr res:%d\n",
- res);
- goto fail_send;
- }
- prod_xfer_elem.flags = GSI_XFER_FLAG_EOT;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &prod_xfer_elem, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer src descr res:%d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- }
- head_descr = list_first_entry(&cons_sys->head_desc_list,
- struct ipa3_dma_xfer_wrapper, link);
- /* in case we are not the head of the list, wait for head to wake us */
- if (xfer_descr != head_descr) {
- mutex_unlock(&ipa3_dma_ctx->sync_lock);
- wait_for_completion(&xfer_descr->xfer_done);
- mutex_lock(&ipa3_dma_ctx->sync_lock);
- head_descr = list_first_entry(&cons_sys->head_desc_list,
- struct ipa3_dma_xfer_wrapper, link);
- /* Unexpected transfer sent from HW */
- ipa_assert_on(xfer_descr != head_descr);
- }
- mutex_unlock(&ipa3_dma_ctx->sync_lock);
- do {
- /* wait for transfer to complete */
- res = gsi_poll_channel(cons_sys->ep->gsi_chan_hdl,
- &gsi_notify);
- if (res == GSI_STATUS_SUCCESS)
- stop_polling = true;
- else if (res != GSI_STATUS_POLL_EMPTY)
- IPADMA_ERR(
- "Failed: gsi_poll_chanel, returned %d loop#:%d\n",
- res, i);
- usleep_range(IPA_DMA_POLLING_MIN_SLEEP_RX,
- IPA_DMA_POLLING_MAX_SLEEP_RX);
- i++;
- } while (!stop_polling);
- /* for prefetch WA we will receive the length of the dummy
- * transfer in the event (because it is the second element)
- */
- if (prefetch_wa)
- ipa_assert_on(gsi_notify.bytes_xfered !=
- IPA_DMA_DUMMY_BUFF_SZ);
- else
- ipa_assert_on(len != gsi_notify.bytes_xfered);
- ipa_assert_on(dest != ((struct ipa3_dma_xfer_wrapper *)
- (gsi_notify.xfer_user_data))->phys_addr_dest);
- mutex_lock(&ipa3_dma_ctx->sync_lock);
- list_del(&head_descr->link);
- cons_sys->len--;
- kmem_cache_free(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache, xfer_descr);
- /* wake the head of the list */
- if (!list_empty(&cons_sys->head_desc_list)) {
- head_descr = list_first_entry(&cons_sys->head_desc_list,
- struct ipa3_dma_xfer_wrapper, link);
- complete(&head_descr->xfer_done);
- }
- mutex_unlock(&ipa3_dma_ctx->sync_lock);
- atomic_inc(&ipa3_dma_ctx->total_sync_memcpy);
- atomic_dec(&ipa3_dma_ctx->sync_memcpy_pending_cnt);
- if (ipa3_dma_ctx->destroy_pending && !ipa3_dma_work_pending())
- complete(&ipa3_dma_ctx->done);
- IPADMA_FUNC_EXIT();
- return res;
- fail_send:
- list_del(&xfer_descr->link);
- cons_sys->len--;
- mutex_unlock(&ipa3_dma_ctx->sync_lock);
- kmem_cache_free(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache, xfer_descr);
- fail_mem_alloc:
- atomic_dec(&ipa3_dma_ctx->sync_memcpy_pending_cnt);
- if (ipa3_dma_ctx->destroy_pending && !ipa3_dma_work_pending())
- complete(&ipa3_dma_ctx->done);
- return res;
- }
- /**
- * ipa3_dma_async_memcpy()- Perform asynchronous memcpy using IPA.
- *
- * @dest: physical address to store the copied data.
- * @src: physical address of the source data to copy.
- * @len: number of bytes to copy.
- * @user_cb: callback function to notify the client when the copy was done.
- * @user_param: cookie for user_cb.
- *
- * Return codes: 0: success
- * -EINVAL: invalid params
- * -EPERM: operation not permitted as ipa_dma isn't enable or
- * initialized
- * -gsi_status : on GSI failures
- * -EFAULT: descr fifo is full.
- */
- int ipa3_dma_async_memcpy(u64 dest, u64 src, int len,
- void (*user_cb)(void *user1), void *user_param)
- {
- int ep_idx;
- int res = 0;
- struct ipa3_dma_xfer_wrapper *xfer_descr = NULL;
- struct ipa3_sys_context *prod_sys;
- struct ipa3_sys_context *cons_sys;
- struct gsi_xfer_elem xfer_elem_cons, xfer_elem_prod;
- unsigned long flags;
- IPADMA_FUNC_ENTRY();
- IPADMA_DBG_LOW("dest = 0x%llx, src = 0x%llx, len = %d\n",
- dest, src, len);
- if (ipa3_dma_ctx == NULL) {
- IPADMA_ERR("IPADMA isn't initialized, can't memcpy\n");
- return -EPERM;
- }
- if ((max(src, dest) - min(src, dest)) < len) {
- IPADMA_ERR("invalid addresses - overlapping buffers\n");
- return -EINVAL;
- }
- if (len > IPA_DMA_MAX_PKT_SZ || len <= 0) {
- IPADMA_ERR("invalid len, %d\n", len);
- return -EINVAL;
- }
- if (!user_cb) {
- IPADMA_ERR("null pointer: user_cb\n");
- return -EINVAL;
- }
- spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->enable_ref_cnt) {
- IPADMA_ERR("can't memcpy, IPA_DMA isn't enabled\n");
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- return -EPERM;
- }
- atomic_inc(&ipa3_dma_ctx->async_memcpy_pending_cnt);
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
- if (-1 == ep_idx) {
- IPADMA_ERR("Client %u is not mapped\n",
- IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
- return -EFAULT;
- }
- cons_sys = ipa3_ctx->ep[ep_idx].sys;
- ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_PROD);
- if (-1 == ep_idx) {
- IPADMA_ERR("Client %u is not mapped\n",
- IPA_CLIENT_MEMCPY_DMA_ASYNC_PROD);
- return -EFAULT;
- }
- prod_sys = ipa3_ctx->ep[ep_idx].sys;
- xfer_descr = kmem_cache_zalloc(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache,
- GFP_KERNEL);
- if (!xfer_descr) {
- res = -ENOMEM;
- goto fail_mem_alloc;
- }
- xfer_descr->phys_addr_dest = dest;
- xfer_descr->phys_addr_src = src;
- xfer_descr->len = len;
- xfer_descr->callback = user_cb;
- xfer_descr->user1 = user_param;
- spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags);
- list_add_tail(&xfer_descr->link, &cons_sys->head_desc_list);
- cons_sys->len++;
- /*
- * when copy is less than 9B we need to chain another dummy
- * copy so the total size will be larger (for ipav3.5)
- */
- if ((ipa_get_hw_type() == IPA_HW_v3_5) && len <
- IPA_DMA_PREFETCH_WA_THRESHOLD) {
- xfer_elem_cons.addr = dest;
- xfer_elem_cons.len = len;
- xfer_elem_cons.type = GSI_XFER_ELEM_DATA;
- xfer_elem_cons.flags = GSI_XFER_FLAG_EOT;
- xfer_elem_cons.xfer_user_data = NULL;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_cons, false);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on dest descr res: %d\n",
- res);
- goto fail_send;
- }
- xfer_elem_cons.addr =
- ipa3_dma_ctx->ipa_dma_dummy_dst_async.phys_base;
- xfer_elem_cons.len = IPA_DMA_DUMMY_BUFF_SZ;
- xfer_elem_cons.type = GSI_XFER_ELEM_DATA;
- xfer_elem_cons.flags = GSI_XFER_FLAG_EOT;
- xfer_elem_cons.xfer_user_data = xfer_descr;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_cons, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on dummy dest descr res: %d\n",
- res);
- goto fail_send;
- }
- xfer_elem_prod.addr = src;
- xfer_elem_prod.len = len;
- xfer_elem_prod.type = GSI_XFER_ELEM_DATA;
- xfer_elem_prod.flags = GSI_XFER_FLAG_CHAIN;
- xfer_elem_prod.xfer_user_data = NULL;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_prod, false);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on src descr res: %d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- xfer_elem_prod.addr =
- ipa3_dma_ctx->ipa_dma_dummy_src_async.phys_base;
- xfer_elem_prod.len = IPA_DMA_DUMMY_BUFF_SZ;
- xfer_elem_prod.type = GSI_XFER_ELEM_DATA;
- xfer_elem_prod.flags = GSI_XFER_FLAG_EOT;
- xfer_elem_prod.xfer_user_data = NULL;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_prod, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on dummy src descr res: %d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- } else {
- xfer_elem_cons.addr = dest;
- xfer_elem_cons.len = len;
- xfer_elem_cons.type = GSI_XFER_ELEM_DATA;
- xfer_elem_cons.flags = GSI_XFER_FLAG_EOT;
- xfer_elem_cons.xfer_user_data = xfer_descr;
- res = gsi_queue_xfer(cons_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_cons, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on dummy dest descr res: %d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- xfer_elem_prod.addr = src;
- xfer_elem_prod.len = len;
- xfer_elem_prod.type = GSI_XFER_ELEM_DATA;
- xfer_elem_prod.flags = GSI_XFER_FLAG_EOT;
- xfer_elem_prod.xfer_user_data = NULL;
- res = gsi_queue_xfer(prod_sys->ep->gsi_chan_hdl, 1,
- &xfer_elem_prod, true);
- if (res) {
- IPADMA_ERR(
- "Failed: gsi_queue_xfer on dummy src descr res: %d\n",
- res);
- ipa_assert();
- goto fail_send;
- }
- }
- spin_unlock_irqrestore(&ipa3_dma_ctx->async_lock, flags);
- IPADMA_FUNC_EXIT();
- return res;
- fail_send:
- list_del(&xfer_descr->link);
- spin_unlock_irqrestore(&ipa3_dma_ctx->async_lock, flags);
- kmem_cache_free(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache, xfer_descr);
- fail_mem_alloc:
- atomic_dec(&ipa3_dma_ctx->async_memcpy_pending_cnt);
- if (ipa3_dma_ctx->destroy_pending && !ipa3_dma_work_pending())
- complete(&ipa3_dma_ctx->done);
- return res;
- }
- /**
- * ipa3_dma_uc_memcpy() - Perform a memcpy action using IPA uC
- * @dest: physical address to store the copied data.
- * @src: physical address of the source data to copy.
- * @len: number of bytes to copy.
- *
- * Return codes: 0: success
- * -EINVAL: invalid params
- * -EPERM: operation not permitted as ipa_dma isn't enable or
- * initialized
- * -EBADF: IPA uC is not loaded
- */
- int ipa3_dma_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len)
- {
- int res;
- unsigned long flags;
- IPADMA_FUNC_ENTRY();
- if (ipa3_dma_ctx == NULL) {
- IPADMA_ERR("IPADMA isn't initialized, can't memcpy\n");
- return -EPERM;
- }
- if ((max(src, dest) - min(src, dest)) < len) {
- IPADMA_ERR("invalid addresses - overlapping buffers\n");
- return -EINVAL;
- }
- if (len > IPA_DMA_MAX_PKT_SZ || len <= 0) {
- IPADMA_ERR("invalid len, %d\n", len);
- return -EINVAL;
- }
- spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->enable_ref_cnt) {
- IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- return -EPERM;
- }
- atomic_inc(&ipa3_dma_ctx->uc_memcpy_pending_cnt);
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- res = ipa3_uc_memcpy(dest, src, len);
- if (res) {
- IPADMA_ERR("ipa3_uc_memcpy failed %d\n", res);
- goto dec_and_exit;
- }
- atomic_inc(&ipa3_dma_ctx->total_uc_memcpy);
- res = 0;
- dec_and_exit:
- atomic_dec(&ipa3_dma_ctx->uc_memcpy_pending_cnt);
- if (ipa3_dma_ctx->destroy_pending && !ipa3_dma_work_pending())
- complete(&ipa3_dma_ctx->done);
- IPADMA_FUNC_EXIT();
- return res;
- }
- /**
- * ipa3_dma_destroy() -teardown IPADMA pipes and release ipadma.
- *
- * this is a blocking function, returns just after destroying IPADMA.
- */
- void ipa3_dma_destroy(void)
- {
- int res = 0;
- IPADMA_FUNC_ENTRY();
- if (!ipa3_dma_init_refcnt_ctrl) {
- IPADMA_ERR("Setup isn't done\n");
- return;
- }
- mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
- if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 1) {
- IPADMA_DBG("Multiple initialization done. refcnt=%d\n",
- ipa3_dma_init_refcnt_ctrl->ref_cnt);
- ipa3_dma_init_refcnt_ctrl->ref_cnt--;
- goto completed;
- }
- if ((!ipa3_dma_ctx) || (ipa3_dma_init_refcnt_ctrl->ref_cnt == 0)) {
- IPADMA_ERR("IPADMA isn't initialized ctx=%pK\n", ipa3_dma_ctx);
- goto completed;
- }
- if (ipa3_dma_work_pending()) {
- ipa3_dma_ctx->destroy_pending = true;
- IPADMA_DBG("There are pending memcpy, wait for completion\n");
- wait_for_completion(&ipa3_dma_ctx->done);
- }
- if (ipa3_dma_ctx->enable_ref_cnt > 0) {
- IPADMA_ERR("IPADMA still enabled\n");
- goto completed;
- }
- res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_async_cons_hdl);
- if (res)
- IPADMA_ERR("teardown IPADMA ASYNC CONS failed\n");
- ipa3_dma_ctx->ipa_dma_async_cons_hdl = 0;
- res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_sync_cons_hdl);
- if (res)
- IPADMA_ERR("teardown IPADMA SYNC CONS failed\n");
- ipa3_dma_ctx->ipa_dma_sync_cons_hdl = 0;
- res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_async_prod_hdl);
- if (res)
- IPADMA_ERR("teardown IPADMA ASYNC PROD failed\n");
- ipa3_dma_ctx->ipa_dma_async_prod_hdl = 0;
- res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_sync_prod_hdl);
- if (res)
- IPADMA_ERR("teardown IPADMA SYNC PROD failed\n");
- ipa3_dma_ctx->ipa_dma_sync_prod_hdl = 0;
- ipa3_dma_debugfs_destroy();
- kmem_cache_destroy(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache);
- dma_free_coherent(ipa3_ctx->pdev, IPA_DMA_DUMMY_BUFF_SZ * 4,
- ipa3_dma_ctx->ipa_dma_dummy_src_sync.base,
- ipa3_dma_ctx->ipa_dma_dummy_src_sync.phys_base);
- kfree(ipa3_dma_ctx);
- ipa3_dma_ctx = NULL;
- ipa3_dma_init_refcnt_ctrl->ref_cnt = 0;
- IPADMA_FUNC_EXIT();
- completed:
- mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
- }
- /**
- * ipa3_dma_async_memcpy_notify_cb() - Callback function which will be called
- * by IPA driver after getting notify on Rx operation is completed (data was
- * written to dest descriptor on async_cons ep).
- *
- * @priv -not in use.
- * @evt - event name - IPA_RECIVE.
- * @data -the ipa_mem_buffer.
- */
- void ipa3_dma_async_memcpy_notify_cb(void *priv
- , enum ipa_dp_evt_type evt, unsigned long data)
- {
- int ep_idx = 0;
- struct ipa3_dma_xfer_wrapper *xfer_descr_expected;
- struct ipa3_sys_context *sys;
- unsigned long flags;
- IPADMA_FUNC_ENTRY();
- ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
- if (ep_idx < 0) {
- IPADMA_ERR("IPA Client mapping failed\n");
- return;
- }
- sys = ipa3_ctx->ep[ep_idx].sys;
- spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags);
- xfer_descr_expected = list_first_entry(&sys->head_desc_list,
- struct ipa3_dma_xfer_wrapper, link);
- list_del(&xfer_descr_expected->link);
- sys->len--;
- spin_unlock_irqrestore(&ipa3_dma_ctx->async_lock, flags);
- atomic_inc(&ipa3_dma_ctx->total_async_memcpy);
- atomic_dec(&ipa3_dma_ctx->async_memcpy_pending_cnt);
- xfer_descr_expected->callback(xfer_descr_expected->user1);
- kmem_cache_free(ipa3_dma_ctx->ipa_dma_xfer_wrapper_cache,
- xfer_descr_expected);
- if (ipa3_dma_ctx->destroy_pending && !ipa3_dma_work_pending())
- complete(&ipa3_dma_ctx->done);
- IPADMA_FUNC_EXIT();
- }
- #ifdef CONFIG_DEBUG_FS
- static struct dentry *dent;
- static struct dentry *dfile_info;
- static ssize_t ipa3_dma_debugfs_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- int nbytes = 0;
- if (!ipa3_dma_init_refcnt_ctrl) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "Setup was not done\n");
- goto completed;
- }
- if (!ipa3_dma_ctx) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "Status:\n Not initialized (ref_cnt=%d)\n",
- ipa3_dma_init_refcnt_ctrl->ref_cnt);
- } else {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "Status:\n Initialized (ref_cnt=%d)\n",
- ipa3_dma_init_refcnt_ctrl->ref_cnt);
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- " %s (ref_cnt=%d)\n",
- (ipa3_dma_ctx->enable_ref_cnt > 0) ?
- "Enabled" : "Disabled",
- ipa3_dma_ctx->enable_ref_cnt);
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "Statistics:\n total sync memcpy: %d\n ",
- atomic_read(&ipa3_dma_ctx->total_sync_memcpy));
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "total async memcpy: %d\n ",
- atomic_read(&ipa3_dma_ctx->total_async_memcpy));
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "total uc memcpy: %d\n ",
- atomic_read(&ipa3_dma_ctx->total_uc_memcpy));
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "pending sync memcpy jobs: %d\n ",
- atomic_read(&ipa3_dma_ctx->sync_memcpy_pending_cnt));
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "pending async memcpy jobs: %d\n ",
- atomic_read(&ipa3_dma_ctx->async_memcpy_pending_cnt));
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPADMA_MAX_MSG_LEN - nbytes,
- "pending uc memcpy jobs: %d\n",
- atomic_read(&ipa3_dma_ctx->uc_memcpy_pending_cnt));
- }
- completed:
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
- }
- static ssize_t ipa3_dma_debugfs_reset_statistics(struct file *file,
- const char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- s8 in_num = 0;
- int ret;
- ret = kstrtos8_from_user(ubuf, count, 0, &in_num);
- if (ret)
- return ret;
- switch (in_num) {
- case 0:
- if (ipa3_dma_work_pending())
- IPADMA_ERR("Note, there are pending memcpy\n");
- atomic_set(&ipa3_dma_ctx->total_async_memcpy, 0);
- atomic_set(&ipa3_dma_ctx->total_sync_memcpy, 0);
- break;
- default:
- IPADMA_ERR("invalid argument: To reset statistics echo 0\n");
- break;
- }
- return count;
- }
- const struct file_operations ipa3_ipadma_stats_ops = {
- .read = ipa3_dma_debugfs_read,
- .write = ipa3_dma_debugfs_reset_statistics,
- };
- static void ipa3_dma_debugfs_init(void)
- {
- const mode_t read_write_mode = 0666;
- dent = debugfs_create_dir("ipa_dma", 0);
- if (IS_ERR(dent)) {
- IPADMA_ERR("fail to create folder ipa_dma\n");
- return;
- }
- dfile_info =
- debugfs_create_file("info", read_write_mode, dent,
- 0, &ipa3_ipadma_stats_ops);
- if (!dfile_info || IS_ERR(dfile_info)) {
- IPADMA_ERR("fail to create file stats\n");
- goto fail;
- }
- return;
- fail:
- debugfs_remove_recursive(dent);
- }
- static void ipa3_dma_debugfs_destroy(void)
- {
- debugfs_remove_recursive(dent);
- }
- #endif /* !CONFIG_DEBUG_FS */
|