[rtems commit] bsp/lpc24xx: Convert SSP driver to Linux API

Sebastian Huber sebh at rtems.org
Tue Mar 12 14:59:59 UTC 2019


Module:    rtems
Branch:    master
Commit:    1400210989e0bf15672e5bd562321ba2a37f5672
Changeset: http://git.rtems.org/rtems/commit/?id=1400210989e0bf15672e5bd562321ba2a37f5672

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Fri Mar  8 11:12:31 2019 +0100

bsp/lpc24xx: Convert SSP driver to Linux API

Use interrupts instead of polled or DMA driven mode.  Change license to
BSD-2-Clause.

Close #3724.

---

 bsps/arm/lpc24xx/include/bsp/ssp.h |  54 ++-
 bsps/arm/lpc24xx/spi/ssp.c         | 898 +++++++++++++++----------------------
 2 files changed, 396 insertions(+), 556 deletions(-)

diff --git a/bsps/arm/lpc24xx/include/bsp/ssp.h b/bsps/arm/lpc24xx/include/bsp/ssp.h
index c8ec97d..b8c870d 100644
--- a/bsps/arm/lpc24xx/include/bsp/ssp.h
+++ b/bsps/arm/lpc24xx/include/bsp/ssp.h
@@ -1,42 +1,62 @@
 /**
  * @file
  *
- * @ingroup RTEMSBSPsARMLPC24XX_libi2c
- *
- * @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
+ * @ingroup RTEMSBSPsARMLPC24XXSSP
  */
 
 /*
- * Copyright (c) 2008
- * Embedded Brains GmbH
- * Obere Lagerstr. 30
- * D-82178 Puchheim
- * Germany
- * rtems at embedded-brains.de
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef LIBBSP_ARM_LPC24XX_SSP_H
 #define LIBBSP_ARM_LPC24XX_SSP_H
 
-#include <rtems/libi2c.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
 /**
- * @ingroup RTEMSBSPsARMLPC24XX_libi2c
+ * @defgroup RTEMSBSPsARMLPC24XXSSP SSP Driver
+ *
+ * @ingroup RTEMSBSPsARMLPC24XX
  *
  * @{
  */
 
-extern rtems_libi2c_bus_t * const lpc24xx_ssp_0;
+#define LPC24XX_SSP_0_BUS_PATH "/dev/ssp-0"
+
+#define LPC24XX_SSP_1_BUS_PATH "/dev/ssp-1"
+
+#define LPC24XX_SSP_2_BUS_PATH "/dev/ssp-2"
+
+int lpc24xx_register_ssp_0(void);
+
+int lpc24xx_register_ssp_1(void);
 
-extern rtems_libi2c_bus_t * const lpc24xx_ssp_1;
+int lpc24xx_register_ssp_2(void);
 
 /** @} */
 
diff --git a/bsps/arm/lpc24xx/spi/ssp.c b/bsps/arm/lpc24xx/spi/ssp.c
index 743276a..59fbf50 100644
--- a/bsps/arm/lpc24xx/spi/ssp.c
+++ b/bsps/arm/lpc24xx/spi/ssp.c
@@ -1,652 +1,472 @@
 /**
  * @file
  *
- * @ingroup RTEMSBSPsARMLPC24XX_libi2c
- *
- * @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
+ * @ingroup RTEMSBSPsARMLPC24XXSSP
  */
 
 /*
- * Copyright (c) 2008
- * Embedded Brains GmbH
- * Obere Lagerstr. 30
- * D-82178 Puchheim
- * Germany
- * rtems at embedded-brains.de
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#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.h>
 #include <bsp/io.h>
+#include <bsp/irq.h>
+#include <bsp/lpc24xx.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
+#include <rtems/score/assert.h>
 
-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;
+#include <dev/spi/spi.h>
 
 typedef struct {
-  rtems_libi2c_bus_t bus;
+  spi_bus base;
   volatile lpc24xx_ssp *regs;
-  unsigned clock;
-  uint32_t idle_char;
-} lpc24xx_ssp_bus_entry;
+  size_t tx_todo;
+  const uint8_t *tx_buf;
+  size_t tx_inc;
+  size_t rx_todo;
+  uint8_t *rx_buf;
+  size_t rx_inc;
+  const spi_ioc_transfer *msg;
+  uint32_t msg_todo;
+  int msg_error;
+  rtems_binary_semaphore sem;
+  lpc24xx_module module;
+  rtems_vector_number irq;
+} lpc24xx_ssp_bus;
 
 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;
-}
+  volatile lpc24xx_ssp *regs;
+  lpc24xx_module module;
+  rtems_vector_number irq;
+} lpc24xx_ssp_config;
 
-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;
-  }
+static uint8_t lpc24xx_ssp_trash;
+
+static const uint8_t lpc24xx_ssp_idle = 0xff;
 
-  regs->icr = icr;
+static void lpc24xx_ssp_done(lpc24xx_ssp_bus *bus, int error)
+{
+  bus->msg_error = error;
+  rtems_binary_semaphore_post(&bus->sem);
 }
 
-static void lpc24xx_ssp_dma_handler(void *arg)
+static int lpc24xx_ssp_do_setup(
+  lpc24xx_ssp_bus *bus,
+  uint32_t speed_hz,
+  uint32_t mode
+)
 {
-  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;
-  }
+  volatile lpc24xx_ssp *regs;
+  uint32_t clk;
+  uint32_t scr_plus_one;
+  uint32_t cr0;
 
-  /* 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;
+  if (speed_hz > bus->base.max_speed_hz || speed_hz == 0) {
+    return -EINVAL;
   }
 
-  /* 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;
+  if ((mode & ~(SPI_CPOL | SPI_CPHA)) != 0) {
+    return -EINVAL;
   }
 
-  /* 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;
-    }
-  }
+  regs = bus->regs;
+  clk = bus->base.max_speed_hz;
+  scr_plus_one = (clk + speed_hz - 1) / speed_hz;
 
-  /* Set status */
-  e->status = status;
-}
+  if (scr_plus_one > 256) {
+    uint32_t pre;
 
-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;
+    pre = (scr_plus_one + 255) / 256;
+
+    if (pre <= 127) {
+      scr_plus_one = (clk / pre + speed_hz - 1) / speed_hz;
+    } else {
+      pre = 127;
+      scr_plus_one = 256;
     }
+
+    regs->cpsr = 2 * pre;
   }
 
-  /* Disable module */
-  regs->cr1 = 0;
+  cr0 = SET_SSP_CR0_DSS(0, 0x7) | SET_SSP_CR0_SCR(0, scr_plus_one - 1);
 
-  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;
+  if ((mode & SPI_CPOL) != 0) {
+    cr0 |= SSP_CR0_CPOL;
   }
 
-  /* Set clock select */
-  sc = lpc24xx_module_enable(module, LPC24XX_MODULE_PCLK_DEFAULT);
-  RTEMS_CHECK_SC(sc, "enable module clock");
+  if ((mode & SPI_CPHA) != 0) {
+    cr0 |= SSP_CR0_CPHA;
+  }
 
-  /* Set serial clock rate to save value */
-  regs->cr0 = SET_SSP_CR0_SCR(0, 255);
+  regs->cr0 = cr0;
+
+  bus->base.speed_hz = speed_hz;
+  bus->base.mode = mode;
+  return 0;
+}
 
-  /* Set clock prescaler */
-  if (pre > 254) {
-    pre = 254;
-  } else if (pre < 2) {
-    pre = 2;
+static bool lpc24xx_ssp_msg_setup(
+  lpc24xx_ssp_bus *bus,
+  const spi_ioc_transfer *msg
+)
+{
+  if (msg->cs_change == 0 || msg->bits_per_word != 8) {
+    lpc24xx_ssp_done(bus, -EINVAL);
+    return false;
   }
-  regs->cpsr = pre;
 
-  /* Save clock value */
-  e->clock = pclk / pre;
+  if (msg->speed_hz != bus->base.speed_hz || msg->mode != bus->base.mode) {
+    int error;
 
-  /* Enable module and loop back mode */
-  regs->cr1 = SSP_CR1_LBM | SSP_CR1_SSE;
+    error = lpc24xx_ssp_do_setup(bus, msg->speed_hz, msg->mode);
+    if (error != 0) {
+      lpc24xx_ssp_done(bus, error);
+      return false;
+    }
+  }
 
-  /* Install interrupt handler */
-  sc = rtems_interrupt_handler_install(
-    vector,
-    "SSP",
-    RTEMS_INTERRUPT_UNIQUE,
-    lpc24xx_ssp_handler,
-    e
-  );
-  RTEMS_CHECK_SC(sc, "install interrupt handler");
+  bus->tx_todo = msg->len;
+  bus->rx_todo = msg->len;
 
-  /* Enable receiver overrun interrupts */
-  e->regs->imsc = SSP_IMSC_RORIM;
+  if (msg->tx_buf != NULL) {
+    bus->tx_buf = msg->tx_buf;
+    bus->tx_inc = 1;
+  } else {
+    bus->tx_buf = &lpc24xx_ssp_idle;
+    bus->tx_inc = 0;
+  }
 
-  return RTEMS_SUCCESSFUL;
-}
+  if (msg->rx_buf != NULL) {
+    bus->rx_buf = msg->rx_buf;
+    bus->rx_inc = 1;
+  } else {
+    bus->rx_buf = &lpc24xx_ssp_trash;
+    bus->rx_inc = 0;
+  }
 
-static rtems_status_code lpc24xx_ssp_send_start(rtems_libi2c_bus_t *bus)
-{
-  return RTEMS_SUCCESSFUL;
+  return true;
 }
 
-static rtems_status_code lpc24xx_ssp_send_stop(rtems_libi2c_bus_t *bus)
+static bool lpc24xx_ssp_do_tx_and_rx(
+  lpc24xx_ssp_bus *bus,
+  volatile lpc24xx_ssp *regs,
+  uint32_t sr
+)
 {
-  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;
+  size_t tx_todo;
+  const uint8_t *tx_buf;
+  size_t tx_inc;
+  size_t rx_todo;
+  uint8_t *rx_buf;
+  size_t rx_inc;
+  uint32_t imsc;
+
+  tx_todo = bus->tx_todo;
+  tx_buf = bus->tx_buf;
+  tx_inc = bus->tx_inc;
+  rx_todo = bus->rx_todo;
+  rx_buf = bus->rx_buf;
+  rx_inc = bus->rx_inc;
+
+  while (tx_todo > 0 && (sr & SSP_SR_TNF) != 0) {
+    regs->dr = *tx_buf;
+    --tx_todo;
+    tx_buf += tx_inc;
+
+    if (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
+      *rx_buf = regs->dr;
+      --rx_todo;
+      rx_buf += rx_inc;
     }
+
+    sr = regs->sr;
   }
 
-  return RTEMS_SUCCESSFUL;
-}
+  while (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
+    *rx_buf = regs->dr;
+    --rx_todo;
+    rx_buf += rx_inc;
 
-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;
+    sr = regs->sr;
+  }
+
+  bus->tx_todo = tx_todo;
+  bus->tx_buf = tx_buf;
+  bus->rx_todo = rx_todo;
+  bus->rx_buf = rx_buf;
 
-  if (lpc24xx_ssp_is_busy(e)) {
-    return RTEMS_RESOURCE_IN_USE;
+  imsc = 0;
+
+  if (tx_todo > 0) {
+    imsc |= SSP_IMSC_TXIM;
+  } else if (rx_todo > 0) {
+    imsc |= SSP_IMSC_RXIM | SSP_IMSC_RTIM;
+    regs->icr = SSP_ICR_RTRIS;
   }
 
-  return RTEMS_SUCCESSFUL;
+  regs->imsc = imsc;
+
+  return tx_todo == 0 && rx_todo == 0;
 }
 
-static int lpc24xx_ssp_set_transfer_mode(
-  rtems_libi2c_bus_t *bus,
-  const rtems_libi2c_tfr_mode_t *mode
+static void lpc24xx_ssp_start(
+  lpc24xx_ssp_bus *bus,
+  const spi_ioc_transfer *msg
 )
 {
-  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;
-  }
+  while (true) {
+    if (lpc24xx_ssp_msg_setup(bus, msg)) {
+      volatile lpc24xx_ssp *regs;
+      uint32_t sr;
+      bool next_msg;
 
-  if (mode->bits_per_char != 8) {
-    return -RTEMS_INVALID_NUMBER;
-  }
+      regs = bus->regs;
+      sr = regs->sr;
 
-  if (mode->lsb_first) {
-    return -RTEMS_INVALID_NUMBER;
-  }
+      if ((sr & (SSP_SR_RNE | SSP_SR_TFE)) != SSP_SR_TFE) {
+        lpc24xx_ssp_done(bus, -EIO);
+        break;
+      }
 
-  if (br == 0) {
-    return -RTEMS_INVALID_NUMBER;
-  }
+      next_msg = lpc24xx_ssp_do_tx_and_rx(bus, regs, sr);
+      if (!next_msg) {
+        break;
+      }
 
-  /* Compute new prescaler if necessary */
-  if (scr > 256 || scr < 1) {
-    unsigned pre = regs->cpsr;
-    unsigned pclk = clk * pre;
+      --bus->msg_todo;
 
-    while (scr > 256) {
-      if (pre > 252) {
-        return -RTEMS_INVALID_NUMBER;
+      if (bus->msg_todo == 0) {
+        lpc24xx_ssp_done(bus, 0);
+        break;
       }
-      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;
+      ++msg;
+      bus->msg = msg;
+    } else {
+      break;
     }
-
-    regs->cpsr = pre;
-    e->clock = clk;
   }
+}
 
-  /* Adjust SCR */
-  --scr;
+static void lpc24xx_ssp_interrupt(void *arg)
+{
+  lpc24xx_ssp_bus *bus;
+  volatile lpc24xx_ssp *regs;
 
-  e->idle_char = mode->idle_char;
+  bus = arg;
+  regs = bus->regs;
 
-  while ((regs->sr & SSP_SR_TFE) == 0) {
-    /* Wait */
-  }
+  while (true) {
+    if (lpc24xx_ssp_do_tx_and_rx(bus, regs, regs->sr)) {
+      --bus->msg_todo;
 
-  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);
+      if (bus->msg_todo > 0) {
+        ++bus->msg;
 
-  return 0;
+        if (!lpc24xx_ssp_msg_setup(bus, bus->msg)) {
+          break;
+        }
+      } else {
+        lpc24xx_ssp_done(bus, 0);
+        break;
+      }
+    } else {
+      break;
+    }
+  }
 }
 
-static int lpc24xx_ssp_read_write(
-  rtems_libi2c_bus_t *bus,
-  unsigned char *in,
-  const unsigned char *out,
-  int n
+static int lpc24xx_ssp_transfer(
+  spi_bus *base,
+  const spi_ioc_transfer *msgs,
+  uint32_t msg_count
 )
 {
-  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;
-  }
+  lpc24xx_ssp_bus *bus;
 
-  if (n < 0) {
-    return -RTEMS_INVALID_SIZE;
+  if (msg_count == 0) {
+    return 0;
   }
 
-  /* Disable DMA on SSP */
-  regs->dmacr = 0;
-
-  if (in == NULL) {
-    dr = 0;
-    in = &trash;
-  }
+  bus = (lpc24xx_ssp_bus *) base;
+  bus->msg = msgs;
+  bus->msg_todo = msg_count;
+  lpc24xx_ssp_start(bus, msgs);
+  rtems_binary_semaphore_wait(&bus->sem);
 
-  if (out == NULL) {
-    dw = 0;
-    out = &idle_char;
-  }
+  return bus->msg_error;
+}
 
-  /*
-   * 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;
-    }
+static void lpc24xx_ssp_destroy(spi_bus *base)
+{
+  lpc24xx_ssp_bus *bus;
+  rtems_status_code sc;
 
-    /* Read */
-    if ((sr & SSP_SR_RNE) != 0) {
-      *in = (unsigned char) regs->dr;
-      ++r;
-      in += dr;
-    }
+  bus = (lpc24xx_ssp_bus *) base;
 
-    /* New status */
-    sr = regs->sr;
-  }
+  sc = rtems_interrupt_handler_remove(
+    bus->irq,
+    lpc24xx_ssp_interrupt,
+    bus
+  );
+  _Assert(sc == RTEMS_SUCCESSFUL);
+  (void) sc;
 
-  /* Read outstanding input */
-  while (r < n) {
-    /* Wait */
-    do {
-      sr = regs->sr;
-    } while ((sr & SSP_SR_RNE) == 0);
+  /* Disable SSP module */
+  bus->regs->cr1 = 0;
 
-    /* Read */
-    *in = (unsigned char) regs->dr;
-    ++r;
-    in += dr;
-  }
+  sc = lpc24xx_module_disable(bus->module);
+  _Assert(sc == RTEMS_SUCCESSFUL);
+  (void) sc;
 
-  return n;
+  rtems_binary_semaphore_destroy(&bus->sem);
+  spi_bus_destroy_and_free(&bus->base);
 }
 
-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
-)
+static int lpc24xx_ssp_setup(spi_bus *base)
 {
-  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;
-  }
+  lpc24xx_ssp_bus *bus;
 
-  /* 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);
+  bus = (lpc24xx_ssp_bus *) base;
 
-      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;
-      }
-    }
+  if (bus->base.bits_per_word != 8) {
+    return -EINVAL;
   }
 
-  /* 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;
-  }
+  return lpc24xx_ssp_do_setup(bus, bus->base.speed_hz, bus->base.mode);
+}
+
+static int lpc24xx_ssp_init(lpc24xx_ssp_bus *bus)
+{
+  rtems_status_code sc;
 
-  /* 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;
+  sc = lpc24xx_module_enable(bus->module, LPC24XX_MODULE_PCLK_DEFAULT);
+  _Assert(sc == RTEMS_SUCCESSFUL);
+  (void) sc;
 
-  /* Enable DMA on SSP */
-  ssp->dmacr = SSP_DMACR_RXDMAE | SSP_DMACR_TXDMAE;
+  /* Disable SSP module */
+  bus->regs->cr1 = 0;
 
-  /* 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;
+  sc = rtems_interrupt_handler_install(
+    bus->irq,
+    "SSP",
+    RTEMS_INTERRUPT_UNIQUE,
+    lpc24xx_ssp_interrupt,
+    bus
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    return EAGAIN;
   }
-  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;
-}
+  rtems_binary_semaphore_init(&bus->sem, "SSP");
 
-static int lpc24xx_ssp_read(rtems_libi2c_bus_t *bus, unsigned char *in, int n)
-{
-  return lpc24xx_ssp_read_write(bus, in, NULL, n);
+  /* Initialize SSP module */
+  bus->regs->dmacr = 0;
+  bus->regs->imsc = 0;
+  bus->regs->cpsr = 2;
+  bus->regs->cr0 = SET_SSP_CR0_DSS(0, 0x7);
+  bus->regs->cr1 = SSP_CR1_SSE;
+
+  return 0;
 }
 
-static int lpc24xx_ssp_write(
-  rtems_libi2c_bus_t *bus,
-  unsigned char *out,
-  int n
+static int spi_bus_register_lpc24xx_ssp(
+  const char *bus_path,
+  const lpc24xx_ssp_config *config
 )
 {
-  return lpc24xx_ssp_read_write(bus, NULL, out, n);
-}
+  lpc24xx_ssp_bus *bus;
+  int eno;
 
-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;
+  bus = (lpc24xx_ssp_bus *) spi_bus_alloc_and_init(sizeof(*bus));
+  if (bus == NULL) {
+    return -1;
+  }
+
+  bus->base.max_speed_hz = LPC24XX_PCLK / 2;
+  bus->base.bits_per_word = 8;
+  bus->base.speed_hz = bus->base.max_speed_hz;
+  bus->regs = config->regs;
+  bus->module = config->module;
+  bus->irq = config->irq;
+
+  eno = lpc24xx_ssp_init(bus);
+  if (eno != 0) {
+    (*bus->base.destroy)(&bus->base);
+    rtems_set_errno_and_return_minus_one(eno);
   }
 
-  return rv;
+  bus->base.transfer = lpc24xx_ssp_transfer;
+  bus->base.destroy = lpc24xx_ssp_destroy;
+  bus->base.setup = lpc24xx_ssp_setup;
+
+  return spi_bus_register(&bus->base, bus_path);
 }
 
-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)
-    },
+int lpc24xx_register_ssp_0(void)
+{
+  static const lpc24xx_ssp_config config = {
     .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)
-    },
+    .module = LPC24XX_MODULE_SSP_0,
+    .irq = LPC24XX_IRQ_SPI_SSP_0
+  };
+
+  return spi_bus_register_lpc24xx_ssp(
+    LPC24XX_SSP_0_BUS_PATH,
+    &config
+  );
+}
+
+int lpc24xx_register_ssp_1(void)
+{
+  static const lpc24xx_ssp_config config = {
     .regs = (volatile lpc24xx_ssp *) SSP1_BASE_ADDR,
-    .clock = 0,
-    .idle_char = 0xffffffff
-  }
-};
+    .module = LPC24XX_MODULE_SSP_1,
+    .irq = LPC24XX_IRQ_SSP_1
+  };
 
-rtems_libi2c_bus_t * const lpc24xx_ssp_0 =
-  (rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [0];
+  return spi_bus_register_lpc24xx_ssp(
+    LPC24XX_SSP_2_BUS_PATH,
+    &config
+  );
+}
 
-rtems_libi2c_bus_t * const lpc24xx_ssp_1 =
-  (rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [1];
+#ifdef ARM_MULTILIB_ARCH_V7M
+int lpc24xx_register_ssp_2(void)
+{
+  static const lpc24xx_ssp_config config = {
+    .regs = (volatile lpc24xx_ssp *) SSP2_BASE_ADDR,
+    .module = LPC24XX_MODULE_SSP_2,
+    .irq = LPC24XX_IRQ_SSP_2
+  };
+
+  return spi_bus_register_lpc24xx_ssp(
+    LPC24XX_SSP_2_BUS_PATH,
+    &config
+  );
+}
+#endif




More information about the vc mailing list