| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2011-2013, 2015, 2017-2019, The Linux Foundation. All rights reserved. |
| */ |
| /* BAM-DMA Manager. */ |
| |
| #ifdef CONFIG_SPS_SUPPORT_BAMDMA |
| |
| #include <linux/export.h> |
| #include <linux/memory.h> /* memset */ |
| |
| #include "spsi.h" |
| #include "bam.h" |
| #include "sps_bam.h" /* bam_dma_thresh_dma */ |
| #include "sps_core.h" /* sps_h2bam() */ |
| |
| /** |
| * registers |
| */ |
| |
| #define DMA_ENBL (0x00000000) |
| #ifdef CONFIG_SPS_SUPPORT_NDP_BAM |
| #define DMA_REVISION (0x00000004) |
| #define DMA_CONFIG (0x00000008) |
| #define DMA_CHNL_CONFIG(n) (0x00001000 + 4096 * (n)) |
| #else |
| #define DMA_CHNL_CONFIG(n) (0x00000004 + 4 * (n)) |
| #define DMA_CONFIG (0x00000040) |
| #endif |
| |
| /** |
| * masks |
| */ |
| |
| /* DMA_CHNL_confign */ |
| #ifdef CONFIG_SPS_SUPPORT_NDP_BAM |
| #define DMA_CHNL_PRODUCER_PIPE_ENABLED 0x40000 |
| #define DMA_CHNL_CONSUMER_PIPE_ENABLED 0x20000 |
| #endif |
| #define DMA_CHNL_HALT_DONE 0x10000 |
| #define DMA_CHNL_HALT 0x1000 |
| #define DMA_CHNL_ENABLE 0x100 |
| #define DMA_CHNL_ACT_THRESH 0x30 |
| #define DMA_CHNL_WEIGHT 0x7 |
| |
| /* DMA_CONFIG */ |
| #define TESTBUS_SELECT 0x3 |
| |
| /** |
| * |
| * Write register with debug info. |
| * |
| * @base - bam base virtual address. |
| * @offset - register offset. |
| * @val - value to write. |
| * |
| */ |
| static inline void dma_write_reg(void *base, u32 offset, u32 val) |
| { |
| iowrite32(val, base + offset); |
| SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x\n", offset, val); |
| } |
| |
| /** |
| * Write register masked field with debug info. |
| * |
| * @base - bam base virtual address. |
| * @offset - register offset. |
| * @mask - register bitmask. |
| * @val - value to write. |
| * |
| */ |
| static inline void dma_write_reg_field(void *base, u32 offset, |
| const u32 mask, u32 val) |
| { |
| u32 shift = find_first_bit((void *)&mask, 32); |
| u32 tmp = ioread32(base + offset); |
| |
| tmp &= ~mask; /* clear written bits */ |
| val = tmp | (val << shift); |
| iowrite32(val, base + offset); |
| SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x\n", offset, val); |
| } |
| |
| /* Round max number of pipes to nearest multiple of 2 */ |
| #define DMA_MAX_PIPES ((BAM_MAX_PIPES / 2) * 2) |
| |
| /* Maximum number of BAM-DMAs supported */ |
| #define MAX_BAM_DMA_DEVICES 1 |
| |
| /* Maximum number of BAMs that will be registered */ |
| #define MAX_BAM_DMA_BAMS 1 |
| |
| /* Pipe enable check values */ |
| #define DMA_PIPES_STATE_DIFF 0 |
| #define DMA_PIPES_BOTH_DISABLED 1 |
| #define DMA_PIPES_BOTH_ENABLED 2 |
| |
| /* Even pipe is tx/dest/input/write, odd pipe is rx/src/output/read */ |
| #define DMA_PIPE_IS_DEST(p) (((p) & 1) == 0) |
| #define DMA_PIPE_IS_SRC(p) (((p) & 1) != 0) |
| |
| /* BAM DMA pipe state */ |
| enum bamdma_pipe_state { |
| PIPE_INACTIVE = 0, |
| PIPE_ACTIVE |
| }; |
| |
| /* BAM DMA channel state */ |
| enum bamdma_chan_state { |
| DMA_CHAN_STATE_FREE = 0, |
| DMA_CHAN_STATE_ALLOC_EXT, /* Client allocation */ |
| DMA_CHAN_STATE_ALLOC_INT /* Internal (resource mgr) allocation */ |
| }; |
| |
| struct bamdma_chan { |
| /* Allocation state */ |
| enum bamdma_chan_state state; |
| |
| /* BAM DMA channel configuration parameters */ |
| u32 threshold; |
| enum sps_dma_priority priority; |
| |
| /* HWIO channel configuration parameters */ |
| enum bam_dma_thresh_dma thresh; |
| enum bam_dma_weight_dma weight; |
| |
| }; |
| |
| /* BAM DMA device state */ |
| struct bamdma_device { |
| /* BAM-DMA device state */ |
| int enabled; |
| int local; |
| |
| /* BAM device state */ |
| struct sps_bam *bam; |
| |
| /* BAM handle, for deregistration */ |
| unsigned long h; |
| |
| /* BAM DMA device virtual mapping */ |
| void *virt_addr; |
| int virtual_mapped; |
| phys_addr_t phys_addr; |
| void *hwio; |
| |
| /* BAM DMA pipe/channel state */ |
| u32 num_pipes; |
| enum bamdma_pipe_state pipes[DMA_MAX_PIPES]; |
| struct bamdma_chan chans[DMA_MAX_PIPES / 2]; |
| |
| }; |
| |
| /* BAM-DMA devices */ |
| static struct bamdma_device bam_dma_dev[MAX_BAM_DMA_DEVICES]; |
| static struct mutex bam_dma_lock; |
| |
| /* |
| * The BAM DMA module registers all BAMs in the BSP properties, but only |
| * uses the first BAM-DMA device for allocations. References to the others |
| * are stored in the following data array. |
| */ |
| static int num_bams; |
| static unsigned long bam_handles[MAX_BAM_DMA_BAMS]; |
| |
| /** |
| * Find BAM-DMA device |
| * |
| * This function finds the BAM-DMA device associated with the BAM handle. |
| * |
| * @h - BAM handle |
| * |
| * @return - pointer to BAM-DMA device, or NULL on error |
| * |
| */ |
| static struct bamdma_device *sps_dma_find_device(unsigned long h) |
| { |
| return &bam_dma_dev[0]; |
| } |
| |
| /** |
| * BAM DMA device enable |
| * |
| * This function enables a BAM DMA device and the associated BAM. |
| * |
| * @dev - pointer to BAM DMA device context |
| * |
| * @return 0 on success, negative value on error |
| * |
| */ |
| static int sps_dma_device_enable(struct bamdma_device *dev) |
| { |
| if (dev->enabled) |
| return 0; |
| |
| /* |
| * If the BAM-DMA device is locally controlled then enable BAM-DMA |
| * device |
| */ |
| if (dev->local) |
| dma_write_reg(dev->virt_addr, DMA_ENBL, 1); |
| |
| /* Enable BAM device */ |
| if (sps_bam_enable(dev->bam)) { |
| SPS_ERR(sps, "sps:Failed to enable BAM DMA's BAM: %pa\n", |
| &dev->phys_addr); |
| return SPS_ERROR; |
| } |
| |
| dev->enabled = true; |
| |
| return 0; |
| } |
| |
| /** |
| * BAM DMA device enable |
| * |
| * This function initializes a BAM DMA device. |
| * |
| * @dev - pointer to BAM DMA device context |
| * |
| * @return 0 on success, negative value on error |
| * |
| */ |
| static int sps_dma_device_disable(struct bamdma_device *dev) |
| { |
| u32 pipe_index; |
| |
| if (!dev->enabled) |
| return 0; |
| |
| /* Do not disable if channels active */ |
| for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { |
| if (dev->pipes[pipe_index] != PIPE_INACTIVE) |
| break; |
| } |
| |
| if (pipe_index < dev->num_pipes) { |
| SPS_ERR(sps, |
| "sps:Fail to disable BAM-DMA %pa:channels are active\n", |
| &dev->phys_addr); |
| return SPS_ERROR; |
| } |
| |
| dev->enabled = false; |
| |
| /* Disable BAM device */ |
| if (sps_bam_disable(dev->bam)) { |
| SPS_ERR(sps, |
| "sps:Fail to disable BAM-DMA BAM:%pa\n", |
| &dev->phys_addr); |
| return SPS_ERROR; |
| } |
| |
| /* Is the BAM-DMA device locally controlled? */ |
| if (dev->local) |
| /* Disable BAM-DMA device */ |
| dma_write_reg(dev->virt_addr, DMA_ENBL, 0); |
| |
| return 0; |
| } |
| |
| /** |
| * Initialize BAM DMA device |
| * |
| */ |
| int sps_dma_device_init(unsigned long h) |
| { |
| struct bamdma_device *dev; |
| struct sps_bam_props *props; |
| int result = SPS_ERROR; |
| |
| mutex_lock(&bam_dma_lock); |
| |
| /* Find a free BAM-DMA device slot */ |
| dev = NULL; |
| if (bam_dma_dev[0].bam != NULL) { |
| SPS_ERR(sps, |
| "sps:%s:BAM-DMA BAM device is already initialized\n", |
| __func__); |
| goto exit_err; |
| } else { |
| dev = &bam_dma_dev[0]; |
| } |
| |
| /* Record BAM */ |
| memset(dev, 0, sizeof(*dev)); |
| dev->h = h; |
| dev->bam = sps_h2bam(h); |
| |
| if (dev->bam == NULL) { |
| SPS_ERR(sps, |
| "sps:%s:BAM-DMA BAM device is not found from the handle\n", |
| __func__); |
| goto exit_err; |
| } |
| |
| /* Map the BAM DMA device into virtual space, if necessary */ |
| props = &dev->bam->props; |
| dev->phys_addr = props->periph_phys_addr; |
| if (props->periph_virt_addr != NULL) { |
| dev->virt_addr = props->periph_virt_addr; |
| dev->virtual_mapped = false; |
| } else { |
| if (props->periph_virt_size == 0) { |
| SPS_ERR(sps, |
| "sps:Unable to map BAM DMA IO memory: %pa %x\n", |
| &dev->phys_addr, props->periph_virt_size); |
| goto exit_err; |
| } |
| |
| dev->virt_addr = ioremap(dev->phys_addr, |
| props->periph_virt_size); |
| if (dev->virt_addr == NULL) { |
| SPS_ERR(sps, |
| "sps:Unable to map BAM DMA IO memory: %pa %x\n", |
| &dev->phys_addr, props->periph_virt_size); |
| goto exit_err; |
| } |
| dev->virtual_mapped = true; |
| } |
| dev->hwio = (void *) dev->virt_addr; |
| |
| /* Is the BAM-DMA device locally controlled? */ |
| if ((props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) { |
| SPS_DBG3(sps, "sps:BAM-DMA is controlled locally: %pa\n", |
| &dev->phys_addr); |
| dev->local = true; |
| } else { |
| SPS_DBG3(sps, "sps:BAM-DMA is controlled remotely: %pa\n", |
| &dev->phys_addr); |
| dev->local = false; |
| } |
| |
| /* |
| * Enable the BAM DMA and determine the number of pipes/channels. |
| * Leave the BAM-DMA enabled, since it is always a shared device. |
| */ |
| if (sps_dma_device_enable(dev)) |
| goto exit_err; |
| |
| dev->num_pipes = dev->bam->props.num_pipes; |
| |
| result = 0; |
| exit_err: |
| if (result) { |
| if (dev != NULL) { |
| if (dev->virtual_mapped) |
| iounmap(dev->virt_addr); |
| |
| dev->bam = NULL; |
| } |
| } |
| |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| |
| /** |
| * De-initialize BAM DMA device |
| * |
| */ |
| int sps_dma_device_de_init(unsigned long h) |
| { |
| struct bamdma_device *dev; |
| u32 pipe_index; |
| u32 chan; |
| int result = 0; |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device(h); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:BAM-DMA: not registered: %pK\n", (void *)h); |
| result = SPS_ERROR; |
| goto exit_err; |
| } |
| |
| /* Check for channel leaks */ |
| for (chan = 0; chan < dev->num_pipes / 2; chan++) { |
| if (dev->chans[chan].state != DMA_CHAN_STATE_FREE) { |
| SPS_ERR(sps, "sps:BAM-DMA: channel not free: %d\n", |
| chan); |
| result = SPS_ERROR; |
| dev->chans[chan].state = DMA_CHAN_STATE_FREE; |
| } |
| } |
| for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { |
| if (dev->pipes[pipe_index] != PIPE_INACTIVE) { |
| SPS_ERR(sps, "sps:BAM-DMA: pipe not inactive: %d\n", |
| pipe_index); |
| result = SPS_ERROR; |
| dev->pipes[pipe_index] = PIPE_INACTIVE; |
| } |
| } |
| |
| /* Disable BAM and BAM-DMA */ |
| if (sps_dma_device_disable(dev)) |
| result = SPS_ERROR; |
| |
| dev->h = BAM_HANDLE_INVALID; |
| dev->bam = NULL; |
| if (dev->virtual_mapped) |
| iounmap(dev->virt_addr); |
| |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| |
| /** |
| * Initialize BAM DMA module |
| * |
| */ |
| int sps_dma_init(const struct sps_bam_props *bam_props) |
| { |
| struct sps_bam_props props; |
| const struct sps_bam_props *bam_reg; |
| unsigned long h; |
| |
| /* Init local data */ |
| memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); |
| num_bams = 0; |
| memset(bam_handles, 0, sizeof(bam_handles)); |
| |
| /* Create a mutex to control access to the BAM-DMA devices */ |
| mutex_init(&bam_dma_lock); |
| |
| /* Are there any BAM DMA devices? */ |
| if (bam_props == NULL) |
| return 0; |
| |
| /* |
| * Registers all BAMs in the BSP properties, but only uses the first |
| * BAM-DMA device for allocations. |
| */ |
| if (bam_props->phys_addr) { |
| /* Force multi-EE option for all BAM-DMAs */ |
| bam_reg = bam_props; |
| if ((bam_props->options & SPS_BAM_OPT_BAMDMA) && |
| (bam_props->manage & SPS_BAM_MGR_MULTI_EE) == 0) { |
| SPS_DBG(sps, |
| "sps:Setting multi-EE options for BAM-DMA: %pa\n", |
| &bam_props->phys_addr); |
| props = *bam_props; |
| props.manage |= SPS_BAM_MGR_MULTI_EE; |
| bam_reg = &props; |
| } |
| |
| /* Register the BAM */ |
| if (sps_register_bam_device(bam_reg, &h)) { |
| SPS_ERR(sps, |
| "sps:Fail to register BAM-DMA BAM device: phys %pa\n", |
| &bam_props->phys_addr); |
| return SPS_ERROR; |
| } |
| |
| /* Record the BAM so that it may be deregistered later */ |
| if (num_bams < MAX_BAM_DMA_BAMS) { |
| bam_handles[num_bams] = h; |
| num_bams++; |
| } else { |
| SPS_ERR(sps, "sps:BAM-DMA: BAM limit exceeded: %d\n", |
| num_bams); |
| return SPS_ERROR; |
| } |
| } else { |
| SPS_ERR(sps, |
| "sps:%s:BAM-DMA phys_addr is zero\n", |
| __func__); |
| return SPS_ERROR; |
| } |
| |
| |
| return 0; |
| } |
| |
| /** |
| * De-initialize BAM DMA module |
| * |
| */ |
| void sps_dma_de_init(void) |
| { |
| int n; |
| |
| /* De-initialize the BAM devices */ |
| for (n = 0; n < num_bams; n++) |
| sps_deregister_bam_device(bam_handles[n]); |
| |
| /* Clear local data */ |
| memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); |
| num_bams = 0; |
| memset(bam_handles, 0, sizeof(bam_handles)); |
| } |
| |
| /** |
| * Allocate a BAM DMA channel |
| * |
| */ |
| int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, |
| struct sps_dma_chan *chan_info) |
| { |
| struct bamdma_device *dev; |
| struct bamdma_chan *chan; |
| u32 pipe_index; |
| enum bam_dma_thresh_dma thresh = (enum bam_dma_thresh_dma) 0; |
| enum bam_dma_weight_dma weight = (enum bam_dma_weight_dma) 0; |
| int result = SPS_ERROR; |
| |
| if (alloc == NULL || chan_info == NULL) { |
| SPS_ERR(sps, |
| "sps:%s:invalid parameters\n", __func__); |
| return SPS_ERROR; |
| } |
| |
| /* Translate threshold and priority to hwio values */ |
| if (alloc->threshold != SPS_DMA_THRESHOLD_DEFAULT) { |
| if (alloc->threshold >= 512) |
| thresh = BAM_DMA_THRESH_512; |
| else if (alloc->threshold >= 256) |
| thresh = BAM_DMA_THRESH_256; |
| else if (alloc->threshold >= 128) |
| thresh = BAM_DMA_THRESH_128; |
| else |
| thresh = BAM_DMA_THRESH_64; |
| } |
| |
| weight = alloc->priority; |
| |
| if ((u32)alloc->priority > (u32)BAM_DMA_WEIGHT_HIGH) { |
| SPS_ERR(sps, "sps:BAM-DMA: invalid priority: %x\n", |
| alloc->priority); |
| return SPS_ERROR; |
| } |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device(alloc->dev); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %pK\n", |
| (void *)alloc->dev); |
| goto exit_err; |
| } |
| |
| /* Search for a free set of pipes */ |
| for (pipe_index = 0, chan = dev->chans; |
| pipe_index < dev->num_pipes; pipe_index += 2, chan++) { |
| if (chan->state == DMA_CHAN_STATE_FREE) { |
| /* Just check pipes for safety */ |
| if (dev->pipes[pipe_index] != PIPE_INACTIVE || |
| dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { |
| SPS_ERR(sps, |
| "sps:BAM-DMA: channel %d state error:%d %d\n", |
| pipe_index / 2, dev->pipes[pipe_index], |
| dev->pipes[pipe_index + 1]); |
| goto exit_err; |
| } |
| break; /* Found free pipe */ |
| } |
| } |
| |
| if (pipe_index >= dev->num_pipes) { |
| SPS_ERR(sps, "sps:BAM-DMA: no free channel num_pipes = %d\n", |
| dev->num_pipes); |
| goto exit_err; |
| } |
| |
| chan->state = DMA_CHAN_STATE_ALLOC_EXT; |
| |
| /* Store config values for use when pipes are activated */ |
| chan = &dev->chans[pipe_index / 2]; |
| chan->threshold = alloc->threshold; |
| chan->thresh = thresh; |
| chan->priority = alloc->priority; |
| chan->weight = weight; |
| |
| SPS_DBG3(sps, "sps:%s. pipe %d\n", __func__, pipe_index); |
| |
| /* Report allocated pipes to client */ |
| chan_info->dev = dev->h; |
| /* Dest/input/write pipex */ |
| chan_info->dest_pipe_index = pipe_index; |
| /* Source/output/read pipe */ |
| chan_info->src_pipe_index = pipe_index + 1; |
| |
| result = 0; |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| EXPORT_SYMBOL(sps_alloc_dma_chan); |
| |
| /** |
| * Free a BAM DMA channel |
| * |
| */ |
| int sps_free_dma_chan(struct sps_dma_chan *chan) |
| { |
| struct bamdma_device *dev; |
| u32 pipe_index; |
| int result = 0; |
| |
| if (chan == NULL) { |
| SPS_ERR(sps, |
| "sps:%s:chan is NULL\n", __func__); |
| return SPS_ERROR; |
| } |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device(chan->dev); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %pK\n", |
| (void *)chan->dev); |
| result = SPS_ERROR; |
| goto exit_err; |
| } |
| |
| /* Verify the pipe indices */ |
| pipe_index = chan->dest_pipe_index; |
| if (pipe_index >= dev->num_pipes || ((pipe_index & 1)) || |
| (pipe_index + 1) != chan->src_pipe_index) { |
| SPS_ERR(sps, |
| "sps:%s. Invalid pipe indices num_pipes=%d dest=%d src=%d\n", |
| __func__, dev->num_pipes, |
| chan->dest_pipe_index, |
| chan->src_pipe_index); |
| result = SPS_ERROR; |
| goto exit_err; |
| } |
| |
| /* Are both pipes inactive? */ |
| if (dev->chans[pipe_index / 2].state != DMA_CHAN_STATE_ALLOC_EXT || |
| dev->pipes[pipe_index] != PIPE_INACTIVE || |
| dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { |
| SPS_ERR(sps, |
| "sps:BAM-DMA: attempt to free active chan %d: %d %d\n", |
| pipe_index / 2, dev->pipes[pipe_index], |
| dev->pipes[pipe_index + 1]); |
| result = SPS_ERROR; |
| goto exit_err; |
| } |
| |
| /* Free the channel */ |
| dev->chans[pipe_index / 2].state = DMA_CHAN_STATE_FREE; |
| |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| EXPORT_SYMBOL(sps_free_dma_chan); |
| |
| /** |
| * Activate a BAM DMA pipe |
| * |
| * This function activates a BAM DMA pipe. |
| * |
| * @dev - pointer to BAM-DMA device descriptor |
| * |
| * @pipe_index - pipe index |
| * |
| * @return 0 on success, negative value on error |
| * |
| */ |
| static u32 sps_dma_check_pipes(struct bamdma_device *dev, u32 pipe_index) |
| { |
| u32 pipe_in; |
| u32 pipe_out; |
| int enabled_in; |
| int enabled_out; |
| u32 check; |
| |
| pipe_in = pipe_index & ~1; |
| pipe_out = pipe_in + 1; |
| enabled_in = bam_pipe_is_enabled(&dev->bam->base, pipe_in); |
| enabled_out = bam_pipe_is_enabled(&dev->bam->base, pipe_out); |
| |
| if (!enabled_in && !enabled_out) |
| check = DMA_PIPES_BOTH_DISABLED; |
| else if (enabled_in && enabled_out) |
| check = DMA_PIPES_BOTH_ENABLED; |
| else |
| check = DMA_PIPES_STATE_DIFF; |
| |
| return check; |
| } |
| |
| /** |
| * Allocate a BAM DMA pipe |
| * |
| */ |
| int sps_dma_pipe_alloc(void *bam_arg, u32 pipe_index, enum sps_mode dir) |
| { |
| struct sps_bam *bam = bam_arg; |
| struct bamdma_device *dev; |
| struct bamdma_chan *chan; |
| u32 channel; |
| int result = SPS_ERROR; |
| |
| if (bam == NULL) { |
| SPS_ERR(sps, "%s\n", "sps:BAM context is NULL"); |
| return SPS_ERROR; |
| } |
| |
| /* Check pipe direction */ |
| if ((DMA_PIPE_IS_DEST(pipe_index) && dir != SPS_MODE_DEST) || |
| (DMA_PIPE_IS_SRC(pipe_index) && dir != SPS_MODE_SRC)) { |
| SPS_ERR(sps, "sps:BAM-DMA: wrong dir for BAM %pa pipe %d\n", |
| &bam->props.phys_addr, pipe_index); |
| return SPS_ERROR; |
| } |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device((unsigned long) bam); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:BAM-DMA: invalid BAM: %pa\n", |
| &bam->props.phys_addr); |
| goto exit_err; |
| } |
| if (pipe_index >= dev->num_pipes) { |
| SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d\n", |
| &bam->props.phys_addr, pipe_index); |
| goto exit_err; |
| } |
| if (dev->pipes[pipe_index] != PIPE_INACTIVE) { |
| SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d already active\n", |
| &bam->props.phys_addr, pipe_index); |
| goto exit_err; |
| } |
| |
| /* Mark pipe active */ |
| dev->pipes[pipe_index] = PIPE_ACTIVE; |
| |
| /* If channel is not allocated, make an internal allocation */ |
| channel = pipe_index / 2; |
| chan = &dev->chans[channel]; |
| if (chan->state != DMA_CHAN_STATE_ALLOC_EXT && |
| chan->state != DMA_CHAN_STATE_ALLOC_INT) { |
| chan->state = DMA_CHAN_STATE_ALLOC_INT; |
| } |
| |
| result = 0; |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| |
| /** |
| * Enable a BAM DMA pipe |
| * |
| */ |
| int sps_dma_pipe_enable(void *bam_arg, u32 pipe_index) |
| { |
| struct sps_bam *bam = bam_arg; |
| struct bamdma_device *dev; |
| struct bamdma_chan *chan; |
| u32 channel; |
| int result = SPS_ERROR; |
| |
| SPS_DBG3(sps, "sps:%s pipe %d\n", __func__, pipe_index); |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device((unsigned long) bam); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM\n", __func__); |
| goto exit_err; |
| } |
| if (pipe_index >= dev->num_pipes) { |
| SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d\n", |
| &bam->props.phys_addr, pipe_index); |
| goto exit_err; |
| } |
| if (dev->pipes[pipe_index] != PIPE_ACTIVE) { |
| SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d not active\n", |
| &bam->props.phys_addr, pipe_index); |
| goto exit_err; |
| } |
| |
| /* |
| * The channel must be enabled when the dest/input/write pipe |
| * is enabled |
| */ |
| if (DMA_PIPE_IS_DEST(pipe_index)) { |
| /* Configure and enable the channel */ |
| channel = pipe_index / 2; |
| chan = &dev->chans[channel]; |
| |
| if (chan->threshold != SPS_DMA_THRESHOLD_DEFAULT) |
| dma_write_reg_field(dev->virt_addr, |
| DMA_CHNL_CONFIG(channel), |
| DMA_CHNL_ACT_THRESH, |
| chan->thresh); |
| |
| if (chan->priority != SPS_DMA_PRI_DEFAULT) |
| dma_write_reg_field(dev->virt_addr, |
| DMA_CHNL_CONFIG(channel), |
| DMA_CHNL_WEIGHT, |
| chan->weight); |
| |
| dma_write_reg_field(dev->virt_addr, |
| DMA_CHNL_CONFIG(channel), |
| DMA_CHNL_ENABLE, 1); |
| } |
| |
| result = 0; |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| |
| /** |
| * Deactivate a BAM DMA pipe |
| * |
| * This function deactivates a BAM DMA pipe. |
| * |
| * @dev - pointer to BAM-DMA device descriptor |
| * |
| * @bam - pointer to BAM device descriptor |
| * |
| * @pipe_index - pipe index |
| * |
| * @return 0 on success, negative value on error |
| * |
| */ |
| static int sps_dma_deactivate_pipe_atomic(struct bamdma_device *dev, |
| struct sps_bam *bam, |
| u32 pipe_index) |
| { |
| u32 channel; |
| |
| if (dev->bam != bam) |
| return SPS_ERROR; |
| if (pipe_index >= dev->num_pipes) |
| return SPS_ERROR; |
| if (dev->pipes[pipe_index] != PIPE_ACTIVE) |
| return SPS_ERROR; /* Pipe is not active */ |
| |
| SPS_DBG3(sps, "sps:BAM-DMA: deactivate pipe %d\n", pipe_index); |
| |
| /* Mark pipe inactive */ |
| dev->pipes[pipe_index] = PIPE_INACTIVE; |
| |
| /* |
| * Channel must be reset when either pipe is disabled, so just always |
| * reset regardless of other pipe's state |
| */ |
| channel = pipe_index / 2; |
| dma_write_reg_field(dev->virt_addr, DMA_CHNL_CONFIG(channel), |
| DMA_CHNL_ENABLE, 0); |
| |
| /* If the peer pipe is also inactive, reset the channel */ |
| if (sps_dma_check_pipes(dev, pipe_index) == DMA_PIPES_BOTH_DISABLED) { |
| /* Free channel if allocated internally */ |
| if (dev->chans[channel].state == DMA_CHAN_STATE_ALLOC_INT) |
| dev->chans[channel].state = DMA_CHAN_STATE_FREE; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Free a BAM DMA pipe |
| * |
| */ |
| int sps_dma_pipe_free(void *bam_arg, u32 pipe_index) |
| { |
| struct bamdma_device *dev; |
| struct sps_bam *bam = bam_arg; |
| int result; |
| |
| mutex_lock(&bam_dma_lock); |
| |
| dev = sps_dma_find_device((unsigned long) bam); |
| if (dev == NULL) { |
| SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM\n", __func__); |
| result = SPS_ERROR; |
| goto exit_err; |
| } |
| |
| result = sps_dma_deactivate_pipe_atomic(dev, bam, pipe_index); |
| |
| exit_err: |
| mutex_unlock(&bam_dma_lock); |
| |
| return result; |
| } |
| |
| /** |
| * Get the BAM handle for BAM-DMA. |
| * |
| * The BAM handle should be use as source/destination in the sps_connect(). |
| * |
| * @return bam handle on success, zero on error |
| */ |
| unsigned long sps_dma_get_bam_handle(void) |
| { |
| return (unsigned long)bam_dma_dev[0].bam; |
| } |
| EXPORT_SYMBOL(sps_dma_get_bam_handle); |
| |
| /** |
| * Free the BAM handle for BAM-DMA. |
| * |
| */ |
| void sps_dma_free_bam_handle(unsigned long h) |
| { |
| } |
| EXPORT_SYMBOL(sps_dma_free_bam_handle); |
| |
| #endif /* CONFIG_SPS_SUPPORT_BAMDMA */ |