diff --git a/drivers/misc/mods/mods_dma.c b/drivers/misc/mods/mods_dma.c index f7a32db5..6f33cd18 100644 --- a/drivers/misc/mods/mods_dma.c +++ b/drivers/misc/mods/mods_dma.c @@ -18,10 +18,15 @@ * If not, see . */ -#include +#include #include +#include +#include +#include #include "mods_internal.h" +static struct device *mods_tegra_dma_dev; + #define MODS_DMA_MAX_CHANNEL 32 struct mods_dma_chan_info { rwlock_t lock; @@ -61,26 +66,14 @@ static void mods_release_dma_id(u32 id) static int mods_get_chan_by_id(u32 id, struct mods_dma_chan_info **p_dma_chan) { - if (id > MODS_DMA_MAX_CHANNEL) - return -ERANGE; + if (id >= MODS_DMA_MAX_CHANNEL) + return -ERANGE; *p_dma_chan = &dma_info_chan_list[id]; return OK; } -void mods_init_dma(void) -{ - struct mods_dma_chan_info *p_chan_info; - int i; - - for (i = 0; i < MODS_DMA_MAX_CHANNEL; i++) { - p_chan_info = &dma_info_chan_list[i]; - rwlock_init(&(p_chan_info->lock)); - p_chan_info->in_use = false; - } -} - static void mods_release_channel(u32 id) { struct mods_dma_chan_info *p_mods_chan; @@ -107,14 +100,6 @@ static void mods_release_channel(u32 id) } } -void mods_exit_dma(void) -{ - int i; - - for (i = 0; i < MODS_DMA_MAX_CHANNEL; i++) - mods_release_channel(i); -} - static bool mods_chan_is_inuse(struct mods_dma_chan_info *p_mods_chan) { bool in_use = false; @@ -128,23 +113,23 @@ static bool mods_chan_is_inuse(struct mods_dma_chan_info *p_mods_chan) } static int mods_get_inuse_chan_by_handle(struct MODS_DMA_HANDLE *p_handle, - struct mods_dma_chan_info **p_mods_chan) + struct mods_dma_chan_info **p_mods_chan) { - int ret; + int err; bool in_use; struct mods_dma_chan_info *p_mods_ch; - ret = mods_get_chan_by_id(p_handle->dma_id, &p_mods_ch); - if (ret != OK) { + err = mods_get_chan_by_id(p_handle->dma_id, &p_mods_ch); + if (err != OK) { mods_error_printk("get dma channel failed, id %d\n", - p_handle->dma_id); + p_handle->dma_id); return -ENODEV; } in_use = mods_chan_is_inuse(p_mods_ch); if (!in_use) { mods_error_printk("invalid dma channel: %d, not in use\n", - p_handle->dma_id); + p_handle->dma_id); return -EINVAL; } *p_mods_chan = p_mods_ch; @@ -152,36 +137,36 @@ static int mods_get_inuse_chan_by_handle(struct MODS_DMA_HANDLE *p_handle, } static int mods_dma_sync_wait(struct MODS_DMA_HANDLE *p_handle, - mods_dma_cookie_t cookie) + mods_dma_cookie_t cookie) { - int ret = OK; + int err = OK; struct mods_dma_chan_info *p_mods_chan; - ret = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); - if (ret != OK) - return ret; + err = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); + if (err != OK) + return err; mods_debug_printk(DEBUG_TEGRADMA, - "Wait on chan: %p\n", p_mods_chan->pch); + "Wait on chan: %p\n", p_mods_chan->pch); read_lock(&(p_mods_chan->lock)); if (dma_sync_wait(p_mods_chan->pch, cookie) != DMA_COMPLETE) - ret = -1; + err = -1; read_unlock(&(p_mods_chan->lock)); - return ret; + return err; } static int mods_dma_async_is_tx_complete(struct MODS_DMA_HANDLE *p_handle, - mods_dma_cookie_t cookie, - __u32 *p_is_complete) + mods_dma_cookie_t cookie, + __u32 *p_is_complete) { - int ret = OK; + int err = OK; struct mods_dma_chan_info *p_mods_chan; enum dma_status status; - ret = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); - if (ret != OK) - return ret; + err = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); + if (err != OK) + return err; read_lock(&(p_mods_chan->lock)); status = dma_async_is_tx_complete(p_mods_chan->pch, cookie, NULL, NULL); @@ -192,49 +177,64 @@ static int mods_dma_async_is_tx_complete(struct MODS_DMA_HANDLE *p_handle, else if (status == DMA_IN_PROGRESS) *p_is_complete = false; else - ret = -EINVAL; + err = -EINVAL; - return ret; + return err; } -int esc_mods_dma_request_channel(struct mods_client *client, - struct MODS_DMA_HANDLE *p_handle) +int esc_mods_dma_request_channel_2(struct mods_client *client, + struct MODS_DMA_HANDLE_2 *p_handle) { - struct dma_chan *chan; - struct mods_dma_chan_info *p_mods_chan; - dma_cap_mask_t mask; - u32 id; - int ret; + struct dma_chan *chan = NULL; + struct mods_dma_chan_info *p_mods_chan = NULL; + u32 id = MODS_DMA_MAX_CHANNEL; + int err = -EINVAL; LOG_ENT(); - ret = mods_get_dma_id(&id); - if (ret != OK) { - cl_error("no dma handle available\n"); - return ret; + if (p_handle->dma_type >= MODS_DMA_TX_TYPE_END) { + cl_error("dma type %d not available\n", p_handle->dma_type); + goto failed; } - ret = mods_get_chan_by_id(id, &p_mods_chan); - if (ret != OK) { + err = mods_get_dma_id(&id); + if (err != OK) { + cl_error("no dma handle available\n"); + goto failed; + } + + err = mods_get_chan_by_id(id, &p_mods_chan); + if (err != OK) { cl_error("get dma channel failed\n"); - return ret; + goto failed; } read_lock(&(p_mods_chan->lock)); if (p_mods_chan->in_use) { cl_error("mods dma channel in use\n"); read_unlock(&(p_mods_chan->lock)); - return -EBUSY; + err = -EBUSY; + goto failed; } read_unlock(&(p_mods_chan->lock)); - dma_cap_zero(mask); - dma_cap_set(p_handle->dma_type, mask); - chan = dma_request_channel(mask, NULL, NULL); + // dma channel is requested in the old-fashion way + if (p_handle->ctrl_dir[0] == '\0') { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(p_handle->dma_type, mask); + chan = dma_request_channel(mask, NULL, NULL); + } else if (mods_tegra_dma_dev) { + // get dma chan from dt node + cl_debug(DEBUG_TEGRADMA, "dmach is asked for %s\n", p_handle->ctrl_dir); + chan = dma_request_chan(mods_tegra_dma_dev, p_handle->ctrl_dir); + } if (!chan) { cl_error("dma channel is not available\n"); mods_release_dma_id(id); - return -EBUSY; + err = -EBUSY; + goto failed; } write_lock(&(p_mods_chan->lock)); @@ -244,30 +244,49 @@ int esc_mods_dma_request_channel(struct mods_client *client, p_handle->dma_id = id; cl_debug(DEBUG_TEGRADMA, "request get dma id: %d\n", id); - LOG_EXT(); - return 0; +failed: + LOG_EXT(); + return err; +} + +int esc_mods_dma_request_channel(struct mods_client *client, + struct MODS_DMA_HANDLE *p_handle) +{ + int err; + struct MODS_DMA_HANDLE_2 handle = {0}; + + handle.ctrl_dir[0] = '\0'; + handle.dma_type = p_handle->dma_type; + err = esc_mods_dma_request_channel_2(client, &handle); + if (err == 0) + p_handle->dma_id = handle.dma_id; + + return err; } int esc_mods_dma_release_channel(struct mods_client *client, - struct MODS_DMA_HANDLE *p_handle) + struct MODS_DMA_HANDLE *p_handle) { mods_release_channel(p_handle->dma_id); return OK; } int esc_mods_dma_set_config(struct mods_client *client, - struct MODS_DMA_CHANNEL_CONFIG *p_config) + struct MODS_DMA_CHANNEL_CONFIG *p_config) { struct dma_slave_config config; struct mods_dma_chan_info *p_mods_chan; - int ret; + int err; LOG_ENT(); - ret = mods_get_inuse_chan_by_handle(&p_config->handle, &p_mods_chan); - if (ret != OK) - return ret; + + err = mods_get_inuse_chan_by_handle(&p_config->handle, &p_mods_chan); + if (err != OK) { + LOG_EXT(); + return err; + } config.direction = p_config->direction; config.src_addr = p_config->src_addr; @@ -282,30 +301,33 @@ int esc_mods_dma_set_config(struct mods_client *client, #endif cl_debug(DEBUG_TEGRADMA, - "ch: %d dir [%d], addr[%p -> %p], burst [%d %d]", - p_config->handle.dma_id, - config.direction, - (void *)config.src_addr, (void *)config.dst_addr, - config.src_maxburst, config.dst_maxburst); + "ch: %d dir [%d], addr[%p -> %p], burst [%d %d] width [%d %d]\n", + p_config->handle.dma_id, + config.direction, + (void *)config.src_addr, (void *)config.dst_addr, + config.src_maxburst, config.dst_maxburst, + config.src_addr_width, config.dst_addr_width); + +#if KERNEL_VERSION(5, 17, 0) > MODS_KERNEL_VERSION cl_debug(DEBUG_TEGRADMA, - "width [%d %d] slave id %d\n", - config.src_addr_width, config.dst_addr_width, - p_config->slave_id); + "slave id %d\n", + config.slave_id); +#endif write_lock(&(p_mods_chan->lock)); - ret = dmaengine_slave_config(p_mods_chan->pch, &config); + err = dmaengine_slave_config(p_mods_chan->pch, &config); write_unlock(&(p_mods_chan->lock)); LOG_EXT(); - return ret; + return err; } int esc_mods_dma_submit_request(struct mods_client *client, struct MODS_DMA_TX_DESC *p_mods_desc) { - int ret = OK; + int err = OK; struct mods_dma_chan_info *p_mods_chan; struct dma_async_tx_descriptor *desc; struct dma_device *dev; @@ -315,12 +337,15 @@ int esc_mods_dma_submit_request(struct mods_client *client, LOG_ENT(); flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - ret = mods_get_inuse_chan_by_handle(&p_mods_desc->handle, &p_mods_chan); - if (ret != OK) - return ret; + err = mods_get_inuse_chan_by_handle(&p_mods_desc->handle, &p_mods_chan); + if (err != OK) { + LOG_EXT(); + return err; + } if (p_mods_desc->mode != MODS_DMA_SINGLE) { cl_error("unsupported mode: %d\n", p_mods_desc->mode); + LOG_EXT(); return -EINVAL; } @@ -337,20 +362,20 @@ int esc_mods_dma_submit_request(struct mods_client *client, flags); } else { cl_debug(DEBUG_TEGRADMA, - "Phys Addr [%p], len [%d], dir [%d]\n", - (void *)p_mods_desc->phys, - p_mods_desc->length, - p_mods_desc->data_dir); + "Phys Addr [%p], len [%d], dir [%d]\n", + (void *)p_mods_desc->phys, + p_mods_desc->length, + p_mods_desc->data_dir); desc = dmaengine_prep_slave_single(p_mods_chan->pch, - p_mods_desc->phys, - p_mods_desc->length, - p_mods_desc->data_dir, - flags); + p_mods_desc->phys, + p_mods_desc->length, + p_mods_desc->data_dir, + flags); } if (desc == NULL) { cl_error("unable to get desc for Tx\n"); - ret = -EIO; + err = -EIO; goto failed; } @@ -362,54 +387,57 @@ failed: write_unlock(&(p_mods_chan->lock)); if (dma_submit_error(cookie)) { cl_error("submit cookie: %x\n", cookie); + LOG_EXT(); return -EIO; } p_mods_desc->cookie = cookie; LOG_EXT(); - return ret; + return err; } int esc_mods_dma_async_issue_pending(struct mods_client *client, - struct MODS_DMA_HANDLE *p_handle) + struct MODS_DMA_HANDLE *p_handle) { - int ret = OK; + int err = OK; struct mods_dma_chan_info *p_mods_chan; LOG_ENT(); - ret = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); - if (ret != OK) - return ret; + err = mods_get_inuse_chan_by_handle(p_handle, &p_mods_chan); + if (err != OK) { + LOG_EXT(); + return err; + } cl_debug(DEBUG_TEGRADMA, "issue pending on chan: %p\n", - p_mods_chan->pch); + p_mods_chan->pch); read_lock(&(p_mods_chan->lock)); dma_async_issue_pending(p_mods_chan->pch); read_unlock(&(p_mods_chan->lock)); LOG_EXT(); - return ret; + return err; } int esc_mods_dma_wait(struct mods_client *client, - struct MODS_DMA_WAIT_DESC *p_wait_desc) + struct MODS_DMA_WAIT_DESC *p_wait_desc) { - int ret; + int err; LOG_ENT(); if (p_wait_desc->type == MODS_DMA_SYNC_WAIT) - ret = mods_dma_sync_wait(&p_wait_desc->handle, - p_wait_desc->cookie); + err = mods_dma_sync_wait(&p_wait_desc->handle, + p_wait_desc->cookie); else if (p_wait_desc->type == MODS_DMA_ASYNC_WAIT) - ret = mods_dma_async_is_tx_complete(&p_wait_desc->handle, - p_wait_desc->cookie, - &p_wait_desc->tx_complete); + err = mods_dma_async_is_tx_complete(&p_wait_desc->handle, + p_wait_desc->cookie, + &p_wait_desc->tx_complete); else - ret = -EINVAL; + err = -EINVAL; LOG_EXT(); - return ret; + return err; } int esc_mods_dma_alloc_coherent(struct mods_client *client, @@ -426,10 +454,10 @@ int esc_mods_dma_alloc_coherent(struct mods_client *client, GFP_KERNEL); cl_debug(DEBUG_MEM, - "num_bytes=%d, p_cpu_addr=%p, p_phys_addr=%p\n", - p->num_bytes, - (void *)p_cpu_addr, - (void *)p_phys_addr); + "num_bytes=%d, p_cpu_addr=%p, p_phys_addr=%p\n", + p->num_bytes, + (void *)p_cpu_addr, + (void *)p_phys_addr); if (!p_cpu_addr) { cl_error( @@ -451,15 +479,15 @@ int esc_mods_dma_alloc_coherent(struct mods_client *client, } int esc_mods_dma_free_coherent(struct mods_client *client, - struct MODS_DMA_COHERENT_MEM_HANDLE *p) + struct MODS_DMA_COHERENT_MEM_HANDLE *p) { LOG_ENT(); cl_debug(DEBUG_MEM, - "num_bytes = %d, p_cpu_addr=%p, p_phys_addr=%p\n", - p->num_bytes, - (void *)(p->memory_handle_virt), - (void *)(p->memory_handle_phys)); + "num_bytes = %d, p_cpu_addr=%p, p_phys_addr=%p\n", + p->num_bytes, + (void *)(p->memory_handle_virt), + (void *)(p->memory_handle_phys)); dma_free_coherent(NULL, p->num_bytes, @@ -474,23 +502,86 @@ int esc_mods_dma_free_coherent(struct mods_client *client, } int esc_mods_dma_copy_to_user(struct mods_client *client, - struct MODS_DMA_COPY_TO_USER *p) + struct MODS_DMA_COPY_TO_USER *p) { int retval; LOG_ENT(); cl_debug(DEBUG_MEM, - "memory_handle_dst=%p, memory_handle_src=%p, num_bytes=%d\n", - (void *)(p->memory_handle_dst), - (void *)(p->memory_handle_src), - p->num_bytes); + "memory_handle_dst=%p, memory_handle_src=%p, num_bytes=%d\n", + (void *)(p->memory_handle_dst), + (void *)(p->memory_handle_src), + p->num_bytes); retval = copy_to_user((void __user *)p->memory_handle_dst, - (void *)p->memory_handle_src, - p->num_bytes); + (void *)p->memory_handle_src, + p->num_bytes); LOG_EXT(); return retval; } + +static int tegra_dma_driver_probe(struct platform_device *pdev) +{ + LOG_ENT(); + + mods_debug_printk(DEBUG_TEGRADMA, "mods_tegra_dma probe\n"); + mods_tegra_dma_dev = get_device(&pdev->dev); + + LOG_EXT(); + return 0; +} + +static int tegra_dma_driver_remove(struct platform_device *pdev) +{ + put_device(&pdev->dev); + mods_tegra_dma_dev = NULL; + return 0; +} + +static const struct of_device_id of_ids[] = { + { .compatible = "nvidia,mods_tegra_dma" }, + { } +}; + +static struct platform_driver mods_tegra_dma_driver = { + .probe = tegra_dma_driver_probe, + .remove = tegra_dma_driver_remove, + .driver = { + .name = "mods_tegra_dma", + .owner = THIS_MODULE, + .of_match_table = of_ids, + }, +}; + +int mods_init_dma(void) +{ + int i; + int err; + + for (i = 0; i < MODS_DMA_MAX_CHANNEL; i++) { + struct mods_dma_chan_info *p_chan_info; + + p_chan_info = &dma_info_chan_list[i]; + rwlock_init(&(p_chan_info->lock)); + p_chan_info->in_use = false; + } + + err = platform_driver_register(&mods_tegra_dma_driver); + if (err < 0) + mods_error_printk("register mods dma driver failed\n"); + + return err; +} + +void mods_exit_dma(void) +{ + int i; + + for (i = 0; i < MODS_DMA_MAX_CHANNEL; i++) + mods_release_channel(i); + + platform_driver_unregister(&mods_tegra_dma_driver); +} diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index 6658ed53..6f8e79ac 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -631,10 +631,12 @@ int esc_mods_oist_status(struct mods_client *client, struct MODS_TEGRA_OIST_STATUS *p); #ifdef CONFIG_DMA_ENGINE -void mods_init_dma(void); +int mods_init_dma(void); void mods_exit_dma(void); int esc_mods_dma_request_channel(struct mods_client *client, struct MODS_DMA_HANDLE *p); +int esc_mods_dma_request_channel_2(struct mods_client *client, + struct MODS_DMA_HANDLE_2 *p_handle_2); int esc_mods_dma_release_channel(struct mods_client *client, struct MODS_DMA_HANDLE *p); int esc_mods_dma_set_config(struct mods_client *client, diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index c2002bbd..754d97a3 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -471,7 +471,9 @@ static int __init mods_init_module(void) #endif #if defined(CONFIG_DMA_ENGINE) - mods_init_dma(); + rc = mods_init_dma(); + if (rc < 0) + return rc; #endif #endif @@ -2409,6 +2411,11 @@ static long mods_krnl_ioctl(struct file *fp, esc_mods_dma_request_channel, MODS_DMA_HANDLE); break; + case MODS_ESC_DMA_REQUEST_HANDLE_2: + MODS_IOCTL(MODS_ESC_DMA_REQUEST_HANDLE_2, + esc_mods_dma_request_channel_2, + MODS_DMA_HANDLE_2); + break; case MODS_ESC_DMA_RELEASE_HANDLE: MODS_IOCTL_NORETVAL(MODS_ESC_DMA_RELEASE_HANDLE, esc_mods_dma_release_channel, @@ -2430,7 +2437,7 @@ static long mods_krnl_ioctl(struct file *fp, MODS_DMA_TX_DESC); break; case MODS_ESC_DMA_TX_WAIT: - MODS_IOCTL(MODS_MODS_ESC_DMA_TX_WAIT, + MODS_IOCTL(MODS_ESC_DMA_TX_WAIT, esc_mods_dma_wait, MODS_DMA_WAIT_DESC); break; diff --git a/include/uapi/misc/mods.h b/include/uapi/misc/mods.h index bd9f326b..dba23884 100644 --- a/include/uapi/misc/mods.h +++ b/include/uapi/misc/mods.h @@ -1616,9 +1616,30 @@ struct MODS_DMA_HANDLE { /* IN */ __u32 dma_type; /* Indicate the DMA Type*/ /* OUT */ - __u32 dma_id; /* Inditify for the DMA */ + __u32 dma_id; /* Identifier for the DMA */ }; +#define MODS_DMA_HANDLE_CTRL_DIR_LEN 20 + +/* Used by ioctls: + * - MODS_ESC_DMA_REQUEST_HANDLE_2 + * + * Available only on Tegra. + */ +struct MODS_DMA_HANDLE_2 { + /* IN */ + __u32 dma_type; /* Indicate the DMA Type*/ + /* OUT */ + __u32 dma_id; /* Identifier for the DMA */ + + /* ctrl#index_dir + * for example, spi0_tx, i2c1_rx + * to keep back-compatibility, strlen of ctrl_dir is allowed to be zero + */ + char ctrl_dir[MODS_DMA_HANDLE_CTRL_DIR_LEN]; +}; + + enum MODS_DMA_TRANSFER_DIRECTION { MODS_DMA_MEM_TO_MEM, MODS_DMA_MEM_TO_DEV, @@ -1962,6 +1983,7 @@ struct MODS_TEGRA_OIST_STATUS { /* Deprecated */ #define MODS_ESC_PCI_UNMAP_RESOURCE MODSIO(W, 79, MODS_PCI_UNMAP_RESOURCE) #define MODS_ESC_DMA_REQUEST_HANDLE MODSIO(R, 80, MODS_DMA_HANDLE) +#define MODS_ESC_DMA_REQUEST_HANDLE_2 MODSIO(R, 80, MODS_DMA_HANDLE_2) #define MODS_ESC_DMA_RELEASE_HANDLE MODSIO(W, 81, MODS_DMA_HANDLE) #define MODS_ESC_DMA_SET_CONFIG MODSIO(W, 82, MODS_DMA_CHANNEL_CONFIG) #define MODS_ESC_DMA_TX_SUBMIT MODSIO(W, 83, MODS_DMA_TX_DESC)