320 lines
8.9 KiB
C
320 lines
8.9 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <heap.h>
|
|
#include <seos.h>
|
|
#include <util.h>
|
|
|
|
#include <plat/cmsis.h>
|
|
#include <plat/dma.h>
|
|
#include <plat/pwr.h>
|
|
|
|
#define DMA_VERBOSE_DEBUG 0
|
|
|
|
#if DMA_VERBOSE_DEBUG
|
|
#define dmaLogDebug(x) osLog(LOG_DEBUG, x "\n")
|
|
#else
|
|
#define dmaLogDebug(x) do {} while(0)
|
|
#endif
|
|
|
|
#define STM_DMA_NUM_DEVS 2
|
|
#define STM_DMA_NUM_STREAMS 8
|
|
|
|
struct StmDmaStreamRegs {
|
|
volatile uint32_t CR;
|
|
volatile uint32_t NDTR;
|
|
volatile uint32_t PAR;
|
|
volatile uint32_t M0AR;
|
|
volatile uint32_t M1AR;
|
|
volatile uint32_t FCR;
|
|
};
|
|
|
|
struct StmDmaRegs {
|
|
volatile uint32_t LISR;
|
|
volatile uint32_t HISR;
|
|
volatile uint32_t LIFCR;
|
|
volatile uint32_t HIFCR;
|
|
struct StmDmaStreamRegs Sx[STM_DMA_NUM_STREAMS];
|
|
};
|
|
|
|
#define STM_DMA_ISR_FEIFx (1 << 0)
|
|
#define STM_DMA_ISR_DMEIFx (1 << 2)
|
|
#define STM_DMA_ISR_TEIFx (1 << 3)
|
|
#define STM_DMA_ISR_HTIFx (1 << 4)
|
|
#define STM_DMA_ISR_TCIFx (1 << 5)
|
|
#define STM_DMA_ISR_MASK \
|
|
(STM_DMA_ISR_FEIFx | STM_DMA_ISR_DMEIFx | STM_DMA_ISR_TEIFx | \
|
|
STM_DMA_ISR_HTIFx | STM_DMA_ISR_TCIFx)
|
|
|
|
#define STM_DMA_CR_EN (1 << 0)
|
|
#define STM_DMA_CR_DMEIE (1 << 1)
|
|
#define STM_DMA_CR_TEIE (1 << 2)
|
|
#define STM_DMA_CR_HTIE (1 << 3)
|
|
#define STM_DMA_CR_TCIE (1 << 4)
|
|
#define STM_DMA_CR_PFCTRL (1 << 5)
|
|
|
|
#define STM_DMA_CR_DIR(x) ((x) << 6)
|
|
|
|
#define STM_DMA_CR_MINC (1 << 10)
|
|
|
|
#define STM_DMA_CR_PSIZE(x) ((x) << 11)
|
|
|
|
#define STM_DMA_CR_MSIZE(x) ((x) << 13)
|
|
|
|
#define STM_DMA_CR_PL(x) ((x) << 16)
|
|
#define STM_DMA_CR_PBURST(x) ((x) << 21)
|
|
#define STM_DMA_CR_MBURST(x) ((x) << 23)
|
|
|
|
#define STM_DMA_CR_CHSEL(x) ((x) << 25)
|
|
#define STM_DMA_CR_CHSEL_MASK STM_DMA_CR_CHSEL(0x7)
|
|
|
|
struct StmDmaStreamState {
|
|
DmaCallbackF callback;
|
|
void *cookie;
|
|
uint16_t tid;
|
|
uint16_t reserved;
|
|
};
|
|
|
|
struct StmDmaDev {
|
|
struct StmDmaRegs *const regs;
|
|
struct StmDmaStreamState streams[STM_DMA_NUM_STREAMS];
|
|
};
|
|
|
|
static void dmaIsr(uint8_t busId, uint8_t stream);
|
|
|
|
#define DECLARE_IRQ_HANDLER(_n, _s) \
|
|
extern void DMA##_n##_Stream##_s##_IRQHandler(void); \
|
|
void DMA##_n##_Stream##_s##_IRQHandler(void) \
|
|
{ \
|
|
dmaIsr(_n - 1, _s); \
|
|
}
|
|
|
|
static struct StmDmaDev gDmaDevs[STM_DMA_NUM_DEVS] = {
|
|
[0] = {
|
|
.regs = (struct StmDmaRegs *)DMA1_BASE,
|
|
},
|
|
[1] = {
|
|
.regs = (struct StmDmaRegs *)DMA2_BASE,
|
|
},
|
|
};
|
|
DECLARE_IRQ_HANDLER(1, 0)
|
|
DECLARE_IRQ_HANDLER(1, 1)
|
|
DECLARE_IRQ_HANDLER(1, 2)
|
|
DECLARE_IRQ_HANDLER(1, 3)
|
|
DECLARE_IRQ_HANDLER(1, 4)
|
|
DECLARE_IRQ_HANDLER(1, 5)
|
|
DECLARE_IRQ_HANDLER(1, 6)
|
|
DECLARE_IRQ_HANDLER(1, 7)
|
|
DECLARE_IRQ_HANDLER(2, 0)
|
|
DECLARE_IRQ_HANDLER(2, 1)
|
|
DECLARE_IRQ_HANDLER(2, 2)
|
|
DECLARE_IRQ_HANDLER(2, 3)
|
|
DECLARE_IRQ_HANDLER(2, 4)
|
|
DECLARE_IRQ_HANDLER(2, 5)
|
|
DECLARE_IRQ_HANDLER(2, 6)
|
|
DECLARE_IRQ_HANDLER(2, 7)
|
|
|
|
static const enum IRQn STM_DMA_IRQ[STM_DMA_NUM_DEVS][STM_DMA_NUM_STREAMS] = {
|
|
[0] = {
|
|
DMA1_Stream0_IRQn,
|
|
DMA1_Stream1_IRQn,
|
|
DMA1_Stream2_IRQn,
|
|
DMA1_Stream3_IRQn,
|
|
DMA1_Stream4_IRQn,
|
|
DMA1_Stream5_IRQn,
|
|
DMA1_Stream6_IRQn,
|
|
DMA1_Stream7_IRQn,
|
|
},
|
|
[1] = {
|
|
DMA2_Stream0_IRQn,
|
|
DMA2_Stream1_IRQn,
|
|
DMA2_Stream2_IRQn,
|
|
DMA2_Stream3_IRQn,
|
|
DMA2_Stream4_IRQn,
|
|
DMA2_Stream5_IRQn,
|
|
DMA2_Stream6_IRQn,
|
|
DMA2_Stream7_IRQn,
|
|
},
|
|
};
|
|
|
|
|
|
static const uint32_t STM_DMA_CLOCK_UNIT[STM_DMA_NUM_DEVS] = {
|
|
PERIPH_AHB1_DMA1,
|
|
PERIPH_AHB1_DMA2
|
|
};
|
|
|
|
static inline struct StmDmaStreamState *dmaGetStreamState(uint8_t busId,
|
|
uint8_t stream)
|
|
{
|
|
return &gDmaDevs[busId].streams[stream];
|
|
}
|
|
|
|
static inline struct StmDmaStreamRegs *dmaGetStreamRegs(uint8_t busId,
|
|
uint8_t stream)
|
|
{
|
|
return &gDmaDevs[busId].regs->Sx[stream];
|
|
}
|
|
|
|
static const unsigned int STM_DMA_FEIFx_OFFSET[] = { 0, 6, 16, 22 };
|
|
|
|
static inline uint8_t dmaGetIsr(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaDev *dev = &gDmaDevs[busId];
|
|
if (stream < 4)
|
|
return (dev->regs->LISR >> STM_DMA_FEIFx_OFFSET[stream]) & STM_DMA_ISR_MASK;
|
|
else
|
|
return (dev->regs->HISR >> STM_DMA_FEIFx_OFFSET[stream - 4]) & STM_DMA_ISR_MASK;
|
|
}
|
|
|
|
static inline void dmaClearIsr(uint8_t busId, uint8_t stream, uint8_t mask)
|
|
{
|
|
struct StmDmaDev *dev = &gDmaDevs[busId];
|
|
if (stream < 4)
|
|
dev->regs->LIFCR = mask << STM_DMA_FEIFx_OFFSET[stream];
|
|
else
|
|
dev->regs->HIFCR = mask << STM_DMA_FEIFx_OFFSET[stream - 4];
|
|
}
|
|
|
|
static void dmaIsrTeif(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
struct StmDmaStreamRegs *regs = dmaGetStreamRegs(busId, stream);
|
|
|
|
dmaLogDebug("teif");
|
|
dmaStop(busId, stream);
|
|
|
|
uint16_t oldTid = osSetCurrentTid(state->tid);
|
|
state->callback(state->cookie, regs->NDTR, -EIO);
|
|
osSetCurrentTid(oldTid);
|
|
}
|
|
|
|
static void dmaIsrTcif(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
struct StmDmaStreamRegs *regs = dmaGetStreamRegs(busId, stream);
|
|
|
|
dmaLogDebug("tcif");
|
|
dmaStop(busId, stream);
|
|
|
|
uint16_t oldTid = osSetCurrentTid(state->tid);
|
|
state->callback(state->cookie, regs->NDTR, 0);
|
|
osSetCurrentTid(oldTid);
|
|
}
|
|
|
|
static void dmaIsr(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
|
|
if (UNLIKELY(!state->callback)) {
|
|
osLog(LOG_WARN, "DMA %u stream %u ISR fired while disabled\n",
|
|
busId, stream);
|
|
dmaStop(busId, stream);
|
|
return;
|
|
}
|
|
|
|
uint8_t isr = dmaGetIsr(busId, stream);
|
|
|
|
if (isr & STM_DMA_ISR_TEIFx)
|
|
dmaIsrTeif(busId, stream);
|
|
else if (isr & STM_DMA_ISR_TCIFx)
|
|
dmaIsrTcif(busId, stream);
|
|
}
|
|
|
|
int dmaStart(uint8_t busId, uint8_t stream, const void *buf, uint16_t size,
|
|
const struct dmaMode *mode, DmaCallbackF callback, void *cookie)
|
|
{
|
|
if (busId >= STM_DMA_NUM_DEVS || stream >= STM_DMA_NUM_STREAMS)
|
|
return -EINVAL;
|
|
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
state->callback = callback;
|
|
state->cookie = cookie;
|
|
state->tid = osGetCurrentTid();
|
|
|
|
pwrUnitClock(PERIPH_BUS_AHB1, STM_DMA_CLOCK_UNIT[busId], true);
|
|
|
|
struct StmDmaStreamRegs *regs = dmaGetStreamRegs(busId, stream);
|
|
dmaClearIsr(busId, stream, STM_DMA_ISR_TEIFx);
|
|
dmaClearIsr(busId, stream, STM_DMA_ISR_TCIFx);
|
|
|
|
regs->NDTR = size;
|
|
regs->PAR = mode->periphAddr;
|
|
regs->M0AR = (uintptr_t)buf;
|
|
regs->FCR = 0;
|
|
regs->CR = STM_DMA_CR_TEIE |
|
|
STM_DMA_CR_TCIE |
|
|
STM_DMA_CR_DIR(mode->direction) |
|
|
STM_DMA_CR_PSIZE(mode->psize) |
|
|
STM_DMA_CR_MSIZE(mode->msize) |
|
|
STM_DMA_CR_PL(mode->priority) |
|
|
STM_DMA_CR_PBURST(mode->pburst) |
|
|
STM_DMA_CR_MBURST(mode->mburst) |
|
|
STM_DMA_CR_CHSEL(mode->channel);
|
|
if (mode->minc)
|
|
regs->CR |= STM_DMA_CR_MINC;
|
|
|
|
NVIC_EnableIRQ(STM_DMA_IRQ[busId][stream]);
|
|
|
|
regs->CR |= STM_DMA_CR_EN;
|
|
return 0;
|
|
}
|
|
|
|
uint16_t dmaBytesLeft(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaStreamRegs *regs = dmaGetStreamRegs(busId, stream);
|
|
return regs->NDTR;
|
|
}
|
|
|
|
void dmaStop(uint8_t busId, uint8_t stream)
|
|
{
|
|
struct StmDmaStreamRegs *regs = dmaGetStreamRegs(busId, stream);
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
|
|
state->tid = 0;
|
|
dmaClearIsr(busId, stream, STM_DMA_ISR_TEIFx);
|
|
dmaClearIsr(busId, stream, STM_DMA_ISR_TCIFx);
|
|
NVIC_DisableIRQ(STM_DMA_IRQ[busId][stream]);
|
|
|
|
regs->CR &= ~STM_DMA_CR_EN;
|
|
while (regs->CR & STM_DMA_CR_EN)
|
|
;
|
|
|
|
}
|
|
|
|
const enum IRQn dmaIrq(uint8_t busId, uint8_t stream)
|
|
{
|
|
return STM_DMA_IRQ[busId][stream];
|
|
}
|
|
|
|
int dmaStopAll(uint32_t tid)
|
|
{
|
|
int busId, stream, count = 0;
|
|
|
|
for (busId = 0; busId < STM_DMA_NUM_DEVS; ++busId) {
|
|
for (stream = 0; stream < STM_DMA_NUM_STREAMS; ++stream) {
|
|
struct StmDmaStreamState *state = dmaGetStreamState(busId, stream);
|
|
if (state->tid == tid) {
|
|
dmaStop(busId, stream);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|