/*- ******************************************************************************* Copyright (C) 2015 Annapurna Labs Ltd. This file may be licensed under the terms of the Annapurna Labs Commercial License Agreement. Alternatively, this file can be distributed under the terms of the GNU General Public License V2 as published by the Free Software Foundation and can be found at http://www.gnu.org/licenses/gpl-2.0.html Alternatively, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /** * @file al_hal_udma_config.c * * @brief Universal DMA HAL driver for configurations * */ #include #include #include /**************** Misc configurations *********************/ /** Configure AXI generic configuration */ int al_udma_axi_set(struct udma_gen_axi *axi_regs, struct al_udma_axi_conf *axi) { uint32_t reg; al_reg_write32(&axi_regs->cfg_1, axi->axi_timeout); reg = al_reg_read32(&axi_regs->cfg_2); reg &= ~UDMA_GEN_AXI_CFG_2_ARB_PROMOTION_MASK; reg |= axi->arb_promotion; al_reg_write32(&axi_regs->cfg_2, reg); reg = al_reg_read32(&axi_regs->endian_cfg); if (axi->swap_8_bytes == AL_TRUE) reg |= UDMA_GEN_AXI_ENDIAN_CFG_SWAP_64B_EN; else reg &= ~UDMA_GEN_AXI_ENDIAN_CFG_SWAP_64B_EN; if (axi->swap_s2m_data == AL_TRUE) reg |= UDMA_GEN_AXI_ENDIAN_CFG_SWAP_S2M_DATA; else reg &= ~UDMA_GEN_AXI_ENDIAN_CFG_SWAP_S2M_DATA; if (axi->swap_s2m_desc == AL_TRUE) reg |= UDMA_GEN_AXI_ENDIAN_CFG_SWAP_S2M_DESC; else reg &= ~UDMA_GEN_AXI_ENDIAN_CFG_SWAP_S2M_DESC; if (axi->swap_m2s_data == AL_TRUE) reg |= UDMA_GEN_AXI_ENDIAN_CFG_SWAP_M2S_DATA; else reg &= ~UDMA_GEN_AXI_ENDIAN_CFG_SWAP_M2S_DATA; if (axi->swap_m2s_desc == AL_TRUE) reg |= UDMA_GEN_AXI_ENDIAN_CFG_SWAP_M2S_DESC; else reg &= ~UDMA_GEN_AXI_ENDIAN_CFG_SWAP_M2S_DESC; al_reg_write32(&axi_regs->endian_cfg, reg); return 0; } /* Configure UDMA AXI M2S configuration */ /** Configure AXI M2S submaster */ static int al_udma_m2s_axi_sm_set(struct al_udma_axi_submaster *m2s_sm, uint32_t *cfg_1, uint32_t *cfg_2, uint32_t *cfg_max_beats) { uint32_t reg; reg = al_reg_read32(cfg_1); reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_1_AWID_MASK; reg |= m2s_sm->id & UDMA_AXI_M2S_COMP_WR_CFG_1_AWID_MASK; reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_1_AWCACHE_MASK; reg |= (m2s_sm->cache_type << UDMA_AXI_M2S_COMP_WR_CFG_1_AWCACHE_SHIFT) & UDMA_AXI_M2S_COMP_WR_CFG_1_AWCACHE_MASK; reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_1_AWBURST_MASK; reg |= (m2s_sm->burst << UDMA_AXI_M2S_COMP_WR_CFG_1_AWBURST_SHIFT) & UDMA_AXI_M2S_COMP_WR_CFG_1_AWBURST_MASK; al_reg_write32(cfg_1, reg); reg = al_reg_read32(cfg_2); reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_2_AWUSER_MASK; reg |= m2s_sm->used_ext & UDMA_AXI_M2S_COMP_WR_CFG_2_AWUSER_MASK; reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_2_AWSIZE_MASK; reg |= (m2s_sm->bus_size << UDMA_AXI_M2S_COMP_WR_CFG_2_AWSIZE_SHIFT) & UDMA_AXI_M2S_COMP_WR_CFG_2_AWSIZE_MASK; reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_2_AWQOS_MASK; reg |= (m2s_sm->qos << UDMA_AXI_M2S_COMP_WR_CFG_2_AWQOS_SHIFT) & UDMA_AXI_M2S_COMP_WR_CFG_2_AWQOS_MASK; reg &= ~UDMA_AXI_M2S_COMP_WR_CFG_2_AWPROT_MASK; reg |= (m2s_sm->prot << UDMA_AXI_M2S_COMP_WR_CFG_2_AWPROT_SHIFT) & UDMA_AXI_M2S_COMP_WR_CFG_2_AWPROT_MASK; al_reg_write32(cfg_2, reg); reg = al_reg_read32(cfg_max_beats); reg &= ~UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK; reg |= m2s_sm->max_beats & UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK; al_reg_write32(cfg_max_beats, reg); return 0; } /** Configure UDMA AXI M2S configuration */ int al_udma_m2s_axi_set(struct al_udma *udma, struct al_udma_m2s_axi_conf *axi_m2s) { uint32_t reg; al_udma_m2s_axi_sm_set(&axi_m2s->comp_write, &udma->udma_regs->m2s.axi_m2s.comp_wr_cfg_1, &udma->udma_regs->m2s.axi_m2s.comp_wr_cfg_2, &udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1); al_udma_m2s_axi_sm_set(&axi_m2s->data_read, &udma->udma_regs->m2s.axi_m2s.data_rd_cfg_1, &udma->udma_regs->m2s.axi_m2s.data_rd_cfg_2, &udma->udma_regs->m2s.axi_m2s.data_rd_cfg); al_udma_m2s_axi_sm_set(&axi_m2s->desc_read, &udma->udma_regs->m2s.axi_m2s.desc_rd_cfg_1, &udma->udma_regs->m2s.axi_m2s.desc_rd_cfg_2, &udma->udma_regs->m2s.axi_m2s.desc_rd_cfg_3); reg = al_reg_read32(&udma->udma_regs->m2s.axi_m2s.data_rd_cfg); if (axi_m2s->break_on_max_boundary == AL_TRUE) reg |= UDMA_AXI_M2S_DATA_RD_CFG_ALWAYS_BREAK_ON_MAX_BOUDRY; else reg &= ~UDMA_AXI_M2S_DATA_RD_CFG_ALWAYS_BREAK_ON_MAX_BOUDRY; al_reg_write32(&udma->udma_regs->m2s.axi_m2s.data_rd_cfg, reg); reg = al_reg_read32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1); reg &= ~UDMA_AXI_M2S_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK; reg |= (axi_m2s->min_axi_beats << UDMA_AXI_M2S_DESC_WR_CFG_1_MIN_AXI_BEATS_SHIFT) & UDMA_AXI_M2S_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK; al_reg_write32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1, reg); reg = al_reg_read32(&udma->udma_regs->m2s.axi_m2s.ostand_cfg); reg &= ~UDMA_AXI_M2S_OSTAND_CFG_MAX_DATA_RD_MASK; reg |= axi_m2s->ostand_max_data_read & UDMA_AXI_M2S_OSTAND_CFG_MAX_DATA_RD_MASK; reg &= ~UDMA_AXI_M2S_OSTAND_CFG_MAX_DESC_RD_MASK; reg |= (axi_m2s->ostand_max_desc_read << UDMA_AXI_M2S_OSTAND_CFG_MAX_DESC_RD_SHIFT) & UDMA_AXI_M2S_OSTAND_CFG_MAX_DESC_RD_MASK; reg &= ~UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_REQ_MASK; reg |= (axi_m2s->ostand_max_comp_req << UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_REQ_SHIFT) & UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_REQ_MASK; reg &= ~UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_DATA_WR_MASK; reg |= (axi_m2s->ostand_max_comp_write << UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_DATA_WR_SHIFT) & UDMA_AXI_M2S_OSTAND_CFG_MAX_COMP_DATA_WR_MASK; al_reg_write32(&udma->udma_regs->m2s.axi_m2s.ostand_cfg, reg); return 0; } /** Configure AXI S2M submaster */ static int al_udma_s2m_axi_sm_set(struct al_udma_axi_submaster *s2m_sm, uint32_t *cfg_1, uint32_t *cfg_2, uint32_t *cfg_max_beats) { uint32_t reg; reg = al_reg_read32(cfg_1); reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_1_AWID_MASK; reg |= s2m_sm->id & UDMA_AXI_S2M_COMP_WR_CFG_1_AWID_MASK; reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_1_AWCACHE_MASK; reg |= (s2m_sm->cache_type << UDMA_AXI_S2M_COMP_WR_CFG_1_AWCACHE_SHIFT) & UDMA_AXI_S2M_COMP_WR_CFG_1_AWCACHE_MASK; reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_1_AWBURST_MASK; reg |= (s2m_sm->burst << UDMA_AXI_S2M_COMP_WR_CFG_1_AWBURST_SHIFT) & UDMA_AXI_S2M_COMP_WR_CFG_1_AWBURST_MASK; al_reg_write32(cfg_1, reg); reg = al_reg_read32(cfg_2); reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_2_AWUSER_MASK; reg |= s2m_sm->used_ext & UDMA_AXI_S2M_COMP_WR_CFG_2_AWUSER_MASK; reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_2_AWSIZE_MASK; reg |= (s2m_sm->bus_size << UDMA_AXI_S2M_COMP_WR_CFG_2_AWSIZE_SHIFT) & UDMA_AXI_S2M_COMP_WR_CFG_2_AWSIZE_MASK; reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_2_AWQOS_MASK; reg |= (s2m_sm->qos << UDMA_AXI_S2M_COMP_WR_CFG_2_AWQOS_SHIFT) & UDMA_AXI_S2M_COMP_WR_CFG_2_AWQOS_MASK; reg &= ~UDMA_AXI_S2M_COMP_WR_CFG_2_AWPROT_MASK; reg |= (s2m_sm->prot << UDMA_AXI_S2M_COMP_WR_CFG_2_AWPROT_SHIFT) & UDMA_AXI_S2M_COMP_WR_CFG_2_AWPROT_MASK; al_reg_write32(cfg_2, reg); reg = al_reg_read32(cfg_max_beats); reg &= ~UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK; reg |= s2m_sm->max_beats & UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK; al_reg_write32(cfg_max_beats, reg); return 0; } /** Configure UDMA AXI S2M configuration */ int al_udma_s2m_axi_set(struct al_udma *udma, struct al_udma_s2m_axi_conf *axi_s2m) { uint32_t reg; al_udma_s2m_axi_sm_set(&axi_s2m->data_write, &udma->udma_regs->s2m.axi_s2m.data_wr_cfg_1, &udma->udma_regs->s2m.axi_s2m.data_wr_cfg_2, &udma->udma_regs->s2m.axi_s2m.data_wr_cfg); al_udma_s2m_axi_sm_set(&axi_s2m->desc_read, &udma->udma_regs->s2m.axi_s2m.desc_rd_cfg_4, &udma->udma_regs->s2m.axi_s2m.desc_rd_cfg_5, &udma->udma_regs->s2m.axi_s2m.desc_rd_cfg_3); al_udma_s2m_axi_sm_set(&axi_s2m->comp_write, &udma->udma_regs->s2m.axi_s2m.comp_wr_cfg_1, &udma->udma_regs->s2m.axi_s2m.comp_wr_cfg_2, &udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1); reg = al_reg_read32(&udma->udma_regs->s2m.axi_s2m.desc_rd_cfg_3); if (axi_s2m->break_on_max_boundary == AL_TRUE) reg |= UDMA_AXI_S2M_DESC_RD_CFG_3_ALWAYS_BREAK_ON_MAX_BOUDRY; else reg &= ~UDMA_AXI_S2M_DESC_RD_CFG_3_ALWAYS_BREAK_ON_MAX_BOUDRY; al_reg_write32(&udma->udma_regs->s2m.axi_s2m.desc_rd_cfg_3, reg); reg = al_reg_read32(&udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1); reg &= ~UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK; reg |= (axi_s2m->min_axi_beats << UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_SHIFT) & UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK; al_reg_write32(&udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1, reg); reg = al_reg_read32(&udma->udma_regs->s2m.axi_s2m.ostand_cfg_rd); reg &= ~UDMA_AXI_S2M_OSTAND_CFG_RD_MAX_DESC_RD_OSTAND_MASK; reg |= axi_s2m->ostand_max_desc_read & UDMA_AXI_S2M_OSTAND_CFG_RD_MAX_DESC_RD_OSTAND_MASK; reg &= ~UDMA_AXI_S2M_OSTAND_CFG_RD_MAX_STREAM_ACK_MASK; reg |= (axi_s2m->ack_fifo_depth << UDMA_AXI_S2M_OSTAND_CFG_RD_MAX_STREAM_ACK_SHIFT) & UDMA_AXI_S2M_OSTAND_CFG_RD_MAX_STREAM_ACK_MASK; al_reg_write32(&udma->udma_regs->s2m.axi_s2m.ostand_cfg_rd, reg); reg = al_reg_read32(&udma->udma_regs->s2m.axi_s2m.ostand_cfg_wr); reg &= ~UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_DATA_WR_OSTAND_MASK; reg |= axi_s2m->ostand_max_data_req & UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_DATA_WR_OSTAND_MASK; reg &= ~UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_DATA_BEATS_WR_OSTAND_MASK; reg |= (axi_s2m->ostand_max_data_write << UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_DATA_BEATS_WR_OSTAND_SHIFT) & UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_DATA_BEATS_WR_OSTAND_MASK; reg &= ~UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_REQ_MASK; reg |= (axi_s2m->ostand_max_comp_req << UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_REQ_SHIFT) & UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_REQ_MASK; reg &= ~UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_DATA_WR_OSTAND_MASK; reg |= (axi_s2m->ostand_max_comp_write << UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_DATA_WR_OSTAND_SHIFT) & UDMA_AXI_S2M_OSTAND_CFG_WR_MAX_COMP_DATA_WR_OSTAND_MASK; al_reg_write32(&udma->udma_regs->s2m.axi_s2m.ostand_cfg_wr, reg); return 0; } /** M2S packet len configuration */ int al_udma_m2s_packet_size_cfg_set(struct al_udma *udma, struct al_udma_m2s_pkt_len_conf *conf) { uint32_t reg = al_reg_read32(&udma->udma_regs->m2s.m2s.cfg_len); uint32_t max_supported_size = UDMA_M2S_CFG_LEN_MAX_PKT_SIZE_MASK; al_assert(udma->type == UDMA_TX); if (conf->encode_64k_as_zero == AL_TRUE) max_supported_size += 1; /* 64K */ if (conf->max_pkt_size > max_supported_size) { al_err("udma [%s]: requested max_pkt_size (0x%x) exceeds the" "supported limit (0x%x)\n", udma->name, conf->max_pkt_size, max_supported_size); return -EINVAL; } reg &= ~UDMA_M2S_CFG_LEN_ENCODE_64K; if (conf->encode_64k_as_zero == AL_TRUE) reg |= UDMA_M2S_CFG_LEN_ENCODE_64K; else reg &= ~UDMA_M2S_CFG_LEN_ENCODE_64K; reg &= ~UDMA_M2S_CFG_LEN_MAX_PKT_SIZE_MASK; reg |= conf->max_pkt_size; al_reg_write32(&udma->udma_regs->m2s.m2s.cfg_len, reg); return 0; } /** Report Error - to be used for abort */ void al_udma_err_report(struct al_udma *udma __attribute__((__unused__))) { return; } /** Statistics - TBD */ void al_udma_stats_get(struct al_udma *udma __attribute__((__unused__))) { return; } /** Configure UDMA M2S descriptor prefetch */ int al_udma_m2s_pref_set(struct al_udma *udma, struct al_udma_m2s_desc_pref_conf *conf) { uint32_t reg; reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_1); reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_1_FIFO_DEPTH_MASK; reg |= conf->desc_fifo_depth; al_reg_write32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_1, reg); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2); if (conf->sch_mode == SRR) reg |= UDMA_M2S_RD_DESC_PREF_CFG_2_PREF_FORCE_RR; else if (conf->sch_mode == STRICT) reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_2_PREF_FORCE_RR; else { al_err("udma [%s]: requested descriptor preferch arbiter " "mode (%d) is invalid\n", udma->name, conf->sch_mode); return -EINVAL; } reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK; reg |= conf->max_desc_per_packet & UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK; al_reg_write32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2, reg); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3); reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_MASK; reg |= conf->min_burst_below_thr & UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_MASK; reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK; reg |=(conf->min_burst_above_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT) & UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK; reg &= ~UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK; reg |= (conf->pref_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT) & UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK; al_reg_write32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3, reg); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.data_cfg); reg &= ~UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK; reg |= conf->data_fifo_depth & UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK; reg &= ~UDMA_M2S_RD_DATA_CFG_MAX_PKT_LIMIT_MASK; reg |= (conf->max_pkt_limit << UDMA_M2S_RD_DATA_CFG_MAX_PKT_LIMIT_SHIFT) & UDMA_M2S_RD_DATA_CFG_MAX_PKT_LIMIT_MASK; al_reg_write32(&udma->udma_regs->m2s.m2s_rd.data_cfg, reg); return 0; } /** Ger the M2S UDMA descriptor prefetch */ int al_udma_m2s_pref_get(struct al_udma *udma, struct al_udma_m2s_desc_pref_conf *conf) { uint32_t reg; reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_1); conf->desc_fifo_depth = AL_REG_FIELD_GET(reg, UDMA_M2S_RD_DESC_PREF_CFG_1_FIFO_DEPTH_MASK, UDMA_M2S_RD_DESC_PREF_CFG_1_FIFO_DEPTH_SHIFT); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2); if (reg & UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK) conf->sch_mode = SRR; else conf->sch_mode = STRICT; conf->max_desc_per_packet = AL_REG_FIELD_GET(reg, UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK, UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_SHIFT); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3); conf->min_burst_below_thr = AL_REG_FIELD_GET(reg, UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_MASK, UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_SHIFT); conf->min_burst_above_thr = AL_REG_FIELD_GET(reg, UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK, UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT); conf->pref_thr = AL_REG_FIELD_GET(reg, UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK, UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT); return 0; } /* set max descriptors */ int al_udma_m2s_max_descs_set(struct al_udma *udma, uint8_t max_descs) { uint32_t pref_thr = max_descs; uint32_t min_burst_above_thr = 4; al_assert(max_descs <= AL_UDMA_M2S_MAX_ALLOWED_DESCS_PER_PACKET); al_assert(max_descs > 0); /* increase min_burst_above_thr so larger burst can be used to fetch * descriptors */ if (pref_thr >= 8) min_burst_above_thr = 8; else { /* don't set prefetch threshold too low so we can have the * min_burst_above_thr >= 4 */ pref_thr = 4; } al_reg_write32_masked(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_2, UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_MASK, max_descs << UDMA_M2S_RD_DESC_PREF_CFG_2_MAX_DESC_PER_PKT_SHIFT); al_reg_write32_masked(&udma->udma_regs->m2s.m2s_rd.desc_pref_cfg_3, UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_MASK | UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK, (pref_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT) | (min_burst_above_thr << UDMA_M2S_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT)); return 0; } /* set s2m max descriptors */ int al_udma_s2m_max_descs_set(struct al_udma *udma, uint8_t max_descs) { uint32_t pref_thr = max_descs; uint32_t min_burst_above_thr = 4; al_assert(max_descs <= AL_UDMA_S2M_MAX_ALLOWED_DESCS_PER_PACKET); al_assert(max_descs > 0); /* increase min_burst_above_thr so larger burst can be used to fetch * descriptors */ if (pref_thr >= 8) min_burst_above_thr = 8; else /* don't set prefetch threshold too low so we can have the * min_burst_above_thr >= 4 */ pref_thr = 4; al_reg_write32_masked(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_3, UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_MASK | UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK, (pref_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT) | (min_burst_above_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT)); return 0; } int al_udma_s2m_full_line_write_set(struct al_udma *udma, al_bool enable) { uint32_t val = 0; if (enable == AL_TRUE) { val = UDMA_S2M_WR_DATA_CFG_2_FULL_LINE_MODE; al_info("udma [%s]: full line write enabled\n", udma->name); } al_reg_write32_masked(&udma->udma_regs->s2m.s2m_wr.data_cfg_2, UDMA_S2M_WR_DATA_CFG_2_FULL_LINE_MODE, val); return 0; } /** Configure S2M UDMA descriptor prefetch */ int al_udma_s2m_pref_set(struct al_udma *udma, struct al_udma_s2m_desc_pref_conf *conf) { uint32_t reg; reg = al_reg_read32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_1); reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_1_FIFO_DEPTH_MASK; reg |= conf->desc_fifo_depth; al_reg_write32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_1, reg); reg = al_reg_read32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_2); if (conf->sch_mode == SRR) reg |= UDMA_S2M_RD_DESC_PREF_CFG_2_PREF_FORCE_RR; else if (conf->sch_mode == STRICT) reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_2_PREF_FORCE_RR; else { al_err("udma [%s]: requested descriptor preferch arbiter " "mode (%d) is invalid\n", udma->name, conf->sch_mode); return -EINVAL; } if (conf->q_promotion == AL_TRUE) reg |= UDMA_S2M_RD_DESC_PREF_CFG_2_Q_PROMOTION; else reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_2_Q_PROMOTION; if (conf->force_promotion == AL_TRUE) reg |= UDMA_S2M_RD_DESC_PREF_CFG_2_FORCE_PROMOTION; else reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_2_FORCE_PROMOTION; if (conf->en_pref_prediction == AL_TRUE) reg |= UDMA_S2M_RD_DESC_PREF_CFG_2_EN_PREF_PREDICTION; else reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_2_EN_PREF_PREDICTION; reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_2_PROMOTION_TH_MASK; reg |= (conf->promotion_th << UDMA_S2M_RD_DESC_PREF_CFG_2_PROMOTION_TH_SHIFT) & UDMA_S2M_RD_DESC_PREF_CFG_2_PROMOTION_TH_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_2, reg); reg = al_reg_read32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_3); reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_MASK; reg |= (conf->pref_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_SHIFT) & UDMA_S2M_RD_DESC_PREF_CFG_3_PREF_THR_MASK; reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_MASK; reg |= conf->min_burst_below_thr & UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_BELOW_THR_MASK; reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK; reg |=(conf->min_burst_above_thr << UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_SHIFT) & UDMA_S2M_RD_DESC_PREF_CFG_3_MIN_BURST_ABOVE_THR_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_3, reg); reg = al_reg_read32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_4); reg &= ~UDMA_S2M_RD_DESC_PREF_CFG_4_A_FULL_THR_MASK; reg |= conf->a_full_thr & UDMA_S2M_RD_DESC_PREF_CFG_4_A_FULL_THR_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_rd.desc_pref_cfg_4, reg); return 0; } /* Configure S2M UDMA data write */ int al_udma_s2m_data_write_set(struct al_udma *udma, struct al_udma_s2m_data_write_conf *conf) { uint32_t reg; reg = al_reg_read32(&udma->udma_regs->s2m.s2m_wr.data_cfg_1); reg &= ~UDMA_S2M_WR_DATA_CFG_1_DATA_FIFO_DEPTH_MASK; reg |= conf->data_fifo_depth & UDMA_S2M_WR_DATA_CFG_1_DATA_FIFO_DEPTH_MASK; reg &= ~UDMA_S2M_WR_DATA_CFG_1_MAX_PKT_LIMIT_MASK; reg |= (conf->max_pkt_limit << UDMA_S2M_WR_DATA_CFG_1_MAX_PKT_LIMIT_SHIFT) & UDMA_S2M_WR_DATA_CFG_1_MAX_PKT_LIMIT_MASK; reg &= ~UDMA_S2M_WR_DATA_CFG_1_FIFO_MARGIN_MASK; reg |= (conf->fifo_margin << UDMA_S2M_WR_DATA_CFG_1_FIFO_MARGIN_SHIFT) & UDMA_S2M_WR_DATA_CFG_1_FIFO_MARGIN_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_wr.data_cfg_1, reg); reg = al_reg_read32(&udma->udma_regs->s2m.s2m_wr.data_cfg_2); reg &= ~UDMA_S2M_WR_DATA_CFG_2_DESC_WAIT_TIMER_MASK; reg |= conf->desc_wait_timer & UDMA_S2M_WR_DATA_CFG_2_DESC_WAIT_TIMER_MASK; reg &= ~(UDMA_S2M_WR_DATA_CFG_2_DROP_IF_NO_DESC | UDMA_S2M_WR_DATA_CFG_2_HINT_IF_NO_DESC | UDMA_S2M_WR_DATA_CFG_2_WAIT_FOR_PREF | UDMA_S2M_WR_DATA_CFG_2_FULL_LINE_MODE | UDMA_S2M_WR_DATA_CFG_2_DIRECT_HDR_USE_BUF1); reg |= conf->flags & (UDMA_S2M_WR_DATA_CFG_2_DROP_IF_NO_DESC | UDMA_S2M_WR_DATA_CFG_2_HINT_IF_NO_DESC | UDMA_S2M_WR_DATA_CFG_2_WAIT_FOR_PREF | UDMA_S2M_WR_DATA_CFG_2_FULL_LINE_MODE | UDMA_S2M_WR_DATA_CFG_2_DIRECT_HDR_USE_BUF1); al_reg_write32(&udma->udma_regs->s2m.s2m_wr.data_cfg_2, reg); return 0; } /* Configure S2M UDMA completion */ int al_udma_s2m_completion_set(struct al_udma *udma, struct al_udma_s2m_completion_conf *conf) { uint32_t reg = al_reg_read32(&udma->udma_regs->s2m.s2m_comp.cfg_1c); reg &= ~UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK; reg |= conf->desc_size & UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK; if (conf->cnt_words == AL_TRUE) reg |= UDMA_S2M_COMP_CFG_1C_CNT_WORDS; else reg &= ~UDMA_S2M_COMP_CFG_1C_CNT_WORDS; if (conf->q_promotion == AL_TRUE) reg |= UDMA_S2M_COMP_CFG_1C_Q_PROMOTION; else reg &= ~UDMA_S2M_COMP_CFG_1C_Q_PROMOTION; if (conf->force_rr == AL_TRUE) reg |= UDMA_S2M_COMP_CFG_1C_FORCE_RR; else reg &= ~UDMA_S2M_COMP_CFG_1C_FORCE_RR; reg &= ~UDMA_S2M_COMP_CFG_1C_Q_FREE_MIN_MASK; reg |= (conf->q_free_min << UDMA_S2M_COMP_CFG_1C_Q_FREE_MIN_SHIFT) & UDMA_S2M_COMP_CFG_1C_Q_FREE_MIN_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_comp.cfg_1c, reg); reg = al_reg_read32(&udma->udma_regs->s2m.s2m_comp.cfg_2c); reg &= ~UDMA_S2M_COMP_CFG_2C_COMP_FIFO_DEPTH_MASK; reg |= conf->comp_fifo_depth & UDMA_S2M_COMP_CFG_2C_COMP_FIFO_DEPTH_MASK; reg &= ~UDMA_S2M_COMP_CFG_2C_UNACK_FIFO_DEPTH_MASK; reg |= (conf->unack_fifo_depth << UDMA_S2M_COMP_CFG_2C_UNACK_FIFO_DEPTH_SHIFT) & UDMA_S2M_COMP_CFG_2C_UNACK_FIFO_DEPTH_MASK; al_reg_write32(&udma->udma_regs->s2m.s2m_comp.cfg_2c, reg); al_reg_write32(&udma->udma_regs->s2m.s2m_comp.cfg_application_ack, conf->timeout); return 0; } /** Configure the M2S UDMA scheduling mode */ int al_udma_m2s_sc_set(struct al_udma *udma, struct al_udma_m2s_dwrr_conf *sched) { uint32_t reg = al_reg_read32(&udma->udma_regs->m2s.m2s_dwrr.cfg_sched); if (sched->enable_dwrr == AL_TRUE) reg |= UDMA_M2S_DWRR_CFG_SCHED_EN_DWRR; else reg &= ~UDMA_M2S_DWRR_CFG_SCHED_EN_DWRR; if (sched->pkt_mode == AL_TRUE) reg |= UDMA_M2S_DWRR_CFG_SCHED_PKT_MODE_EN; else reg &= ~UDMA_M2S_DWRR_CFG_SCHED_PKT_MODE_EN; reg &= ~UDMA_M2S_DWRR_CFG_SCHED_WEIGHT_INC_MASK; reg |= sched->weight << UDMA_M2S_DWRR_CFG_SCHED_WEIGHT_INC_SHIFT; reg &= ~UDMA_M2S_DWRR_CFG_SCHED_INC_FACTOR_MASK; reg |= sched->inc_factor << UDMA_M2S_DWRR_CFG_SCHED_INC_FACTOR_SHIFT; al_reg_write32(&udma->udma_regs->m2s.m2s_dwrr.cfg_sched, reg); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_dwrr.ctrl_deficit_cnt); reg &= ~UDMA_M2S_DWRR_CTRL_DEFICIT_CNT_INIT_MASK; reg |= sched->deficit_init_val; al_reg_write32(&udma->udma_regs->m2s.m2s_dwrr.ctrl_deficit_cnt, reg); return 0; } /** Configure the M2S UDMA rate limitation */ int al_udma_m2s_rlimit_set(struct al_udma *udma, struct al_udma_m2s_rlimit_mode *mode) { uint32_t reg = al_reg_read32( &udma->udma_regs->m2s.m2s_rate_limiter.gen_cfg); if (mode->pkt_mode_en == AL_TRUE) reg |= UDMA_M2S_RATE_LIMITER_GEN_CFG_PKT_MODE_EN; else reg &= ~UDMA_M2S_RATE_LIMITER_GEN_CFG_PKT_MODE_EN; reg &= ~UDMA_M2S_RATE_LIMITER_GEN_CFG_SHORT_CYCLE_SIZE_MASK; reg |= mode->short_cycle_sz & UDMA_M2S_RATE_LIMITER_GEN_CFG_SHORT_CYCLE_SIZE_MASK; al_reg_write32(&udma->udma_regs->m2s.m2s_rate_limiter.gen_cfg, reg); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_rate_limiter.ctrl_token); reg &= ~UDMA_M2S_RATE_LIMITER_CTRL_TOKEN_RST_MASK; reg |= mode->token_init_val & UDMA_M2S_RATE_LIMITER_CTRL_TOKEN_RST_MASK; al_reg_write32(&udma->udma_regs->m2s.m2s_rate_limiter.ctrl_token, reg); return 0; } int al_udma_m2s_rlimit_reset(struct al_udma *udma) { uint32_t reg = al_reg_read32( &udma->udma_regs->m2s.m2s_rate_limiter.ctrl_cycle_cnt); reg |= UDMA_M2S_RATE_LIMITER_CTRL_CYCLE_CNT_RST; al_reg_write32(&udma->udma_regs->m2s.m2s_rate_limiter.ctrl_cycle_cnt, reg); return 0; } /** Configure the Stream/Q rate limitation */ static int al_udma_common_rlimit_set(struct udma_rlimit_common *regs, struct al_udma_m2s_rlimit_cfg *conf) { uint32_t reg = al_reg_read32(®s->cfg_1s); /* mask max burst size, and enable/pause control bits */ reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_MAX_BURST_SIZE_MASK; reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_EN; reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_PAUSE; reg |= conf->max_burst_sz & UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_MAX_BURST_SIZE_MASK; al_reg_write32(®s->cfg_1s, reg); reg = al_reg_read32(®s->cfg_cycle); reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_CYCLE_LONG_CYCLE_SIZE_MASK; reg |= conf->long_cycle_sz & UDMA_M2S_STREAM_RATE_LIMITER_CFG_CYCLE_LONG_CYCLE_SIZE_MASK; al_reg_write32(®s->cfg_cycle, reg); reg = al_reg_read32(®s->cfg_token_size_1); reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_TOKEN_SIZE_1_LONG_CYCLE_MASK; reg |= conf->long_cycle & UDMA_M2S_STREAM_RATE_LIMITER_CFG_TOKEN_SIZE_1_LONG_CYCLE_MASK; al_reg_write32(®s->cfg_token_size_1, reg); reg = al_reg_read32(®s->cfg_token_size_2); reg &= ~UDMA_M2S_STREAM_RATE_LIMITER_CFG_TOKEN_SIZE_2_SHORT_CYCLE_MASK; reg |= conf->short_cycle & UDMA_M2S_STREAM_RATE_LIMITER_CFG_TOKEN_SIZE_2_SHORT_CYCLE_MASK; al_reg_write32(®s->cfg_token_size_2, reg); reg = al_reg_read32(®s->mask); reg &= ~0xf; /* only bits 0-3 defined */ reg |= conf->mask & 0xf; al_reg_write32(®s->mask, reg); return 0; } static int al_udma_common_rlimit_act(struct udma_rlimit_common *regs, enum al_udma_m2s_rlimit_action act) { uint32_t reg; switch (act) { case AL_UDMA_STRM_RLIMIT_ENABLE: reg = al_reg_read32(®s->cfg_1s); reg |= UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_EN; al_reg_write32(®s->cfg_1s, reg); break; case AL_UDMA_STRM_RLIMIT_PAUSE: reg = al_reg_read32(®s->cfg_1s); reg |= UDMA_M2S_STREAM_RATE_LIMITER_CFG_1S_PAUSE; al_reg_write32(®s->cfg_1s, reg); break; case AL_UDMA_STRM_RLIMIT_RESET: reg = al_reg_read32(®s->sw_ctrl); reg |= UDMA_M2S_STREAM_RATE_LIMITER_SW_CTRL_RST_TOKEN_CNT; al_reg_write32(®s->sw_ctrl, reg); break; default: return -EINVAL; } return 0; } /** Configure the M2S Stream rate limitation */ int al_udma_m2s_strm_rlimit_set(struct al_udma *udma, struct al_udma_m2s_rlimit_cfg *conf) { struct udma_rlimit_common *rlimit_regs = &udma->udma_regs->m2s.m2s_stream_rate_limiter.rlimit; return al_udma_common_rlimit_set(rlimit_regs, conf); } int al_udma_m2s_strm_rlimit_act(struct al_udma *udma, enum al_udma_m2s_rlimit_action act) { struct udma_rlimit_common *rlimit_regs = &udma->udma_regs->m2s.m2s_stream_rate_limiter.rlimit; if (al_udma_common_rlimit_act(rlimit_regs, act) == -EINVAL) { al_err("udma [%s]: udma stream rate limit invalid action " "(%d)\n", udma->name, act); return -EINVAL; } return 0; } /** Configure the M2S UDMA Q rate limitation */ int al_udma_m2s_q_rlimit_set(struct al_udma_q *udma_q, struct al_udma_m2s_rlimit_cfg *conf) { struct udma_rlimit_common *rlimit_regs = &udma_q->q_regs->m2s_q.rlimit; return al_udma_common_rlimit_set(rlimit_regs, conf); } int al_udma_m2s_q_rlimit_act(struct al_udma_q *udma_q, enum al_udma_m2s_rlimit_action act) { struct udma_rlimit_common *rlimit_regs = &udma_q->q_regs->m2s_q.rlimit; if (al_udma_common_rlimit_act(rlimit_regs, act) == -EINVAL) { al_err("udma [%s %d]: udma stream rate limit invalid action " "(%d)\n", udma_q->udma->name, udma_q->qid, act); return -EINVAL; } return 0; } /** Configure the M2S UDMA Q scheduling mode */ int al_udma_m2s_q_sc_set(struct al_udma_q *udma_q, struct al_udma_m2s_q_dwrr_conf *conf) { uint32_t reg = al_reg_read32(&udma_q->q_regs->m2s_q.dwrr_cfg_1); reg &= ~UDMA_M2S_Q_DWRR_CFG_1_MAX_DEFICIT_CNT_SIZE_MASK; reg |= conf->max_deficit_cnt_sz & UDMA_M2S_Q_DWRR_CFG_1_MAX_DEFICIT_CNT_SIZE_MASK; if (conf->strict == AL_TRUE) reg |= UDMA_M2S_Q_DWRR_CFG_1_STRICT; else reg &= ~UDMA_M2S_Q_DWRR_CFG_1_STRICT; al_reg_write32(&udma_q->q_regs->m2s_q.dwrr_cfg_1, reg); reg = al_reg_read32(&udma_q->q_regs->m2s_q.dwrr_cfg_2); reg &= ~UDMA_M2S_Q_DWRR_CFG_2_Q_QOS_MASK; reg |= (conf->axi_qos << UDMA_M2S_Q_DWRR_CFG_2_Q_QOS_SHIFT) & UDMA_M2S_Q_DWRR_CFG_2_Q_QOS_MASK; reg &= ~UDMA_M2S_Q_DWRR_CFG_2_Q_QOS_MASK; reg |= conf->q_qos & UDMA_M2S_Q_DWRR_CFG_2_Q_QOS_MASK; al_reg_write32(&udma_q->q_regs->m2s_q.dwrr_cfg_2, reg); reg = al_reg_read32(&udma_q->q_regs->m2s_q.dwrr_cfg_3); reg &= ~UDMA_M2S_Q_DWRR_CFG_3_WEIGHT_MASK; reg |= conf->weight & UDMA_M2S_Q_DWRR_CFG_3_WEIGHT_MASK; al_reg_write32(&udma_q->q_regs->m2s_q.dwrr_cfg_3, reg); return 0; } int al_udma_m2s_q_sc_pause(struct al_udma_q *udma_q, al_bool set) { uint32_t reg = al_reg_read32(&udma_q->q_regs->m2s_q.dwrr_cfg_1); if (set == AL_TRUE) reg |= UDMA_M2S_Q_DWRR_CFG_1_PAUSE; else reg &= ~UDMA_M2S_Q_DWRR_CFG_1_PAUSE; al_reg_write32(&udma_q->q_regs->m2s_q.dwrr_cfg_1, reg); return 0; } int al_udma_m2s_q_sc_reset(struct al_udma_q *udma_q) { uint32_t reg = al_reg_read32(&udma_q->q_regs->m2s_q.dwrr_sw_ctrl); reg |= UDMA_M2S_Q_DWRR_SW_CTRL_RST_CNT; al_reg_write32(&udma_q->q_regs->m2s_q.dwrr_sw_ctrl, reg); return 0; } /** M2S UDMA completion and application timeouts */ int al_udma_m2s_comp_timeouts_set(struct al_udma *udma, struct al_udma_m2s_comp_timeouts *conf) { uint32_t reg = al_reg_read32(&udma->udma_regs->m2s.m2s_comp.cfg_1c); if (conf->sch_mode == SRR) reg |= UDMA_M2S_COMP_CFG_1C_FORCE_RR; else if (conf->sch_mode == STRICT) reg &= ~UDMA_M2S_COMP_CFG_1C_FORCE_RR; else { al_err("udma [%s]: requested completion descriptor preferch " "arbiter mode (%d) is invalid\n", udma->name, conf->sch_mode); return -EINVAL; } if (conf->enable_q_promotion == AL_TRUE) reg |= UDMA_M2S_COMP_CFG_1C_Q_PROMOTION; else reg &= ~UDMA_M2S_COMP_CFG_1C_Q_PROMOTION; reg &= ~UDMA_M2S_COMP_CFG_1C_COMP_FIFO_DEPTH_MASK; reg |= conf->comp_fifo_depth << UDMA_M2S_COMP_CFG_1C_COMP_FIFO_DEPTH_SHIFT; reg &= ~UDMA_M2S_COMP_CFG_1C_UNACK_FIFO_DEPTH_MASK; reg |= conf->unack_fifo_depth << UDMA_M2S_COMP_CFG_1C_UNACK_FIFO_DEPTH_SHIFT; al_reg_write32(&udma->udma_regs->m2s.m2s_comp.cfg_1c, reg); al_reg_write32(&udma->udma_regs->m2s.m2s_comp.cfg_coal , conf->coal_timeout); reg = al_reg_read32(&udma->udma_regs->m2s.m2s_comp.cfg_application_ack); reg &= ~UDMA_M2S_COMP_CFG_APPLICATION_ACK_TOUT_MASK; reg |= conf->app_timeout << UDMA_M2S_COMP_CFG_APPLICATION_ACK_TOUT_SHIFT; al_reg_write32(&udma->udma_regs->m2s.m2s_comp.cfg_application_ack, reg); return 0; } int al_udma_m2s_comp_timeouts_get(struct al_udma *udma, struct al_udma_m2s_comp_timeouts *conf) { uint32_t reg = al_reg_read32(&udma->udma_regs->m2s.m2s_comp.cfg_1c); if (reg & UDMA_M2S_COMP_CFG_1C_FORCE_RR) conf->sch_mode = SRR; else conf->sch_mode = STRICT; if (reg & UDMA_M2S_COMP_CFG_1C_Q_PROMOTION) conf->enable_q_promotion = AL_TRUE; else conf->enable_q_promotion = AL_FALSE; conf->comp_fifo_depth = AL_REG_FIELD_GET(reg, UDMA_M2S_COMP_CFG_1C_COMP_FIFO_DEPTH_MASK, UDMA_M2S_COMP_CFG_1C_COMP_FIFO_DEPTH_SHIFT); conf->unack_fifo_depth = AL_REG_FIELD_GET(reg, UDMA_M2S_COMP_CFG_1C_UNACK_FIFO_DEPTH_MASK, UDMA_M2S_COMP_CFG_1C_UNACK_FIFO_DEPTH_SHIFT); conf->coal_timeout = al_reg_read32( &udma->udma_regs->m2s.m2s_comp.cfg_coal); reg = al_reg_read32( &udma->udma_regs->m2s.m2s_comp.cfg_application_ack); conf->app_timeout = AL_REG_FIELD_GET(reg, UDMA_M2S_COMP_CFG_APPLICATION_ACK_TOUT_MASK, UDMA_M2S_COMP_CFG_APPLICATION_ACK_TOUT_SHIFT); return 0; } /** * S2M UDMA configure no descriptors behaviour */ int al_udma_s2m_no_desc_cfg_set(struct al_udma *udma, al_bool drop_packet, al_bool gen_interrupt, uint32_t wait_for_desc_timeout) { uint32_t reg; reg = al_reg_read32(&udma->udma_regs->s2m.s2m_wr.data_cfg_2); if ((drop_packet == AL_TRUE) && (wait_for_desc_timeout == 0)) { al_err("udam [%s]: setting timeout to 0 will cause the udma to wait forever instead of dropping the packet", udma->name); return -EINVAL; } if (drop_packet == AL_TRUE) reg |= UDMA_S2M_WR_DATA_CFG_2_DROP_IF_NO_DESC; else reg &= ~UDMA_S2M_WR_DATA_CFG_2_DROP_IF_NO_DESC; if (gen_interrupt == AL_TRUE) reg |= UDMA_S2M_WR_DATA_CFG_2_HINT_IF_NO_DESC; else reg &= ~UDMA_S2M_WR_DATA_CFG_2_HINT_IF_NO_DESC; AL_REG_FIELD_SET(reg, UDMA_S2M_WR_DATA_CFG_2_DESC_WAIT_TIMER_MASK, UDMA_S2M_WR_DATA_CFG_2_DESC_WAIT_TIMER_SHIFT, wait_for_desc_timeout); al_reg_write32(&udma->udma_regs->s2m.s2m_wr.data_cfg_2, reg); return 0; } /* S2M UDMA configure a queue's completion update */ int al_udma_s2m_q_compl_updade_config(struct al_udma_q *udma_q, al_bool enable) { uint32_t reg = al_reg_read32(&udma_q->q_regs->s2m_q.comp_cfg); if (enable == AL_TRUE) reg |= UDMA_S2M_Q_COMP_CFG_EN_COMP_RING_UPDATE; else reg &= ~UDMA_S2M_Q_COMP_CFG_EN_COMP_RING_UPDATE; al_reg_write32(&udma_q->q_regs->s2m_q.comp_cfg, reg); return 0; } /* S2M UDMA configure a queue's completion descriptors coalescing */ int al_udma_s2m_q_compl_coal_config(struct al_udma_q *udma_q, al_bool enable, uint32_t coal_timeout) { uint32_t reg = al_reg_read32(&udma_q->q_regs->s2m_q.comp_cfg); if (enable == AL_TRUE) reg &= ~UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL; else reg |= UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL; al_reg_write32(&udma_q->q_regs->s2m_q.comp_cfg, reg); al_reg_write32(&udma_q->q_regs->s2m_q.comp_cfg_2, coal_timeout); return 0; } /* S2M UDMA configure completion descriptors write burst parameters */ int al_udma_s2m_compl_desc_burst_config(struct al_udma *udma, uint16_t burst_size) { if ((burst_size != 64) && (burst_size != 128) && (burst_size != 256)) { al_err("%s: invalid burst_size value (%d)\n", __func__, burst_size); return -EINVAL; } /* convert burst size from bytes to beats (16 byte) */ burst_size = burst_size / 16; al_reg_write32_masked(&udma->udma_regs->s2m.axi_s2m.desc_wr_cfg_1, UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_MASK | UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK, burst_size << UDMA_AXI_S2M_DESC_WR_CFG_1_MIN_AXI_BEATS_SHIFT | burst_size << UDMA_AXI_S2M_DESC_WR_CFG_1_MAX_AXI_BEATS_SHIFT); return 0; } /* S2M UDMA configure a queue's completion descriptors header split */ int al_udma_s2m_q_compl_hdr_split_config(struct al_udma_q *udma_q, al_bool enable, al_bool force_hdr_split, uint32_t hdr_len) { uint32_t reg = al_reg_read32(&udma_q->q_regs->s2m_q.pkt_cfg); reg &= ~UDMA_S2M_Q_PKT_CFG_HDR_SPLIT_SIZE_MASK; reg &= ~UDMA_S2M_Q_PKT_CFG_EN_HDR_SPLIT; reg &= ~UDMA_S2M_Q_PKT_CFG_FORCE_HDR_SPLIT; if (enable == AL_TRUE) { reg |= hdr_len & UDMA_S2M_Q_PKT_CFG_HDR_SPLIT_SIZE_MASK; reg |= UDMA_S2M_Q_PKT_CFG_EN_HDR_SPLIT; if (force_hdr_split == AL_TRUE) reg |= UDMA_S2M_Q_PKT_CFG_FORCE_HDR_SPLIT; } al_reg_write32(&udma_q->q_regs->s2m_q.pkt_cfg, reg); return 0; } /* S2M UDMA per queue completion configuration */ int al_udma_s2m_q_comp_set(struct al_udma_q *udma_q, struct al_udma_s2m_q_comp_conf *conf) { uint32_t reg = al_reg_read32(&udma_q->q_regs->s2m_q.comp_cfg); if (conf->en_comp_ring_update == AL_TRUE) reg |= UDMA_S2M_Q_COMP_CFG_EN_COMP_RING_UPDATE; else reg &= ~UDMA_S2M_Q_COMP_CFG_EN_COMP_RING_UPDATE; if (conf->dis_comp_coal == AL_TRUE) reg |= UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL; else reg &= ~UDMA_S2M_Q_COMP_CFG_DIS_COMP_COAL; al_reg_write32(&udma_q->q_regs->s2m_q.comp_cfg, reg); al_reg_write32(&udma_q->q_regs->s2m_q.comp_cfg_2, conf->comp_timer); reg = al_reg_read32(&udma_q->q_regs->s2m_q.pkt_cfg); reg &= ~UDMA_S2M_Q_PKT_CFG_HDR_SPLIT_SIZE_MASK; reg |= conf->hdr_split_size & UDMA_S2M_Q_PKT_CFG_HDR_SPLIT_SIZE_MASK; if (conf->force_hdr_split == AL_TRUE) reg |= UDMA_S2M_Q_PKT_CFG_FORCE_HDR_SPLIT; else reg &= ~UDMA_S2M_Q_PKT_CFG_FORCE_HDR_SPLIT; if (conf->en_hdr_split == AL_TRUE) reg |= UDMA_S2M_Q_PKT_CFG_EN_HDR_SPLIT; else reg &= ~UDMA_S2M_Q_PKT_CFG_EN_HDR_SPLIT; al_reg_write32(&udma_q->q_regs->s2m_q.pkt_cfg, reg); reg = al_reg_read32(&udma_q->q_regs->s2m_q.qos_cfg); reg &= ~UDMA_S2M_QOS_CFG_Q_QOS_MASK; reg |= conf->q_qos & UDMA_S2M_QOS_CFG_Q_QOS_MASK; al_reg_write32(&udma_q->q_regs->s2m_q.qos_cfg, reg); return 0; } /* UDMA Target-ID control configuration per queue */ void al_udma_gen_tgtid_conf_queue_set( struct unit_regs *unit_regs, struct al_udma_gen_tgtid_conf *conf, uint32_t qid) { uint32_t *tx_tgtid_reg, *rx_tgtid_reg, *tx_tgtaddr_reg, *rx_tgtaddr_reg; unsigned int rev_id; al_assert(qid < DMA_MAX_Q); rev_id = al_udma_get_revision(unit_regs); /* Target-ID TX DESC EN */ al_reg_write32_masked(&unit_regs->gen.tgtid.cfg_tgtid_0, (conf->tx_q_conf[qid].desc_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_TX_Q_TGTID_DESC_EN_SHIFT, (conf->tx_q_conf[qid].desc_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_TX_Q_TGTID_DESC_EN_SHIFT); /* Target-ID TX QUEUE EN */ al_reg_write32_masked(&unit_regs->gen.tgtid.cfg_tgtid_0, (conf->tx_q_conf[qid].queue_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_TX_Q_TGTID_QUEUE_EN_SHIFT, (conf->tx_q_conf[qid].queue_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_TX_Q_TGTID_QUEUE_EN_SHIFT); /* Target-ID RX DESC EN */ al_reg_write32_masked(&unit_regs->gen.tgtid.cfg_tgtid_0, (conf->rx_q_conf[qid].desc_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_RX_Q_TGTID_DESC_EN_SHIFT, (conf->rx_q_conf[qid].desc_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_RX_Q_TGTID_DESC_EN_SHIFT); /* Target-ID RX QUEUE EN */ al_reg_write32_masked(&unit_regs->gen.tgtid.cfg_tgtid_0, (conf->rx_q_conf[qid].queue_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_RX_Q_TGTID_QUEUE_EN_SHIFT, (conf->rx_q_conf[qid].queue_en << qid) << UDMA_GEN_TGTID_CFG_TGTID_0_RX_Q_TGTID_QUEUE_EN_SHIFT); switch (qid) { case 0: case 1: tx_tgtid_reg = &unit_regs->gen.tgtid.cfg_tgtid_1; rx_tgtid_reg = &unit_regs->gen.tgtid.cfg_tgtid_3; tx_tgtaddr_reg = &unit_regs->gen.tgtaddr.cfg_tgtaddr_0; rx_tgtaddr_reg = &unit_regs->gen.tgtaddr.cfg_tgtaddr_2; break; case 2: case 3: tx_tgtid_reg = &unit_regs->gen.tgtid.cfg_tgtid_2; rx_tgtid_reg = &unit_regs->gen.tgtid.cfg_tgtid_4; tx_tgtaddr_reg = &unit_regs->gen.tgtaddr.cfg_tgtaddr_1; rx_tgtaddr_reg = &unit_regs->gen.tgtaddr.cfg_tgtaddr_3; break; default: al_assert(AL_FALSE); return; } al_reg_write32_masked(tx_tgtid_reg, UDMA_GEN_TGTID_CFG_TGTID_MASK(qid), conf->tx_q_conf[qid].tgtid << UDMA_GEN_TGTID_CFG_TGTID_SHIFT(qid)); al_reg_write32_masked(rx_tgtid_reg, UDMA_GEN_TGTID_CFG_TGTID_MASK(qid), conf->rx_q_conf[qid].tgtid << UDMA_GEN_TGTID_CFG_TGTID_SHIFT(qid)); if (rev_id >= AL_UDMA_REV_ID_REV2) { al_reg_write32_masked(tx_tgtaddr_reg, UDMA_GEN_TGTADDR_CFG_MASK(qid), conf->tx_q_conf[qid].tgtaddr << UDMA_GEN_TGTADDR_CFG_SHIFT(qid)); al_reg_write32_masked(rx_tgtaddr_reg, UDMA_GEN_TGTADDR_CFG_MASK(qid), conf->rx_q_conf[qid].tgtaddr << UDMA_GEN_TGTADDR_CFG_SHIFT(qid)); } } /* UDMA Target-ID control configuration */ void al_udma_gen_tgtid_conf_set( struct unit_regs *unit_regs, struct al_udma_gen_tgtid_conf *conf) { int i; for (i = 0; i < DMA_MAX_Q; i++) al_udma_gen_tgtid_conf_queue_set(unit_regs, conf, i); } /* UDMA Target-ID MSIX control configuration */ void al_udma_gen_tgtid_msix_conf_set( struct unit_regs *unit_regs, struct al_udma_gen_tgtid_msix_conf *conf) { al_reg_write32_masked( &unit_regs->gen.tgtid.cfg_tgtid_0, UDMA_GEN_TGTID_CFG_TGTID_0_MSIX_TGTID_ACCESS_EN | UDMA_GEN_TGTID_CFG_TGTID_0_MSIX_TGTID_SEL, (conf->access_en ? UDMA_GEN_TGTID_CFG_TGTID_0_MSIX_TGTID_ACCESS_EN : 0) | (conf->sel ? UDMA_GEN_TGTID_CFG_TGTID_0_MSIX_TGTID_SEL : 0)); }