blob: 8091591bd47bf79106b32ff375ca8761cb8e87ce [file] [log] [blame]
/******************************************************************************
* *
* License Agreement *
* *
* Copyright (c) 2014 Altera Corporation, San Jose, California, USA. *
* All rights reserved. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
* *
* *
******************************************************************************/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "altera_msgdma_descriptor_regs.h"
#include "altera_msgdma_csr_regs.h"
#include "altera_msgdma_response_regs.h"
#include "io.h"
#include "altera_msgdma.h"
#include "priv/alt_busy_sleep.h"
#include "sys/alt_errno.h"
#include "sys/alt_irq.h"
#include "sys/alt_stdio.h"
/*******************************************************************************
* Private API
******************************************************************************/
static int alt_msgdma_write_standard_descriptor (
alt_u32 *csr_base,
alt_u32 *descriptor_base,
alt_msgdma_standard_descriptor *descriptor);
static int alt_msgdma_write_extended_descriptor (
alt_u32 *csr_base,
alt_u32 *descriptor_base,
alt_msgdma_extended_descriptor *descriptor);
static void alt_msgdma_irq(void *context);
static int alt_msgdma_construct_standard_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control);
static int alt_msgdma_construct_extended_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u8 write_burst_count,
alt_u16 read_stride,
alt_u16 write_stride);
static int alt_msgdma_descriptor_async_transfer (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *standard_desc,
alt_msgdma_extended_descriptor *extended_desc);
static int alt_msgdma_descriptor_sync_transfer (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *standard_desc,
alt_msgdma_extended_descriptor *extended_desc);
/* The list of registered msgdma components */
ALT_LLIST_HEAD(alt_msgdma_list);
/*
* Functions for writing descriptor structure to the dispatcher. If you disable
* some of the extended features in the hardware then you should pass in 0 for
* that particular descriptor element. These disabled elements will not be
* buffered by the dispatcher block.
*
* This function is non-blocking and will return an error code if there is no
* room to write another descriptor to the dispatcher. It is recommended to call
* 'read_descriptor_buffer_full' and make sure it returns '0' before calling
* this function.
*/
static int alt_msgdma_write_standard_descriptor (
alt_u32 *csr_base,
alt_u32 *descriptor_base,
alt_msgdma_standard_descriptor *descriptor)
{
if (0 != (IORD_ALTERA_MSGDMA_CSR_STATUS(csr_base) &
ALTERA_MSGDMA_CSR_DESCRIPTOR_BUFFER_FULL_MASK))
{
/*at least one descriptor buffer is full, returning so that this function
is non-blocking*/
return -ENOSPC;
}
IOWR_ALTERA_MSGDMA_DESCRIPTOR_READ_ADDRESS(descriptor_base,
(alt_u32)descriptor->read_address);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_WRITE_ADDRESS(descriptor_base,
( alt_u32)descriptor->write_address);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_LENGTH(descriptor_base,
descriptor->transfer_length);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_CONTROL_STANDARD(descriptor_base,
descriptor->control);
return 0;
}
/*
* This function is used for writing extended descriptors to the dispatcher.
It handles only 32-bit descriptors.
*/
static int alt_msgdma_write_extended_descriptor (
alt_u32 *csr_base,
alt_u32 *descriptor_base,
alt_msgdma_extended_descriptor *descriptor)
{
if (0 != (IORD_ALTERA_MSGDMA_CSR_STATUS(csr_base) &
ALTERA_MSGDMA_CSR_DESCRIPTOR_BUFFER_FULL_MASK))
{
/*at least one descriptor buffer is full, returning so that this function
is non-blocking*/
return -ENOSPC;
}
IOWR_ALTERA_MSGDMA_DESCRIPTOR_READ_ADDRESS(
descriptor_base,
(alt_u32)descriptor->read_address_low);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_WRITE_ADDRESS(
descriptor_base,
(alt_u32)descriptor->write_address_low);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_LENGTH(
descriptor_base,
descriptor->transfer_length);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_SEQUENCE_NUMBER(
descriptor_base,
descriptor->sequence_number);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_READ_BURST(
descriptor_base,
descriptor->read_burst_count);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_WRITE_BURST(
descriptor_base,
descriptor->write_burst_count);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_READ_STRIDE(
descriptor_base,
descriptor->read_stride);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_WRITE_STRIDE(
descriptor_base,
descriptor->write_stride);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_READ_ADDRESS_HIGH(descriptor_base, 0);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_WRITE_ADDRESS_HIGH(descriptor_base, 0);
IOWR_ALTERA_MSGDMA_DESCRIPTOR_CONTROL_ENHANCED(
descriptor_base,
descriptor->control);
return 0;
}
/*
* alt_msgdma_irq()
*
* Interrupt handler for the Modular Scatter-Gather DMA controller.
*/
static void alt_msgdma_irq(void *context)
{
alt_msgdma_dev *dev = (alt_msgdma_dev *) context;
alt_irq_context cpu_sr;
alt_u32 temporary_control;
/* disable global interrupt*/
if (dev->prefetcher_enable)
{
temporary_control =
IORD_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base)
& ALT_MSGDMA_PREFETCHER_CTRL_GLOBAL_INTR_EN_CLR_MASK;
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base,
temporary_control);
/* clear the IRQ status- W1C */
IOWR_ALT_MSGDMA_PREFETCHER_STATUS(dev->prefetcher_base,
ALT_MSGDMA_PREFETCHER_STATUS_IRQ_SET_MASK);
}
else
{
temporary_control = IORD_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base)
& (~ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK);
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, temporary_control);
/* clear the IRQ status */
IOWR_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base,
ALTERA_MSGDMA_CSR_IRQ_SET_MASK);
}
/*
* Other interrupts are explicitly disabled if callbacks
* are registered because there is no guarantee that they are
* pre-emption-safe. This allows the driver to support
* interrupt pre-emption.
*/
if(dev->callback)
{
cpu_sr = alt_irq_disable_all();
dev->callback (dev->callback_context);
alt_irq_enable_all(cpu_sr);
}
/* enable global interrupt */
if (dev->prefetcher_enable)
{
temporary_control =
IORD_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base)
| ALT_MSGDMA_PREFETCHER_CTRL_GLOBAL_INTR_EN_SET_MASK;
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base,
temporary_control);
}
else
{
temporary_control = IORD_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base)
| (ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK);
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, temporary_control);
}
return;
}
/*
* Helper functions for constructing mm_to_st, st_to_mm, mm_to_mm standard
* descriptors. Unnecessary elements are set to 0 for completeness and will be
* ignored by the hardware.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
static int alt_msgdma_construct_standard_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control)
{
if(dev->max_byte < length ||
dev->enhanced_features != 0
)
{
return -EINVAL;
}
descriptor->read_address = read_address;
descriptor->write_address = write_address;
descriptor->transfer_length = length;
descriptor->control = control | ALTERA_MSGDMA_DESCRIPTOR_CONTROL_GO_MASK;
return 0;
}
/*
* Helper functions for constructing mm_to_st, st_to_mm, mm_to_mm extended
* descriptors. Unnecessary elements are set to 0 for completeness and will be
* ignored by the hardware.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
static int alt_msgdma_construct_extended_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u8 write_burst_count,
alt_u16 read_stride,
alt_u16 write_stride)
{
if(dev->max_byte < length ||
dev->max_stride < read_stride ||
dev->max_stride < write_stride ||
dev->enhanced_features != 1
)
{
return -EINVAL;
}
descriptor->read_address_low = read_address;
descriptor->write_address_low = write_address;
descriptor->transfer_length = length;
descriptor->sequence_number = sequence_number;
descriptor->read_burst_count = read_burst_count;
descriptor->write_burst_count = write_burst_count;
descriptor->read_stride = read_stride;
descriptor->write_stride = write_stride;
descriptor->read_address_high = NULL;
descriptor->write_address_high = NULL;
descriptor->control = control | ALTERA_MSGDMA_DESCRIPTOR_CONTROL_GO_MASK;
return 0 ;
}
/*
* Helper functions for descriptor in async transfer.
* Arguments:# This driver supports HAL types
* - *dev: Pointer to msgdma device (instance) structure.
* - *standard_desc: Pointer to single standard descriptor.
* - *extended_desc: Pointer to single extended descriptor.
*
*note: Either one of both *standard_desc and *extended_desc must
* be assigned with NULL, another with proper pointer value.
* Failing to do so can cause the function return with "-EPERM "
*
* If a callback routine has been previously registered with this
* particular msgdma controller, transfer will be set up to enable interrupt
* generation. It is the responsibility of the application developer to check
* source interruption, status completion and creating suitable interrupt
* handling. Note: "stop on error" of CSR control register is always masking
* within this function. The CSR control can be set by user through calling
* "alt_register_callback" by passing user used defined control setting.
*
* Returns:
* 0 -> success
* -ENOSPC -> FIFO descriptor buffer is full
* -EPERM -> operation not permitted due to descriptor type conflict
* -ETIME -> Time out and skipping the looping after 5 msec.
*/
static int alt_msgdma_descriptor_async_transfer (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *standard_desc,
alt_msgdma_extended_descriptor *extended_desc)
{
alt_u32 control = 0;
alt_irq_context context = 0;
alt_u16 counter = 0;
alt_u32 fifo_read_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_OFFSET;
alt_u32 fifo_write_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_OFFSET;
/* Return with error immediately if one of read/write buffer is full */
if((dev->descriptor_fifo_depth <= fifo_write_fill_level) ||
(dev->descriptor_fifo_depth <= fifo_read_fill_level))
{
/*at least one write or read FIFO descriptor buffer is full,
returning so that this function is non-blocking*/
return -ENOSPC;
}
/*
* When running in a multi threaded environment, obtain the "regs_lock"
* semaphore. This ensures that accessing registers is thread-safe.
*/
ALT_SEM_PEND (dev->regs_lock, 0);
/* Stop the msgdma dispatcher from issuing more descriptors to the
read or write masters */
/* stop issuing more descriptors */
control = ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK;
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, control);
/*
* Clear any (previous) status register information
* that might occlude our error checking later.
*/
IOWR_ALTERA_MSGDMA_CSR_STATUS(
dev->csr_base,
IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base));
alt_irq_enable_all(context);
if (NULL != standard_desc && NULL == extended_desc)
{
/*writing descriptor structure to the dispatcher, wait until descriptor
write is succeed*/
while(0 != alt_msgdma_write_standard_descriptor (
dev->csr_base, dev->descriptor_base, standard_desc))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while waiting"
" free FIFO buffer for storing standard descriptor\n");
/*
* Now that access to the registers is complete, release the
* registers semaphore so that other threads can access the
* registers.
*/
ALT_SEM_POST (dev->regs_lock);
return -ETIME;
}
counter++;
}
}
else if (NULL == standard_desc && NULL != extended_desc)
{
counter = 0; /* reset counter */
/*writing descriptor structure to the dispatcher, wait until descriptor
write is succeed*/
while(0 != alt_msgdma_write_extended_descriptor (
dev->csr_base,
dev->descriptor_base,
extended_desc))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while waiting free FIFO buffer"
" for storing extended descriptor\n");
/*
* Now that access to the registers is complete, release the
* registers semaphore so that other threads can access the
* registers.
*/
ALT_SEM_POST (dev->regs_lock);
return -ETIME;
}
counter++;
}
}
else
{
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
/* operation not permitted due to descriptor type conflict */
return -EPERM;
}
/*
* If a callback routine has been previously registered which will be
* called from the msgdma ISR. Set up controller to:
* - Run
* - Stop on an error with any particular descriptor
*/
if(dev->callback)
{
control |= (dev->control |
ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK |
ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK );
control &= (~ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK);
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, control);
alt_irq_enable_all(context);
}
/*
* No callback has been registered. Set up controller to:
* - Run
* - Stop on an error with any particular descriptor
* - Disable interrupt generation
*/
else
{
control |= (dev->control |
ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK );
control &= (~ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK) &
(~ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK);
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, control);
alt_irq_enable_all(context);
}
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
return 0;
}
/*
* Helper functions for descriptor in sync transfer.
* Arguments:
* - *dev: Pointer to msgdma device (instance) structure.
* - *standard_desc: Pointer to single standard descriptor.
* - *extended_desc: Pointer to single extended descriptor.
*
* Note: Either one of both *standard_desc and *extended_desc must
* be assigned with NULL, another with proper pointer value.
* Failing to do so can cause the function return with "-EPERM "
*
* "stop on error" of CSR control register is always being masked and interrupt
* is always disabled within this function.
* The CSR control can be set by user through calling "alt_register_callback"
* with passing user defined control setting.
*
* Returns:
* 0 -> success
* error -> errors or conditions causing msgdma stop issuing commands to masters.
* check the bit set in the error with CSR status register.
* -EPERM -> operation not permitted due to descriptor type conflict
* -ETIME -> Time out and skipping the looping after 5 msec.
*/
static int alt_msgdma_descriptor_sync_transfer (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *standard_desc,
alt_msgdma_extended_descriptor *extended_desc)
{
alt_u32 control=0;
alt_irq_context context=0;
alt_u32 csr_status = 0;
alt_u16 counter = 0;
alt_u32 fifo_read_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_OFFSET;
alt_u32 fifo_write_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_OFFSET;
alt_u32 error = ALTERA_MSGDMA_CSR_STOPPED_ON_ERROR_MASK |
ALTERA_MSGDMA_CSR_STOPPED_ON_EARLY_TERMINATION_MASK |
ALTERA_MSGDMA_CSR_STOP_STATE_MASK |
ALTERA_MSGDMA_CSR_RESET_STATE_MASK;
/* Wait for available FIFO buffer to store new descriptor*/
while ((dev->descriptor_fifo_depth <= fifo_write_fill_level) ||
(dev->descriptor_fifo_depth <= fifo_read_fill_level))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while waiting free FIFO buffer"
" for storing descriptor\n");
return -ETIME;
}
counter++;
fifo_read_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_READ_FILL_LEVEL_OFFSET;
fifo_write_fill_level = (
IORD_ALTERA_MSGDMA_CSR_DESCRIPTOR_FILL_LEVEL(dev->csr_base) &
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_MASK) >>
ALTERA_MSGDMA_CSR_WRITE_FILL_LEVEL_OFFSET;
}
/*
* When running in a multi threaded environment, obtain the "regs_lock"
* semaphore. This ensures that accessing registers is thread-safe.
*/
ALT_SEM_PEND (dev->regs_lock, 0);
/* Stop the msgdma dispatcher from issuing more descriptors to the
read or write masters */
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base,
ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK);
/*
* Clear any (previous) status register information
* that might occlude our error checking later.
*/
IOWR_ALTERA_MSGDMA_CSR_STATUS(
dev->csr_base,
IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base));
if (NULL != standard_desc && NULL == extended_desc)
{
counter = 0; /* reset counter */
/*writing descriptor structure to the dispatcher, wait until descriptor
write is succeed*/
while(0 != alt_msgdma_write_standard_descriptor (
dev->csr_base, dev->descriptor_base, standard_desc))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while writing standard"
" descriptor to FIFO\n");
/*
* Now that access to the registers is complete, release the
* registers semaphore so that other threads can access the
* registers.
*/
ALT_SEM_POST (dev->regs_lock);
return -ETIME;
}
counter++;
}
}
else if (NULL == standard_desc && NULL != extended_desc)
{
counter = 0; /* reset counter */
/*writing descriptor structure to the dispatcher, wait until descriptor
write is succeed*/
while(0 != alt_msgdma_write_extended_descriptor (
dev->csr_base, dev->descriptor_base, extended_desc))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while writing extended"
" descriptor to FIFO\n");
/*
* Now that access to the registers is complete, release the
* registers semaphore so that other threads can access the
* registers.
*/
ALT_SEM_POST (dev->regs_lock);
return -ETIME;
}
counter++;
}
}
else
{
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
/* operation not permitted due to descriptor type conflict */
return -EPERM;
}
/*
* Set up msgdma controller to:
* - Disable interrupt generation
* - Run once a valid descriptor is written to controller
* - Stop on an error with any particular descriptor
*/
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base,
(dev->control |
ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK ) &
(~ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK) &
(~ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK)) ;
alt_irq_enable_all(context);
counter = 0; /* reset counter */
csr_status = IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base);
/* Wait for any pending transfers to complete or checking any errors or
conditions causing descriptor to stop dispatching */
while (!(csr_status & error) && (csr_status & ALTERA_MSGDMA_CSR_BUSY_MASK))
{
alt_busy_sleep(1); /* delay 1us */
if(5000 <= counter) /* time_out if waiting longer than 5 msec */
{
alt_printf("time out after 5 msec while waiting for any pending"
" transfer complete\n");
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
return -ETIME;
}
counter++;
csr_status = IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base);
}
/*Errors or conditions causing the dispatcher stopping issuing read/write
commands to masters*/
if(0 != (csr_status & error))
{
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
return error;
}
/* Stop the msgdma dispatcher from issuing more descriptors to the
read or write masters */
/* stop issuing more descriptors */
control = IORD_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base) |
ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK;
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, control);
/*
* Clear any (previous) status register information
* that might occlude our error checking later.
*/
IOWR_ALTERA_MSGDMA_CSR_STATUS(
dev->csr_base,
IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base));
alt_irq_enable_all(context);
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
return 0;
}
/*
* Functions for constructing standard descriptors. Unnecessary elements are
* set to 0 for completeness and will be ignored by the hardware.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
int alt_msgdma_construct_standard_st_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *descriptor,
alt_u32 *write_address, alt_u32 length, alt_u32 control)
{
return alt_msgdma_construct_standard_descriptor(dev, descriptor, NULL,
write_address, length, control);
}
int alt_msgdma_construct_standard_mm_to_st_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 length,
alt_u32 control)
{
return alt_msgdma_construct_standard_descriptor(dev, descriptor, read_address,
NULL, length, control);
}
int alt_msgdma_construct_standard_mm_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control)
{
return alt_msgdma_construct_standard_descriptor(dev, descriptor, read_address,
write_address, length, control);
}
/*
* Functions for constructing extended descriptors. If you disable some of the
* extended features in the hardware then you should pass in 0 for that
* particular descriptor element. These disabled elements will not be buffered
* by the dispatcher block.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
int alt_msgdma_construct_extended_st_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *descriptor,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 write_burst_count,
alt_u16 write_stride)
{
return alt_msgdma_construct_extended_descriptor(dev, descriptor,
NULL, write_address, length, control, sequence_number, 0,
write_burst_count, 0, write_stride);
}
int alt_msgdma_construct_extended_mm_to_st_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u16 read_stride)
{
return alt_msgdma_construct_extended_descriptor(dev, descriptor, read_address,
NULL, length, control, sequence_number, read_burst_count, 0,
read_stride, 0);
}
int alt_msgdma_construct_extended_mm_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *descriptor,
alt_u32 *read_address,
alt_u32 *write_address,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u8 write_burst_count,
alt_u16 read_stride,
alt_u16 write_stride)
{
return alt_msgdma_construct_extended_descriptor(dev, descriptor,
read_address, write_address, length, control, sequence_number,
read_burst_count, write_burst_count, read_stride, write_stride);
}
/********************** MSGDMA PREFETCHER PRIVATE APIs *************************/
/*
* Base functions for constructing mm_to_st, st_to_mm, mm_to_mm standard
* descriptors for the prefetcher. Unnecessary elements are set to 0 for
* completeness and will be ignored by the hardware.
* The descriptor created will be suitable for park since this API will set next_ptr
* to itself as park_mode requires. Additionally OWN_BY_HW bit left as 0 (owned by sw)
* until the prefetcher is started with this descriptor in the list.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
static int alt_msgdma_construct_prefetcher_standard_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_standard_descriptor *descriptor,
alt_u32 read_address,
alt_u32 write_address,
alt_u32 length,
alt_u32 control)
{
if(dev->max_byte < length ||
dev->enhanced_features != 0
)
{
return -EINVAL;
}
descriptor->read_address = read_address;
descriptor->write_address = write_address;
descriptor->transfer_length = length;
/* have descriptor point to itself for park_mode */
descriptor->next_desc_ptr = (alt_u32)descriptor;
/* clear control own_by_hw bit field (SW owns this descriptor)*/
descriptor->control = (control
& ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_CLR_MASK)
| ALTERA_MSGDMA_DESCRIPTOR_CONTROL_GO_MASK;
return 0;
}
/*
* Base functions for constructing mm_to_st, st_to_mm, mm_to_mm extended
* descriptors. Unnecessary elements are set to 0 for completeness and will be
* ignored by the hardware. The descriptor created will be suitable for park
* mode since this API will set next_ptr to itself as park_mode requires.
* Additionally OWN_BY_HW bit left as 0 (owned by sw) until the prefetcher is
* started with this descriptor in the list.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
static int alt_msgdma_construct_prefetcher_extended_descriptor(
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_extended_descriptor *descriptor,
alt_u32 read_address_high,
alt_u32 read_address_low,
alt_u32 write_address_high,
alt_u32 write_address_low,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u8 write_burst_count,
alt_u16 read_stride,
alt_u16 write_stride)
{
msgdma_addr64 node_addr;
if(dev->max_byte < length ||
dev->max_stride < read_stride ||
dev->max_stride < write_stride ||
dev->enhanced_features != 1
)
{
return -EINVAL;
}
descriptor->read_address_high = read_address_high;
descriptor->read_address_low = read_address_low;
descriptor->write_address_high = write_address_high;
descriptor->write_address_low = write_address_low;
descriptor->transfer_length = length;
descriptor->sequence_number = sequence_number;
descriptor->read_burst_count = read_burst_count;
descriptor->write_burst_count = write_burst_count;
descriptor->read_stride = read_stride;
descriptor->write_stride = write_stride;
/* have descriptor point to itself */
node_addr.u64 = (uintptr_t)descriptor;
descriptor->next_desc_ptr_low = node_addr.u32[0];
descriptor->next_desc_ptr_high = node_addr.u32[1];
/* clear control own_by_hw bit field (SW still owns this descriptor). */
descriptor->control = (control
& ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_CLR_MASK)
| ALTERA_MSGDMA_DESCRIPTOR_CONTROL_GO_MASK;
return 0 ;
}
/********************** MSGDMA PREFETCHER PUBLIC APIs ************************/
/*
* Functions for constructing standard descriptors. Unnecessary elements are
* set to 0 for completeness and will be ignored by the hardware.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
int alt_msgdma_construct_prefetcher_standard_mm_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_standard_descriptor *descriptor,
alt_u32 read_address,
alt_u32 write_address,
alt_u32 length,
alt_u32 control)
{
return alt_msgdma_construct_prefetcher_standard_descriptor(dev, descriptor,
read_address, write_address, length, control);
}
int alt_msgdma_construct_prefetcher_standard_st_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_standard_descriptor *descriptor,
alt_u32 write_address,
alt_u32 length,
alt_u32 control)
{
return alt_msgdma_construct_prefetcher_standard_descriptor(dev, descriptor,
0, write_address, length, control);
}
int alt_msgdma_construct_prefetcher_standard_mm_to_st_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_standard_descriptor *descriptor,
alt_u32 read_address,
alt_u32 length,
alt_u32 control)
{
return alt_msgdma_construct_prefetcher_standard_descriptor(dev, descriptor,
read_address, 0, length, control);
}
/*
* Functions for constructing extended descriptors. If you disable some of the
* extended features in the hardware then you should pass in 0 for that
* particular descriptor element. These disabled elements will not be buffered
* by the dispatcher block.
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to argument which
* has larger value than hardware setting value)
*/
int alt_msgdma_construct_prefetcher_extended_st_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_extended_descriptor *descriptor,
alt_u32 write_address_high,
alt_u32 write_address_low,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 write_burst_count,
alt_u16 write_stride)
{
return alt_msgdma_construct_prefetcher_extended_descriptor(dev, descriptor,
0, 0, write_address_high, write_address_low, length, control,
sequence_number, 0, write_burst_count, 0, write_stride);
}
int alt_msgdma_construct_prefetcher_extended_mm_to_st_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_extended_descriptor *descriptor,
alt_u32 read_address_high,
alt_u32 read_address_low,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u16 read_stride)
{
return alt_msgdma_construct_prefetcher_extended_descriptor(dev, descriptor,
read_address_high, read_address_low, 0, 0, length, control,
sequence_number, read_burst_count, 0, read_stride, 0);
}
int alt_msgdma_construct_prefetcher_extended_mm_to_mm_descriptor (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_extended_descriptor *descriptor,
alt_u32 read_address_high,
alt_u32 read_address_low,
alt_u32 write_address_high,
alt_u32 write_address_low,
alt_u32 length,
alt_u32 control,
alt_u16 sequence_number,
alt_u8 read_burst_count,
alt_u8 write_burst_count,
alt_u16 read_stride,
alt_u16 write_stride)
{
return alt_msgdma_construct_prefetcher_extended_descriptor(dev, descriptor,
read_address_high, read_address_low, write_address_high,
write_address_low, length, control, sequence_number,
read_burst_count, write_burst_count, read_stride, write_stride);
}
/* PREFETCHER linked list APIs */
/*
* Function for adding standard descriptors to a standard descriptor list
* Returns:
* - status: return 0 (success)
* return -EINVAL (invalid argument, could be due to descriptor
* already being in the list, descriptor pointer being NULL, or
* descriptor.next_ptr not pointing back to itslef)
*/
int alt_msgdma_prefetcher_add_standard_desc_to_list (
alt_msgdma_prefetcher_standard_descriptor** list,
alt_msgdma_prefetcher_standard_descriptor* descriptor)
{
alt_msgdma_prefetcher_standard_descriptor *last_descr_ptr;
if (descriptor == NULL)
{
return -EINVAL; /* this descriptor cannot be NULL */
}
if (descriptor->next_desc_ptr != (alt_u32)descriptor)
{
return -EINVAL; /* descriptor.next_ptr must point to itself */
}
if (*list == NULL)
{
*list = descriptor; /* make this root-node if list is empty */
return 0; /* successfully added */
}
if (*list == descriptor)
{
return -EINVAL; /* this descriptor cannot already be root-node */
}
/* get to last node in the list */
last_descr_ptr = *list; /* start at list root-node */
/* traverse list until you get the last node */
while (last_descr_ptr->next_desc_ptr != (alt_u32)*list)
{
if (last_descr_ptr->next_desc_ptr == (alt_u32)descriptor)
{
return -EINVAL; /* descriptor cannot already be in the list */
}
last_descr_ptr =
(alt_msgdma_prefetcher_standard_descriptor*)(last_descr_ptr->next_desc_ptr);
}
/* add this descriptor to end of list */
last_descr_ptr->next_desc_ptr = (alt_u32)((uintptr_t)descriptor);
/* ensure new last pointer points the start of the list */
descriptor->next_desc_ptr = (alt_u32)((uintptr_t)*list);
return 0; /* successfully added */
}
int alt_msgdma_prefetcher_add_extended_desc_to_list (
alt_msgdma_prefetcher_extended_descriptor** list,
alt_msgdma_prefetcher_extended_descriptor* descriptor)
{
alt_msgdma_prefetcher_extended_descriptor *last_descr_ptr;
msgdma_addr64 root_node_addr, next_node_addr;
if (descriptor == NULL)
{
return -EINVAL; /* this descriptor cannot be NULL */
}
next_node_addr.u64 = (uintptr_t)descriptor;
if( (descriptor->next_desc_ptr_low != next_node_addr.u32[0]) &&
(descriptor->next_desc_ptr_high != next_node_addr.u32[1]))
{
return -EINVAL; /* descriptor.next_ptr must point to itself */
}
if (*list == NULL)
{
*list = descriptor; /* make this the root-node if list is empty */
return 0;
}
if (*list == descriptor)
{
return -EINVAL; /* this descriptor cannot already be root-node */
}
/* get to last node in the list */
last_descr_ptr = *list; /* start at list root-node */
/* the last nodes next ptr should point to the root node*/
root_node_addr.u64 = (uintptr_t)*list;
/* traverse list until you get the last node */
while ((last_descr_ptr->next_desc_ptr_low != root_node_addr.u32[0])
&& (last_descr_ptr->next_desc_ptr_high != root_node_addr.u32[1]))
{
/* first check if descriptor already in the list */
next_node_addr.u64 = (uintptr_t)descriptor;
if ((last_descr_ptr->next_desc_ptr_low == next_node_addr.u32[0])
&& (last_descr_ptr->next_desc_ptr_high == next_node_addr.u32[1]))
{
return -EINVAL; /* descriptor cannot already be in the list */
}
/* go to next node in list, using 64 bit address */
next_node_addr.u32[0] = last_descr_ptr->next_desc_ptr_low;
next_node_addr.u32[1] = last_descr_ptr->next_desc_ptr_high;
last_descr_ptr =
(alt_msgdma_prefetcher_extended_descriptor*)((uintptr_t)next_node_addr.u64);
}
/* add this descriptor to end of list */
next_node_addr.u64 = (uintptr_t)descriptor;
last_descr_ptr->next_desc_ptr_low = next_node_addr.u32[0];
last_descr_ptr->next_desc_ptr_high = next_node_addr.u32[1];
/* ensure new last pointer points the beginning of the list */
descriptor->next_desc_ptr_low = root_node_addr.u32[0];
descriptor->next_desc_ptr_high = root_node_addr.u32[1];
return 0;
}
/*
* Functions to set all the own-by-hw bits, need to call right before starting
* prefetcher since if used the create descriptor APIs the set_by_hw bits are
* still set to SW owned.
*/
int alt_msgdma_prefetcher_set_std_list_own_by_hw_bits (
alt_msgdma_prefetcher_standard_descriptor *list)
{
alt_u32 descriptor_control_field = 0;
alt_msgdma_prefetcher_standard_descriptor *last_descr_ptr;
if (list == NULL)
{
return -EINVAL; /* this list cannot be empty */
}
/* update all nodes in the list */
last_descr_ptr = list; /* start at list root-node */
/* traverse list to update all of the nodes */
while (last_descr_ptr->next_desc_ptr != (alt_u32)list)
{
/* get current value */
descriptor_control_field = last_descr_ptr->control;
/* update own_by_hw bit only */
last_descr_ptr->control = descriptor_control_field
| ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_SET_MASK;
/* go to next node in list */
last_descr_ptr =
(alt_msgdma_prefetcher_standard_descriptor*)(last_descr_ptr->next_desc_ptr);
}
/* update the last node in the list, currently last_descr_ptr after while loop */
descriptor_control_field = last_descr_ptr->control; /* get current value */
/* update own_by_hw bit only */
last_descr_ptr->control = descriptor_control_field
| ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_SET_MASK;
return 0;
}
/*
* Functions to set all the own-by-hw bits, need to call right before starting
* prefetcher since if used the create descriptor APIs the set_by_hw bits are
* still set to SW owned.
*/
int alt_msgdma_prefetcher_set_extd_list_own_by_hw_bits (
alt_msgdma_prefetcher_extended_descriptor *list)
{
alt_u32 descriptor_control_field = 0;
msgdma_addr64 root_node_addr, next_node_addr;
alt_msgdma_prefetcher_extended_descriptor *last_descr_ptr;
if (list == NULL)
{
return -EINVAL; /* this list cannot be empty */
}
/* update all nodes in the list */
last_descr_ptr = list; /* start at list root-node */
/* the last nodes next ptr should point to the root node*/
root_node_addr.u64 = (uintptr_t)list;
/* traverse list until you get the last node */
while ((last_descr_ptr->next_desc_ptr_low != root_node_addr.u32[0])
&& (last_descr_ptr->next_desc_ptr_high != root_node_addr.u32[1]))
{
/* start with current value */
descriptor_control_field = last_descr_ptr->control;
/* update own_by_hw bit only */
last_descr_ptr->control = descriptor_control_field
| ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_SET_MASK;
/* go to next node in list, using 64 bit address */
next_node_addr.u32[0] = last_descr_ptr->next_desc_ptr_low;
next_node_addr.u32[1] = last_descr_ptr->next_desc_ptr_high;
last_descr_ptr =
(alt_msgdma_prefetcher_extended_descriptor*)((uintptr_t)next_node_addr.u64);
}
/* update the last node in the list, currently last_descr_ptr after while loop */
descriptor_control_field = last_descr_ptr->control; /* start with current value */
/* update own_by_hw bit only */
last_descr_ptr->control = descriptor_control_field
| ALT_MSGDMA_PREFETCHER_DESCRIPTOR_CTRL_OWN_BY_HW_SET_MASK;
return 0;
}
/*
* Functions to start the prefetcher. Will return error if prefetcher already
* started.
*
* Arguments:# This driver supports HAL types
* - *dev: Pointer to msgdma device (instance) structure.
* - *standard_desc: Pointer to single standard descriptor OR *extended_desc:
* Pointer to single extended descriptor.
* - park_mode_en: setting for prefetcher park mode
* - poll_en: setting for poll_en (IF poll frequency still 0 this API will
* also set that to a default non-zero value)
*
*note: Must call API specific to descriptor type. Either
* alt_msgdma_start_prefetcher_with_std_desc_list OR
* alt_msgdma_start_prefetcher_with_extd_desc_list
* where the list paratmeter is the root-node of your linked list. Then those
* APIs will call the base function accordingly.
*
* If a callback routine has been previously registered with this
* particular msgdma controller, transfer will be set up to enable interrupt
* generation. It is the responsibility of the application developer to check
* source interruption, status completion and creating suitable interrupt
* handling.
* Note: "stop on error" of CSR control register is always masking within this
* function. The CSR control can be set by user through calling
* "alt_register_callback" by passing user used defined control setting.
*
* Returns:
* 0 -> success
* -EBUSY -> prefetcher busy processing list already, it is up to user to stop
* prefetcher/dispatcher correctly before calling this function.
* if already busy will always return error.
*/
/*
* Base function to start prefetcher.
*/
int alt_msgdma_start_prefetcher_with_list_addr (
alt_msgdma_dev *dev,
alt_u64 list_addr,
alt_u8 park_mode_en,
alt_u8 poll_en)
{
alt_u32 prefetcher_ctl = 0;
alt_u32 dispatcher_ctl = 0;
alt_irq_context context = 0;
/* use helper struct to get easy access to hi/low address */
msgdma_addr64 root_node_addr;
root_node_addr.u64 = list_addr;
/*
* When running in a multi threaded environment, obtain the "regs_lock"
* semaphore. This ensures that accessing registers is thread-safe.
*/
ALT_SEM_PEND (dev->regs_lock, 0);
/* case where prefetcher already started, return busy error */
prefetcher_ctl = IORD_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base);
if(ALT_MSGDMA_PREFETCHER_CTRL_RUN_GET(prefetcher_ctl)){
/* release the registers semaphore */
ALT_SEM_POST (dev->regs_lock);
return -EBUSY;
}
/* Stop the msgdma dispatcher from issuing more descriptors to the
read or write masters */
/* stop issuing more descriptors */
dispatcher_ctl = ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK;
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, dispatcher_ctl);
/*
* Clear any (previous) status register information
* that might occlude our error checking later.
*/
IOWR_ALTERA_MSGDMA_CSR_STATUS( dev->csr_base,
IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base));
alt_irq_enable_all(context);
/*
* If a callback routine has been previously registered which will be
* called from the msgdma ISR. Set up dispatcher to:
* - Run
* - Stop on an error with any particular descriptor
*/
if(dev->callback)
{
dispatcher_ctl |= (dev->control | ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK
| ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK );
dispatcher_ctl &= (~ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK);
prefetcher_ctl |= ALT_MSGDMA_PREFETCHER_CTRL_GLOBAL_INTR_EN_SET_MASK;
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, dispatcher_ctl);
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base, prefetcher_ctl);
alt_irq_enable_all(context);
}
/*
* No callback has been registered. Set up dispatcher to:
* - Run
* - Stop on an error with any particular descriptor
* - Disable interrupt generation
*/
else
{
dispatcher_ctl |= (dev->control | ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK);
dispatcher_ctl &= (~ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK)
& (~ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK);
prefetcher_ctl &= ALT_MSGDMA_PREFETCHER_CTRL_GLOBAL_INTR_EN_CLR_MASK;
/* making sure the read-modify-write below can't be pre-empted */
context = alt_irq_disable_all();
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, dispatcher_ctl);
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base, prefetcher_ctl);
alt_irq_enable_all(context);
}
/* set next descriptor registers to point to the list root-node */
IOWR_ALT_MSGDMA_PREFETCHER_NEXT_DESCRIPTOR_PTR_LOW(dev->prefetcher_base,
root_node_addr.u32[0]);
IOWR_ALT_MSGDMA_PREFETCHER_NEXT_DESCRIPTOR_PTR_HIGH(dev->prefetcher_base,
root_node_addr.u32[1]);
/* set park-mode */
if (park_mode_en){
prefetcher_ctl |= ALT_MSGDMA_PREFETCHER_CTRL_PARK_MODE_SET_MASK;
}
else {
prefetcher_ctl &= ALT_MSGDMA_PREFETCHER_CTRL_PARK_MODE_CLR_MASK;
}
/* set poll-en */
if (poll_en){
prefetcher_ctl |= ALT_MSGDMA_PREFETCHER_CTRL_DESC_POLL_EN_MASK;
if(IORD_ALT_MSGDMA_PREFETCHER_DESCRIPTOR_POLLING_FREQ(
dev->prefetcher_base) == 0){
/* set poll frequency to some non-zero default value */
IOWR_ALT_MSGDMA_PREFETCHER_DESCRIPTOR_POLLING_FREQ(
dev->prefetcher_base, 0xFF);
}
}
else {
prefetcher_ctl &= ALT_MSGDMA_PREFETCHER_CTRL_DESC_POLL_EN_CLR_MASK;
}
/* set the prefetcher run bit */
prefetcher_ctl |= ALT_MSGDMA_PREFETCHER_CTRL_RUN_SET_MASK;
/* start the dma since run bit is set */
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base, prefetcher_ctl);
/*
* Now that access to the registers is complete, release the registers
* semaphore so that other threads can access the registers.
*/
ALT_SEM_POST (dev->regs_lock);
return 0;
}
/*
* Public functions to start prefetcher.
*/
int alt_msgdma_start_prefetcher_with_std_desc_list (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_standard_descriptor *list,
alt_u8 park_mode_en,
alt_u8 poll_en)
{
if (alt_msgdma_prefetcher_set_std_list_own_by_hw_bits(list) != 0){
return -EINVAL;
}
return alt_msgdma_start_prefetcher_with_list_addr (dev, (uintptr_t)list,
park_mode_en, poll_en);
}
int alt_msgdma_start_prefetcher_with_extd_desc_list (
alt_msgdma_dev *dev,
alt_msgdma_prefetcher_extended_descriptor *list,
alt_u8 park_mode_en,
alt_u8 poll_en)
{
if (alt_msgdma_prefetcher_set_extd_list_own_by_hw_bits(list) != 0){
return -EINVAL;
}
return alt_msgdma_start_prefetcher_with_list_addr (dev, (uintptr_t)list,
park_mode_en, poll_en);
}
/*
* alt_msgdma_open - Retrieve a pointer to the msgdma
*
* Search the list of registered msgdma for one with the supplied name.
*
* The return value will be NULL on failure, and non-NULL otherwise.
*
* Arguments:
* - *name: Character pointer to name of msgdma peripheral as registered
* with the HAL. For example, an msgdma controller named "msgdma_0"
* in Qsys would be opened by asking for "/dev/msgdma_0_csr".
*
* Returns:
* - Pointer to msgdma device instance structure, or null if the device
* could not be opened.
*/
alt_msgdma_dev* alt_msgdma_open (const char* name)
{
alt_msgdma_dev* dev = NULL;
dev = (alt_msgdma_dev*) alt_find_dev (name, &alt_msgdma_list);
if (NULL == dev)
{
ALT_ERRNO = ENODEV;
}
return dev;
}
/*
* alt_msgdma_init()
*
* Initializes the Modular Scatter-Gather DMA controller. This routine is called
* from the ALTERA_MSGDMA_INIT macro and is called automatically
* by alt_sys_init.c
*
* This routine disables interrupts, descriptor processing,
* registers a specific instance of the device with the HAL,
* and installs an interrupt handler for the device.
*/
void alt_msgdma_init (alt_msgdma_dev *dev, alt_u32 ic_id, alt_u32 irq)
{
extern alt_llist alt_msgdma_list;
alt_u32 temporary_control;
int error;
if (dev->prefetcher_enable)
{
/* start prefetcher reset sequence */
IOWR_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base,
ALT_MSGDMA_PREFETCHER_CTRL_RESET_SET_MASK);
/* wait until hw clears the bit */
while(ALT_MSGDMA_PREFETCHER_CTRL_RESET_GET(
IORD_ALT_MSGDMA_PREFETCHER_CONTROL(dev->prefetcher_base)));
/*
* This reset is intended to be used along with reset dispatcher in
* dispatcher core. Once the reset sequence in prefetcher core has
* completed, software is expected to reset the dispatcher core,
* and polls for dispatcherÂ’s reset sequence to be completed.
*/
}
/* Reset the registers and FIFOs of the dispatcher and master modules */
/* set the reset bit, no need to read the control register first since
this write is going to clear it out */
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, ALTERA_MSGDMA_CSR_RESET_MASK);
while(0 != (IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base)
& ALTERA_MSGDMA_CSR_RESET_STATE_MASK));
/*
* Disable interrupts, halt descriptor processing,
* and clear status register content
*/
/* disable global interrupt */
temporary_control = IORD_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base)
& (~ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK);
/* stopping descriptor */
temporary_control |= ALTERA_MSGDMA_CSR_STOP_DESCRIPTORS_MASK;
IOWR_ALTERA_MSGDMA_CSR_CONTROL(dev->csr_base, temporary_control);
/* clear the CSR status register */
IOWR_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base,
IORD_ALTERA_MSGDMA_CSR_STATUS(dev->csr_base));
if (dev->prefetcher_enable)
{
/* clear all status bits that are set, since theyre W1C */
IOWR_ALT_MSGDMA_PREFETCHER_STATUS(dev->prefetcher_base,
IORD_ALT_MSGDMA_PREFETCHER_STATUS(dev->prefetcher_base));
}
/* Register this instance of the msgdma controller with HAL */
alt_dev_llist_insert((alt_dev_llist*) dev, &alt_msgdma_list);
/*
* Creating semaphores used to protect access to the registers
* when running in a multi-threaded environment.
*/
error = ALT_SEM_CREATE (&dev->regs_lock, 1);
if (!error)
{
/* Install IRQ handler */
alt_ic_isr_register(ic_id, irq, alt_msgdma_irq, dev, 0x0);
}
else
{
alt_printf("failed to create semaphores\n");
}
return;
}
/*
* alt_msgdma_register_callback
*
* Associate a user-specific routine with the msgdma interrupt handler.
* If a callback is registered, all non-blocking msgdma transfers will
* enable interrupts that will cause the callback to be executed.
* The callback runs as part of the interrupt service routine, and
* great care must be taken to follow the guidelines for acceptable
* interrupt service routine behaviour as described in the Nios II
* Software Developer's Handbook.However, user can change some of the CSR
* control setting in blocking transfer by calling this function.
*
* Note: To disable callbacks after registering one, this routine
* may be called passing 0x0 to the callback argument.
*
* Arguments:
* - *dev: Pointer to msgdma device (instance) structure.
* - callback: Pointer to callback routine to execute at interrupt level
* - control: For masking the source interruption and setting configuration in
* control register
*/
void alt_msgdma_register_callback(
alt_msgdma_dev *dev,
alt_msgdma_callback callback,
alt_u32 control,
void *context)
{
dev->callback = callback;
dev->callback_context = context;
dev->control = control;
return ;
}
/*
* alt_msgdma_standard_descriptor_async_transfer
*
* Set up and commence a non-blocking transfer of one descriptors at a time.
*
* If the FIFO buffer for one of read/write is full at the time of this call,
* the routine will immediately return -ENOSPC, the application can then decide
* how to proceed without being blocked.
*
* Arguments:
* - *dev: Pointer to msgdma device (instance) struct.
* - *desc: Pointer to single (ready to run) descriptor.
*
* Returns:
* 0 -> success
* -ENOSPC -> FIFO descriptor buffer is full
* -EPERM -> operation not permitted due to descriptor type conflict
* -ETIME -> Time out and skipping the looping after 5 msec.
*/
int alt_msgdma_standard_descriptor_async_transfer(
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *desc)
{
/*
* Error detection/handling should be performed at the application
* or callback level as appropriate.
*/
return alt_msgdma_descriptor_async_transfer(dev, desc, NULL);
}
/*
* alt_msgdma_extended_descriptor_async_transfer
*
* Set up and commence a non-blocking transfer of one descriptors at a time.
*
* If the FIFO buffer for one of read/write is full at the time of this call,
* the routine will immediately return -ENOSPC, the application can then
* decide how to proceed without being blocked.
*
* Arguments:
* - *dev: Pointer to msgdma device (instance) struct.
* - *desc: Pointer to single (ready to run) descriptor.
*
* Returns:
* 0 -> success
* -ENOSPC -> FIFO descriptor buffer is full
* -EPERM -> operation not permitted due to descriptor type conflict
* -ETIME -> Time out and skipping the looping after 5 msec.
*/
int alt_msgdma_extended_descriptor_async_transfer(
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *desc)
{
/*
* Error detection/handling should be performed at the application
* or callback level as appropriate.
*/
return alt_msgdma_descriptor_async_transfer(dev, NULL, desc);
}
/*
* alt_msgdma_standard_descriptor_sync_transfer
*
* This function will start commencing a blocking transfer of one standard
* descriptor at a time. If the FIFO buffer for one of read/write is full at the
* time of this call, the routine will wait until free FIFO buffer available for
* continue processing.
*
* The function will return "-1" if errors or conditions causing the dispatcher
* stop issuing the commands to both read and write masters before both read and
* write command buffers are empty.
*
* Additional error information is available in the status bits of
* each descriptor that the msgdma processed; it is the responsibility
* of the user's application to search through the descriptor
* to gather specific error information.
*
* Arguments:
* - *dev: Pointer to msgdma device (instance) structure.
* - *desc: Pointer to single (ready to run) descriptor.
*
* Returns:
* - status: return 0 (success)
* return error (errors or conditions causing msgdma stop issuing
* commands to masters)
* Suggest suggest checking the bit set in the error with CSR status
* register.
* return -EPERM (operation not permitted due to descriptor type
* conflict)
* return -ETIME (Time out and skipping the looping after 5 msec)
*/
int alt_msgdma_standard_descriptor_sync_transfer(
alt_msgdma_dev *dev,
alt_msgdma_standard_descriptor *desc)
{
return alt_msgdma_descriptor_sync_transfer(dev, desc, NULL);
}
/*
* alt_msgdma_extended_descriptor_sync_transfer
*
* This function will start commencing a blocking transfer of one extended
* descriptor at a time. If the FIFO buffer for one of read/write is full at the
* time of this call, the routine will wait until free FIFO buffer available for
* continue processing.
*
* The function will return "-1" if errors or conditions causing the dispatcher
* stop issuing the commands to both read and write masters before both read and
* write command buffers are empty.
*
* Additional error information is available in the status bits of
* each descriptor that the msgdma processed; it is the responsibility
* of the user's application to search through the descriptor
* to gather specific error information.
*
*
* Arguments:
* - *dev: Pointer to msgdma device (instance) structure.
* - *desc: Pointer to single (ready to run) descriptor.
*
* Returns:
* - status: return 0 (success)
* return error (errors or conditions causing msgdma stop issuing
* commands to masters)
* Suggest suggest checking the bit set in the error with CSR status
* register.
* return -EPERM (operation not permitted due to descriptor type
* conflict)
* return -ETIME (Time out and skipping the looping after 5 msec)
*/
int alt_msgdma_extended_descriptor_sync_transfer(
alt_msgdma_dev *dev,
alt_msgdma_extended_descriptor *desc)
{
return alt_msgdma_descriptor_sync_transfer(dev, NULL, desc);
}