diff --git a/hw/dma/esp32c3_gdma.c b/hw/dma/esp32c3_gdma.c new file mode 100644 index 000000000000..e6dfe215024c --- /dev/null +++ b/hw/dma/esp32c3_gdma.c @@ -0,0 +1,806 @@ +/* + * ESP32-C3 GDMA emulation + * + * Copyright (c) 2023 Espressif Systems (Shanghai) Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "sysemu/dma.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/dma/esp32c3_gdma.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" + +#define GDMA_WARNING 0 +#define GDMA_DEBUG 0 + +/** + * Check the header file for more info about this function + */ +int esp32c3_gdma_get_channel_periph(ESP32C3GdmaState *s, GdmaPeripheral periph, int dir) +{ + /* If the state, the peripheral or the direction is invalid, return directly */ + if (s == NULL || + periph > GDMA_LAST || periph == GDMA_RSVD1 || periph == GDMA_RSVD4 || periph == GDMA_RSVD5 || + dir < 0 || dir >= ESP32C3_GDMA_CONF_COUNT) + { + return -1; + } + + /* Check all the channels of the GDMA */ + for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { + /* IN/OUT PERI registers have the same organization, can use any macro */ + if (FIELD_EX32(s->ch_conf[i][dir].peripheral, DMA_IN_PERI_SEL_CH0, PERI_IN_SEL_CH0) == periph) { + return i; + } + } + + return -1; +} + + +/** + * @brief Structure defining how linked lists are represented in hardware for the GDMA module + */ +typedef struct GdmaLinkedList { + union { + struct { + uint32_t size: 12; // Size of the buffer (mainly used in a receive transaction) + uint32_t length: 12; // Number of valid bytes in the buffer. In a transmit, written by software. + // In receive, written by hardware. + uint32_t rsvd_24: 4; // Reserved + uint32_t err_eof: 1; // Set if received data has errors. Used with UHCI0 only. + uint32_t rsvd_29: 1; // Reserved + uint32_t suc_eof: 1; // Set if curent node is the last one (of the list). Set by software in a transmit transaction, + // Set by the hardware in case of a receive transaction. + uint32_t owner: 1; // 0: CPU can access the buffer, 1: GDMA can access the buffer. Cleared automatically + // by hardware in a transmit descriptor. In a receive descriptor, cleared by hardware + // only if GDMA_OUT_AUTO_WRBACK_CHn is set to 1. + }; + uint32_t val; + } config; + uint32_t buf_addr; + uint32_t next_addr; +} GdmaLinkedList; + + +static uint32_t read_addr_word(uint32_t* ptr, uint32_t offset) { + return ptr[offset / sizeof(uint32_t)]; +} + + +static uint64_t esp32c3_gdma_read(void *opaque, hwaddr addr, unsigned int size) +{ + ESP32C3GdmaState *s = ESP32C3_GDMA(opaque); + uint64_t r = 0; + +#if GDMA_DEBUG + info_report("[GDMA] Reading from %08lx (%08lx)\n", addr, r); +#endif + + switch(addr) { + case A_DMA_INT_RAW_CH0 ... A_DMA_INT_CLR_CH2: + r = read_addr_word((uint32_t*) s->ch_int, addr - A_DMA_INT_RAW_CH0); + break; + + case A_DMA_MISC_CONF: + r = s->misc_conf; + break; + + case A_DMA_IN_CONF0_CH0 ... A_DMA_IN_PERI_SEL_CH0: + r = read_addr_word((uint32_t*) &s->ch_conf[0][ESP32C3_GDMA_IN_IDX], addr - A_DMA_IN_CONF0_CH0); + break; + + case A_DMA_OUT_CONF0_CH0 ... A_DMA_OUT_PERI_SEL_CH0: + r = read_addr_word((uint32_t*) &s->ch_conf[0][ESP32C3_GDMA_OUT_IDX], addr - A_DMA_OUT_CONF0_CH0); + break; + + case A_DMA_IN_CONF0_CH1 ... A_DMA_IN_PERI_SEL_CH1: + r = read_addr_word((uint32_t*) &s->ch_conf[1][ESP32C3_GDMA_IN_IDX], addr - A_DMA_IN_CONF0_CH1); + break; + + case A_DMA_OUT_CONF0_CH1 ... A_DMA_OUT_PERI_SEL_CH1: + r = read_addr_word((uint32_t*) &s->ch_conf[1][ESP32C3_GDMA_OUT_IDX], addr - A_DMA_OUT_CONF0_CH1); + break; + + case A_DMA_IN_CONF0_CH2 ... A_DMA_IN_PERI_SEL_CH2: + r = read_addr_word((uint32_t*) &s->ch_conf[2][ESP32C3_GDMA_IN_IDX], addr - A_DMA_IN_CONF0_CH2); + break; + + case A_DMA_OUT_CONF0_CH2 ... A_DMA_OUT_PERI_SEL_CH2: + r = read_addr_word((uint32_t*) &s->ch_conf[2][ESP32C3_GDMA_OUT_IDX], addr - A_DMA_OUT_CONF0_CH2); + break; + + default: +#if GDMA_WARNING + warn_report("[GDMA] Unsupported read to %08lx\n", addr); +#endif + break; + } + + return r; +} + + +/** + * @brief Check whether the new status of any interrupt should trigger an interrupt + * + * @param s GDMA state structure + * @param chan Channel that has just been updated + */ +static void esp32c3_gdma_check_interrupt_status(ESP32C3GdmaState *s, uint32_t chan) +{ + DmaIntState* const int_st = &s->ch_int[chan]; + const uint32_t former = int_st->st; + + /* Calculate the new status and check for any difference */ + int_st->st = int_st->raw & int_st->ena; + + if (former != int_st->st) { + /* If all the status bits became low, lower the IRQ pin, else, raise it */ + qemu_set_irq(s->irq[chan], int_st->st ? 1 : 0); + } +} + + +/** + * @brief Set the status bit for the given channel. If the status triggers an interrupt, the corresponding + * IRQ will be set. +*/ +static void esp32c3_gdma_set_status(ESP32C3GdmaState *s, uint32_t chan, uint32_t mask) +{ + s->ch_int[chan].raw |= mask; + esp32c3_gdma_check_interrupt_status(s, chan); +} + + + +/** + * @brief Clear the status bit for the given channel +*/ +static void esp32c3_gdma_clear_status(ESP32C3GdmaState *s, uint32_t chan, uint32_t mask) +{ + s->ch_int[chan].raw &= ~mask; + esp32c3_gdma_check_interrupt_status(s, chan); +} + + +/** + * @brief Function called when a write to a channel interrupt register is performed + * + * @param s GDMA state structure + * @param chan Index of the channel to be written + * @param reg Offset, in bytes, of the register to modify + * @param value New value for the register + */ +static void esp32c3_gdma_write_int_state(ESP32C3GdmaState *s, uint32_t chan, uint32_t reg, uint32_t value) +{ + switch (reg) { + case offsetof(DmaIntState, ena): + s->ch_int[chan].ena = value; + break; + + case offsetof(DmaIntState, raw): + case offsetof(DmaIntState, clr): + /* Clear the bits that are set to 1, keep the remaining to their original value */ + s->ch_int[chan].raw &= ~value; + break; + + case offsetof(DmaIntState, st): + default: + /* Nothing to do, read-only register, return directly */ + return; + } + + /* Update the status and check if any interrupt needs to occur */ + esp32c3_gdma_check_interrupt_status(s, chan); +} + + +/** + * @brief Function called when a reset FIFO is requested + * + * @param s GDMA state structure + * @param chan Index of the channel + * @param in_out Index of the direction, ESP32C3_GDMA_IN_IDX or ESP32C3_GDMA_OUT_IDX, + * that needs a FIFO reset + */ +static void esp32c3_gdma_reset_fifo(ESP32C3GdmaState *s, uint32_t chan, uint32_t in_out) +{ +#if GDMA_DEBUG + info_report("Resetting FIFO for chan %d, direction: %d\n", chan, in_out); +#endif + /* Set the FIFO empty bit to 1, full bit to 0, and number of bytes of data to 0 */ + s->ch_conf[chan][in_out].status = R_DMA_INFIFO_STATUS_CH0_INFIFO_EMPTY_CH0_MASK; +} + + +/** + * @brief Check that the given 32-bit address is in a given range + * + * @param range_start Start address of range + * @param range_size Size of the SoC DRAM + * @param addr Linked-list node address + * + * @returns true if the node is valid (in DRAM), false else + */ +static inline bool esp32c3_gdma_addr_in_range(uint32_t range_start, uint32_t range_size, uint32_t addr) +{ + return range_start <= addr && addr < (range_start + range_size); +} + + +/** + * @brief Read a descriptor from the guest machine + * + * @param s GDMA state structure + * @param addr Guest machine address + * + * @returns true if the transfer was a success, false else + */ +static bool esp32c3_gdma_read_descr(ESP32C3GdmaState *s, uint32_t addr, GdmaLinkedList* out) +{ + MemTxResult res = dma_memory_read(&s->dma_as, addr, out, sizeof(GdmaLinkedList), MEMTXATTRS_UNSPECIFIED); + return res == MEMTX_OK; +} + +/** + * @brief Write a descriptor to the guest machine + * + * @param s GDMA state structure + * @param addr Guest machine address + * + * @returns true if the transfer was a success, false else + */ +static bool esp32c3_gdma_write_descr(ESP32C3GdmaState *s, uint32_t addr, GdmaLinkedList* in) +{ + MemTxResult res = dma_memory_write(&s->dma_as, addr, in, sizeof(GdmaLinkedList), MEMTXATTRS_UNSPECIFIED); + return res == MEMTX_OK; +} + + + +/** + * @brief Read and write arbitrary data from and to the guest machine + * + * @param s GDMA state structure + * @param addr Guest machine address + * + * @returns true if the transfer was a success, false else + */ +static bool esp32c3_gdma_read_guest(ESP32C3GdmaState *s, uint32_t addr, void* data, uint32_t len) +{ + MemTxResult res = dma_memory_read(&s->dma_as, addr, data, len, MEMTXATTRS_UNSPECIFIED); + return res == MEMTX_OK; +} + +static bool esp32c3_gdma_write_guest(ESP32C3GdmaState *s, uint32_t addr, void* data, uint32_t len) +{ + MemTxResult res = dma_memory_write(&s->dma_as, addr, data, len, MEMTXATTRS_UNSPECIFIED); + return res == MEMTX_OK; +} + + +/** + * @brief Push current node (guest) address in the list of descriptors registers + * + * @param s GDMA state structure + * @param chan Channel to update + * @param chan Direction to update + * @param current New node (guest) address to set as the current + */ +static void esp32c3_gdma_push_descriptor(ESP32C3GdmaState *s, uint32_t chan, uint32_t dir, uint32_t current) +{ + GdmaLinkedList next_node; + uint32_t next = 0; + DmaConfigState* state = &s->ch_conf[chan][dir]; + + /* Assign the current descriptor address to the state register */ + state->state = current & R_DMA_OUT_STATE_CH0_OUTLINK_DSCR_ADDR_CH0_MASK; + + /* On real hardware, if the former address is incorrect, the current address is copied to this + * register. */ + state->bfr_bfr_desc_addr = state->bfr_desc_addr; + /* On real hardware, state->bfr_desc_addr is taken from state->desc_addr, even is `current` is valid */ + state->bfr_desc_addr = state->desc_addr; + + /* Get the next address out of the guest RAM */ + const bool valid = esp32c3_gdma_read_descr(s, current, &next_node); + if (valid) { + next = next_node.next_addr; + } + state->desc_addr = next; +} + + + +/** + * @brief Jump to the next node list and assign it to the given node + * + * @param s GDMA state structure + * @param node Node to get the next neighbor of + * + * @returns true if the next node is valid, false else + */ +static inline bool esp32c3_gdma_next_list_node(ESP32C3GdmaState *s, uint32_t chan, uint32_t dir, GdmaLinkedList* node) +{ + const uint32_t current = node->next_addr; + esp32c3_gdma_push_descriptor(s, chan, dir, current); + return esp32c3_gdma_read_descr(s, current, node); +} + + +/** + * @brief Get the first descriptor to process when a restart is requested. + * We need to get the "next" node of the last one processed, which is in `desc_addr` register + * + * @param s GDMA state structure + * @param chan Channel to restart + * @param dir Direction (INT or OUT) to restart + * @param out Filled with the output guest address + */ +static void esp32c3_gdma_get_restart_buffer(ESP32C3GdmaState *s, uint32_t chan, uint32_t dir, uint32_t* out) +{ + DmaConfigState* state = &s->ch_conf[chan][dir]; + // GdmaLinkedList* list = NULL; + /* The next node to use is taken from state->state's lowest 18 bit. Append it to the DRAM address */ + const uint32_t dram_upper_bits = ESP32C3_GDMA_RAM_ADDR & (~R_DMA_OUT_STATE_CH0_OUTLINK_DSCR_ADDR_CH0_MASK); + const uint32_t guest_addr = dram_upper_bits | FIELD_EX32(state->state, DMA_OUT_STATE_CH0, OUTLINK_DSCR_ADDR_CH0); + + *out = guest_addr; +} + + +/** + * @brief Check if a memory-to-memory transfer can be started and start it if possible + * + * @param s GDMA state structure + * @param chan Index of the channel + */ +static void esp32c3_gdma_check_and_start_mem_transfer(ESP32C3GdmaState *s, uint32_t chan) +{ + DmaConfigState* state = s->ch_conf[chan]; + /* Keep the distinction between start and restart because it influences the first descriptor to process */ + const bool in_start = FIELD_EX32(state[ESP32C3_GDMA_IN_IDX].link, DMA_IN_LINK_CH0, INLINK_START_CH0) ? true : false; + const bool in_restart = FIELD_EX32(state[ESP32C3_GDMA_IN_IDX].link, DMA_IN_LINK_CH0, INLINK_RESTART_CH0) ? true : false; + const bool out_start = FIELD_EX32(state[ESP32C3_GDMA_OUT_IDX].link, DMA_OUT_LINK_CH0, OUTLINK_START_CH0) ? true : false; + const bool out_restart = FIELD_EX32(state[ESP32C3_GDMA_OUT_IDX].link, DMA_OUT_LINK_CH0, OUTLINK_RESTART_CH0) ? true : false; + + /* A memory-to-memory transfer can be started if MEM_TRANS is enabled, OUTLINK_(RE)START is set + * and INLINK_(RE)START is set */ + if (FIELD_EX32(state[ESP32C3_GDMA_IN_IDX].conf0, DMA_IN_CONF0_CH0, MEM_TRANS_EN_CH0) + && (in_start || in_restart) + && (out_start || out_restart)) + { + /* Clear the (RE)START fields, i.e., only keep the link address */ + state[ESP32C3_GDMA_OUT_IDX].link &= R_DMA_OUT_LINK_CH0_OUTLINK_ADDR_CH0_MASK; + state[ESP32C3_GDMA_IN_IDX].link &= R_DMA_IN_LINK_CH0_INLINK_ADDR_CH0_MASK; + /* Same goes for the status */ + esp32c3_gdma_clear_status(s, chan, R_DMA_INT_RAW_CH0_IN_DONE_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_OUT_DONE_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_OUT_EOF_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_IN_SUC_EOF_CH0_INT_RAW_MASK); + + /* Get highest 12 bits of the DRAM address */ + const uint32_t high = (ESP32C3_GDMA_RAM_ADDR >> 20) << 20; + + /* TODO: in an inlink, when burst mode is enabled, size and buffer address must be word-aligned. */ + /* If a start was performed, the first descriptor address to process is in DMA_OUT_LINK_CHn register, + * if a restart was performed, the first buffer is the `next` node of `desc_addr` register */ + uint32_t out_addr = high; + uint32_t in_addr = high; + + if (out_start) { + out_addr |= FIELD_EX32(state[ESP32C3_GDMA_OUT_IDX].link, DMA_OUT_LINK_CH0, OUTLINK_ADDR_CH0); + } else { + esp32c3_gdma_get_restart_buffer(s, chan, ESP32C3_GDMA_OUT_IDX, &out_addr); + } + + if (in_start) { + in_addr |= FIELD_EX32(state[ESP32C3_GDMA_IN_IDX].link, DMA_IN_LINK_CH0, INLINK_ADDR_CH0); + } else { + esp32c3_gdma_get_restart_buffer(s, chan, ESP32C3_GDMA_IN_IDX, &in_addr); + } + + /* Boolean to mark whether we need to check the owner for in and out buffers */ + const bool owner_check_out = FIELD_EX32(state[ESP32C3_GDMA_OUT_IDX].conf1, DMA_OUT_CONF1_CH0, OUT_CHECK_OWNER_CH0); + const bool owner_check_in = FIELD_EX32(state[ESP32C3_GDMA_IN_IDX].conf1, DMA_IN_CONF1_CH0, IN_CHECK_OWNER_CH0); + /* Boolean to mark whether the transmit (out) buffers must have their owner bit cleared here */ + const bool clear_out = FIELD_EX32(state[ESP32C3_GDMA_OUT_IDX].conf0, DMA_OUT_CONF0_CH0, OUT_AUTO_WRBACK_CH0); + + /* Pointer to the lists that will be browsed by the loop below */ + GdmaLinkedList out_list = { 0 }; + GdmaLinkedList in_list = { 0 }; + /* Boolean to mark whether a descriptor error occurred during the transfer */ + bool valid = true; + bool error = false; + + /* Get the content of the descriptor located at guest address out_addr */ + valid = esp32c3_gdma_read_descr(s, out_addr, &out_list); + esp32c3_gdma_push_descriptor(s, chan, ESP32C3_GDMA_OUT_IDX, out_addr); + + /* Check that the address is valid. If the owner must be checked, make sure owner is the DMA controller. + * On the real hardware, both in and out are checked at the same time, so in case of an error, both bits + * are set. Replicate the same behavior here. */ + if ( !valid || (owner_check_out && !out_list.config.owner) ) { + /* In case of an error, go directly to the next node (as the C3 hardware does) */ + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_OUT_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } + + valid = esp32c3_gdma_read_descr(s, in_addr, &in_list); + esp32c3_gdma_push_descriptor(s, chan, ESP32C3_GDMA_IN_IDX, in_addr); + + if ( !valid || (owner_check_in && !in_list.config.owner) ) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_IN_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } + + /* If any of the error bit has been set, return directly */ + if (error) { + return; + } + + /* We can keep track of the total amount of bytes written in order to simulate a more or less + * accurate timing (and an interrupt) */ + int total = 0; + + /* Clear the number of bytes written to the "in" buffer and the owner */ + in_list.config.length = 0; + + /* Number of bytes remaining in the current "out" buffer */ + uint32_t remaining = out_list.config.length; + /* Store the current number of bytes consumed in the "out" buffer */ + uint32_t consumed = 0; + + bool exit_loop = false; + + /* Allocate a temporary buffer big enough to store any descriptor data */ + void* tmp_buffer = g_malloc(4096 * sizeof(uint8_t)); + if (tmp_buffer == NULL) { + error_report("[GDMA] No more memory in host\n"); + return; + } + + while (!exit_loop && !error) { + + /* Calculate the number of bytes to send to the in channel */ + const uint32_t min = MIN(in_list.config.size, out_list.config.length); + + /* Perform the actual copy, for the same reasons as stated above, use the error boolean */ + valid = esp32c3_gdma_read_guest(s, out_list.buf_addr + consumed, tmp_buffer, min); + if (!valid) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_OUT_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } + valid = esp32c3_gdma_write_guest(s, in_list.buf_addr + in_list.config.length, tmp_buffer, min); + if (!valid) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_IN_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } + + /* Update the number of bytes written to the "in" buffer */ + in_list.config.length += min; + consumed += min; + total += min; + + /* Even if we reached the end of the TX descriptor, we still have to update RX descriptors + * and registers, use `exit_loop` instead of break or return */ + /* If we don't have any more bytes in the "out" buffer, we can skip to the next buffer */ + if (remaining == consumed) { + /* Before jumping to the next node, clear the owner bit */ + if (clear_out) { + out_list.config.owner = 0; + /* Write back the modified descriptor, should always be valid */ + valid = esp32c3_gdma_write_descr(s, out_addr, &out_list); + assert(valid); + } + exit_loop = out_list.config.suc_eof ? true : false; + + const uint32_t next_addr = out_list.next_addr; + valid = esp32c3_gdma_next_list_node(s, chan, ESP32C3_GDMA_OUT_IDX, &out_list); + + /* Only check the valid flag and the owner if we don't have to exit the loop*/ + if ( !exit_loop && (!valid || (owner_check_out && !out_list.config.owner)) ) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_OUT_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } else { + /* Update "remaining" with the number of bytes to transfer from the new buffer */ + out_addr = next_addr; + remaining = out_list.config.length; + consumed = 0; + } + } + + + /* If we reached the end of the "node", go to the next one */ + if (in_list.config.size == in_list.config.length) { + + in_list.config.owner = 0; + + /* Write back the IN node to guest RAM */ + valid = esp32c3_gdma_write_descr(s, in_addr, &in_list); + assert(valid); + + /* Check that we do have more "in" buffers, if that's not the case, raise an error.. + * TODO: Check if the behavior is the same as Peripheral-to-Memory transfers, where + * this bit is only used to generate and interrupt. */ + if (!exit_loop && in_list.config.suc_eof) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_IN_DSCR_EMPTY_CH0_INT_RAW_MASK); + error = true; + break; + } + + const uint32_t next_addr = in_list.next_addr; + + /* In the case where the transfer is finished, we should still "push" the next node + * to our descriptors stack, but we should not modify the structure itself as we will + * reset the owner and update the suc_eof flag */ + if (exit_loop) { + esp32c3_gdma_push_descriptor(s, chan, ESP32C3_GDMA_IN_IDX, next_addr); + break; + } + + /* We have to continue the loop, so fetch the next node, it will also update the descriptors stack */ + valid = esp32c3_gdma_next_list_node(s, chan, ESP32C3_GDMA_IN_IDX, &in_list); + + /* Check the validity of the next node if we have to continue the loop (transfer finished) */ + if (!valid || (owner_check_in && !in_list.config.owner)) { + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_IN_DSCR_ERR_CH0_INT_RAW_MASK); + error = true; + } else { + /* Continue the loop normally, next RX descriptor set to current */ + in_list.config.length = 0; + + /* Update the current in guest address */ + in_addr = next_addr; + } + } + + } + + if (!error) { + /* In all cases (error or not), let's set the End-of-list in the receiver */ + in_list.config.suc_eof = 1; + in_list.config.owner = 0; + + /* Write back the previous changes */ + valid = esp32c3_gdma_write_descr(s, in_addr, &in_list); + assert(valid); + + /* And store the EOF RX descriptor GUEST address in the correct register. + * This can be used in the ISR to know which buffer has just been processed. */ + state[ESP32C3_GDMA_IN_IDX].suc_eof_desc_addr = in_addr; + + /* Set the transfer as completed for both the IN and OUT link */ + esp32c3_gdma_set_status(s, chan, R_DMA_INT_RAW_CH0_IN_DONE_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_OUT_DONE_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_OUT_EOF_CH0_INT_RAW_MASK | + R_DMA_INT_RAW_CH0_IN_SUC_EOF_CH0_INT_RAW_MASK); + } + + g_free(tmp_buffer); + } +} + + + +/** + * @brief Check whether the given register offset corresponds to a read-only register + * + * @param offset Offset of the register in DmaConfigState structure, in bytes + * + * @return true is the register is read-only, false else + */ +static bool esp32c3_gdma_register_read_only(uint32_t offset) +{ + return offset >= offsetof(DmaConfigState, state) && offset < offsetof(DmaConfigState, priority); +} + + +/** + * @brief Function called when a configuration register was written to + * + * @param s GDMA state structure + * @param chan Index of the channel to be written + * @param in_out Index of the sub-channel, which is ESP32C3_GDMA_IN_IDX or ESP32C3_GDMA_OUT_IDX + * @param addr_in_block Address in bytes of the register being written to in the block + * @param value 32-bit value being written to the register + */ +static void esp32c3_gdma_write_chan_conf(ESP32C3GdmaState *s, uint32_t chan, uint32_t in_out, + uint32_t addr_in_block, uint32_t value) +{ + DmaConfigState* state = &s->ch_conf[chan][in_out]; + uint32_t* const reg_addr = (uint32_t*) ((uint8_t*) state + addr_in_block); + + /* Dereference the former value of the register being written to */ + const uint32_t former = *reg_addr; + + /* Write the new value to the register if not read-only! */ + if (!esp32c3_gdma_register_read_only(addr_in_block)) { + *reg_addr = value; + } + + /* We will only support a subset of GDMA registers for now. To add support for more registers, + * the following snippet can be update */ + uint32_t start_mask = 0; + uint32_t restart_mask = 0; + switch(addr_in_block) { + + /* No matter the channel and in/out direction, the registers are organized the same way, + * so we can use the macros for any channel */ + case offsetof(DmaConfigState, conf0): + /* Check the reset bit, call the reset function on negative edge */ + if (FIELD_EX32(value, DMA_IN_CONF0_CH0, IN_RST_CH0) == 0 && + FIELD_EX32(former, DMA_IN_CONF0_CH0, IN_RST_CH0) != 0) + { + esp32c3_gdma_reset_fifo(s, chan, in_out); + } + /* Check if memory transfer has just been enabled (only valid for IN channels) */ + if (in_out == ESP32C3_GDMA_IN_IDX && + FIELD_EX32(value, DMA_IN_CONF0_CH0, MEM_TRANS_EN_CH0)) + { + esp32c3_gdma_check_and_start_mem_transfer(s, chan); + } + break; + + case offsetof(DmaConfigState, link): + /* For IN and OUT, the START bit is not at the same offset, so we need to test both separately */ + start_mask = ESP32C3_GDMA_IN_IDX ? R_DMA_IN_LINK_CH0_INLINK_START_CH0_MASK : R_DMA_OUT_LINK_CH0_OUTLINK_START_CH0_MASK; + restart_mask = ESP32C3_GDMA_IN_IDX ? R_DMA_IN_LINK_CH0_INLINK_RESTART_CH0_MASK : R_DMA_OUT_LINK_CH0_OUTLINK_RESTART_CH0_MASK; + + /* Check if any of the previous two bits has just been enabled */ + if ((value & start_mask) || (value & restart_mask)) + { + esp32c3_gdma_check_and_start_mem_transfer(s, chan); + } + + default: + break; + } +} + + +static void esp32c3_gdma_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + ESP32C3GdmaState *s = ESP32C3_GDMA(opaque); + +#if GDMA_DEBUG + info_report("[GDMA] Writing to %08lx (%08lx)\n", addr, value); +#endif + + switch(addr) { + case A_DMA_INT_RAW_CH0 ... A_DMA_INT_CLR_CH2: + esp32c3_gdma_write_int_state(s, + addr / ESP32C3_GDMA_INT_REGS_SIZE, + addr % ESP32C3_GDMA_INT_REGS_SIZE, + value); + break; + + case A_DMA_MISC_CONF: + s->misc_conf = value; + break; + + case A_DMA_IN_CONF0_CH0 ... A_DMA_IN_PERI_SEL_CH0: + esp32c3_gdma_write_chan_conf(s, 0, ESP32C3_GDMA_IN_IDX, addr - A_DMA_IN_CONF0_CH0, value); + break; + + case A_DMA_OUT_CONF0_CH0 ... A_DMA_OUT_PERI_SEL_CH0: + esp32c3_gdma_write_chan_conf(s, 0, ESP32C3_GDMA_OUT_IDX, addr - A_DMA_OUT_CONF0_CH0, value); + break; + + case A_DMA_IN_CONF0_CH1 ... A_DMA_IN_PERI_SEL_CH1: + esp32c3_gdma_write_chan_conf(s, 1, ESP32C3_GDMA_IN_IDX, addr - A_DMA_IN_CONF0_CH1, value); + break; + + case A_DMA_OUT_CONF0_CH1 ... A_DMA_OUT_PERI_SEL_CH1: + esp32c3_gdma_write_chan_conf(s, 1, ESP32C3_GDMA_OUT_IDX, addr - A_DMA_OUT_CONF0_CH1, value); + break; + + case A_DMA_IN_CONF0_CH2 ... A_DMA_IN_PERI_SEL_CH2: + esp32c3_gdma_write_chan_conf(s, 2, ESP32C3_GDMA_IN_IDX, addr - A_DMA_IN_CONF0_CH2, value); + break; + + case A_DMA_OUT_CONF0_CH2 ... A_DMA_OUT_PERI_SEL_CH2: + esp32c3_gdma_write_chan_conf(s, 2, ESP32C3_GDMA_OUT_IDX, addr - A_DMA_OUT_CONF0_CH2, value); + break; + + default: +#if GDMA_WARNING + warn_report("[GDMA] Unsupported write to %08lx (%08lx)\n", addr, value); +#endif + break; + } + +} + +static const MemoryRegionOps esp32c3_gdma_ops = { + .read = esp32c3_gdma_read, + .write = esp32c3_gdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + + +static Property esp32c3_gdma_properties[] = { + DEFINE_PROP_LINK("soc_mr", ESP32C3GdmaState, soc_mr, TYPE_MEMORY_REGION, MemoryRegion*), + DEFINE_PROP_END_OF_LIST(), +}; + + +static void esp32c3_gdma_reset(DeviceState *dev) +{ + ESP32C3GdmaState *s = ESP32C3_GDMA(dev); + memset(s->ch_int, 0, sizeof(s->ch_int)); + memset(s->ch_conf, 0, sizeof(s->ch_conf)); + s->misc_conf = 0; + + /* Set the FIFOs as empty */ + for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { + qemu_irq_lower(s->irq[i]); + + for (int j = 0; j < ESP32C3_GDMA_CONF_COUNT; j++) { + esp32c3_gdma_reset_fifo(s, i, j); + } + } + +} + + +static void esp32c3_gdma_realize(DeviceState *dev, Error **errp) +{ + ESP32C3GdmaState *s = ESP32C3_GDMA(dev); + + /* Make sure the DRAM MemoryRegion was set */ + assert(s->soc_mr != NULL); + + address_space_init(&s->dma_as, s->soc_mr, "esp32c3.gdma"); +} + + +static void esp32c3_gdma_init(Object *obj) +{ + ESP32C3GdmaState *s = ESP32C3_GDMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32c3_gdma_ops, s, + TYPE_ESP32C3_GDMA, ESP32C3_GDMA_REGS_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + + esp32c3_gdma_reset((DeviceState*) s); +} + + +static void esp32c3_gdma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = esp32c3_gdma_reset; + dc->realize = esp32c3_gdma_realize; + device_class_set_props(dc, esp32c3_gdma_properties); +} + +static const TypeInfo esp32c3_gdma_info = { + .name = TYPE_ESP32C3_GDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ESP32C3GdmaState), + .instance_init = esp32c3_gdma_init, + .class_init = esp32c3_gdma_class_init +}; + +static void esp32c3_gdma_register_types(void) +{ + type_register_static(&esp32c3_gdma_info); +} + +type_init(esp32c3_gdma_register_types) diff --git a/hw/dma/meson.build b/hw/dma/meson.build index f3f0661bc3c3..925f3cc2bdc5 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -14,3 +14,4 @@ softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) softmmu_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +softmmu_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3_gdma.c')) diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index 8bd7d6108e8b..bea0f720ec20 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -41,6 +41,7 @@ #include "hw/misc/esp32c3_rtc_cntl.h" #include "hw/misc/esp32c3_aes.h" #include "hw/misc/esp32c3_jtag.h" +#include "hw/dma/esp32c3_gdma.h" #define ESP32C3_IO_WARNING 0 @@ -66,6 +67,7 @@ struct Esp32C3MachineState { ESP32C3CacheState cache; ESP32C3EfuseState efuse; ESP32C3ClockState clock; + ESP32C3GdmaState gdma; ESP32C3AesState aes; ESP32C3ShaState sha; ESP32C3TimgState timg[2]; @@ -171,6 +173,7 @@ static void esp32c3_cpu_reset(void* opaque, int n, int level) device_cold_reset(DEVICE(&s->cache)); device_cold_reset(DEVICE(&s->efuse)); device_cold_reset(DEVICE(&s->clock)); + device_cold_reset(DEVICE(&s->gdma)); device_cold_reset(DEVICE(&s->aes)); device_cold_reset(DEVICE(&s->sha)); device_cold_reset(DEVICE(&s->systimer)); @@ -313,6 +316,7 @@ static void esp32c3_machine_init(MachineState *machine) object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK); object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA); object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES); + object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA); object_initialize_child(OBJECT(machine), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG); object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], TYPE_ESP32C3_TIMG); object_initialize_child(OBJECT(machine), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER); @@ -469,6 +473,20 @@ static void esp32c3_machine_init(MachineState *machine) } } + /* GDMA Realization */ + { + object_property_set_link(OBJECT(&ms->gdma), "soc_mr", OBJECT(dram), &error_abort); + qdev_realize(DEVICE(&ms->gdma), &ms->periph_bus, &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->gdma), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_GDMA_BASE, mr, 0); + /* Connect the IRQs to the Interrupt Matrix */ + for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->gdma), i, + qdev_get_gpio_in(intmatrix_dev, ETS_DMA_CH0_INTR_SOURCE + i)); + } + + } + /* Open and load the "bios", which is the ROM binary, also named "first stage bootloader" */ char *rom_binary = qemu_find_file(QEMU_FILE_TYPE_BIOS, "esp32c3-rom.bin"); if (rom_binary == NULL) { diff --git a/include/hw/dma/esp32c3_gdma.h b/include/hw/dma/esp32c3_gdma.h new file mode 100644 index 000000000000..c3e81cf1294b --- /dev/null +++ b/include/hw/dma/esp32c3_gdma.h @@ -0,0 +1,647 @@ +/* + * ESP32-C3 GDMA emulation + * + * Copyright (c) 2023 Espressif Systems (Shanghai) Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#pragma once + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/esp32c3_reg.h" + +#define TYPE_ESP32C3_GDMA "esp32c3.gdma" +#define ESP32C3_GDMA(obj) OBJECT_CHECK(ESP32C3GdmaState, (obj), TYPE_ESP32C3_GDMA) + +#define ESP32C3_GDMA_REGS_SIZE (A_DMA_OUT_PERI_SEL_CH2 + 4) + +#define ESP32C3_GDMA_CHANNEL_COUNT 3 + +#define ESP32C3_GDMA_IN_IDX 0 +#define ESP32C3_GDMA_OUT_IDX 1 +#define ESP32C3_GDMA_CONF_COUNT (ESP32C3_GDMA_OUT_IDX + 1) + +#define ESP32C3_GDMA_RAM_ADDR 0x3FC80000 + + +/** + * @brief Number for each peripheral that can access GDMA + */ +typedef enum { + GDMA_SPI2 = 0, + GDMA_RSVD1 = 1, + GDMA_UHCI0 = 2, + GDMA_I2S = 3, + GDMA_RSVD4 = 4, + GDMA_RSVD5 = 5, + GDMA_AES = 6, + GDMA_SHA = 7, + GDMA_ADC = 8, + GDMA_LAST = GDMA_ADC, +} GdmaPeripheral; + + +/** + * Size of the interrupt registers, in bytes, for a single channel + */ +#define ESP32C3_GDMA_INT_REGS_SIZE 0x10 + +typedef struct { + uint32_t raw; + uint32_t st; + uint32_t ena; + /* Not really necessary to have this variable here as it will never contain + * any data, but will simplify the code (offset calculation) */ + uint32_t clr; +} DmaIntState; + + +typedef struct { + uint32_t conf0; + uint32_t conf1; + uint32_t status; + uint32_t push_pop; + uint32_t link; + /* Status registers */ + uint32_t state; + uint32_t suc_eof_desc_addr; // Address of descriptor when EOF bit is 1 + uint32_t err_eof_desc_addr; // Address of descriptor when error occurs (UHCI0 only) + uint32_t desc_addr; // Address of the next descriptor (n + 1) + uint32_t bfr_desc_addr; // Address of the current descriptor (n) + uint32_t bfr_bfr_desc_addr; // Address of the previous descriptor (n - 1) + uint32_t priority; + uint32_t peripheral; +} DmaConfigState; + + +typedef struct ESP32C3GdmaState { + SysBusDevice parent_object; + MemoryRegion iomem; + qemu_irq irq[ESP32C3_GDMA_CHANNEL_COUNT]; + + DmaIntState ch_int[ESP32C3_GDMA_CHANNEL_COUNT]; + DmaConfigState ch_conf[ESP32C3_GDMA_CHANNEL_COUNT][ESP32C3_GDMA_CONF_COUNT]; + + /* Use this register mainly for enabling and disabling priorities */ + uint32_t misc_conf; + + /* Keep a pointer to the SoC DRAM */ + MemoryRegion* soc_mr; + AddressSpace dma_as; + +} ESP32C3GdmaState; + + +/** + * @brief Get the channel configured for the given peripheral + * + * @param s GDMA state + * @param periph Peripheral to search + * @param dir Direction: ESP32C3_GDMA_IN_IDX or ESP32C3_GDMA_OUT_IDX + * + * @returns index of the GDMA channel bound to the peripheral, -1 if not found + */ +int esp32c3_gdma_get_channel_periph(ESP32C3GdmaState *s, GdmaPeripheral periph, int dir); + + +REG32(DMA_INT_RAW_CH0, 0x000) + FIELD(DMA_INT_RAW_CH0, OUTFIFO_UDF_CH0_INT_RAW, 12, 1) + FIELD(DMA_INT_RAW_CH0, OUTFIFO_OVF_CH0_INT_RAW, 11, 1) + FIELD(DMA_INT_RAW_CH0, INFIFO_UDF_CH0_INT_RAW, 10, 1) + FIELD(DMA_INT_RAW_CH0, INFIFO_OVF_CH0_INT_RAW, 9, 1) + FIELD(DMA_INT_RAW_CH0, OUT_TOTAL_EOF_CH0_INT_RAW, 8, 1) + FIELD(DMA_INT_RAW_CH0, IN_DSCR_EMPTY_CH0_INT_RAW, 7, 1) + FIELD(DMA_INT_RAW_CH0, OUT_DSCR_ERR_CH0_INT_RAW, 6, 1) + FIELD(DMA_INT_RAW_CH0, IN_DSCR_ERR_CH0_INT_RAW, 5, 1) + FIELD(DMA_INT_RAW_CH0, OUT_EOF_CH0_INT_RAW, 4, 1) + FIELD(DMA_INT_RAW_CH0, OUT_DONE_CH0_INT_RAW, 3, 1) + FIELD(DMA_INT_RAW_CH0, IN_ERR_EOF_CH0_INT_RAW, 2, 1) + FIELD(DMA_INT_RAW_CH0, IN_SUC_EOF_CH0_INT_RAW, 1, 1) + FIELD(DMA_INT_RAW_CH0, IN_DONE_CH0_INT_RAW, 0, 1) + +REG32(DMA_INT_ST_CH0, 0x004) + FIELD(DMA_INT_ST_CH0, OUTFIFO_UDF_CH0_INT_ST, 12, 1) + FIELD(DMA_INT_ST_CH0, OUTFIFO_OVF_CH0_INT_ST, 11, 1) + FIELD(DMA_INT_ST_CH0, INFIFO_UDF_CH0_INT_ST, 10, 1) + FIELD(DMA_INT_ST_CH0, INFIFO_OVF_CH0_INT_ST, 9, 1) + FIELD(DMA_INT_ST_CH0, OUT_TOTAL_EOF_CH0_INT_ST, 8, 1) + FIELD(DMA_INT_ST_CH0, IN_DSCR_EMPTY_CH0_INT_ST, 7, 1) + FIELD(DMA_INT_ST_CH0, OUT_DSCR_ERR_CH0_INT_ST, 6, 1) + FIELD(DMA_INT_ST_CH0, IN_DSCR_ERR_CH0_INT_ST, 5, 1) + FIELD(DMA_INT_ST_CH0, OUT_EOF_CH0_INT_ST, 4, 1) + FIELD(DMA_INT_ST_CH0, OUT_DONE_CH0_INT_ST, 3, 1) + FIELD(DMA_INT_ST_CH0, IN_ERR_EOF_CH0_INT_ST, 2, 1) + FIELD(DMA_INT_ST_CH0, IN_SUC_EOF_CH0_INT_ST, 1, 1) + FIELD(DMA_INT_ST_CH0, IN_DONE_CH0_INT_ST, 0, 1) + +REG32(DMA_INT_ENA_CH0, 0x008) + FIELD(DMA_INT_ENA_CH0, OUTFIFO_UDF_CH0_INT_ENA, 12, 1) + FIELD(DMA_INT_ENA_CH0, OUTFIFO_OVF_CH0_INT_ENA, 11, 1) + FIELD(DMA_INT_ENA_CH0, INFIFO_UDF_CH0_INT_ENA, 10, 1) + FIELD(DMA_INT_ENA_CH0, INFIFO_OVF_CH0_INT_ENA, 9, 1) + FIELD(DMA_INT_ENA_CH0, OUT_TOTAL_EOF_CH0_INT_ENA, 8, 1) + FIELD(DMA_INT_ENA_CH0, IN_DSCR_EMPTY_CH0_INT_ENA, 7, 1) + FIELD(DMA_INT_ENA_CH0, OUT_DSCR_ERR_CH0_INT_ENA, 6, 1) + FIELD(DMA_INT_ENA_CH0, IN_DSCR_ERR_CH0_INT_ENA, 5, 1) + FIELD(DMA_INT_ENA_CH0, OUT_EOF_CH0_INT_ENA, 4, 1) + FIELD(DMA_INT_ENA_CH0, OUT_DONE_CH0_INT_ENA, 3, 1) + FIELD(DMA_INT_ENA_CH0, IN_ERR_EOF_CH0_INT_ENA, 2, 1) + FIELD(DMA_INT_ENA_CH0, IN_SUC_EOF_CH0_INT_ENA, 1, 1) + FIELD(DMA_INT_ENA_CH0, IN_DONE_CH0_INT_ENA, 0, 1) + +REG32(DMA_INT_CLR_CH0, 0x00C) + FIELD(DMA_INT_CLR_CH0, OUTFIFO_UDF_CH0_INT_CLR, 12, 1) + FIELD(DMA_INT_CLR_CH0, OUTFIFO_OVF_CH0_INT_CLR, 11, 1) + FIELD(DMA_INT_CLR_CH0, INFIFO_UDF_CH0_INT_CLR, 10, 1) + FIELD(DMA_INT_CLR_CH0, INFIFO_OVF_CH0_INT_CLR, 9, 1) + FIELD(DMA_INT_CLR_CH0, OUT_TOTAL_EOF_CH0_INT_CLR, 8, 1) + FIELD(DMA_INT_CLR_CH0, IN_DSCR_EMPTY_CH0_INT_CLR, 7, 1) + FIELD(DMA_INT_CLR_CH0, OUT_DSCR_ERR_CH0_INT_CLR, 6, 1) + FIELD(DMA_INT_CLR_CH0, IN_DSCR_ERR_CH0_INT_CLR, 5, 1) + FIELD(DMA_INT_CLR_CH0, OUT_EOF_CH0_INT_CLR, 4, 1) + FIELD(DMA_INT_CLR_CH0, OUT_DONE_CH0_INT_CLR, 3, 1) + FIELD(DMA_INT_CLR_CH0, IN_ERR_EOF_CH0_INT_CLR, 2, 1) + FIELD(DMA_INT_CLR_CH0, IN_SUC_EOF_CH0_INT_CLR, 1, 1) + FIELD(DMA_INT_CLR_CH0, IN_DONE_CH0_INT_CLR, 0, 1) + +REG32(DMA_INT_RAW_CH1, 0x010) + FIELD(DMA_INT_RAW_CH1, OUTFIFO_UDF_CH1_INT_RAW, 12, 1) + FIELD(DMA_INT_RAW_CH1, OUTFIFO_OVF_CH1_INT_RAW, 11, 1) + FIELD(DMA_INT_RAW_CH1, INFIFO_UDF_CH1_INT_RAW, 10, 1) + FIELD(DMA_INT_RAW_CH1, INFIFO_OVF_CH1_INT_RAW, 9, 1) + FIELD(DMA_INT_RAW_CH1, OUT_TOTAL_EOF_CH1_INT_RAW, 8, 1) + FIELD(DMA_INT_RAW_CH1, IN_DSCR_EMPTY_CH1_INT_RAW, 7, 1) + FIELD(DMA_INT_RAW_CH1, OUT_DSCR_ERR_CH1_INT_RAW, 6, 1) + FIELD(DMA_INT_RAW_CH1, IN_DSCR_ERR_CH1_INT_RAW, 5, 1) + FIELD(DMA_INT_RAW_CH1, OUT_EOF_CH1_INT_RAW, 4, 1) + FIELD(DMA_INT_RAW_CH1, OUT_DONE_CH1_INT_RAW, 3, 1) + FIELD(DMA_INT_RAW_CH1, IN_ERR_EOF_CH1_INT_RAW, 2, 1) + FIELD(DMA_INT_RAW_CH1, IN_SUC_EOF_CH1_INT_RAW, 1, 1) + FIELD(DMA_INT_RAW_CH1, IN_DONE_CH1_INT_RAW, 0, 1) + +REG32(DMA_INT_ST_CH1, 0x014) + FIELD(DMA_INT_ST_CH1, OUTFIFO_UDF_CH1_INT_ST, 12, 1) + FIELD(DMA_INT_ST_CH1, OUTFIFO_OVF_CH1_INT_ST, 11, 1) + FIELD(DMA_INT_ST_CH1, INFIFO_UDF_CH1_INT_ST, 10, 1) + FIELD(DMA_INT_ST_CH1, INFIFO_OVF_CH1_INT_ST, 9, 1) + FIELD(DMA_INT_ST_CH1, OUT_TOTAL_EOF_CH1_INT_ST, 8, 1) + FIELD(DMA_INT_ST_CH1, IN_DSCR_EMPTY_CH1_INT_ST, 7, 1) + FIELD(DMA_INT_ST_CH1, OUT_DSCR_ERR_CH1_INT_ST, 6, 1) + FIELD(DMA_INT_ST_CH1, IN_DSCR_ERR_CH1_INT_ST, 5, 1) + FIELD(DMA_INT_ST_CH1, OUT_EOF_CH1_INT_ST, 4, 1) + FIELD(DMA_INT_ST_CH1, OUT_DONE_CH1_INT_ST, 3, 1) + FIELD(DMA_INT_ST_CH1, IN_ERR_EOF_CH1_INT_ST, 2, 1) + FIELD(DMA_INT_ST_CH1, IN_SUC_EOF_CH1_INT_ST, 1, 1) + FIELD(DMA_INT_ST_CH1, IN_DONE_CH1_INT_ST, 0, 1) + +REG32(DMA_INT_ENA_CH1, 0x018) + FIELD(DMA_INT_ENA_CH1, OUTFIFO_UDF_CH1_INT_ENA, 12, 1) + FIELD(DMA_INT_ENA_CH1, OUTFIFO_OVF_CH1_INT_ENA, 11, 1) + FIELD(DMA_INT_ENA_CH1, INFIFO_UDF_CH1_INT_ENA, 10, 1) + FIELD(DMA_INT_ENA_CH1, INFIFO_OVF_CH1_INT_ENA, 9, 1) + FIELD(DMA_INT_ENA_CH1, OUT_TOTAL_EOF_CH1_INT_ENA, 8, 1) + FIELD(DMA_INT_ENA_CH1, IN_DSCR_EMPTY_CH1_INT_ENA, 7, 1) + FIELD(DMA_INT_ENA_CH1, OUT_DSCR_ERR_CH1_INT_ENA, 6, 1) + FIELD(DMA_INT_ENA_CH1, IN_DSCR_ERR_CH1_INT_ENA, 5, 1) + FIELD(DMA_INT_ENA_CH1, OUT_EOF_CH1_INT_ENA, 4, 1) + FIELD(DMA_INT_ENA_CH1, OUT_DONE_CH1_INT_ENA, 3, 1) + FIELD(DMA_INT_ENA_CH1, IN_ERR_EOF_CH1_INT_ENA, 2, 1) + FIELD(DMA_INT_ENA_CH1, IN_SUC_EOF_CH1_INT_ENA, 1, 1) + FIELD(DMA_INT_ENA_CH1, IN_DONE_CH1_INT_ENA, 0, 1) + +REG32(DMA_INT_CLR_CH1, 0x01C) + FIELD(DMA_INT_CLR_CH1, OUTFIFO_UDF_CH1_INT_CLR, 12, 1) + FIELD(DMA_INT_CLR_CH1, OUTFIFO_OVF_CH1_INT_CLR, 11, 1) + FIELD(DMA_INT_CLR_CH1, INFIFO_UDF_CH1_INT_CLR, 10, 1) + FIELD(DMA_INT_CLR_CH1, INFIFO_OVF_CH1_INT_CLR, 9, 1) + FIELD(DMA_INT_CLR_CH1, OUT_TOTAL_EOF_CH1_INT_CLR, 8, 1) + FIELD(DMA_INT_CLR_CH1, IN_DSCR_EMPTY_CH1_INT_CLR, 7, 1) + FIELD(DMA_INT_CLR_CH1, OUT_DSCR_ERR_CH1_INT_CLR, 6, 1) + FIELD(DMA_INT_CLR_CH1, IN_DSCR_ERR_CH1_INT_CLR, 5, 1) + FIELD(DMA_INT_CLR_CH1, OUT_EOF_CH1_INT_CLR, 4, 1) + FIELD(DMA_INT_CLR_CH1, OUT_DONE_CH1_INT_CLR, 3, 1) + FIELD(DMA_INT_CLR_CH1, IN_ERR_EOF_CH1_INT_CLR, 2, 1) + FIELD(DMA_INT_CLR_CH1, IN_SUC_EOF_CH1_INT_CLR, 1, 1) + FIELD(DMA_INT_CLR_CH1, IN_DONE_CH1_INT_CLR, 0, 1) + +REG32(DMA_INT_RAW_CH2, 0x020) + FIELD(DMA_INT_RAW_CH2, OUTFIFO_UDF_CH2_INT_RAW, 12, 1) + FIELD(DMA_INT_RAW_CH2, OUTFIFO_OVF_CH2_INT_RAW, 11, 1) + FIELD(DMA_INT_RAW_CH2, INFIFO_UDF_CH2_INT_RAW, 10, 1) + FIELD(DMA_INT_RAW_CH2, INFIFO_OVF_CH2_INT_RAW, 9, 1) + FIELD(DMA_INT_RAW_CH2, OUT_TOTAL_EOF_CH2_INT_RAW, 8, 1) + FIELD(DMA_INT_RAW_CH2, IN_DSCR_EMPTY_CH2_INT_RAW, 7, 1) + FIELD(DMA_INT_RAW_CH2, OUT_DSCR_ERR_CH2_INT_RAW, 6, 1) + FIELD(DMA_INT_RAW_CH2, IN_DSCR_ERR_CH2_INT_RAW, 5, 1) + FIELD(DMA_INT_RAW_CH2, OUT_EOF_CH2_INT_RAW, 4, 1) + FIELD(DMA_INT_RAW_CH2, OUT_DONE_CH2_INT_RAW, 3, 1) + FIELD(DMA_INT_RAW_CH2, IN_ERR_EOF_CH2_INT_RAW, 2, 1) + FIELD(DMA_INT_RAW_CH2, IN_SUC_EOF_CH2_INT_RAW, 1, 1) + FIELD(DMA_INT_RAW_CH2, IN_DONE_CH2_INT_RAW, 0, 1) + +REG32(DMA_INT_ST_CH2, 0x024) + FIELD(DMA_INT_ST_CH2, OUTFIFO_UDF_CH2_INT_ST, 12, 1) + FIELD(DMA_INT_ST_CH2, OUTFIFO_OVF_CH2_INT_ST, 11, 1) + FIELD(DMA_INT_ST_CH2, INFIFO_UDF_CH2_INT_ST, 10, 1) + FIELD(DMA_INT_ST_CH2, INFIFO_OVF_CH2_INT_ST, 9, 1) + FIELD(DMA_INT_ST_CH2, OUT_TOTAL_EOF_CH2_INT_ST, 8, 1) + FIELD(DMA_INT_ST_CH2, IN_DSCR_EMPTY_CH2_INT_ST, 7, 1) + FIELD(DMA_INT_ST_CH2, OUT_DSCR_ERR_CH2_INT_ST, 6, 1) + FIELD(DMA_INT_ST_CH2, IN_DSCR_ERR_CH2_INT_ST, 5, 1) + FIELD(DMA_INT_ST_CH2, OUT_EOF_CH2_INT_ST, 4, 1) + FIELD(DMA_INT_ST_CH2, OUT_DONE_CH2_INT_ST, 3, 1) + FIELD(DMA_INT_ST_CH2, IN_ERR_EOF_CH2_INT_ST, 2, 1) + FIELD(DMA_INT_ST_CH2, IN_SUC_EOF_CH2_INT_ST, 1, 1) + FIELD(DMA_INT_ST_CH2, IN_DONE_CH2_INT_ST, 0, 1) + +REG32(DMA_INT_ENA_CH2, 0x028) + FIELD(DMA_INT_ENA_CH2, OUTFIFO_UDF_CH2_INT_ENA, 12, 1) + FIELD(DMA_INT_ENA_CH2, OUTFIFO_OVF_CH2_INT_ENA, 11, 1) + FIELD(DMA_INT_ENA_CH2, INFIFO_UDF_CH2_INT_ENA, 10, 1) + FIELD(DMA_INT_ENA_CH2, INFIFO_OVF_CH2_INT_ENA, 9, 1) + FIELD(DMA_INT_ENA_CH2, OUT_TOTAL_EOF_CH2_INT_ENA, 8, 1) + FIELD(DMA_INT_ENA_CH2, IN_DSCR_EMPTY_CH2_INT_ENA, 7, 1) + FIELD(DMA_INT_ENA_CH2, OUT_DSCR_ERR_CH2_INT_ENA, 6, 1) + FIELD(DMA_INT_ENA_CH2, IN_DSCR_ERR_CH2_INT_ENA, 5, 1) + FIELD(DMA_INT_ENA_CH2, OUT_EOF_CH2_INT_ENA, 4, 1) + FIELD(DMA_INT_ENA_CH2, OUT_DONE_CH2_INT_ENA, 3, 1) + FIELD(DMA_INT_ENA_CH2, IN_ERR_EOF_CH2_INT_ENA, 2, 1) + FIELD(DMA_INT_ENA_CH2, IN_SUC_EOF_CH2_INT_ENA, 1, 1) + FIELD(DMA_INT_ENA_CH2, IN_DONE_CH2_INT_ENA, 0, 1) + +REG32(DMA_INT_CLR_CH2, 0x02C) + FIELD(DMA_INT_CLR_CH2, OUTFIFO_UDF_CH2_INT_CLR, 12, 1) + FIELD(DMA_INT_CLR_CH2, OUTFIFO_OVF_CH2_INT_CLR, 11, 1) + FIELD(DMA_INT_CLR_CH2, INFIFO_UDF_CH2_INT_CLR, 10, 1) + FIELD(DMA_INT_CLR_CH2, INFIFO_OVF_CH2_INT_CLR, 9, 1) + FIELD(DMA_INT_CLR_CH2, OUT_TOTAL_EOF_CH2_INT_CLR, 8, 1) + FIELD(DMA_INT_CLR_CH2, IN_DSCR_EMPTY_CH2_INT_CLR, 7, 1) + FIELD(DMA_INT_CLR_CH2, OUT_DSCR_ERR_CH2_INT_CLR, 6, 1) + FIELD(DMA_INT_CLR_CH2, IN_DSCR_ERR_CH2_INT_CLR, 5, 1) + FIELD(DMA_INT_CLR_CH2, OUT_EOF_CH2_INT_CLR, 4, 1) + FIELD(DMA_INT_CLR_CH2, OUT_DONE_CH2_INT_CLR, 3, 1) + FIELD(DMA_INT_CLR_CH2, IN_ERR_EOF_CH2_INT_CLR, 2, 1) + FIELD(DMA_INT_CLR_CH2, IN_SUC_EOF_CH2_INT_CLR, 1, 1) + FIELD(DMA_INT_CLR_CH2, IN_DONE_CH2_INT_CLR, 0, 1) + +REG32(DMA_AHB_TEST, 0x040) + FIELD(DMA_AHB_TEST, AHB_TESTADDR, 4, 2) + FIELD(DMA_AHB_TEST, AHB_TESTMODE, 0, 3) + +REG32(DMA_MISC_CONF, 0x044) + FIELD(DMA_MISC_CONF, CLK_EN, 3, 1) + FIELD(DMA_MISC_CONF, ARB_PRI_DIS, 2, 1) + FIELD(DMA_MISC_CONF, AHBM_RST_INTER, 0, 1) + +REG32(DMA_DATE, 0x048) + FIELD(DMA_DATE, DATE, 0, 32) + +REG32(DMA_IN_CONF0_CH0, 0x070) + FIELD(DMA_IN_CONF0_CH0, MEM_TRANS_EN_CH0, 4, 1) + FIELD(DMA_IN_CONF0_CH0, IN_DATA_BURST_EN_CH0, 3, 1) + FIELD(DMA_IN_CONF0_CH0, INDSCR_BURST_EN_CH0, 2, 1) + FIELD(DMA_IN_CONF0_CH0, IN_LOOP_TEST_CH0, 1, 1) + FIELD(DMA_IN_CONF0_CH0, IN_RST_CH0, 0, 1) + +REG32(DMA_IN_CONF1_CH0, 0x074) + FIELD(DMA_IN_CONF1_CH0, IN_CHECK_OWNER_CH0, 12, 1) + +REG32(DMA_INFIFO_STATUS_CH0, 0x078) + FIELD(DMA_INFIFO_STATUS_CH0, IN_BUF_HUNGRY_CH0, 27, 1) + FIELD(DMA_INFIFO_STATUS_CH0, IN_REMAIN_UNDER_4B_CH0, 26, 1) + FIELD(DMA_INFIFO_STATUS_CH0, IN_REMAIN_UNDER_3B_CH0, 25, 1) + FIELD(DMA_INFIFO_STATUS_CH0, IN_REMAIN_UNDER_2B_CH0, 24, 1) + FIELD(DMA_INFIFO_STATUS_CH0, IN_REMAIN_UNDER_1B_CH0, 23, 1) + FIELD(DMA_INFIFO_STATUS_CH0, INFIFO_CNT_CH0, 2, 6) + FIELD(DMA_INFIFO_STATUS_CH0, INFIFO_EMPTY_CH0, 1, 1) + FIELD(DMA_INFIFO_STATUS_CH0, INFIFO_FULL_CH0, 0, 1) + +REG32(DMA_IN_POP_CH0, 0x07C) + FIELD(DMA_IN_POP_CH0, INFIFO_POP_CH0, 12, 1) + FIELD(DMA_IN_POP_CH0, INFIFO_RDATA_CH0, 0, 12) + +REG32(DMA_IN_LINK_CH0, 0x080) + FIELD(DMA_IN_LINK_CH0, INLINK_PARK_CH0, 24, 1) + FIELD(DMA_IN_LINK_CH0, INLINK_RESTART_CH0, 23, 1) + FIELD(DMA_IN_LINK_CH0, INLINK_START_CH0, 22, 1) + FIELD(DMA_IN_LINK_CH0, INLINK_STOP_CH0, 21, 1) + FIELD(DMA_IN_LINK_CH0, INLINK_AUTO_RET_CH0, 20, 1) + FIELD(DMA_IN_LINK_CH0, INLINK_ADDR_CH0, 0, 20) + +REG32(DMA_IN_STATE_CH0, 0x084) + FIELD(DMA_IN_STATE_CH0, IN_STATE_CH0, 20, 3) + FIELD(DMA_IN_STATE_CH0, IN_DSCR_STATE_CH0, 18, 2) + FIELD(DMA_IN_STATE_CH0, INLINK_DSCR_ADDR_CH0, 0, 18) + +REG32(DMA_IN_SUC_EOF_DES_ADDR_CH0, 0x088) + FIELD(DMA_IN_SUC_EOF_DES_ADDR_CH0, IN_SUC_EOF_DES_ADDR_CH0, 0, 32) + +REG32(DMA_IN_ERR_EOF_DES_ADDR_CH0, 0x08C) + FIELD(DMA_IN_ERR_EOF_DES_ADDR_CH0, IN_ERR_EOF_DES_ADDR_CH0, 0, 32) + +REG32(DMA_IN_DSCR_CH0, 0x090) + FIELD(DMA_IN_DSCR_CH0, INLINK_DSCR_CH0, 0, 32) + +REG32(DMA_IN_DSCR_BF0_CH0, 0x094) + FIELD(DMA_IN_DSCR_BF0_CH0, INLINK_DSCR_BF0_CH0, 0, 32) + +REG32(DMA_IN_DSCR_BF1_CH0, 0x098) + FIELD(DMA_IN_DSCR_BF1_CH0, INLINK_DSCR_BF1_CH0, 0, 32) + +REG32(DMA_IN_PRI_CH0, 0x09C) + FIELD(DMA_IN_PRI_CH0, RX_PRI_CH0, 0, 4) + +REG32(DMA_IN_PERI_SEL_CH0, 0x0A0) + FIELD(DMA_IN_PERI_SEL_CH0, PERI_IN_SEL_CH0, 0, 6) + +REG32(DMA_OUT_CONF0_CH0, 0x0D0) + FIELD(DMA_OUT_CONF0_CH0, OUT_DATA_BURST_EN_CH0, 5, 1) + FIELD(DMA_OUT_CONF0_CH0, OUTDSCR_BURST_EN_CH0, 4, 1) + FIELD(DMA_OUT_CONF0_CH0, OUT_EOF_MODE_CH0, 3, 1) + FIELD(DMA_OUT_CONF0_CH0, OUT_AUTO_WRBACK_CH0, 2, 1) + FIELD(DMA_OUT_CONF0_CH0, OUT_LOOP_TEST_CH0, 1, 1) + FIELD(DMA_OUT_CONF0_CH0, OUT_RST_CH0, 0, 1) + +REG32(DMA_OUT_CONF1_CH0, 0x0D4) + FIELD(DMA_OUT_CONF1_CH0, OUT_CHECK_OWNER_CH0, 12, 1) + +REG32(DMA_OUTFIFO_STATUS_CH0, 0x0D8) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUT_REMAIN_UNDER_4B_CH0, 26, 1) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUT_REMAIN_UNDER_3B_CH0, 25, 1) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUT_REMAIN_UNDER_2B_CH0, 24, 1) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUT_REMAIN_UNDER_1B_CH0, 23, 1) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUTFIFO_CNT_CH0, 2, 6) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUTFIFO_EMPTY_CH0, 1, 1) + FIELD(DMA_OUTFIFO_STATUS_CH0, OUTFIFO_FULL_CH0, 0, 1) + +REG32(DMA_OUT_PUSH_CH0, 0x0DC) + FIELD(DMA_OUT_PUSH_CH0, OUTFIFO_PUSH_CH0, 9, 1) + FIELD(DMA_OUT_PUSH_CH0, OUTFIFO_WDATA_CH0, 0, 9) + +REG32(DMA_OUT_LINK_CH0, 0x0E0) + FIELD(DMA_OUT_LINK_CH0, OUTLINK_PARK_CH0, 23, 1) + FIELD(DMA_OUT_LINK_CH0, OUTLINK_RESTART_CH0, 22, 1) + FIELD(DMA_OUT_LINK_CH0, OUTLINK_START_CH0, 21, 1) + FIELD(DMA_OUT_LINK_CH0, OUTLINK_STOP_CH0, 20, 1) + FIELD(DMA_OUT_LINK_CH0, OUTLINK_ADDR_CH0, 0, 20) + +REG32(DMA_OUT_STATE_CH0, 0x0E4) + FIELD(DMA_OUT_STATE_CH0, OUT_STATE_CH0, 20, 3) + FIELD(DMA_OUT_STATE_CH0, OUT_DSCR_STATE_CH0, 18, 2) + FIELD(DMA_OUT_STATE_CH0, OUTLINK_DSCR_ADDR_CH0, 0, 18) + +REG32(DMA_OUT_EOF_DES_ADDR_CH0, 0x0E8) + FIELD(DMA_OUT_EOF_DES_ADDR_CH0, OUT_EOF_DES_ADDR_CH0, 0, 32) + +REG32(DMA_OUT_EOF_BFR_DES_ADDR_CH0, 0x0EC) + FIELD(DMA_OUT_EOF_BFR_DES_ADDR_CH0, OUT_EOF_BFR_DES_ADDR_CH0, 0, 32) + +REG32(DMA_OUT_DSCR_CH0, 0x0F0) + FIELD(DMA_OUT_DSCR_CH0, OUTLINK_DSCR_CH0, 0, 32) + +REG32(DMA_OUT_DSCR_BF0_CH0, 0x0F4) + FIELD(DMA_OUT_DSCR_BF0_CH0, OUTLINK_DSCR_BF0_CH0, 0, 32) + +REG32(DMA_OUT_DSCR_BF1_CH0, 0x0F8) + FIELD(DMA_OUT_DSCR_BF1_CH0, OUTLINK_DSCR_BF1_CH0, 0, 32) + +REG32(DMA_OUT_PRI_CH0, 0x0FC) + FIELD(DMA_OUT_PRI_CH0, TX_PRI_CH0, 0, 4) + +REG32(DMA_OUT_PERI_SEL_CH0, 0x100) + FIELD(DMA_OUT_PERI_SEL_CH0, PERI_OUT_SEL_CH0, 0, 6) + +REG32(DMA_IN_CONF0_CH1, 0x130) + FIELD(DMA_IN_CONF0_CH1, MEM_TRANS_EN_CH1, 4, 1) + FIELD(DMA_IN_CONF0_CH1, IN_DATA_BURST_EN_CH1, 3, 1) + FIELD(DMA_IN_CONF0_CH1, INDSCR_BURST_EN_CH1, 2, 1) + FIELD(DMA_IN_CONF0_CH1, IN_LOOP_TEST_CH1, 1, 1) + FIELD(DMA_IN_CONF0_CH1, IN_RST_CH1, 0, 1) + +REG32(DMA_IN_CONF1_CH1, 0x134) + FIELD(DMA_IN_CONF1_CH1, IN_CHECK_OWNER_CH1, 12, 1) + +REG32(DMA_INFIFO_STATUS_CH1, 0x138) + FIELD(DMA_INFIFO_STATUS_CH1, IN_BUF_HUNGRY_CH1, 27, 1) + FIELD(DMA_INFIFO_STATUS_CH1, IN_REMAIN_UNDER_4B_CH1, 26, 1) + FIELD(DMA_INFIFO_STATUS_CH1, IN_REMAIN_UNDER_3B_CH1, 25, 1) + FIELD(DMA_INFIFO_STATUS_CH1, IN_REMAIN_UNDER_2B_CH1, 24, 1) + FIELD(DMA_INFIFO_STATUS_CH1, IN_REMAIN_UNDER_1B_CH1, 23, 1) + FIELD(DMA_INFIFO_STATUS_CH1, INFIFO_CNT_CH1, 2, 6) + FIELD(DMA_INFIFO_STATUS_CH1, INFIFO_EMPTY_CH1, 1, 1) + FIELD(DMA_INFIFO_STATUS_CH1, INFIFO_FULL_CH1, 0, 1) + +REG32(DMA_IN_POP_CH1, 0x13C) + FIELD(DMA_IN_POP_CH1, INFIFO_POP_CH1, 12, 1) + FIELD(DMA_IN_POP_CH1, INFIFO_RDATA_CH1, 0, 12) + +REG32(DMA_IN_LINK_CH1, 0x140) + FIELD(DMA_IN_LINK_CH1, INLINK_PARK_CH1, 24, 1) + FIELD(DMA_IN_LINK_CH1, INLINK_RESTART_CH1, 23, 1) + FIELD(DMA_IN_LINK_CH1, INLINK_START_CH1, 22, 1) + FIELD(DMA_IN_LINK_CH1, INLINK_STOP_CH1, 21, 1) + FIELD(DMA_IN_LINK_CH1, INLINK_AUTO_RET_CH1, 20, 1) + FIELD(DMA_IN_LINK_CH1, INLINK_ADDR_CH1, 0, 20) + +REG32(DMA_IN_STATE_CH1, 0x144) + FIELD(DMA_IN_STATE_CH1, IN_STATE_CH1, 20, 3) + FIELD(DMA_IN_STATE_CH1, IN_DSCR_STATE_CH1, 18, 2) + FIELD(DMA_IN_STATE_CH1, INLINK_DSCR_ADDR_CH1, 0, 18) + +REG32(DMA_IN_SUC_EOF_DES_ADDR_CH1, 0x148) + FIELD(DMA_IN_SUC_EOF_DES_ADDR_CH1, IN_SUC_EOF_DES_ADDR_CH1, 0, 32) + +REG32(DMA_IN_ERR_EOF_DES_ADDR_CH1, 0x14C) + FIELD(DMA_IN_ERR_EOF_DES_ADDR_CH1, IN_ERR_EOF_DES_ADDR_CH1, 0, 32) + +REG32(DMA_IN_DSCR_CH1, 0x150) + FIELD(DMA_IN_DSCR_CH1, INLINK_DSCR_CH1, 0, 32) + +REG32(DMA_IN_DSCR_BF0_CH1, 0x154) + FIELD(DMA_IN_DSCR_BF0_CH1, INLINK_DSCR_BF0_CH1, 0, 32) + +REG32(DMA_IN_DSCR_BF1_CH1, 0x158) + FIELD(DMA_IN_DSCR_BF1_CH1, INLINK_DSCR_BF1_CH1, 0, 32) + +REG32(DMA_IN_PRI_CH1, 0x15C) + FIELD(DMA_IN_PRI_CH1, RX_PRI_CH1, 0, 4) + +REG32(DMA_IN_PERI_SEL_CH1, 0x160) + FIELD(DMA_IN_PERI_SEL_CH1, PERI_IN_SEL_CH1, 0, 6) + +REG32(DMA_OUT_CONF0_CH1, 0x190) + FIELD(DMA_OUT_CONF0_CH1, OUT_DATA_BURST_EN_CH1, 5, 1) + FIELD(DMA_OUT_CONF0_CH1, OUTDSCR_BURST_EN_CH1, 4, 1) + FIELD(DMA_OUT_CONF0_CH1, OUT_EOF_MODE_CH1, 3, 1) + FIELD(DMA_OUT_CONF0_CH1, OUT_AUTO_WRBACK_CH1, 2, 1) + FIELD(DMA_OUT_CONF0_CH1, OUT_LOOP_TEST_CH1, 1, 1) + FIELD(DMA_OUT_CONF0_CH1, OUT_RST_CH1, 0, 1) + +REG32(DMA_OUT_CONF1_CH1, 0x194) + FIELD(DMA_OUT_CONF1_CH1, OUT_CHECK_OWNER_CH1, 12, 1) + +REG32(DMA_OUTFIFO_STATUS_CH1, 0x198) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUT_REMAIN_UNDER_4B_CH1, 26, 1) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUT_REMAIN_UNDER_3B_CH1, 25, 1) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUT_REMAIN_UNDER_2B_CH1, 24, 1) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUT_REMAIN_UNDER_1B_CH1, 23, 1) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUTFIFO_CNT_CH1, 2, 6) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUTFIFO_EMPTY_CH1, 1, 1) + FIELD(DMA_OUTFIFO_STATUS_CH1, OUTFIFO_FULL_CH1, 0, 1) + +REG32(DMA_OUT_PUSH_CH1, 0x19C) + FIELD(DMA_OUT_PUSH_CH1, OUTFIFO_PUSH_CH1, 9, 1) + FIELD(DMA_OUT_PUSH_CH1, OUTFIFO_WDATA_CH1, 0, 9) + +REG32(DMA_OUT_LINK_CH1, 0x1A0) + FIELD(DMA_OUT_LINK_CH1, OUTLINK_PARK_CH1, 23, 1) + FIELD(DMA_OUT_LINK_CH1, OUTLINK_RESTART_CH1, 22, 1) + FIELD(DMA_OUT_LINK_CH1, OUTLINK_START_CH1, 21, 1) + FIELD(DMA_OUT_LINK_CH1, OUTLINK_STOP_CH1, 20, 1) + FIELD(DMA_OUT_LINK_CH1, OUTLINK_ADDR_CH1, 0, 20) + +REG32(DMA_OUT_STATE_CH1, 0x1A4) + FIELD(DMA_OUT_STATE_CH1, OUT_STATE_CH1, 20, 3) + FIELD(DMA_OUT_STATE_CH1, OUT_DSCR_STATE_CH1, 18, 2) + FIELD(DMA_OUT_STATE_CH1, OUTLINK_DSCR_ADDR_CH1, 0, 18) + +REG32(DMA_OUT_EOF_DES_ADDR_CH1, 0x1A8) + FIELD(DMA_OUT_EOF_DES_ADDR_CH1, OUT_EOF_DES_ADDR_CH1, 0, 32) + +REG32(DMA_OUT_EOF_BFR_DES_ADDR_CH1, 0x1AC) + FIELD(DMA_OUT_EOF_BFR_DES_ADDR_CH1, OUT_EOF_BFR_DES_ADDR_CH1, 0, 32) + +REG32(DMA_OUT_DSCR_CH1, 0x1B0) + FIELD(DMA_OUT_DSCR_CH1, OUTLINK_DSCR_CH1, 0, 32) + +REG32(DMA_OUT_DSCR_BF0_CH1, 0x1B4) + FIELD(DMA_OUT_DSCR_BF0_CH1, OUTLINK_DSCR_BF0_CH1, 0, 32) + +REG32(DMA_OUT_DSCR_BF1_CH1, 0x1B8) + FIELD(DMA_OUT_DSCR_BF1_CH1, OUTLINK_DSCR_BF1_CH1, 0, 32) + +REG32(DMA_OUT_PRI_CH1, 0x1BC) + FIELD(DMA_OUT_PRI_CH1, TX_PRI_CH1, 0, 4) + +REG32(DMA_OUT_PERI_SEL_CH1, 0x1C0) + FIELD(DMA_OUT_PERI_SEL_CH1, PERI_OUT_SEL_CH1, 0, 6) + +REG32(DMA_IN_CONF0_CH2, 0x1F0) + FIELD(DMA_IN_CONF0_CH2, MEM_TRANS_EN_CH2, 4, 1) + FIELD(DMA_IN_CONF0_CH2, IN_DATA_BURST_EN_CH2, 3, 1) + FIELD(DMA_IN_CONF0_CH2, INDSCR_BURST_EN_CH2, 2, 1) + FIELD(DMA_IN_CONF0_CH2, IN_LOOP_TEST_CH2, 1, 1) + FIELD(DMA_IN_CONF0_CH2, IN_RST_CH2, 0, 1) + +REG32(DMA_IN_CONF1_CH2, 0x1F4) + FIELD(DMA_IN_CONF1_CH2, IN_CHECK_OWNER_CH2, 12, 1) + +REG32(DMA_INFIFO_STATUS_CH2, 0x1F8) + FIELD(DMA_INFIFO_STATUS_CH2, IN_BUF_HUNGRY_CH2, 27, 1) + FIELD(DMA_INFIFO_STATUS_CH2, IN_REMAIN_UNDER_4B_CH2, 26, 1) + FIELD(DMA_INFIFO_STATUS_CH2, IN_REMAIN_UNDER_3B_CH2, 25, 1) + FIELD(DMA_INFIFO_STATUS_CH2, IN_REMAIN_UNDER_2B_CH2, 24, 1) + FIELD(DMA_INFIFO_STATUS_CH2, IN_REMAIN_UNDER_1B_CH2, 23, 1) + FIELD(DMA_INFIFO_STATUS_CH2, INFIFO_CNT_CH2, 2, 6) + FIELD(DMA_INFIFO_STATUS_CH2, INFIFO_EMPTY_CH2, 1, 1) + FIELD(DMA_INFIFO_STATUS_CH2, INFIFO_FULL_CH2, 0, 1) + +REG32(DMA_IN_POP_CH2, 0x1FC) + FIELD(DMA_IN_POP_CH2, INFIFO_POP_CH2, 12, 1) + FIELD(DMA_IN_POP_CH2, INFIFO_RDATA_CH2, 0, 12) + +REG32(DMA_IN_LINK_CH2, 0x200) + FIELD(DMA_IN_LINK_CH2, INLINK_PARK_CH2, 24, 1) + FIELD(DMA_IN_LINK_CH2, INLINK_RESTART_CH2, 23, 1) + FIELD(DMA_IN_LINK_CH2, INLINK_START_CH2, 22, 1) + FIELD(DMA_IN_LINK_CH2, INLINK_STOP_CH2, 21, 1) + FIELD(DMA_IN_LINK_CH2, INLINK_AUTO_RET_CH2, 20, 1) + FIELD(DMA_IN_LINK_CH2, INLINK_ADDR_CH2, 0, 20) + +REG32(DMA_IN_STATE_CH2, 0x204) + FIELD(DMA_IN_STATE_CH2, IN_STATE_CH2, 20, 3) + FIELD(DMA_IN_STATE_CH2, IN_DSCR_STATE_CH2, 18, 2) + FIELD(DMA_IN_STATE_CH2, INLINK_DSCR_ADDR_CH2, 0, 18) + +REG32(DMA_IN_SUC_EOF_DES_ADDR_CH2, 0x208) + FIELD(DMA_IN_SUC_EOF_DES_ADDR_CH2, IN_SUC_EOF_DES_ADDR_CH2, 0, 32) + +REG32(DMA_IN_ERR_EOF_DES_ADDR_CH2, 0x20C) + FIELD(DMA_IN_ERR_EOF_DES_ADDR_CH2, IN_ERR_EOF_DES_ADDR_CH2, 0, 32) + +REG32(DMA_IN_DSCR_CH2, 0x210) + FIELD(DMA_IN_DSCR_CH2, INLINK_DSCR_CH2, 0, 32) + +REG32(DMA_IN_DSCR_BF0_CH2, 0x214) + FIELD(DMA_IN_DSCR_BF0_CH2, INLINK_DSCR_BF0_CH2, 0, 32) + +REG32(DMA_IN_DSCR_BF1_CH2, 0x218) + FIELD(DMA_IN_DSCR_BF1_CH2, INLINK_DSCR_BF1_CH2, 0, 32) + +REG32(DMA_IN_PRI_CH2, 0x21C) + FIELD(DMA_IN_PRI_CH2, RX_PRI_CH2, 0, 4) + +REG32(DMA_IN_PERI_SEL_CH2, 0x220) + FIELD(DMA_IN_PERI_SEL_CH2, PERI_IN_SEL_CH2, 0, 6) + +REG32(DMA_OUT_CONF0_CH2, 0x250) + FIELD(DMA_OUT_CONF0_CH2, OUT_DATA_BURST_EN_CH2, 5, 1) + FIELD(DMA_OUT_CONF0_CH2, OUTDSCR_BURST_EN_CH2, 4, 1) + FIELD(DMA_OUT_CONF0_CH2, OUT_EOF_MODE_CH2, 3, 1) + FIELD(DMA_OUT_CONF0_CH2, OUT_AUTO_WRBACK_CH2, 2, 1) + FIELD(DMA_OUT_CONF0_CH2, OUT_LOOP_TEST_CH2, 1, 1) + FIELD(DMA_OUT_CONF0_CH2, OUT_RST_CH2, 0, 1) + +REG32(DMA_OUT_CONF1_CH2, 0x254) + FIELD(DMA_OUT_CONF1_CH2, OUT_CHECK_OWNER_CH2, 12, 1) + +REG32(DMA_OUTFIFO_STATUS_CH2, 0x258) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUT_REMAIN_UNDER_4B_CH2, 26, 1) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUT_REMAIN_UNDER_3B_CH2, 25, 1) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUT_REMAIN_UNDER_2B_CH2, 24, 1) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUT_REMAIN_UNDER_1B_CH2, 23, 1) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUTFIFO_CNT_CH2, 2, 6) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUTFIFO_EMPTY_CH2, 1, 1) + FIELD(DMA_OUTFIFO_STATUS_CH2, OUTFIFO_FULL_CH2, 0, 1) + +REG32(DMA_OUT_PUSH_CH2, 0x25C) + FIELD(DMA_OUT_PUSH_CH2, OUTFIFO_PUSH_CH2, 9, 1) + FIELD(DMA_OUT_PUSH_CH2, OUTFIFO_WDATA_CH2, 0, 9) + +REG32(DMA_OUT_LINK_CH2, 0x260) + FIELD(DMA_OUT_LINK_CH2, OUTLINK_PARK_CH2, 23, 1) + FIELD(DMA_OUT_LINK_CH2, OUTLINK_RESTART_CH2, 22, 1) + FIELD(DMA_OUT_LINK_CH2, OUTLINK_START_CH2, 21, 1) + FIELD(DMA_OUT_LINK_CH2, OUTLINK_STOP_CH2, 20, 1) + FIELD(DMA_OUT_LINK_CH2, OUTLINK_ADDR_CH2, 0, 20) + +REG32(DMA_OUT_STATE_CH2, 0x264) + FIELD(DMA_OUT_STATE_CH2, OUT_STATE_CH2, 20, 3) + FIELD(DMA_OUT_STATE_CH2, OUT_DSCR_STATE_CH2, 18, 2) + FIELD(DMA_OUT_STATE_CH2, OUTLINK_DSCR_ADDR_CH2, 0, 18) + +REG32(DMA_OUT_EOF_DES_ADDR_CH2, 0x268) + FIELD(DMA_OUT_EOF_DES_ADDR_CH2, OUT_EOF_DES_ADDR_CH2, 0, 32) + +REG32(DMA_OUT_EOF_BFR_DES_ADDR_CH2, 0x26C) + FIELD(DMA_OUT_EOF_BFR_DES_ADDR_CH2, OUT_EOF_BFR_DES_ADDR_CH2, 0, 32) + +REG32(DMA_OUT_DSCR_CH2, 0x270) + FIELD(DMA_OUT_DSCR_CH2, OUTLINK_DSCR_CH2, 0, 32) + +REG32(DMA_OUT_DSCR_BF0_CH2, 0x274) + FIELD(DMA_OUT_DSCR_BF0_CH2, OUTLINK_DSCR_BF0_CH2, 0, 32) + +REG32(DMA_OUT_DSCR_BF1_CH2, 0x278) + FIELD(DMA_OUT_DSCR_BF1_CH2, OUTLINK_DSCR_BF1_CH2, 0, 32) + +REG32(DMA_OUT_PRI_CH2, 0x27C) + FIELD(DMA_OUT_PRI_CH2, TX_PRI_CH2, 0, 4) + +REG32(DMA_OUT_PERI_SEL_CH2, 0x280) + FIELD(DMA_OUT_PERI_SEL_CH2, PERI_OUT_SEL_CH2, 0, 6)