487 lines
14 KiB
C
487 lines
14 KiB
C
|
/* Copyright (c) 2012, 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 <bam.h>
|
||
|
#include <reg.h>
|
||
|
#include <debug.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <arch/ops.h>
|
||
|
#include <platform.h>
|
||
|
#include <platform/interrupts.h>
|
||
|
#include <platform/iomap.h>
|
||
|
#include <platform/irqs.h>
|
||
|
#include <pow2.h>
|
||
|
|
||
|
#define HLOS_EE_INDEX 0
|
||
|
|
||
|
/* Reset BAM registers and pipes */
|
||
|
static void bam_reset(struct bam_instance *bam)
|
||
|
{
|
||
|
/* Initiate SW reset */
|
||
|
writel(BAM_SW_RST_BIT_MASK, BAM_CTRL_REG(bam->base));
|
||
|
|
||
|
/* No delay required */
|
||
|
|
||
|
/* Disable SW reset */
|
||
|
writel(~BAM_SW_RST_BIT_MASK, BAM_CTRL_REG(bam->base));
|
||
|
}
|
||
|
|
||
|
/* 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));
|
||
|
}
|
||
|
|
||
|
static enum handler_return bam_interrupt_handler(void* arg)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* 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 bamsts;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
/* Wait for a interrupt on the right pipe */
|
||
|
do{
|
||
|
/* Determine the pipe causing the interrupt */
|
||
|
val = readl(BAM_IRQ_SRCS(bam->base, bam->ee));
|
||
|
/* Flush out the right most global interrupt bit */
|
||
|
} while (!((val & 0x7FFF) & (1 << bam->pipe[pipe_num].pipe_num)));
|
||
|
|
||
|
/* Check the reason for this BAM interrupt */
|
||
|
bamsts = readl(BAM_IRQ_STTS(bam->base));
|
||
|
if (bamsts)
|
||
|
{
|
||
|
dprintf(CRITICAL,"ERROR:BAM_IRQ_STTS %u \n", bamsts);
|
||
|
goto bam_wait_int_error;
|
||
|
}
|
||
|
|
||
|
/* 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:
|
||
|
|
||
|
dprintf(CRITICAL, "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));
|
||
|
|
||
|
/* Unmask the QGIC interrupts only in the case of
|
||
|
* interrupt based transfer.
|
||
|
* Use polling othwerwise.
|
||
|
*/
|
||
|
if (bam->pipe[pipe_num].int_mode)
|
||
|
{
|
||
|
/* Register interrupt handler */
|
||
|
register_int_handler(bam->pipe[pipe_num].spi_num, bam_interrupt_handler, 0);
|
||
|
|
||
|
/* Unmask the interrupt */
|
||
|
unmask_interrupt(bam->pipe[pipe_num].spi_num);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Reset and initialize the bam module */
|
||
|
void bam_init(struct bam_instance *bam)
|
||
|
{
|
||
|
uint32_t val = 0;
|
||
|
|
||
|
// bam_reset(bam);
|
||
|
|
||
|
/* 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));
|
||
|
|
||
|
/* Enable the BAM */
|
||
|
writel(BAM_ENABLE_BIT_MASK, 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)
|
||
|
{
|
||
|
dprintf(CRITICAL,
|
||
|
"Size exceeds max size for a descriptor(0x7FFF)\n");
|
||
|
return BAM_RESULT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* Check if fifo start is 8-byte alligned */
|
||
|
ASSERT(!((uint32_t)PA((addr_t)bam->pipe[pipe_num].fifo.head & 0x7)));
|
||
|
|
||
|
/* Check if fifo size is a power of 2.
|
||
|
* The circular fifo logic in lk expects this.
|
||
|
*/
|
||
|
ASSERT(ispow2(bam->pipe[pipe_num].fifo.size));
|
||
|
|
||
|
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)PA((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) {
|
||
|
dprintf(CRITICAL,
|
||
|
"Max allowed desc is one less than the fifo length\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Update the fifo peer offset */
|
||
|
val = (num_desc - 1) * BAM_DESC_SIZE;
|
||
|
val += bam->pipe[pipe_num].fifo.offset;
|
||
|
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 : FIFO offset where the next descriptor should be written.
|
||
|
* Note : S/W maintains the circular properties of the FIFO and updates
|
||
|
* the offsets accordingly.
|
||
|
*/
|
||
|
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;
|
||
|
|
||
|
dprintf(SPEW, "Offset value is %d \n", offset);
|
||
|
|
||
|
/* Save the next offset to be written to. */
|
||
|
bam->pipe[pipe_num].fifo.current = (struct bam_desc*)
|
||
|
((uint32_t)bam->pipe[pipe_num].fifo.head + offset);
|
||
|
|
||
|
bam->pipe[pipe_num].fifo.offset = offset + BAM_DESC_SIZE ;
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
|
||
|
dprintf(SPEW, "Data length for BAM transfer is %u\n", data_len);
|
||
|
|
||
|
if (data_ptr == NULL || len == 0)
|
||
|
{
|
||
|
dprintf(CRITICAL, "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)
|
||
|
{
|
||
|
dprintf(CRITICAL, "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)
|
||
|
{
|
||
|
dprintf(CRITICAL, "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)
|
||
|
{
|
||
|
dprintf(CRITICAL, "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))
|
||
|
{
|
||
|
dprintf(CRITICAL, "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))
|
||
|
{
|
||
|
dprintf(CRITICAL, "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)
|
||
|
{
|
||
|
dprintf(CRITICAL, "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;
|
||
|
|
||
|
arch_clean_invalidate_cache_range((addr_t) desc, BAM_DESC_SIZE);
|
||
|
|
||
|
/* 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 & ~(0xFF000000)) | (cmd_type << 24);
|
||
|
|
||
|
/* Do not mask any of the addr bits by default */
|
||
|
ptr->reg_mask = 0xFFFFFFFF;
|
||
|
|
||
|
/* Write the value to be written */
|
||
|
ptr->reg_data = value;
|
||
|
|
||
|
/* Return the address to add the next element to */
|
||
|
return ptr + 1;
|
||
|
}
|