/* Copyright (c) 2012,2015-2018 The Linux Foundation. All rights reserved. * * 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. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * 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. */ #include #include #include #include #define HLOS_EE_INDEX 0 #define TIMEOUT 2000 /* Resets pipe registers and state machines */ void bam_pipe_reset(struct bam_instance *bam, uint8_t pipe_num) { /* Start sw reset of the pipe to be allocated */ writel(1, BAM_P_RSTn(bam->pipe[pipe_num].pipe_num, bam->base)); /* No delay required */ /* Stop sw reset of the pipe to be allocated */ writel(0, BAM_P_RSTn(bam->pipe[pipe_num].pipe_num, bam->base)); } /* A blocking function that waits till an interrupt is signalled. * bam : BAM instance for the descriptors to be queued. * pipe_num : pipe number for the descriptors to be queued. * interrupt: interrupt to wait for. */ int bam_wait_for_interrupt(struct bam_instance *bam, uint8_t pipe_num, enum p_int_type interrupt) { uint32_t val; uint32_t start; while (1) { start = get_timer(0); /* Wait for a interrupt on the right pipe */ do{ /* Determine the pipe causing the interrupt */ val = readl(BAM_IRQ_SRCS(bam->base, bam->ee)); if(get_timer(start) >= TIMEOUT) return BAM_RESULT_FAILURE; /* Flush out the right most global interrupt bit */ } while (!((val & BAM_IRQ_SRCS_PIPE_MASK) & (1 << bam->pipe[pipe_num].pipe_num))); /* Check the interrupt type */ /* Read interrupt status register */ val = readl(BAM_P_IRQ_STTSn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Check for error */ if (val & P_ERR_EN_MASK) goto bam_wait_int_error; if (val & interrupt) { /* Correct interrupt was fired. */ /* Clear the other interrupts */ val = P_OUT_OF_DESC_EN_MASK | P_PRCSD_DESC_EN_MASK | P_TRNSFR_END_EN_MASK; writel (val, BAM_P_IRQ_CLRn(bam->pipe[pipe_num].pipe_num, bam->base)); return BAM_RESULT_SUCCESS; } } bam_wait_int_error: printf("bam: Unexpected interrupt : val %u\n", val); return BAM_RESULT_FAILURE; } /* Enable BAM and pipe level interrupts */ void bam_enable_interrupts(struct bam_instance *bam, uint8_t pipe_num) { uint32_t int_mask = P_ERR_EN_MASK | P_OUT_OF_DESC_EN_MASK | P_PRCSD_DESC_EN_MASK; uint32_t val; /* Leave BAM error interrupts disabled. */ /* Enable the interrupts for the pipe by enabling the relevant bits * in the BAM_PIPE_INTERRUPT_ENABLE register. */ writel(int_mask, BAM_P_IRQ_ENn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Enable pipe interrups */ /* Do read-modify-write */ val = readl(BAM_IRQ_SRCS_MSK(bam->base, bam->ee)); writel((1 << bam->pipe[pipe_num].pipe_num) | val, BAM_IRQ_SRCS_MSK(bam->base, bam->ee)); } /* Reset and initialize the bam module */ void bam_init(struct bam_instance *bam) { uint32_t val = 0; /* Check for only one pipe's direction. * The other is assumed to be the opposite system * transaction. */ if (bam->pipe[0].trans_type == SYS2BAM || bam->pipe[0].trans_type == BAM2SYS) { /* Program the threshold count */ writel(bam->threshold, BAM_DESC_CNT_TRSHLD_REG(bam->base)); } /* Program config register for H/W bug fixes */ val = 0xffffffff & ~(1 << 11); writel(val, BAM_CNFG_BITS(bam->base)); val = readl(BAM_CTRL_REG(bam->base)); val = val | BAM_ENABLE_BIT_MASK; /* Enable the BAM */ writel(val, BAM_CTRL_REG(bam->base)); } /* Funtion to setup a simple fifo structure. * Note: Addr should be 8 byte aligned. * bam : BAM instance for the descriptors to be queued. * pipe_num : pipe number for the descriptors to be queued. */ int bam_pipe_fifo_init(struct bam_instance *bam, uint8_t pipe_num) { if (bam->pipe[pipe_num].fifo.size > 0x7FFF) { printf("bam: Size exceeds max size for a descriptor(0x7FFF)\n"); return BAM_RESULT_FAILURE; } /* Check if fifo start is 8-byte alligned */ if ((addr_t)bam->pipe[pipe_num].fifo.head & 0x7) { return BAM_RESULT_FAILURE; } /* Check if fifo size is a power of 2. * The circular fifo logic in lk expects this. */ if (!(ispow2(bam->pipe[pipe_num].fifo.size))) { return BAM_RESULT_FAILURE; } bam->pipe[pipe_num].fifo.current = bam->pipe[pipe_num].fifo.head; /* Set the descriptor buffer size. Must be a multiple of 8 */ writel(bam->pipe[pipe_num].fifo.size * BAM_DESC_SIZE, BAM_P_FIFO_SIZESn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Write descriptors FIFO base addr must be 8-byte aligned */ /* Needs a physical address conversion as we are setting up * the base of the FIFO for the BAM state machine. */ writel((uint32_t)((addr_t)bam->pipe[pipe_num].fifo.head), BAM_P_DESC_FIFO_ADDRn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Initialize FIFO offset for the first read */ bam->pipe[pipe_num].fifo.offset = BAM_DESC_SIZE; writel(P_ENABLE | readl(BAM_P_CTRLn(bam->pipe[pipe_num].pipe_num, bam->base)), BAM_P_CTRLn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Everything is set. * Flag pipe init done. */ bam->pipe[pipe_num].initialized = 1; return BAM_RESULT_SUCCESS; } void bam_sys_pipe_init(struct bam_instance *bam, uint8_t pipe_num) { /* Reset the pipe to be allocated */ bam_pipe_reset(bam, pipe_num); /* Enable minimal interrupts */ bam_enable_interrupts(bam, pipe_num); /* Pipe event threshold register is not relevant in sys modes */ /* Enable pipe in system mode and set the direction */ writel(P_SYS_MODE_MASK | bam->pipe[pipe_num].lock_grp << P_LOCK_GRP_SHIFT | (bam->pipe[pipe_num].trans_type << P_DIRECTION_SHIFT), BAM_P_CTRLn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Mark the pipe FIFO as uninitialized. */ bam->pipe[pipe_num].initialized = 0; } /* Function to notify written descriptors to BAM. * bam : BAM instance for the descriptors to be queued. * pipe_num : pipe number for the descriptors to be queued. * num_desc : number of the descriptors. * fifo : Circular FIFO used for the descriptors. */ void bam_sys_gen_event(struct bam_instance *bam, uint8_t pipe_num, unsigned int num_desc) { uint32_t val = 0; if (num_desc >= bam->pipe[pipe_num].fifo.size) { printf("bam: Max allowed desc is one less than the fifo length\n"); return; } /* bits 0:15 of BAM_P_EVNT_REGn denotes the offset. We read the offset, * and update the offset to notify BAM HW that new descriptors have been written */ val = readl(BAM_P_EVNT_REGn(bam->pipe[pipe_num].pipe_num, bam->base)); /* Update the fifo peer offset */ val += (num_desc) * BAM_DESC_SIZE; val &= (bam->pipe[pipe_num].fifo.size * BAM_DESC_SIZE - 1); writel(val, BAM_P_EVNT_REGn(bam->pipe[pipe_num].pipe_num, bam->base)); } /* Function to read the updates for FIFO offsets. * bam : BAM that uses the FIFO. * pipe : BAM pipe that uses the FIFO. * return : void. * Note : As per IPCAT This register denotes the pointer Offset of the first un-Acknowledged Descriptor. * This register is only used by the Software. After receiving an interrupt, software reads this register * in order to know what descriptors has been processed. Although being Writable, Software * should never write to this register. */ void bam_read_offset_update(struct bam_instance *bam, unsigned int pipe_num) { uint32_t offset; offset = readl(BAM_P_SW_OFSTSn(bam->pipe[pipe_num].pipe_num, bam->base)); offset &= 0xFFFF; } /* Function to get the next desc address. * Keeps track of circular properties of the FIFO * and returns the appropriate address. */ static struct bam_desc* fifo_getnext(struct bam_desc_fifo *fifo, struct bam_desc* desc) { uint16_t offset; offset = desc - fifo->head; if (offset == (fifo->size - 1)) return fifo->head; else return desc + 1; } /* Function to add BAM descriptors for a given fifo. * bam : BAM instance to be used. * data_ptr : Memory address for data transfer. * data_len : Length of the data_ptr. * flags : Flags to be set on the last desc added. * * Note: This function also notifies the BAM about the added descriptors. */ int bam_add_desc(struct bam_instance *bam, unsigned int pipe_num, unsigned char *data_ptr, unsigned int data_len, unsigned flags) { int bam_ret = BAM_RESULT_SUCCESS; unsigned int len = data_len; unsigned int desc_len; unsigned int n = 0; unsigned int desc_flags; if (data_ptr == NULL || len == 0) { printf("bam: Wrong params for BAM transfer \n"); bam_ret = BAM_RESULT_FAILURE; goto bam_add_desc_error; } /* Check if we have enough space in FIFO */ if (len > (unsigned)bam->pipe[pipe_num].fifo.size * bam->max_desc_len) { printf("bam: Data transfer exceeds desc fifo length.\n"); bam_ret = BAM_RESULT_FAILURE; goto bam_add_desc_error; } while (len) { /* There are only 16 bits to write data length. * If more bits are needed, create more * descriptors. */ if (len > bam->max_desc_len) { desc_len = bam->max_desc_len; len -= bam->max_desc_len; desc_flags = 0; } else { desc_len = len; len = 0; /* Set correct flags on the last desc. */ desc_flags = flags; } /* Write descriptor */ bam_add_one_desc(bam, pipe_num, data_ptr, desc_len, desc_flags); data_ptr += bam->max_desc_len; n++; } /* Create a read/write event to notify the periperal of the added desc. */ bam_sys_gen_event(bam, pipe_num, n); bam_add_desc_error: return bam_ret; } /* Function to add a BAM descriptor for a given fifo. * bam : BAM instance to be used. * data_ptr : Memory address for data transfer. * data_len : Length of the data_ptr. * flags : Flags to be set on the desc added. * * Note: This function does not notify the BAM about the added descriptor. */ int bam_add_one_desc(struct bam_instance *bam, unsigned int pipe_num, unsigned char* data_ptr, uint32_t len, uint8_t flags) { struct bam_desc *desc = bam->pipe[pipe_num].fifo.current; int bam_ret = BAM_RESULT_SUCCESS; if (data_ptr == NULL || len == 0) { printf("bam: Wrong params for BAM transfer \n"); bam_ret = BAM_RESULT_FAILURE; goto bam_add_one_desc_error; } /* Check if the FIFO is allocated for the pipe */ if (!bam->pipe[pipe_num].initialized) { printf("bam: Please allocate the FIFO for the BAM pipe %d\n", bam->pipe[pipe_num].pipe_num); bam_ret = BAM_RESULT_FAILURE; goto bam_add_one_desc_error; } if ((flags & BAM_DESC_LOCK_FLAG) && (flags & BAM_DESC_UNLOCK_FLAG)) { printf("bam: Can't lock and unlock in the same desc\n"); bam_ret = BAM_RESULT_FAILURE; goto bam_add_one_desc_error; } /* Setting EOT flag on a CMD desc is not valid */ if ((flags & BAM_DESC_EOT_FLAG) && (flags & BAM_DESC_CMD_FLAG)) { printf("bam: EOT flag set on the CMD desc\n"); bam_ret = BAM_RESULT_FAILURE; goto bam_add_one_desc_error; } /* Check for the length of the desc. */ if (len > bam->max_desc_len) { printf("bam: len of the desc exceeds max length" " %d > %d\n", len, bam->max_desc_len); bam_ret = BAM_RESULT_FAILURE; goto bam_add_one_desc_error; } desc->flags = flags; desc->addr = (uint32_t)data_ptr; desc->size = (uint16_t)len; desc->reserved = 0; #if !defined(CONFIG_SYS_DCACHE_OFF) flush_dcache_range((addr_t)desc, ((addr_t)desc + BAM_DESC_SIZE)); flush_dcache_range((addr_t)data_ptr, (addr_t)data_ptr + len); #endif /* Update the FIFO to point to the head */ bam->pipe[pipe_num].fifo.current = fifo_getnext(&bam->pipe[pipe_num].fifo, desc); bam_add_one_desc_error: return bam_ret; } struct cmd_element* bam_add_cmd_element(struct cmd_element *ptr, uint32_t reg_addr, uint32_t value, enum bam_ce_cmd_t cmd_type) { /* Write cmd type. * Also, write the register address. */ ptr->addr_n_cmd = (reg_addr & ~(BAM_CE_REG_ADDR_MASK)) | (cmd_type << (BAM_CE_CMD_TYPE_SHIFT)); /* Do not mask any of the addr bits by default */ ptr->reg_mask = BAM_CE_REG_MASK; /* Write the value to be written */ ptr->reg_data = value; #if !defined(CONFIG_SYS_DCACHE_OFF) if(cmd_type == CE_READ_TYPE) flush_dcache_range((addr_t)value, ((addr_t)value + sizeof(uint32_t))); #endif /* Return the address to add the next element to */ return ptr + 1; }