194 lines
4.8 KiB
C
194 lines
4.8 KiB
C
|
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
/* DMA module for ISH */
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "console.h"
|
||
|
#include "registers.h"
|
||
|
#include "ish_dma.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
static int dma_init_called; /* If ish_dma_init is called */
|
||
|
|
||
|
static int dma_poll(uint32_t addr, uint32_t expected, uint32_t mask)
|
||
|
{
|
||
|
int retval = -1;
|
||
|
uint32_t counter = 0;
|
||
|
|
||
|
/*
|
||
|
* The timeout is approximately 2.2 seconds according to
|
||
|
* value of UINT32_MAX, 120MHZ ISH clock frequency and
|
||
|
* instruction count which is around 4.
|
||
|
*/
|
||
|
while (counter < (UINT32_MAX / 64)) {
|
||
|
/* test condition */
|
||
|
if ((REG32(addr) & mask) == expected) {
|
||
|
retval = DMA_RC_OK;
|
||
|
break;
|
||
|
}
|
||
|
counter++;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
void ish_dma_ocp_timeout_disable(void)
|
||
|
{
|
||
|
uint32_t ctrl = OCP_AGENT_CONTROL;
|
||
|
|
||
|
OCP_AGENT_CONTROL = ctrl & OCP_RESPONSE_TO_DISABLE;
|
||
|
}
|
||
|
|
||
|
static inline uint32_t interrupt_lock(void)
|
||
|
{
|
||
|
uint32_t eflags = 0;
|
||
|
__asm__ volatile("pushfl;" /* save eflag value */
|
||
|
"popl %0;"
|
||
|
"cli;"
|
||
|
: "=r"(eflags)); /* shut off interrupts */
|
||
|
return eflags;
|
||
|
}
|
||
|
|
||
|
static inline void interrupt_unlock(uint32_t eflags)
|
||
|
{
|
||
|
__asm__ volatile("pushl %0;" /* restore elfag values */
|
||
|
"popfl;"
|
||
|
:
|
||
|
: "r"(eflags));
|
||
|
}
|
||
|
|
||
|
void dma_configure_psize(void)
|
||
|
{
|
||
|
/* Give chan0 512 bytes for high performance, and chan1 128 bytes. */
|
||
|
DMA_PSIZE_01 = DMA_PSIZE_UPDATE |
|
||
|
(DMA_PSIZE_CHAN1_SIZE << DMA_PSIZE_CHAN1_OFFSET) |
|
||
|
(DMA_PSIZE_CHAN0_SIZE << DMA_PSIZE_CHAN0_OFFSET);
|
||
|
}
|
||
|
|
||
|
void ish_dma_init(void)
|
||
|
{
|
||
|
uint32_t uma_msb;
|
||
|
|
||
|
ish_dma_ocp_timeout_disable();
|
||
|
|
||
|
/* configure DMA partition size */
|
||
|
dma_configure_psize();
|
||
|
|
||
|
/* set DRAM address 32 MSB for DMA transactions on UMA */
|
||
|
uma_msb = IPC_UMA_RANGE_LOWER_1;
|
||
|
ish_dma_set_msb(PAGING_CHAN, uma_msb, uma_msb);
|
||
|
|
||
|
dma_init_called = 1;
|
||
|
}
|
||
|
|
||
|
int ish_dma_copy(uint32_t chan, uint32_t dst, uint32_t src, uint32_t length,
|
||
|
enum dma_mode mode)
|
||
|
{
|
||
|
uint32_t chan_reg = DMA_REG_BASE + (DMA_CH_REGS_SIZE * chan);
|
||
|
int rc = DMA_RC_OK;
|
||
|
uint32_t eflags;
|
||
|
uint32_t chunk;
|
||
|
|
||
|
__asm__ volatile("\twbinvd\n"); /* Flush cache before dma start */
|
||
|
|
||
|
/* Bringup VNN power rail for accessing SoC fabric */
|
||
|
PMU_VNN_REQ = (1 << VNN_ID_DMA(chan));
|
||
|
while (!(PMU_VNN_REQ_ACK & PMU_VNN_REQ_ACK_STATUS))
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* shut off interrupts to assure no simultanious
|
||
|
* access to DMA registers
|
||
|
*/
|
||
|
eflags = interrupt_lock();
|
||
|
|
||
|
MISC_CHID_CFG_REG = chan; /* Set channel to configure */
|
||
|
|
||
|
mode |= NON_SNOOP;
|
||
|
MISC_DMA_CTL_REG(chan) = mode; /* Set transfer direction */
|
||
|
|
||
|
DMA_CFG_REG = DMA_ENABLE; /* Enable DMA module */
|
||
|
DMA_LLP(chan_reg) = 0; /* Linked lists are not used */
|
||
|
DMA_CTL_LOW(chan_reg) =
|
||
|
0 /* Set transfer parameters */ |
|
||
|
(DMA_CTL_TT_FC_M2M_DMAC << DMA_CTL_TT_FC_SHIFT) |
|
||
|
(DMA_CTL_ADDR_INC << DMA_CTL_SINC_SHIFT) |
|
||
|
(DMA_CTL_ADDR_INC << DMA_CTL_DINC_SHIFT) |
|
||
|
(SRC_TR_WIDTH << DMA_CTL_SRC_TR_WIDTH_SHIFT) |
|
||
|
(DEST_TR_WIDTH << DMA_CTL_DST_TR_WIDTH_SHIFT) |
|
||
|
(SRC_BURST_SIZE << DMA_CTL_SRC_MSIZE_SHIFT) |
|
||
|
(DEST_BURST_SIZE << DMA_CTL_DEST_MSIZE_SHIFT);
|
||
|
|
||
|
interrupt_unlock(eflags);
|
||
|
while (length) {
|
||
|
chunk = (length > DMA_MAX_BLOCK_SIZE) ? DMA_MAX_BLOCK_SIZE
|
||
|
: length;
|
||
|
|
||
|
if (rc != DMA_RC_OK)
|
||
|
break;
|
||
|
|
||
|
eflags = interrupt_lock();
|
||
|
MISC_CHID_CFG_REG = chan; /* Set channel to configure */
|
||
|
DMA_CTL_HIGH(chan_reg) =
|
||
|
chunk; /* Set number of bytes to transfer */
|
||
|
DMA_DAR(chan_reg) = dst; /* Destination address */
|
||
|
DMA_SAR(chan_reg) = src; /* Source address */
|
||
|
DMA_EN_REG = DMA_CH_EN_BIT(chan) |
|
||
|
DMA_CH_EN_WE_BIT(chan); /* Enable the channel */
|
||
|
interrupt_unlock(eflags);
|
||
|
|
||
|
rc = ish_wait_for_dma_done(
|
||
|
chan); /* Wait for trans completion */
|
||
|
|
||
|
dst += chunk;
|
||
|
src += chunk;
|
||
|
length -= chunk;
|
||
|
}
|
||
|
|
||
|
/* Mark the DMA VNN power rail as no longer needed */
|
||
|
PMU_VNN_REQ = (1 << VNN_ID_DMA(chan));
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
void ish_dma_disable(void)
|
||
|
{
|
||
|
uint32_t channel;
|
||
|
uint32_t counter;
|
||
|
|
||
|
/* Disable DMA on per-channel basis. */
|
||
|
for (channel = 0; channel <= DMA_MAX_CHANNEL; channel++) {
|
||
|
MISC_CHID_CFG_REG = channel;
|
||
|
if (DMA_EN_REG & DMA_CH_EN_BIT(channel)) {
|
||
|
/* Write 0 to channel enable bit ... */
|
||
|
DMA_EN_REG = DMA_CH_EN_WE_BIT(channel);
|
||
|
|
||
|
/* Wait till it shuts up. */
|
||
|
counter = 0;
|
||
|
while ((DMA_EN_REG & DMA_CH_EN_BIT(channel)) &&
|
||
|
counter < (UINT32_MAX / 64))
|
||
|
counter++;
|
||
|
}
|
||
|
}
|
||
|
DMA_CLR_ERR_REG = 0xFFFFFFFF;
|
||
|
DMA_CLR_BLOCK_REG = 0xFFFFFFFF;
|
||
|
|
||
|
DMA_CFG_REG = 0; /* Disable DMA module */
|
||
|
}
|
||
|
|
||
|
int ish_wait_for_dma_done(uint32_t ch)
|
||
|
{
|
||
|
return dma_poll(DMA_EN_REG_ADDR, 0, DMA_CH_EN_BIT(ch));
|
||
|
}
|
||
|
|
||
|
void ish_dma_set_msb(uint32_t chan, uint32_t dst_msb, uint32_t src_msb)
|
||
|
{
|
||
|
uint32_t eflags = interrupt_lock();
|
||
|
MISC_CHID_CFG_REG = chan; /* Set channel to configure */
|
||
|
MISC_SRC_FILLIN_DMA(chan) = src_msb;
|
||
|
MISC_DST_FILLIN_DMA(chan) = dst_msb;
|
||
|
interrupt_unlock(eflags);
|
||
|
}
|