mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
Add support for DMA controller
Add some basic functions to start DMA operations for transmit and receive. BUG=chromium-os:28925 TEST=build on daisy and discovery; run on daisy Change-Id: Ifceeed2af80cf5f00e1ce1a49b1139a76585b0bf Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
@@ -8,6 +8,6 @@
|
||||
# STM32L15xx SoC family has a Cortex-M3 ARM core
|
||||
CORE:=cortex-m
|
||||
|
||||
chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o
|
||||
chip-y=clock.o dma.o gpio.o hwtimer.o jtag.o system.o uart.o
|
||||
chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o
|
||||
chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o
|
||||
|
||||
167
chip/stm32l/dma.c
Normal file
167
chip/stm32l/dma.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/* Copyright (c) 2012 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.
|
||||
*/
|
||||
|
||||
#include "dma.h"
|
||||
#include "registers.h"
|
||||
#include "timer.h"
|
||||
#include "uart.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/**
|
||||
* Get a pointer to a DMA channel.
|
||||
*
|
||||
* @param channel Channel number to read (DMAC_...)
|
||||
* @return pointer to DMA channel registers
|
||||
*/
|
||||
static struct dma_channel *get_channel(int channel)
|
||||
{
|
||||
struct dma_channel *chan;
|
||||
struct dma_ctlr *dma;
|
||||
|
||||
/* Get a pointer to the correct controller and channel */
|
||||
ASSERT(channel < DMA_NUM_CHANNELS);
|
||||
if (channel < DMA1_NUM_CHANNELS) {
|
||||
dma = (struct dma_ctlr *)STM32L_DMA1_BASE;
|
||||
chan = &dma->chan[channel];
|
||||
} else {
|
||||
dma = (struct dma_ctlr *)STM32L_DMA2_BASE;
|
||||
chan = &dma->chan[channel - DMA1_NUM_CHANNELS];
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
void dma_disable(unsigned channel)
|
||||
{
|
||||
struct dma_channel *chan;
|
||||
|
||||
chan = get_channel(channel);
|
||||
|
||||
if (REG32(&chan->ccr) & DMA_EN)
|
||||
REG32(&chan->ccr) &= ~DMA_EN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a channel for use and start it
|
||||
*
|
||||
* @param channel Channel number to read (DMAC_...)
|
||||
* @param count Number of bytes to transfer
|
||||
* @param periph Pointer to peripheral data register
|
||||
* @param memory Pointer to memory address
|
||||
* @param flags DMA flags for the control register, normally:
|
||||
* DMA_DIR_FROM_MEM_MASK for tx
|
||||
* 0 for rx
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int prepare_channel(unsigned channel, unsigned count, void *periph,
|
||||
const void *memory, unsigned flags)
|
||||
{
|
||||
struct dma_channel *chan;
|
||||
uint32_t ctrl;
|
||||
|
||||
chan = get_channel(channel);
|
||||
|
||||
if (REG32(&chan->ccr) & DMA_EN)
|
||||
REG32(&chan->ccr) &= ~DMA_EN;
|
||||
|
||||
/* Following the order in Doc ID 15965 Rev 5 p194 */
|
||||
REG32(&chan->cpar) = (uint32_t)periph;
|
||||
REG32(&chan->cmar) = (uint32_t)memory;
|
||||
REG32(&chan->cndtr) = count;
|
||||
ctrl = DMA_PL_VERY_HIGH << DMA_PL_SHIFT;
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
|
||||
ctrl |= DMA_MINC_MASK | flags;
|
||||
ctrl |= 0 << 10; /* MSIZE (memory size in bytes) */
|
||||
ctrl |= 1 << 8; /* PSIZE (16-bits for now) */
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
|
||||
/* Fire it up */
|
||||
ctrl |= DMA_EN;
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_start_tx(unsigned channel, unsigned count, void *periph,
|
||||
const void *memory)
|
||||
{
|
||||
return prepare_channel(channel, count, periph, memory,
|
||||
DMA_DIR_FROM_MEM_MASK);
|
||||
}
|
||||
|
||||
int dma_start_rx(unsigned channel, unsigned count, void *periph,
|
||||
const void *memory)
|
||||
{
|
||||
return prepare_channel(channel, count, periph, memory, 0);
|
||||
}
|
||||
|
||||
/* Hide this code behind an undefined CONFIG for now */
|
||||
#ifdef CONFIG_DMA_TEST
|
||||
|
||||
void dma_check(int channel, char *buff)
|
||||
{
|
||||
struct dma_channel *chan;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
chan = get_channel(channel);
|
||||
count = REG32(&chan->cndtr);
|
||||
uart_printf("c=%d\n", count);
|
||||
udelay(1000 * 100);
|
||||
uart_printf("c=%d\n",
|
||||
REG32(&chan->cndtr));
|
||||
for (i = 0; i < count; i++)
|
||||
uart_printf("%02x ", buff[i]);
|
||||
udelay(1000 * 100);
|
||||
uart_printf("c=%d\n",
|
||||
REG32(&chan->cndtr));
|
||||
for (i = 0; i < count; i++)
|
||||
uart_printf("%02x ", buff[i]);
|
||||
}
|
||||
|
||||
/* Run a check of memory-to-memory DMA */
|
||||
void dma_test(void)
|
||||
{
|
||||
unsigned channel = 3;
|
||||
struct dma_channel *chan;
|
||||
uint32_t ctrl;
|
||||
char periph[16], memory[16];
|
||||
unsigned count = sizeof(periph);
|
||||
int i;
|
||||
|
||||
chan = get_channel(channel);
|
||||
memset(memory, '\0', sizeof(memory));
|
||||
for (i = 0; i < count; i++)
|
||||
periph[i] = 10 + i;
|
||||
|
||||
/* Following the order in Doc ID 15965 Rev 5 p194 */
|
||||
REG32(&chan->cpar) = (uint32_t)periph;
|
||||
REG32(&chan->cmar) = (uint32_t)memory;
|
||||
REG32(&chan->cndtr) = count;
|
||||
ctrl = DMA_PL_MEDIUM << DMA_PL_SHIFT;
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
|
||||
ctrl |= DMA_MINC_MASK; /* | DMA_DIR_FROM_MEM_MASK */;
|
||||
ctrl |= 1 << 14; /* MEM2MEM */
|
||||
ctrl |= 1 << 6; /* PINC */
|
||||
/* ctrl |= 2 << 10; */
|
||||
/* ctrl |= 2 << 8; */
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
|
||||
ctrl |= DMA_EN;
|
||||
REG32(&chan->ccr) = ctrl;
|
||||
for (i = 0; i < count; i++)
|
||||
uart_printf("%d/%d ", periph[i], memory[i]);
|
||||
uart_printf("\ncount=%d\n", REG32(&chan->cndtr));
|
||||
}
|
||||
#endif /* CONFIG_TEST */
|
||||
|
||||
void dma_init(void)
|
||||
{
|
||||
/* Enable DMA1, we don't support DMA2 yet */
|
||||
STM32L_RCC_AHBENR |= 1 << 24;
|
||||
}
|
||||
127
chip/stm32l/dma.h
Normal file
127
chip/stm32l/dma.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/* Copyright (c) 2012 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.
|
||||
*
|
||||
* Register map and API for STM32L processor dma registers
|
||||
*/
|
||||
|
||||
#ifndef __STM32L_DMA
|
||||
#define __STM32L_DMA
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* Available DMA channels, numbered from 0
|
||||
*
|
||||
* Note: The STM datasheet tends to number things from 1. We should ask
|
||||
* the European elevator engineers to talk to MCU engineer counterparts
|
||||
* about this.
|
||||
*/
|
||||
enum {
|
||||
DMAC_ADC,
|
||||
DMAC_SPI1_RX,
|
||||
DMAC_SPI1_TX,
|
||||
DMAC_SPI2_RX,
|
||||
DMAC_SPI2_TX,
|
||||
|
||||
/* DMA1 has 7 channels, DMA2 has 5 */
|
||||
DMA1_NUM_CHANNELS = 7,
|
||||
DMA2_NUM_CHANNELS = 5,
|
||||
DMA_NUM_CHANNELS = DMA1_NUM_CHANNELS + DMA2_NUM_CHANNELS,
|
||||
};
|
||||
|
||||
/* A single channel of the DMA controller */
|
||||
struct dma_channel {
|
||||
uint32_t ccr; /* Control */
|
||||
uint32_t cndtr; /* Number of data to transfer */
|
||||
uint32_t cpar; /* Peripheral address */
|
||||
uint32_t cmar; /* Memory address */
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
/* Registers for the DMA controller */
|
||||
struct dma_ctlr {
|
||||
uint32_t isr;
|
||||
uint32_t ifcr;
|
||||
struct dma_channel chan[DMA_NUM_CHANNELS];
|
||||
};
|
||||
|
||||
/* Defines for accessing DMA ccr */
|
||||
#define DMA_PL_SHIFT 12
|
||||
#define DMA_PL_MASK (3 << DMA_PL_SHIFT)
|
||||
enum {
|
||||
DMA_PL_LOW,
|
||||
DMA_PL_MEDIUM,
|
||||
DMA_PL_HIGH,
|
||||
DMA_PL_VERY_HIGH,
|
||||
};
|
||||
|
||||
#define DMA_MINC_MASK (1 << 7)
|
||||
#define DMA_DIR_FROM_MEM_MASK (1 << 4)
|
||||
#define DMA_EN (1 << 0)
|
||||
|
||||
/*
|
||||
* Certain DMA channels must be used for certain peripherals and transfer
|
||||
* directions. We provide an easy way for drivers to select the correct
|
||||
* channel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param spi SPI port to request: STM32L_SPI1_PORT or STM32L_SPI2_PORT
|
||||
* @return DMA channel to use for rx / tx on that port
|
||||
*/
|
||||
#define DMA_CHANNEL_FOR_SPI_RX(spi) \
|
||||
((spi) == STM32L_SPI1_PORT ? DMAC_SPI1_RX : DMAC_SPI2_RX)
|
||||
#define DMA_CHANNEL_FOR_SPI_TX(spi) \
|
||||
((spi) == STM32L_SPI1_PORT ? DMAC_SPI1_TX : DMAC_SPI2_TX)
|
||||
|
||||
/**
|
||||
* Start a DMA transfer to transmit data from memory to a peripheral
|
||||
*
|
||||
* @param channel Channel number to read (DMAC_...)
|
||||
* @param count Number of bytes to transfer
|
||||
* @param periph Pointer to peripheral data register
|
||||
* @param memory Pointer to memory address
|
||||
*/
|
||||
int dma_start_tx(unsigned channel, unsigned count, void *periph,
|
||||
const void *memory);
|
||||
|
||||
/**
|
||||
* Start a DMA transfer to receive data to memory from a peripheral
|
||||
*
|
||||
* @param channel Channel number to read (DMAC_...)
|
||||
* @param count Number of bytes to transfer
|
||||
* @param periph Pointer to peripheral data register
|
||||
* @param memory Pointer to memory address
|
||||
*/
|
||||
int dma_start_rx(unsigned channel, unsigned count, void *periph,
|
||||
const void *memory);
|
||||
|
||||
/**
|
||||
* Stop a DMA transfer on a channel
|
||||
*
|
||||
* Disable the DMA channel and immediate stop all transfers on it.
|
||||
*
|
||||
* @param channel Channel number to stop (DMAC_...)
|
||||
*/
|
||||
void dma_disable(unsigned channel);
|
||||
|
||||
/**
|
||||
* Testing: Print out the data transferred by a channel
|
||||
*
|
||||
* @param channel Channel number to read (DMAC_...)
|
||||
* @param buff Start of DMA buffer
|
||||
*/
|
||||
void dma_check(int channel, char *buff);
|
||||
|
||||
/**
|
||||
* Testing: Test that DMA works correctly for memory to memory transfers
|
||||
*/
|
||||
void dma_test(void);
|
||||
|
||||
/**
|
||||
* Init DMA peripheral ready for use
|
||||
*/
|
||||
void dma_init(void);
|
||||
|
||||
#endif
|
||||
@@ -287,5 +287,7 @@
|
||||
#define STM32L_SPI2_BASE 0x40003800
|
||||
#define STM32L_CRC_BASE 0x40023000
|
||||
#define STM32L_LCD_BASE 0x40002400
|
||||
#define STM32L_DMA1_BASE 0x40026000
|
||||
#define STM32L_DMA2_BASE 0x40026400
|
||||
|
||||
#endif /* __STM32L_REGISTERS */
|
||||
|
||||
Reference in New Issue
Block a user