diff options
Diffstat (limited to 'c/src/lib/libbsp/arm/lpc24xx/ssp/ssp.c')
-rw-r--r-- | c/src/lib/libbsp/arm/lpc24xx/ssp/ssp.c | 652 |
1 files changed, 0 insertions, 652 deletions
diff --git a/c/src/lib/libbsp/arm/lpc24xx/ssp/ssp.c b/c/src/lib/libbsp/arm/lpc24xx/ssp/ssp.c deleted file mode 100644 index 6563d047b2..0000000000 --- a/c/src/lib/libbsp/arm/lpc24xx/ssp/ssp.c +++ /dev/null @@ -1,652 +0,0 @@ -/** - * @file - * - * @ingroup lpc24xx_libi2c - * - * @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.org/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> -#include <bsp/io.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(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 ((mis & SSP_MIS_RORRIS) != 0) { - /* TODO */ - icr |= SSP_ICR_RORRIS; - } - - regs->icr = icr; -} - -static void lpc24xx_ssp_dma_handler(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 ((status & LPC24XX_SSP_DMA_TRANSFER_FLAG) == 0) { - 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 ((tc & (GPDMA_STATUS_CH_0 | GPDMA_STATUS_CH_1)) != 0) { - status = LPC24XX_SSP_DMA_DONE; - } else if ((tc & GPDMA_STATUS_CH_0) != 0) { - status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1; - } else if ((tc & GPDMA_STATUS_CH_1) != 0) { - status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0; - } - break; - case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0: - if ((tc & GPDMA_STATUS_CH_1) != 0) { - status = LPC24XX_SSP_DMA_ERROR; - } else if ((tc & GPDMA_STATUS_CH_0) != 0) { - status = LPC24XX_SSP_DMA_DONE; - } - break; - case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1: - if ((tc & GPDMA_STATUS_CH_0) != 0) { - status = LPC24XX_SSP_DMA_ERROR; - } else if ((tc & GPDMA_STATUS_CH_1) != 0) { - 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; - lpc24xx_module module = LPC24XX_MODULE_SSP_0; - 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 - ); - RTEMS_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; - - switch ((uintptr_t) regs) { - case SSP0_BASE_ADDR: - module = LPC24XX_MODULE_SSP_0; - vector = LPC24XX_IRQ_SPI_SSP_0; - break; - case SSP1_BASE_ADDR: - module = LPC24XX_MODULE_SSP_1; - vector = LPC24XX_IRQ_SSP_1; - break; - default: - return RTEMS_IO_ERROR; - } - - /* Set clock select */ - sc = lpc24xx_module_enable(module, LPC24XX_MODULE_PCLK_DEFAULT); - RTEMS_CHECK_SC(sc, "enable module clock"); - - /* 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 - ); - RTEMS_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 ((regs->sr & SSP_SR_TFE) == 0) { - /* 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 = 0; - - 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 ((sr & SSP_SR_TNF) != 0 && m < LPC24XX_SSP_FIFO_SIZE) { - regs->dr = *out; - ++w; - out += dw; - } - - /* Read */ - if ((sr & SSP_SR_RNE) != 0) { - *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 ((sr & SSP_SR_RNE) == 0); - - /* 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_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) { - rtems_status_code cs0 = lpc24xx_dma_channel_obtain(0); - rtems_status_code cs1 = lpc24xx_dma_channel_obtain(1); - - if (cs0 != RTEMS_SUCCESSFUL || cs1 != RTEMS_SUCCESSFUL) { - if (cs0 == RTEMS_SUCCESSFUL) { - lpc24xx_dma_channel_release(0); - } - if (cs1 == RTEMS_SUCCESSFUL) { - lpc24xx_dma_channel_release(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->desc.dest = (uint32_t) in; - } else { - receive_channel->desc.dest = (uint32_t) &lpc24xx_ssp_trash; - di = 0; - } - receive_channel->desc.src = (uint32_t) &ssp->dr; - receive_channel->desc.lli = 0; - receive_channel->desc.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->desc.src = (uint32_t) out; - } else { - transmit_channel->desc.src = (uint32_t) &e->idle_char; - si = 0; - } - transmit_channel->desc.dest = (uint32_t) &ssp->dr; - transmit_channel->desc.lli = 0; - transmit_channel->desc.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]; |