nuttx/arch/risc-v/src/mpfs/mpfs_dma.c
2021-06-16 12:22:54 -05:00

517 lines
14 KiB
C
Executable File

/****************************************************************************
* arch/risc-v/src/mpfs/mpfs_dma.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include "riscv_arch.h"
#include "hardware/mpfs_dma.h"
#include "mpfs_dma.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DMA_MAX_TRANSACTION_SIZE (0x0f)
/****************************************************************************
* Private Data
****************************************************************************/
static uint8_t g_channel_nextcfg_wsize[MPFS_DMA_NUM_CHANNELS] =
{
DMA_MAX_TRANSACTION_SIZE, DMA_MAX_TRANSACTION_SIZE,
DMA_MAX_TRANSACTION_SIZE, DMA_MAX_TRANSACTION_SIZE
};
static uint8_t g_channel_nextcfg_rsize[MPFS_DMA_NUM_CHANNELS] =
{
DMA_MAX_TRANSACTION_SIZE, DMA_MAX_TRANSACTION_SIZE,
DMA_MAX_TRANSACTION_SIZE, DMA_MAX_TRANSACTION_SIZE
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mpfs_dma_setup_transfer
*
* Description:
* Set DMA transfer config for channel.
*
* Parameters:
* channel - Channel number 0-3
* config - Pointer to the config structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int mpfs_dma_setup_transfer(unsigned int channel,
struct mpfs_dma_channel_config *config)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel");
return -EINVAL;
}
if (config->src_addr == 0)
{
dmawarn("Illegal source address\n");
return -EINVAL;
}
if (config->dest_addr == 0)
{
dmawarn("Illegal destination address\n");
return -EINVAL;
}
/* If transaction is in progress, return error. */
if (getreg32(MPFS_DMA_REG_OFFSET(channel)) & DMA_CONTROL_RUN_MASK)
{
dmawarn("channel busy\n");
return -EBUSY;
}
/* Set or clear the interrupts for the transfer. */
if (config->enable_done_int)
{
dmainfo("enable done irq\n");
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
0, DMA_CONTROL_DONEIE);
}
else
{
dmainfo("disable done irq\n");
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
DMA_CONTROL_DONEIE_MASK, 0);
}
if (config->enable_err_int)
{
dmainfo("enable err irq\n");
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
0, DMA_CONTROL_ERRORIE);
}
else
{
dmainfo("disable err irq\n");
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
DMA_CONTROL_ERRORIE_MASK, 0);
}
/* clear Next registers. */
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
0, DMA_CONTROL_CLAIM);
/* clear Done and Error */
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
DMA_CONTROL_DONE_MASK | DMA_CONTROL_ERROR_MASK, 0);
/* Setup the source and destination addresses. */
putreg64(config->dest_addr,
MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_DESTINATION_OFFSET);
putreg64(config->src_addr,
MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_SOURCE_OFFSET);
/* Set the transfer size. */
putreg64(config->num_bytes,
MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_BYTES_OFFSET);
/* Setup repeat and force order requirements. */
if (config->repeat)
{
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
0, DMA_NEXT_CONFIG_REPEAT);
}
else
{
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
DMA_NEXT_CONFIG_REPEAT_MASK, 0);
}
if (config->force_order)
{
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
0, DMA_NEXT_CONFIG_ORDER);
}
else
{
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
DMA_NEXT_CONFIG_ORDER_MASK, 0);
}
/* Set PDMA transaction size to maximum. */
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
DMA_NEXT_CONFIG_WSIZE_MASK,
DMA_NEXT_CONFIG_WSIZE(g_channel_nextcfg_wsize[channel]));
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_NEXT_CONFIG_OFFSET,
DMA_NEXT_CONFIG_RSIZE_MASK,
DMA_NEXT_CONFIG_RSIZE(g_channel_nextcfg_wsize[channel]));
dmainfodumpbuffer("dma regs", (uint8_t *)MPFS_DMA_REG_OFFSET(channel),
MPFS_DMA_NEXT_SOURCE_OFFSET + 8);
return OK;
}
/****************************************************************************
* Name: mpfs_dma_set_transaction_size
*
* Description:
* Set read and write transaction size for mpfs_dma_setup_transfer()
*
* Parameters:
* channel - Channel number 0-3
* write_size - Write transaction size
* read_size - Read transaction size
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int mpfs_dma_set_transaction_size(unsigned int channel,
uint8_t write_size, uint8_t read_size)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return -EINVAL;
}
if (write_size > DMA_MAX_TRANSACTION_SIZE)
{
dmawarn("Illegal write size\n");
return -EINVAL;
}
if (read_size > DMA_MAX_TRANSACTION_SIZE)
{
dmawarn("Illegal write size\n");
return -EINVAL;
}
dmainfo("new default dma transaction size. channel:%d write:%d, read:%d\n",
channel, write_size, read_size);
g_channel_nextcfg_wsize[channel] = write_size;
g_channel_nextcfg_rsize[channel] = read_size;
return OK;
}
/****************************************************************************
* Name: mpfs_dma_start
*
* Description:
* Start DMA transfer.
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int mpfs_dma_start(unsigned int channel)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return -EINVAL;
}
dmainfo("start dma channel:%d\n", channel);
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
0, DMA_CONTROL_RUN);
return OK;
}
/****************************************************************************
* Name: mpfs_dma_get_transfer_type
*
* Description:
* Return channels active config.
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* 0 - Illegal channel
* bit 2 - Repeat
* bit 3 - Strict ordering
*
****************************************************************************/
uint32_t mpfs_dma_get_transfer_type(unsigned int channel)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
return getreg32(MPFS_DMA_REG_OFFSET(channel) +
MPFS_DMA_EXEC_CONFIG_OFFSET);
}
/****************************************************************************
* Name: mpfs_dma_get_bytes_remaining
*
* Description:
* Return number of bytes remaining on transfer.
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* Number of bytes remaining
*
****************************************************************************/
uint64_t mpfs_dma_get_bytes_remaining(unsigned int channel)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
return getreg64(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_EXEC_BYTES_OFFSET);
}
/****************************************************************************
* Name: mpfs_dma_get_current_dest
*
* Description:
* Return current destination address of transfer
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* Current destination address
*
****************************************************************************/
uint64_t mpfs_dma_get_current_destination(unsigned int channel)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
return getreg64(MPFS_DMA_REG_OFFSET(channel) +
MPFS_DMA_EXEC_DESTINATION_OFFSET);
}
/****************************************************************************
* Name: mpfs_dma_get_current_source
*
* Description:
* Return current sourceaddress of transfer
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* Current source address
*
****************************************************************************/
uint64_t mpfs_dma_get_current_source(unsigned int channel)
{
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
return getreg64(MPFS_DMA_REG_OFFSET(channel) +
MPFS_DMA_EXEC_SOURCE_OFFSET);
}
/****************************************************************************
* Name: mpfs_dma_get_complete_status
*
* Description:
* Return complete status for DMA channel
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* 0 - Not completed
* 1 - Transfer completed
*
****************************************************************************/
int mpfs_dma_get_complete_status(unsigned int channel)
{
uint32_t control;
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
control = getreg32(MPFS_DMA_REG_OFFSET(channel) +
MPFS_DMA_CONTROL_OFFSET);
return ((control & DMA_CONTROL_DONE_MASK) >> DMA_CONTROL_DONE_SHIFT);
}
/****************************************************************************
* Name: mpfs_dma_get_error_status
*
* Description:
* Return error status for DMA channel
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* 0 - No Error
* 1 - Transfer Errror
*
****************************************************************************/
int mpfs_dma_get_error_status(unsigned int channel)
{
uint32_t control;
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
control = getreg32(MPFS_DMA_REG_OFFSET(channel) +
MPFS_DMA_CONTROL_OFFSET);
return ((control & DMA_CONTROL_ERROR_MASK) >> DMA_CONTROL_ERROR_SHIFT);
}
/****************************************************************************
* Name: mpfs_dma_clear_complete_status
*
* Description:
* Clear and return complete status for DMA channel
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* 0 - Not completed
* 1 - Transfer completed
*
****************************************************************************/
int mpfs_dma_clear_complete_status(unsigned int channel)
{
int status = 0;
uint32_t control;
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
control = getreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET);
if (control & DMA_CONTROL_DONE)
{
status = 1;
}
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
DMA_CONTROL_DONE_MASK, 0);
return status;
}
/****************************************************************************
* Name: mpfs_dma_clear_error_status
*
* Description:
* Clear and return error status for DMA channel
*
* Parameters:
* channel - Channel number 0-3
*
* Returned Value:
* 0 - No Error
* 1 - Transfer Errror
*
****************************************************************************/
int mpfs_dma_clear_error_status(unsigned int channel)
{
int status = 0;
uint32_t control;
if (channel >= MPFS_DMA_NUM_CHANNELS)
{
dmawarn("Illegal channel\n");
return 0;
}
control = getreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET);
if (control & DMA_CONTROL_ERROR)
{
status = 1;
}
modifyreg32(MPFS_DMA_REG_OFFSET(channel) + MPFS_DMA_CONTROL_OFFSET,
DMA_CONTROL_ERROR, 0);
return status;
}