added SSP support files, fixed some typos

This commit is contained in:
Thomas Doerfler
2008-09-30 10:01:38 +00:00
parent 1196226358
commit 29cc14771b
12 changed files with 2951 additions and 1527 deletions

View File

@@ -1,3 +1,17 @@
2008-09-30 Sebastian Huber <sebastian.huber@embedded-brains.de>
* ssp/ssp.c, misc/dma.c, include/dma.h, include/ssp.h: New files.
* Makefile.am, preinstall.am, README: Update.
* include/irq.h: Fixed typos.
* include/lpc24xx.h: New defines and types. Converted to UNIX line
endings.
* misc/system-clocks.c, startup/bspstart.c: Update for utility.h
changes.
2008-09-30 Ralf Corsépius <ralf.corsepius@rtems.org>
* clock/clock-config.c: include "../../../shared/clockdrv_shell.h".

View File

@@ -2,7 +2,7 @@
#
# @file
#
# @brief Makefile of LibBSP for the LPC247X boards.
# @brief Makefile of LibBSP for the LPC24XX boards.
#
# $Id$
@@ -22,8 +22,8 @@ dist_project_lib_DATA = bsp_specs
include_HEADERS = include/bsp.h
nodist_include_HEADERS = include/bspopts.h
nodist_include_bsp_HEADERS = ../../shared/include/bootcard.h
DISTCLEANFILES = include/bspopts.h
include_bsp_HEADERS = ../../shared/include/utility.h \
../../shared/include/irq-generic.h \
@@ -33,25 +33,32 @@ include_bsp_HEADERS = ../../shared/include/utility.h \
include/irq-config.h \
include/irq.h \
include/lpc24xx.h \
include/system-clocks.h
include/system-clocks.h \
include/ssp.h \
include/dma.h
###############################################################################
# Data #
###############################################################################
noinst_LIBRARIES = libbspstart.a
libbspstart_a_SOURCES = ../shared/start/start.S
project_lib_DATA = start.$(OBJEXT)
dist_project_lib_DATA += ../shared/startup/linkcmds.base startup/linkcmds
dist_project_lib_DATA += ../shared/startup/linkcmds.base \
startup/linkcmds
###############################################################################
# LibBSP #
###############################################################################
noinst_LIBRARIES += libbsp.a
libbsp_a_SOURCES =
# shared
# Shared
libbsp_a_SOURCES += ../../shared/bootcard.c \
../../shared/bspclean.c \
../../shared/bspreset.c \
@@ -64,35 +71,39 @@ libbsp_a_SOURCES += ../../shared/bootcard.c \
../../shared/sbrk.c \
../shared/abort/simple_abort.c
# startup
# Startup
libbsp_a_SOURCES += startup/bspstart.c
# irq
# IRQ
libbsp_a_SOURCES += ../../shared/src/irq-generic.c \
../../shared/src/irq-legacy.c \
../shared/irq/irq_asm.S \
irq/irq.c
# console
# Console
libbsp_a_SOURCES += ../../shared/console.c \
console/console-config.c
# clock
libbsp_a_SOURCES += clock/clock-config.c ../../../shared/clockdrv_shell.h
# Clock
libbsp_a_SOURCES += clock/clock-config.c \
../../../shared/clockdrv_shell.h
# rtc
# RTC
libbsp_a_SOURCES += ../../shared/tod.c \
rtc/rtc-config.c
# misc
libbsp_a_SOURCES += misc/system-clocks.c
# Misc
libbsp_a_SOURCES += misc/system-clocks.c \
misc/dma.c
# SSP
libbsp_a_SOURCES += ssp/ssp.c
###############################################################################
# Special Rules #
###############################################################################
start.$(OBJEXT): ../shared/start/start.S
$(CPPASCOMPILE) -o $@ -c $<
DISTCLEANFILES = include/bspopts.h
include $(srcdir)/preinstall.am
include $(top_srcdir)/../../../../automake/local.am

View File

@@ -10,3 +10,4 @@ Drivers:
o Console
o Clock
o RTC
o SSP (SPI mode): This driver is in active development. Use with care.

View File

@@ -0,0 +1,42 @@
/**
* @file
*
* @ingroup lpc24xx
*
* @brief DMA support.
*/
/*
* Copyright (c) 2008
* Embedded Brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* rtems@embedded-brains.de
*
* The license and distribution terms for this file may be found in the file
* LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
*/
#ifndef LIBBSP_ARM_LPC24XX_DMA_H
#define LIBBSP_ARM_LPC24XX_DMA_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void lpc24xx_dma_initialize( void);
bool lpc24xx_dma_channel_obtain( unsigned channel);
void lpc24xx_dma_channel_release( unsigned channel);
void lpc24xx_dma_channel_disable( unsigned channel, bool force);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LIBBSP_ARM_LPC24XX_DMA_H */

View File

@@ -35,16 +35,16 @@
#define LPC24XX_IRQ_WDT 0
#define LPC24XX_IRQ_SOFTWARE 1
#define LPC24XX_IRQ_ARM CORE_0 2
#define LPC24XX_IRQ_ARM CORE_1 3
#define LPC24XX_IRQ_ARM_CORE_0 2
#define LPC24XX_IRQ_ARM_CORE_1 3
#define LPC24XX_IRQ_TIMER_0 4
#define LPC24XX_IRQ_TIMER_1 5
#define LPC24XX_IRQ_UART_0 6
#define LPC24XX_IRQ_UART_1 7
#define LPC24XX_IRQ_PWM 8
#define LPC24XX_IRQ_I2C_0 9
#define LPC24XX_IRQ_SPI_SSP0 10
#define LPC24XX_IRQ_SSP1 11
#define LPC24XX_IRQ_SPI_SSP_0 10
#define LPC24XX_IRQ_SSP_1 11
#define LPC24XX_IRQ_PLL 12
#define LPC24XX_IRQ_RTC 13
#define LPC24XX_IRQ_EINT_0 14
@@ -61,8 +61,8 @@
#define LPC24XX_IRQ_DMA 25
#define LPC24XX_IRQ_TIMER_2 26
#define LPC24XX_IRQ_TIMER_3 27
#define LPC24XX_IRQ_UART2 28
#define LPC24XX_IRQ_UART3 29
#define LPC24XX_IRQ_UART_2 28
#define LPC24XX_IRQ_UART_3 29
#define LPC24XX_IRQ_I2C_2 30
#define LPC24XX_IRQ_I2S 31
@@ -76,8 +76,6 @@
*/
#define BSP_INTERRUPT_VECTOR_MAX LPC24XX_IRQ_I2S
#define BSP_FEATURE_IRQ_EXTENSION
/** @} */
#endif /* ASM */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
/**
* @file
*
* @ingroup lpc24xx
*
* @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
*/
/*
* Copyright (c) 2008
* Embedded Brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* rtems@embedded-brains.de
*
* The license and distribution terms for this file may be found in the file
* LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
*/
#ifndef LIBBSP_ARM_LPC24XX_SSP_H
#define LIBBSP_ARM_LPC24XX_SSP_H
#include <rtems/libi2c.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern rtems_libi2c_bus_t * const lpc24xx_ssp_0;
extern rtems_libi2c_bus_t * const lpc24xx_ssp_1;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LIBBSP_ARM_LPC24XX_SSP_H */

View File

@@ -0,0 +1,124 @@
/**
* @file
*
* @ingroup lpc24xx
*
* @brief DMA support.
*/
/*
* Copyright (c) 2008
* Embedded Brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* rtems@embedded-brains.de
*
* The license and distribution terms for this file may be found in the file
* LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
*/
#include <rtems/endian.h>
#include <bsp/lpc24xx.h>
#include <bsp/dma.h>
/**
* @brief Table that indicates if a channel is currently occupied.
*/
static bool lpc24xx_dma_channel_occupation [GPDMA_CH_NUMBER];
/**
* @brief Initializes the general purpose DMA.
*/
void lpc24xx_dma_initialize( void)
{
rtems_interrupt_level level;
/* Enable power */
rtems_interrupt_disable( level);
PCONP = SET_FLAG( PCONP, PCONP_PCGPDMA);
rtems_interrupt_enable( level);
/* Disable module */
GPDMA_CONFIG = 0;
/* Enable module */
#if BYTE_ORDER == LITTLE_ENDIAN
GPDMA_CONFIG = GPDMA_CONFIG_EN;
#else
GPDMA_CONFIG = GPDMA_CONFIG_EN | GPDMA_CONFIG_MODE;
#endif
/* Reset registers */
GPDMA_SOFT_SREQ = 0;
GPDMA_SOFT_BREQ = 0;
GPDMA_SOFT_LSREQ = 0;
GPDMA_SOFT_LBREQ = 0;
GPDMA_SYNC = 0;
}
/**
* @brief Returns true if the channel @a channel was obtained.
*
* If the channel number @a channel is out of range the last valid channel will
* be used.
*/
bool lpc24xx_dma_channel_obtain( unsigned channel)
{
rtems_interrupt_level level;
bool occupation = true;
if (channel > GPDMA_CH_NUMBER) {
channel = GPDMA_CH_NUMBER - 1;
}
rtems_interrupt_disable( level);
occupation = lpc24xx_dma_channel_occupation [channel];
lpc24xx_dma_channel_occupation [channel] = true;
rtems_interrupt_enable( level);
return !occupation;
}
/**
* @brief Releases the channel @a channel. You must have obtained this channel
* with lpc24xx_dma_channel_obtain() previously.
*
* If the channel number @a channel is out of range the last valid channel will
* be used.
*/
void lpc24xx_dma_channel_release( unsigned channel)
{
if (channel > GPDMA_CH_NUMBER) {
channel = GPDMA_CH_NUMBER - 1;
}
lpc24xx_dma_channel_occupation [channel] = false;
}
/**
* @brief Disables the channel @a channel.
*
* If @a force is false the channel will be halted and disabled when the
* channel is inactive. If the channel number @a channel is out of range the
* last valid channel will be used.
*/
void lpc24xx_dma_channel_disable( unsigned channel, bool force)
{
volatile lpc24xx_dma_channel *ch = GPDMA_CH_BASE_ADDR( channel);
uint32_t cfg = ch->cfg;
if (!force) {
/* Halt */
ch->cfg = SET_FLAG( cfg, GPDMA_CH_CFG_HALT);
/* Wait for inactive */
do {
cfg = ch->cfg;
} while (IS_FLAG_SET( cfg, GPDMA_CH_CFG_ACTIVE));
}
/* Disable */
ch->cfg = CLEAR_FLAG( cfg, GPDMA_CH_CFG_EN);
}

View File

@@ -71,7 +71,7 @@ unsigned lpc24xx_cclk( void)
}
/* Get PLL output frequency */
if (REG_FLAG_IS_SET( PLLSTAT, PLLSTAT_PLLC)) {
if (IS_FLAG_SET( PLLSTAT, PLLSTAT_PLLC)) {
uint32_t pllcfg = PLLCFG;
unsigned n = GET_PLLCFG_NSEL( pllcfg) + 1;
unsigned m = GET_PLLCFG_MSEL( pllcfg) + 1;
@@ -110,10 +110,10 @@ static void lpc24xx_pll_config( uint32_t val)
*/
void lpc24xx_set_pll( unsigned clksrc, unsigned nsel, unsigned msel, unsigned cclksel)
{
bool pll_enabled = REG_FLAG_IS_SET( PLLSTAT, PLLSTAT_PLLE);
bool pll_enabled = IS_FLAG_SET( PLLSTAT, PLLSTAT_PLLE);
/* Disconnect PLL if necessary */
if (REG_FLAG_IS_SET( PLLSTAT, PLLSTAT_PLLC)) {
if (IS_FLAG_SET( PLLSTAT, PLLSTAT_PLLC)) {
if (pll_enabled) {
lpc24xx_pll_config( PLLCON_PLLE);
} else {
@@ -139,7 +139,7 @@ void lpc24xx_set_pll( unsigned clksrc, unsigned nsel, unsigned msel, unsigned cc
lpc24xx_pll_config( PLLCON_PLLE);
/* Wait for lock */
while (REG_FLAG_IS_CLEARED( PLLSTAT, PLLSTAT_PLOCK)) {
while (IS_FLAG_CLEARED( PLLSTAT, PLLSTAT_PLOCK)) {
/* Wait */
}

View File

@@ -85,6 +85,14 @@ $(PROJECT_INCLUDE)/bsp/system-clocks.h: include/system-clocks.h $(PROJECT_INCLUD
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/system-clocks.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/system-clocks.h
$(PROJECT_INCLUDE)/bsp/ssp.h: include/ssp.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/ssp.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/ssp.h
$(PROJECT_INCLUDE)/bsp/dma.h: include/dma.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/dma.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/dma.h
$(PROJECT_LIB)/start.$(OBJEXT): start.$(OBJEXT) $(PROJECT_LIB)/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_LIB)/start.$(OBJEXT)
TMPINSTALL_FILES += $(PROJECT_LIB)/start.$(OBJEXT)

View File

@@ -0,0 +1,654 @@
/**
* @file
*
* @ingroup lpc24xx
*
* @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
*/
/*
* Copyright (c) 2008
* Embedded Brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* rtems@embedded-brains.de
*
* The license and distribution terms for this file may be found in the file
* LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
*/
#include <stdbool.h>
#include <bsp/ssp.h>
#include <bsp/lpc24xx.h>
#include <bsp/irq.h>
#include <bsp/system-clocks.h>
#include <bsp/dma.h>
#define RTEMS_STATUS_CHECKS_USE_PRINTK
#include <rtems/status-checks.h>
#define LPC24XX_SSP_NUMBER 2
#define LPC24XX_SSP_FIFO_SIZE 8
#define LPC24XX_SSP_BAUD_RATE 2000000
typedef enum {
LPC24XX_SSP_DMA_INVALID = 0,
LPC24XX_SSP_DMA_AVAILABLE = 1,
LPC24XX_SSP_DMA_NOT_INITIALIZED = 2,
LPC24XX_SSP_DMA_INITIALIZATION = 3,
LPC24XX_SSP_DMA_TRANSFER_FLAG = 0x80000000U,
LPC24XX_SSP_DMA_WAIT = 1 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0 = 2 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1 = 3 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
LPC24XX_SSP_DMA_ERROR = 4 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
LPC24XX_SSP_DMA_DONE = 5 | LPC24XX_SSP_DMA_TRANSFER_FLAG
} lpc24xx_ssp_dma_status;
typedef struct {
rtems_libi2c_bus_t bus;
volatile lpc24xx_ssp *regs;
unsigned clock;
uint32_t idle_char;
} lpc24xx_ssp_bus_entry;
typedef struct {
lpc24xx_ssp_dma_status status;
lpc24xx_ssp_bus_entry *bus;
rtems_libi2c_read_write_done_t done;
int n;
void *arg;
} lpc24xx_ssp_dma_entry;
static lpc24xx_ssp_dma_entry lpc24xx_ssp_dma_data = {
.status = LPC24XX_SSP_DMA_NOT_INITIALIZED,
.bus = NULL,
.done = NULL,
.n = 0,
.arg = NULL
};
static uint32_t lpc24xx_ssp_trash = 0;
static inline bool lpc24xx_ssp_is_busy( const lpc24xx_ssp_bus_entry *bus)
{
return lpc24xx_ssp_dma_data.bus == bus
&& lpc24xx_ssp_dma_data.status != LPC24XX_SSP_DMA_AVAILABLE;
}
static void lpc24xx_ssp_handler( rtems_vector_number vector, void *arg)
{
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) arg;
volatile lpc24xx_ssp *regs = e->regs;
uint32_t mis = regs->mis;
uint32_t icr = 0;
if (IS_FLAG_SET( mis, SSP_MIS_RORRIS)) {
/* TODO */
printk( "%s: Receiver overrun!\n", __func__);
icr |= SSP_ICR_RORRIS;
}
regs->icr = icr;
}
static void lpc24xx_ssp_dma_handler( rtems_vector_number vector, void *arg)
{
lpc24xx_ssp_dma_entry *e = (lpc24xx_ssp_dma_entry *) arg;
lpc24xx_ssp_dma_status status = e->status;
uint32_t tc = 0;
uint32_t err = 0;
int rv = 0;
/* Return if we are not in a transfer status */
if (IS_FLAG_CLEARED( status, LPC24XX_SSP_DMA_TRANSFER_FLAG)) {
return;
}
/* Get interrupt status */
tc = GPDMA_INT_TCSTAT;
err = GPDMA_INT_ERR_STAT;
/* Clear interrupt status */
GPDMA_INT_TCCLR = tc;
GPDMA_INT_ERR_CLR = err;
/* Change status */
if (err == 0) {
switch (status) {
case LPC24XX_SSP_DMA_WAIT:
if (ARE_FLAGS_SET( tc, GPDMA_STATUS_CH_0 | GPDMA_STATUS_CH_1)) {
status = LPC24XX_SSP_DMA_DONE;
} else if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_0)) {
status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1;
} else if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_1)) {
status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0;
}
break;
case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0:
if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_1)) {
status = LPC24XX_SSP_DMA_ERROR;
} else if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_0)) {
status = LPC24XX_SSP_DMA_DONE;
}
break;
case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1:
if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_0)) {
status = LPC24XX_SSP_DMA_ERROR;
} else if (IS_FLAG_SET( tc, GPDMA_STATUS_CH_1)) {
status = LPC24XX_SSP_DMA_DONE;
}
break;
default:
status = LPC24XX_SSP_DMA_ERROR;
break;
}
} else {
status = LPC24XX_SSP_DMA_ERROR;
}
/* Error cleanup */
if (status == LPC24XX_SSP_DMA_ERROR) {
lpc24xx_dma_channel_disable( 0, true);
lpc24xx_dma_channel_disable( 1, true);
status = LPC24XX_SSP_DMA_DONE;
rv = -RTEMS_IO_ERROR;
}
/* Done */
if (status == LPC24XX_SSP_DMA_DONE) {
status = LPC24XX_SSP_DMA_AVAILABLE;
if (e->done != NULL) {
e->done( rv, e->n, e->arg);
e->done = NULL;
}
}
/* Set status */
e->status = status;
}
static rtems_status_code lpc24xx_ssp_init( rtems_libi2c_bus_t *bus)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
rtems_interrupt_level level;
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
volatile lpc24xx_ssp *regs = e->regs;
unsigned pclk = lpc24xx_cclk();
unsigned pre =
((pclk + LPC24XX_SSP_BAUD_RATE - 1) / LPC24XX_SSP_BAUD_RATE + 1) & ~1U;
rtems_vector_number vector = UINT32_MAX;
if (lpc24xx_ssp_dma_data.status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
lpc24xx_ssp_dma_status status = LPC24XX_SSP_DMA_INVALID;
/* Test and set DMA support status */
rtems_interrupt_disable( level);
status = lpc24xx_ssp_dma_data.status;
if (status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_INITIALIZATION;
}
rtems_interrupt_enable( level);
if (status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
/* Install DMA interrupt handler */
sc = rtems_interrupt_handler_install(
LPC24XX_IRQ_DMA,
"SSP DMA",
RTEMS_INTERRUPT_SHARED,
lpc24xx_ssp_dma_handler,
&lpc24xx_ssp_dma_data
);
CHECK_SC( sc, "Install DMA interrupt handler");
/* Set DMA support status */
lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_AVAILABLE;
}
}
/* Disable module */
regs->cr1 = 0;
/* Set clock select and get vector number */
switch ((uintptr_t) regs) {
case SSP0_BASE_ADDR:
rtems_interrupt_disable( level);
SET_PCLKSEL1_PCLK_SSP0( PCLKSEL1, 1);
rtems_interrupt_enable( level);
vector = LPC24XX_IRQ_SPI_SSP_0;
break;
case SSP1_BASE_ADDR:
rtems_interrupt_disable( level);
SET_PCLKSEL0_PCLK_SSP1( PCLKSEL0, 1);
rtems_interrupt_enable( level);
vector = LPC24XX_IRQ_SSP_1;
break;
default:
return RTEMS_IO_ERROR;
}
/* Set serial clock rate to save value */
regs->cr0 = SET_SSP_CR0_SCR( 0, 255);
/* Set clock prescaler */
if (pre > 254) {
pre = 254;
} else if (pre < 2) {
pre = 2;
}
regs->cpsr = pre;
/* Save clock value */
e->clock = pclk / pre;
/* Enable module and loop back mode */
regs->cr1 = SSP_CR1_LBM | SSP_CR1_SSE;
/* Install interrupt handler */
sc = rtems_interrupt_handler_install(
vector,
"SSP",
RTEMS_INTERRUPT_UNIQUE,
lpc24xx_ssp_handler,
e
);
CHECK_SC( sc, "Install interrupt handler");
/* Enable receiver overrun interrupts */
e->regs->imsc = SSP_IMSC_RORIM;
return RTEMS_SUCCESSFUL;
}
static rtems_status_code lpc24xx_ssp_send_start( rtems_libi2c_bus_t *bus)
{
return RTEMS_SUCCESSFUL;
}
static rtems_status_code lpc24xx_ssp_send_stop( rtems_libi2c_bus_t *bus)
{
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
/* Release DMA support */
if (lpc24xx_ssp_dma_data.bus == e) {
if (lpc24xx_ssp_dma_data.status == LPC24XX_SSP_DMA_AVAILABLE) {
lpc24xx_dma_channel_release( 0);
lpc24xx_dma_channel_release( 1);
lpc24xx_ssp_dma_data.bus = NULL;
} else {
return RTEMS_RESOURCE_IN_USE;
}
}
return RTEMS_SUCCESSFUL;
}
static rtems_status_code lpc24xx_ssp_send_addr(
rtems_libi2c_bus_t *bus,
uint32_t addr,
int rw
)
{
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
if (lpc24xx_ssp_is_busy( e)) {
return RTEMS_RESOURCE_IN_USE;
}
return RTEMS_SUCCESSFUL;
}
static int lpc24xx_ssp_set_transfer_mode(
rtems_libi2c_bus_t *bus,
const rtems_libi2c_tfr_mode_t *mode
)
{
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
volatile lpc24xx_ssp *regs = e->regs;
unsigned clk = e->clock;
unsigned br = mode->baudrate;
unsigned scr = (clk + br - 1) / br;
if (lpc24xx_ssp_is_busy( e)) {
return -RTEMS_RESOURCE_IN_USE;
}
if (mode->bits_per_char != 8) {
return -RTEMS_INVALID_NUMBER;
}
if (mode->lsb_first) {
return -RTEMS_INVALID_NUMBER;
}
if (br == 0) {
return -RTEMS_INVALID_NUMBER;
}
/* Compute new prescaler if necessary */
if (scr > 256 || scr < 1) {
unsigned pre = regs->cpsr;
unsigned pclk = clk * pre;
while (scr > 256) {
if (pre > 252) {
return -RTEMS_INVALID_NUMBER;
}
pre += 2;
clk = pclk / pre;
scr = (clk + br - 1) / br;
}
while (scr < 1) {
if (pre < 4) {
return -RTEMS_INVALID_NUMBER;
}
pre -= 2;
clk = pclk / pre;
scr = (clk + br - 1) / br;
}
regs->cpsr = pre;
e->clock = clk;
}
/* Adjust SCR */
--scr;
e->idle_char = mode->idle_char;
while (IS_FLAG_CLEARED( regs->sr, SSP_SR_TFE)) {
/* Wait */
}
regs->cr0 = SET_SSP_CR0_DSS( 0, 0x7)
| SET_SSP_CR0_SCR( 0, scr)
| (mode->clock_inv ? SSP_CR0_CPOL : 0)
| (mode->clock_phs ? SSP_CR0_CPHA : 0);
return 0;
}
static int lpc24xx_ssp_read_write(
rtems_libi2c_bus_t *bus,
unsigned char *in,
const unsigned char *out,
int n
)
{
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
volatile lpc24xx_ssp *regs = e->regs;
int r = 0;
int w = 0;
int dr = 1;
int dw = 1;
int m = 0;
uint32_t sr = regs->sr;
unsigned char trash = 0;
unsigned char idle_char = (unsigned char) e->idle_char;
if (lpc24xx_ssp_is_busy( e)) {
return -RTEMS_RESOURCE_IN_USE;
}
if (n < 0) {
return -RTEMS_INVALID_SIZE;
}
/* Disable DMA on SSP */
regs->dmacr = SSP_DMACR_RXDMAE | SSP_DMACR_TXDMAE;
if (in == NULL) {
dr = 0;
in = &trash;
}
if (out == NULL) {
dw = 0;
out = &idle_char;
}
/*
* Assumption: The transmit and receive FIFOs are empty. If this assumption
* is not true an input buffer overflow may occur or we may never exit the
* loop due to data loss. This is only possible if entities external to this
* driver operate on the SSP.
*/
while (w < n) {
/* FIFO capacity */
m = w - r;
/* Write */
if (IS_FLAG_SET( sr, SSP_SR_TNF) && m < LPC24XX_SSP_FIFO_SIZE) {
regs->dr = *out;
++w;
out += dw;
}
/* Read */
if (IS_FLAG_SET( sr, SSP_SR_RNE)) {
*in = (unsigned char) regs->dr;
++r;
in += dr;
}
/* New status */
sr = regs->sr;
}
/* Read outstanding input */
while (r < n) {
/* Wait */
do {
sr = regs->sr;
} while (IS_FLAG_CLEARED( sr, SSP_SR_RNE));
/* Read */
*in = (unsigned char) regs->dr;
++r;
in += dr;
}
return n;
}
static int lpc24xx_ssp_read_write_async(
rtems_libi2c_bus_t *bus,
unsigned char *in,
const unsigned char *out,
int n,
rtems_libi2c_read_write_done_t done,
void *arg
)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
rtems_interrupt_level level;
lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
volatile lpc24xx_ssp *ssp = e->regs;
volatile lpc24xx_dma_channel *receive_channel = GPDMA_CH_BASE_ADDR( 0);
volatile lpc24xx_dma_channel *transmit_channel = GPDMA_CH_BASE_ADDR( 1);
uint32_t di = GPDMA_CH_CTRL_DI;
uint32_t si = GPDMA_CH_CTRL_SI;
if (n < 0 || n > (int) GPDMA_CH_CTRL_TSZ_MAX) {
return -RTEMS_INVALID_SIZE;
}
/* Try to reserve DMA support for this bus */
if (lpc24xx_ssp_dma_data.bus == NULL) {
rtems_interrupt_disable( level);
if (lpc24xx_ssp_dma_data.bus == NULL) {
lpc24xx_ssp_dma_data.bus = e;
}
rtems_interrupt_enable( level);
/* Try to obtain DMA channels */
if (lpc24xx_ssp_dma_data.bus == e) {
bool channel_0 = lpc24xx_dma_channel_obtain( 0);
bool channel_1 = lpc24xx_dma_channel_obtain( 1);
if (!channel_0 && channel_1) {
lpc24xx_dma_channel_release( 1);
lpc24xx_ssp_dma_data.bus = NULL;
} else if (channel_0 && !channel_1) {
lpc24xx_dma_channel_release( 0);
lpc24xx_ssp_dma_data.bus = NULL;
} else if (!channel_0 || !channel_1) {
lpc24xx_ssp_dma_data.bus = NULL;
}
}
}
/* Check if DMA support is available */
if (lpc24xx_ssp_dma_data.bus != e
|| lpc24xx_ssp_dma_data.status != LPC24XX_SSP_DMA_AVAILABLE) {
return -RTEMS_RESOURCE_IN_USE;
}
/* Set DMA support status and parameter */
lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_WAIT;
lpc24xx_ssp_dma_data.done = done;
lpc24xx_ssp_dma_data.n = n;
lpc24xx_ssp_dma_data.arg = arg;
/* Enable DMA on SSP */
ssp->dmacr = SSP_DMACR_RXDMAE | SSP_DMACR_TXDMAE;
/* Receive */
if (in != NULL) {
receive_channel->dest = (uint32_t) in;
} else {
receive_channel->dest = (uint32_t) &lpc24xx_ssp_trash;
di = 0;
}
receive_channel->src = (uint32_t) &ssp->dr;
receive_channel->lli = 0;
receive_channel->ctrl = SET_GPDMA_CH_CTRL_TSZ( 0, n)
| SET_GPDMA_CH_CTRL_SBSZ( 0, GPDMA_CH_CTRL_BSZ_4)
| SET_GPDMA_CH_CTRL_DBSZ( 0, GPDMA_CH_CTRL_BSZ_4)
| SET_GPDMA_CH_CTRL_SW( 0, GPDMA_CH_CTRL_W_8)
| SET_GPDMA_CH_CTRL_DW( 0, GPDMA_CH_CTRL_W_8)
| GPDMA_CH_CTRL_ITC
| di;
receive_channel->cfg = SET_GPDMA_CH_CFG_SRCPER( 0, GPDMA_CH_CFG_PER_SSP1_RX)
| SET_GPDMA_CH_CFG_FLOW( 0, GPDMA_CH_CFG_FLOW_PER_TO_MEM_DMA)
| GPDMA_CH_CFG_IE
| GPDMA_CH_CFG_ITC
| GPDMA_CH_CFG_EN;
/* Transmit */
if (out != NULL) {
transmit_channel->src = (uint32_t) out;
} else {
transmit_channel->src = (uint32_t) &e->idle_char;
si = 0;
}
transmit_channel->dest = (uint32_t) &ssp->dr;
transmit_channel->lli = 0;
transmit_channel->ctrl = SET_GPDMA_CH_CTRL_TSZ( 0, n)
| SET_GPDMA_CH_CTRL_SBSZ( 0, GPDMA_CH_CTRL_BSZ_4)
| SET_GPDMA_CH_CTRL_DBSZ( 0, GPDMA_CH_CTRL_BSZ_4)
| SET_GPDMA_CH_CTRL_SW( 0, GPDMA_CH_CTRL_W_8)
| SET_GPDMA_CH_CTRL_DW( 0, GPDMA_CH_CTRL_W_8)
| GPDMA_CH_CTRL_ITC
| si;
transmit_channel->cfg = SET_GPDMA_CH_CFG_DESTPER( 0, GPDMA_CH_CFG_PER_SSP1_TX)
| SET_GPDMA_CH_CFG_FLOW( 0, GPDMA_CH_CFG_FLOW_MEM_TO_PER_DMA)
| GPDMA_CH_CFG_IE
| GPDMA_CH_CFG_ITC
| GPDMA_CH_CFG_EN;
return 0;
}
static int lpc24xx_ssp_read( rtems_libi2c_bus_t *bus, unsigned char *in, int n)
{
return lpc24xx_ssp_read_write( bus, in, NULL, n);
}
static int lpc24xx_ssp_write(
rtems_libi2c_bus_t *bus,
unsigned char *out,
int n
)
{
return lpc24xx_ssp_read_write( bus, NULL, out, n);
}
static int lpc24xx_ssp_ioctl( rtems_libi2c_bus_t *bus, int cmd, void *arg)
{
int rv = -1;
const rtems_libi2c_tfr_mode_t *tm = (const rtems_libi2c_tfr_mode_t *) arg;
rtems_libi2c_read_write_t *rw = (rtems_libi2c_read_write_t *) arg;
rtems_libi2c_read_write_async_t *rwa =
(rtems_libi2c_read_write_async_t *) arg;
switch (cmd) {
case RTEMS_LIBI2C_IOCTL_READ_WRITE:
rv = lpc24xx_ssp_read_write( bus, rw->rd_buf, rw->wr_buf, rw->byte_cnt);
break;
case RTEMS_LIBI2C_IOCTL_READ_WRITE_ASYNC:
rv = lpc24xx_ssp_read_write_async(
bus,
rwa->rd_buf,
rwa->wr_buf,
rwa->byte_cnt,
rwa->done,
rwa->arg
);
break;
case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
rv = lpc24xx_ssp_set_transfer_mode( bus, tm);
break;
default:
rv = -RTEMS_NOT_DEFINED;
break;
}
return rv;
}
static const rtems_libi2c_bus_ops_t lpc24xx_ssp_ops = {
.init = lpc24xx_ssp_init,
.send_start = lpc24xx_ssp_send_start,
.send_stop = lpc24xx_ssp_send_stop,
.send_addr = lpc24xx_ssp_send_addr,
.read_bytes = lpc24xx_ssp_read,
.write_bytes = lpc24xx_ssp_write,
.ioctl = lpc24xx_ssp_ioctl
};
static lpc24xx_ssp_bus_entry lpc24xx_ssp_bus_table [LPC24XX_SSP_NUMBER] = {
{
/* SSP 0 */
.bus = {
.ops = &lpc24xx_ssp_ops,
.size = sizeof( lpc24xx_ssp_bus_entry)
},
.regs = (volatile lpc24xx_ssp *) SSP0_BASE_ADDR,
.clock = 0,
.idle_char = 0xffffffff
}, {
/* SSP 1 */
.bus = {
.ops = &lpc24xx_ssp_ops,
.size = sizeof( lpc24xx_ssp_bus_entry)
},
.regs = (volatile lpc24xx_ssp *) SSP1_BASE_ADDR,
.clock = 0,
.idle_char = 0xffffffff
}
};
rtems_libi2c_bus_t * const lpc24xx_ssp_0 =
(rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [0];
rtems_libi2c_bus_t * const lpc24xx_ssp_1 =
(rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [1];

View File

@@ -22,6 +22,7 @@
#include <bsp.h>
#include <bsp/bootcard.h>
#include <bsp/dma.h>
#include <bsp/irq.h>
#include <bsp/linker-symbols.h>
#include <bsp/lpc24xx.h>
@@ -61,23 +62,26 @@ void bsp_start( void)
/* Spin forever */
}
}
/* DMA */
lpc24xx_dma_initialize();
}
#define ULSR_THRE 0x00000020U
static void my_BSP_output_char( char c)
static void lpc24xx_BSP_output_char( char c)
{
while (REG_FLAG_IS_CLEARED( U0LSR, ULSR_THRE)) {
while (IS_FLAG_CLEARED( U0LSR, ULSR_THRE)) {
/* Wait */
}
U0THR = c;
if (c == '\n') {
while (REG_FLAG_IS_CLEARED( U0LSR, ULSR_THRE)) {
while (IS_FLAG_CLEARED( U0LSR, ULSR_THRE)) {
/* Wait */
}
U0THR = '\r';
}
}
BSP_output_char_function_type BSP_output_char = my_BSP_output_char;
BSP_output_char_function_type BSP_output_char = lpc24xx_BSP_output_char;