Changeset 1400210 in rtems


Ignore:
Timestamp:
Mar 8, 2019, 10:12:31 AM (6 weeks ago)
Author:
Sebastian Huber <sebastian.huber@…>
Branches:
master
Children:
c10e8b6
Parents:
b2eaa6c
git-author:
Sebastian Huber <sebastian.huber@…> (03/08/19 10:12:31)
git-committer:
Sebastian Huber <sebastian.huber@…> (03/12/19 14:59:26)
Message:

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.

Location:
bsps/arm/lpc24xx
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • bsps/arm/lpc24xx/include/bsp/ssp.h

    rb2eaa6c r1400210  
    22 * @file
    33 *
    4  * @ingroup RTEMSBSPsARMLPC24XX_libi2c
    5  *
    6  * @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
     4 * @ingroup RTEMSBSPsARMLPC24XXSSP
    75 */
    86
    97/*
    10  * Copyright (c) 2008
    11  * Embedded Brains GmbH
    12  * Obere Lagerstr. 30
    13  * D-82178 Puchheim
    14  * Germany
    15  * rtems@embedded-brains.de
     8 * SPDX-License-Identifier: BSD-2-Clause
    169 *
    17  * The license and distribution terms for this file may be
    18  * found in the file LICENSE in this distribution or at
    19  * http://www.rtems.org/license/LICENSE.
     10 * Copyright (C) 2008, 2019 embedded brains GmbH
     11 *
     12 * Redistribution and use in source and binary forms, with or without
     13 * modification, are permitted provided that the following conditions
     14 * are met:
     15 * 1. Redistributions of source code must retain the above copyright
     16 *    notice, this list of conditions and the following disclaimer.
     17 * 2. Redistributions in binary form must reproduce the above copyright
     18 *    notice, this list of conditions and the following disclaimer in the
     19 *    documentation and/or other materials provided with the distribution.
     20 *
     21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31 * POSSIBILITY OF SUCH DAMAGE.
    2032 */
    2133
    2234#ifndef LIBBSP_ARM_LPC24XX_SSP_H
    2335#define LIBBSP_ARM_LPC24XX_SSP_H
    24 
    25 #include <rtems/libi2c.h>
    2636
    2737#ifdef __cplusplus
     
    3040
    3141/**
    32  * @ingroup RTEMSBSPsARMLPC24XX_libi2c
     42 * @defgroup RTEMSBSPsARMLPC24XXSSP SSP Driver
     43 *
     44 * @ingroup RTEMSBSPsARMLPC24XX
    3345 *
    3446 * @{
    3547 */
    3648
    37 extern rtems_libi2c_bus_t * const lpc24xx_ssp_0;
     49#define LPC24XX_SSP_0_BUS_PATH "/dev/ssp-0"
    3850
    39 extern rtems_libi2c_bus_t * const lpc24xx_ssp_1;
     51#define LPC24XX_SSP_1_BUS_PATH "/dev/ssp-1"
     52
     53#define LPC24XX_SSP_2_BUS_PATH "/dev/ssp-2"
     54
     55int lpc24xx_register_ssp_0(void);
     56
     57int lpc24xx_register_ssp_1(void);
     58
     59int lpc24xx_register_ssp_2(void);
    4060
    4161/** @} */
  • bsps/arm/lpc24xx/spi/ssp.c

    rb2eaa6c r1400210  
    22 * @file
    33 *
    4  * @ingroup RTEMSBSPsARMLPC24XX_libi2c
     4 * @ingroup RTEMSBSPsARMLPC24XXSSP
     5 */
     6
     7/*
     8 * SPDX-License-Identifier: BSD-2-Clause
    59 *
    6  * @brief LibI2C bus driver for the Synchronous Serial Port (SSP).
     10 * Copyright (C) 2008, 2019 embedded brains GmbH
     11 *
     12 * Redistribution and use in source and binary forms, with or without
     13 * modification, are permitted provided that the following conditions
     14 * are met:
     15 * 1. Redistributions of source code must retain the above copyright
     16 *    notice, this list of conditions and the following disclaimer.
     17 * 2. Redistributions in binary form must reproduce the above copyright
     18 *    notice, this list of conditions and the following disclaimer in the
     19 *    documentation and/or other materials provided with the distribution.
     20 *
     21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31 * POSSIBILITY OF SUCH DAMAGE.
    732 */
    833
    9 /*
    10  * Copyright (c) 2008
    11  * Embedded Brains GmbH
    12  * Obere Lagerstr. 30
    13  * D-82178 Puchheim
    14  * Germany
    15  * rtems@embedded-brains.de
    16  *
    17  * The license and distribution terms for this file may be
    18  * found in the file LICENSE in this distribution or at
    19  * http://www.rtems.org/license/LICENSE.
    20  */
    21 
    22 #include <stdbool.h>
    23 
    2434#include <bsp/ssp.h>
     35#include <bsp.h>
     36#include <bsp/io.h>
     37#include <bsp/irq.h>
    2538#include <bsp/lpc24xx.h>
    26 #include <bsp/irq.h>
    27 #include <bsp/system-clocks.h>
    28 #include <bsp/dma.h>
    29 #include <bsp/io.h>
    30 
    31 #define RTEMS_STATUS_CHECKS_USE_PRINTK
    32 
    33 #include <rtems/status-checks.h>
    34 
    35 #define LPC24XX_SSP_NUMBER 2
    36 
    37 #define LPC24XX_SSP_FIFO_SIZE 8
    38 
    39 #define LPC24XX_SSP_BAUD_RATE 2000000
    40 
    41 typedef enum {
    42   LPC24XX_SSP_DMA_INVALID = 0,
    43   LPC24XX_SSP_DMA_AVAILABLE = 1,
    44   LPC24XX_SSP_DMA_NOT_INITIALIZED = 2,
    45   LPC24XX_SSP_DMA_INITIALIZATION = 3,
    46   LPC24XX_SSP_DMA_TRANSFER_FLAG = 0x80000000U,
    47   LPC24XX_SSP_DMA_WAIT = 1 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
    48   LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0 = 2 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
    49   LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1 = 3 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
    50   LPC24XX_SSP_DMA_ERROR = 4 | LPC24XX_SSP_DMA_TRANSFER_FLAG,
    51   LPC24XX_SSP_DMA_DONE = 5 | LPC24XX_SSP_DMA_TRANSFER_FLAG
    52 } lpc24xx_ssp_dma_status;
     39
     40#include <rtems/score/assert.h>
     41
     42#include <dev/spi/spi.h>
    5343
    5444typedef struct {
    55   rtems_libi2c_bus_t bus;
     45  spi_bus base;
    5646  volatile lpc24xx_ssp *regs;
    57   unsigned clock;
    58   uint32_t idle_char;
    59 } lpc24xx_ssp_bus_entry;
     47  size_t tx_todo;
     48  const uint8_t *tx_buf;
     49  size_t tx_inc;
     50  size_t rx_todo;
     51  uint8_t *rx_buf;
     52  size_t rx_inc;
     53  const spi_ioc_transfer *msg;
     54  uint32_t msg_todo;
     55  int msg_error;
     56  rtems_binary_semaphore sem;
     57  lpc24xx_module module;
     58  rtems_vector_number irq;
     59} lpc24xx_ssp_bus;
    6060
    6161typedef struct {
    62   lpc24xx_ssp_dma_status status;
    63   lpc24xx_ssp_bus_entry *bus;
    64   rtems_libi2c_read_write_done_t done;
    65   int n;
    66   void *arg;
    67 } lpc24xx_ssp_dma_entry;
    68 
    69 static lpc24xx_ssp_dma_entry lpc24xx_ssp_dma_data = {
    70   .status = LPC24XX_SSP_DMA_NOT_INITIALIZED,
    71   .bus = NULL,
    72   .done = NULL,
    73   .n = 0,
    74   .arg = NULL
    75 };
    76 
    77 static uint32_t lpc24xx_ssp_trash = 0;
    78 
    79 static inline bool lpc24xx_ssp_is_busy(const lpc24xx_ssp_bus_entry *bus)
    80 {
    81   return lpc24xx_ssp_dma_data.bus == bus
    82     && lpc24xx_ssp_dma_data.status != LPC24XX_SSP_DMA_AVAILABLE;
    83 }
    84 
    85 static void lpc24xx_ssp_handler(void *arg)
    86 {
    87   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) arg;
    88   volatile lpc24xx_ssp *regs = e->regs;
    89   uint32_t mis = regs->mis;
    90   uint32_t icr = 0;
    91 
    92   if ((mis & SSP_MIS_RORRIS) != 0) {
    93     /* TODO */
    94     icr |= SSP_ICR_RORRIS;
    95   }
    96 
    97   regs->icr = icr;
    98 }
    99 
    100 static void lpc24xx_ssp_dma_handler(void *arg)
    101 {
    102   lpc24xx_ssp_dma_entry *e = (lpc24xx_ssp_dma_entry *) arg;
    103   lpc24xx_ssp_dma_status status = e->status;
    104   uint32_t tc = 0;
    105   uint32_t err = 0;
    106   int rv = 0;
    107 
    108   /* Return if we are not in a transfer status */
    109   if ((status & LPC24XX_SSP_DMA_TRANSFER_FLAG) == 0) {
    110     return;
    111   }
    112 
    113   /* Get interrupt status */
    114   tc = GPDMA_INT_TCSTAT;
    115   err = GPDMA_INT_ERR_STAT;
    116 
    117   /* Clear interrupt status */
    118   GPDMA_INT_TCCLR = tc;
    119   GPDMA_INT_ERR_CLR = err;
    120 
    121   /* Change status */
    122   if (err == 0) {
    123     switch (status) {
    124       case LPC24XX_SSP_DMA_WAIT:
    125         if ((tc & (GPDMA_STATUS_CH_0 | GPDMA_STATUS_CH_1)) != 0) {
    126           status = LPC24XX_SSP_DMA_DONE;
    127         } else if ((tc & GPDMA_STATUS_CH_0) != 0) {
    128           status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1;
    129         } else if ((tc & GPDMA_STATUS_CH_1) != 0) {
    130           status = LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0;
     62  volatile lpc24xx_ssp *regs;
     63  lpc24xx_module module;
     64  rtems_vector_number irq;
     65} lpc24xx_ssp_config;
     66
     67static uint8_t lpc24xx_ssp_trash;
     68
     69static const uint8_t lpc24xx_ssp_idle = 0xff;
     70
     71static void lpc24xx_ssp_done(lpc24xx_ssp_bus *bus, int error)
     72{
     73  bus->msg_error = error;
     74  rtems_binary_semaphore_post(&bus->sem);
     75}
     76
     77static int lpc24xx_ssp_do_setup(
     78  lpc24xx_ssp_bus *bus,
     79  uint32_t speed_hz,
     80  uint32_t mode
     81)
     82{
     83  volatile lpc24xx_ssp *regs;
     84  uint32_t clk;
     85  uint32_t scr_plus_one;
     86  uint32_t cr0;
     87
     88  if (speed_hz > bus->base.max_speed_hz || speed_hz == 0) {
     89    return -EINVAL;
     90  }
     91
     92  if ((mode & ~(SPI_CPOL | SPI_CPHA)) != 0) {
     93    return -EINVAL;
     94  }
     95
     96  regs = bus->regs;
     97  clk = bus->base.max_speed_hz;
     98  scr_plus_one = (clk + speed_hz - 1) / speed_hz;
     99
     100  if (scr_plus_one > 256) {
     101    uint32_t pre;
     102
     103    pre = (scr_plus_one + 255) / 256;
     104
     105    if (pre <= 127) {
     106      scr_plus_one = (clk / pre + speed_hz - 1) / speed_hz;
     107    } else {
     108      pre = 127;
     109      scr_plus_one = 256;
     110    }
     111
     112    regs->cpsr = 2 * pre;
     113  }
     114
     115  cr0 = SET_SSP_CR0_DSS(0, 0x7) | SET_SSP_CR0_SCR(0, scr_plus_one - 1);
     116
     117  if ((mode & SPI_CPOL) != 0) {
     118    cr0 |= SSP_CR0_CPOL;
     119  }
     120
     121  if ((mode & SPI_CPHA) != 0) {
     122    cr0 |= SSP_CR0_CPHA;
     123  }
     124
     125  regs->cr0 = cr0;
     126
     127  bus->base.speed_hz = speed_hz;
     128  bus->base.mode = mode;
     129  return 0;
     130}
     131
     132static bool lpc24xx_ssp_msg_setup(
     133  lpc24xx_ssp_bus *bus,
     134  const spi_ioc_transfer *msg
     135)
     136{
     137  if (msg->cs_change == 0 || msg->bits_per_word != 8) {
     138    lpc24xx_ssp_done(bus, -EINVAL);
     139    return false;
     140  }
     141
     142  if (msg->speed_hz != bus->base.speed_hz || msg->mode != bus->base.mode) {
     143    int error;
     144
     145    error = lpc24xx_ssp_do_setup(bus, msg->speed_hz, msg->mode);
     146    if (error != 0) {
     147      lpc24xx_ssp_done(bus, error);
     148      return false;
     149    }
     150  }
     151
     152  bus->tx_todo = msg->len;
     153  bus->rx_todo = msg->len;
     154
     155  if (msg->tx_buf != NULL) {
     156    bus->tx_buf = msg->tx_buf;
     157    bus->tx_inc = 1;
     158  } else {
     159    bus->tx_buf = &lpc24xx_ssp_idle;
     160    bus->tx_inc = 0;
     161  }
     162
     163  if (msg->rx_buf != NULL) {
     164    bus->rx_buf = msg->rx_buf;
     165    bus->rx_inc = 1;
     166  } else {
     167    bus->rx_buf = &lpc24xx_ssp_trash;
     168    bus->rx_inc = 0;
     169  }
     170
     171  return true;
     172}
     173
     174static bool lpc24xx_ssp_do_tx_and_rx(
     175  lpc24xx_ssp_bus *bus,
     176  volatile lpc24xx_ssp *regs,
     177  uint32_t sr
     178)
     179{
     180  size_t tx_todo;
     181  const uint8_t *tx_buf;
     182  size_t tx_inc;
     183  size_t rx_todo;
     184  uint8_t *rx_buf;
     185  size_t rx_inc;
     186  uint32_t imsc;
     187
     188  tx_todo = bus->tx_todo;
     189  tx_buf = bus->tx_buf;
     190  tx_inc = bus->tx_inc;
     191  rx_todo = bus->rx_todo;
     192  rx_buf = bus->rx_buf;
     193  rx_inc = bus->rx_inc;
     194
     195  while (tx_todo > 0 && (sr & SSP_SR_TNF) != 0) {
     196    regs->dr = *tx_buf;
     197    --tx_todo;
     198    tx_buf += tx_inc;
     199
     200    if (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
     201      *rx_buf = regs->dr;
     202      --rx_todo;
     203      rx_buf += rx_inc;
     204    }
     205
     206    sr = regs->sr;
     207  }
     208
     209  while (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
     210    *rx_buf = regs->dr;
     211    --rx_todo;
     212    rx_buf += rx_inc;
     213
     214    sr = regs->sr;
     215  }
     216
     217  bus->tx_todo = tx_todo;
     218  bus->tx_buf = tx_buf;
     219  bus->rx_todo = rx_todo;
     220  bus->rx_buf = rx_buf;
     221
     222  imsc = 0;
     223
     224  if (tx_todo > 0) {
     225    imsc |= SSP_IMSC_TXIM;
     226  } else if (rx_todo > 0) {
     227    imsc |= SSP_IMSC_RXIM | SSP_IMSC_RTIM;
     228    regs->icr = SSP_ICR_RTRIS;
     229  }
     230
     231  regs->imsc = imsc;
     232
     233  return tx_todo == 0 && rx_todo == 0;
     234}
     235
     236static void lpc24xx_ssp_start(
     237  lpc24xx_ssp_bus *bus,
     238  const spi_ioc_transfer *msg
     239)
     240{
     241  while (true) {
     242    if (lpc24xx_ssp_msg_setup(bus, msg)) {
     243      volatile lpc24xx_ssp *regs;
     244      uint32_t sr;
     245      bool next_msg;
     246
     247      regs = bus->regs;
     248      sr = regs->sr;
     249
     250      if ((sr & (SSP_SR_RNE | SSP_SR_TFE)) != SSP_SR_TFE) {
     251        lpc24xx_ssp_done(bus, -EIO);
     252        break;
     253      }
     254
     255      next_msg = lpc24xx_ssp_do_tx_and_rx(bus, regs, sr);
     256      if (!next_msg) {
     257        break;
     258      }
     259
     260      --bus->msg_todo;
     261
     262      if (bus->msg_todo == 0) {
     263        lpc24xx_ssp_done(bus, 0);
     264        break;
     265      }
     266
     267      ++msg;
     268      bus->msg = msg;
     269    } else {
     270      break;
     271    }
     272  }
     273}
     274
     275static void lpc24xx_ssp_interrupt(void *arg)
     276{
     277  lpc24xx_ssp_bus *bus;
     278  volatile lpc24xx_ssp *regs;
     279
     280  bus = arg;
     281  regs = bus->regs;
     282
     283  while (true) {
     284    if (lpc24xx_ssp_do_tx_and_rx(bus, regs, regs->sr)) {
     285      --bus->msg_todo;
     286
     287      if (bus->msg_todo > 0) {
     288        ++bus->msg;
     289
     290        if (!lpc24xx_ssp_msg_setup(bus, bus->msg)) {
     291          break;
    131292        }
     293      } else {
     294        lpc24xx_ssp_done(bus, 0);
    132295        break;
    133       case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_0:
    134         if ((tc & GPDMA_STATUS_CH_1) != 0) {
    135           status = LPC24XX_SSP_DMA_ERROR;
    136         } else if ((tc & GPDMA_STATUS_CH_0) != 0) {
    137           status = LPC24XX_SSP_DMA_DONE;
    138         }
    139         break;
    140       case LPC24XX_SSP_DMA_WAIT_FOR_CHANNEL_1:
    141         if ((tc & GPDMA_STATUS_CH_0) != 0) {
    142           status = LPC24XX_SSP_DMA_ERROR;
    143         } else if ((tc & GPDMA_STATUS_CH_1) != 0) {
    144           status = LPC24XX_SSP_DMA_DONE;
    145         }
    146         break;
    147       default:
    148         status = LPC24XX_SSP_DMA_ERROR;
    149         break;
     296      }
     297    } else {
     298      break;
    150299    }
    151   } else {
    152     status = LPC24XX_SSP_DMA_ERROR;
    153   }
    154 
    155   /* Error cleanup */
    156   if (status == LPC24XX_SSP_DMA_ERROR) {
    157     lpc24xx_dma_channel_disable(0, true);
    158     lpc24xx_dma_channel_disable(1, true);
    159     status = LPC24XX_SSP_DMA_DONE;
    160     rv = -RTEMS_IO_ERROR;
    161   }
    162 
    163   /* Done */
    164   if (status == LPC24XX_SSP_DMA_DONE) {
    165     status = LPC24XX_SSP_DMA_AVAILABLE;
    166     if (e->done != NULL) {
    167       e->done(rv, e->n, e->arg);
    168       e->done = NULL;
    169     }
    170   }
    171 
    172   /* Set status */
    173   e->status = status;
    174 }
    175 
    176 static rtems_status_code lpc24xx_ssp_init(rtems_libi2c_bus_t *bus)
    177 {
    178   rtems_status_code sc = RTEMS_SUCCESSFUL;
    179   rtems_interrupt_level level;
    180   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    181   volatile lpc24xx_ssp *regs = e->regs;
    182   unsigned pclk = lpc24xx_cclk();
    183   unsigned pre =
    184     ((pclk + LPC24XX_SSP_BAUD_RATE - 1) / LPC24XX_SSP_BAUD_RATE + 1) & ~1U;
    185   lpc24xx_module module = LPC24XX_MODULE_SSP_0;
    186   rtems_vector_number vector = UINT32_MAX;
    187 
    188   if (lpc24xx_ssp_dma_data.status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
    189     lpc24xx_ssp_dma_status status = LPC24XX_SSP_DMA_INVALID;
    190 
    191     /* Test and set DMA support status */
    192     rtems_interrupt_disable(level);
    193     status = lpc24xx_ssp_dma_data.status;
    194     if (status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
    195       lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_INITIALIZATION;
    196     }
    197     rtems_interrupt_enable(level);
    198 
    199     if (status == LPC24XX_SSP_DMA_NOT_INITIALIZED) {
    200       /* Install DMA interrupt handler */
    201       sc = rtems_interrupt_handler_install(
    202         LPC24XX_IRQ_DMA,
    203         "SSP DMA",
    204         RTEMS_INTERRUPT_SHARED,
    205         lpc24xx_ssp_dma_handler,
    206         &lpc24xx_ssp_dma_data
    207       );
    208       RTEMS_CHECK_SC(sc, "install DMA interrupt handler");
    209 
    210       /* Set DMA support status */
    211       lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_AVAILABLE;
    212     }
    213   }
    214 
    215   /* Disable module */
    216   regs->cr1 = 0;
    217 
    218   switch ((uintptr_t) regs) {
    219     case SSP0_BASE_ADDR:
    220       module = LPC24XX_MODULE_SSP_0;
    221       vector = LPC24XX_IRQ_SPI_SSP_0;
    222       break;
    223     case SSP1_BASE_ADDR:
    224       module = LPC24XX_MODULE_SSP_1;
    225       vector = LPC24XX_IRQ_SSP_1;
    226       break;
    227     default:
    228       return RTEMS_IO_ERROR;
    229   }
    230 
    231   /* Set clock select */
    232   sc = lpc24xx_module_enable(module, LPC24XX_MODULE_PCLK_DEFAULT);
    233   RTEMS_CHECK_SC(sc, "enable module clock");
    234 
    235   /* Set serial clock rate to save value */
    236   regs->cr0 = SET_SSP_CR0_SCR(0, 255);
    237 
    238   /* Set clock prescaler */
    239   if (pre > 254) {
    240     pre = 254;
    241   } else if (pre < 2) {
    242     pre = 2;
    243   }
    244   regs->cpsr = pre;
    245 
    246   /* Save clock value */
    247   e->clock = pclk / pre;
    248 
    249   /* Enable module and loop back mode */
    250   regs->cr1 = SSP_CR1_LBM | SSP_CR1_SSE;
    251 
    252   /* Install interrupt handler */
     300  }
     301}
     302
     303static int lpc24xx_ssp_transfer(
     304  spi_bus *base,
     305  const spi_ioc_transfer *msgs,
     306  uint32_t msg_count
     307)
     308{
     309  lpc24xx_ssp_bus *bus;
     310
     311  if (msg_count == 0) {
     312    return 0;
     313  }
     314
     315  bus = (lpc24xx_ssp_bus *) base;
     316  bus->msg = msgs;
     317  bus->msg_todo = msg_count;
     318  lpc24xx_ssp_start(bus, msgs);
     319  rtems_binary_semaphore_wait(&bus->sem);
     320
     321  return bus->msg_error;
     322}
     323
     324static void lpc24xx_ssp_destroy(spi_bus *base)
     325{
     326  lpc24xx_ssp_bus *bus;
     327  rtems_status_code sc;
     328
     329  bus = (lpc24xx_ssp_bus *) base;
     330
     331  sc = rtems_interrupt_handler_remove(
     332    bus->irq,
     333    lpc24xx_ssp_interrupt,
     334    bus
     335  );
     336  _Assert(sc == RTEMS_SUCCESSFUL);
     337  (void) sc;
     338
     339  /* Disable SSP module */
     340  bus->regs->cr1 = 0;
     341
     342  sc = lpc24xx_module_disable(bus->module);
     343  _Assert(sc == RTEMS_SUCCESSFUL);
     344  (void) sc;
     345
     346  rtems_binary_semaphore_destroy(&bus->sem);
     347  spi_bus_destroy_and_free(&bus->base);
     348}
     349
     350static int lpc24xx_ssp_setup(spi_bus *base)
     351{
     352  lpc24xx_ssp_bus *bus;
     353
     354  bus = (lpc24xx_ssp_bus *) base;
     355
     356  if (bus->base.bits_per_word != 8) {
     357    return -EINVAL;
     358  }
     359
     360  return lpc24xx_ssp_do_setup(bus, bus->base.speed_hz, bus->base.mode);
     361}
     362
     363static int lpc24xx_ssp_init(lpc24xx_ssp_bus *bus)
     364{
     365  rtems_status_code sc;
     366
     367  sc = lpc24xx_module_enable(bus->module, LPC24XX_MODULE_PCLK_DEFAULT);
     368  _Assert(sc == RTEMS_SUCCESSFUL);
     369  (void) sc;
     370
     371  /* Disable SSP module */
     372  bus->regs->cr1 = 0;
     373
    253374  sc = rtems_interrupt_handler_install(
    254     vector,
     375    bus->irq,
    255376    "SSP",
    256377    RTEMS_INTERRUPT_UNIQUE,
    257     lpc24xx_ssp_handler,
    258     e
     378    lpc24xx_ssp_interrupt,
     379    bus
    259380  );
    260   RTEMS_CHECK_SC(sc, "install interrupt handler");
    261 
    262   /* Enable receiver overrun interrupts */
    263   e->regs->imsc = SSP_IMSC_RORIM;
    264 
    265   return RTEMS_SUCCESSFUL;
    266 }
    267 
    268 static rtems_status_code lpc24xx_ssp_send_start(rtems_libi2c_bus_t *bus)
    269 {
    270   return RTEMS_SUCCESSFUL;
    271 }
    272 
    273 static rtems_status_code lpc24xx_ssp_send_stop(rtems_libi2c_bus_t *bus)
    274 {
    275   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    276 
    277   /* Release DMA support */
    278   if (lpc24xx_ssp_dma_data.bus == e) {
    279     if (lpc24xx_ssp_dma_data.status == LPC24XX_SSP_DMA_AVAILABLE) {
    280       lpc24xx_dma_channel_release(0);
    281       lpc24xx_dma_channel_release(1);
    282       lpc24xx_ssp_dma_data.bus = NULL;
    283     } else {
    284       return RTEMS_RESOURCE_IN_USE;
    285     }
    286   }
    287 
    288   return RTEMS_SUCCESSFUL;
    289 }
    290 
    291 static rtems_status_code lpc24xx_ssp_send_addr(
    292   rtems_libi2c_bus_t *bus,
    293   uint32_t addr,
    294   int rw
    295 )
    296 {
    297   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    298 
    299   if (lpc24xx_ssp_is_busy(e)) {
    300     return RTEMS_RESOURCE_IN_USE;
    301   }
    302 
    303   return RTEMS_SUCCESSFUL;
    304 }
    305 
    306 static int lpc24xx_ssp_set_transfer_mode(
    307   rtems_libi2c_bus_t *bus,
    308   const rtems_libi2c_tfr_mode_t *mode
    309 )
    310 {
    311   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    312   volatile lpc24xx_ssp *regs = e->regs;
    313   unsigned clk = e->clock;
    314   unsigned br = mode->baudrate;
    315   unsigned scr = (clk + br - 1) / br;
    316 
    317   if (lpc24xx_ssp_is_busy(e)) {
    318     return -RTEMS_RESOURCE_IN_USE;
    319   }
    320 
    321   if (mode->bits_per_char != 8) {
    322     return -RTEMS_INVALID_NUMBER;
    323   }
    324 
    325   if (mode->lsb_first) {
    326     return -RTEMS_INVALID_NUMBER;
    327   }
    328 
    329   if (br == 0) {
    330     return -RTEMS_INVALID_NUMBER;
    331   }
    332 
    333   /* Compute new prescaler if necessary */
    334   if (scr > 256 || scr < 1) {
    335     unsigned pre = regs->cpsr;
    336     unsigned pclk = clk * pre;
    337 
    338     while (scr > 256) {
    339       if (pre > 252) {
    340         return -RTEMS_INVALID_NUMBER;
    341       }
    342       pre += 2;
    343       clk = pclk / pre;
    344       scr = (clk + br - 1) / br;
    345     }
    346 
    347     while (scr < 1) {
    348       if (pre < 4) {
    349         return -RTEMS_INVALID_NUMBER;
    350       }
    351       pre -= 2;
    352       clk = pclk / pre;
    353       scr = (clk + br - 1) / br;
    354     }
    355 
    356     regs->cpsr = pre;
    357     e->clock = clk;
    358   }
    359 
    360   /* Adjust SCR */
    361   --scr;
    362 
    363   e->idle_char = mode->idle_char;
    364 
    365   while ((regs->sr & SSP_SR_TFE) == 0) {
    366     /* Wait */
    367   }
    368 
    369   regs->cr0 = SET_SSP_CR0_DSS(0, 0x7)
    370     | SET_SSP_CR0_SCR(0, scr)
    371     | (mode->clock_inv ? SSP_CR0_CPOL : 0)
    372     | (mode->clock_phs ? SSP_CR0_CPHA : 0);
     381  if (sc != RTEMS_SUCCESSFUL) {
     382    return EAGAIN;
     383  }
     384
     385  rtems_binary_semaphore_init(&bus->sem, "SSP");
     386
     387  /* Initialize SSP module */
     388  bus->regs->dmacr = 0;
     389  bus->regs->imsc = 0;
     390  bus->regs->cpsr = 2;
     391  bus->regs->cr0 = SET_SSP_CR0_DSS(0, 0x7);
     392  bus->regs->cr1 = SSP_CR1_SSE;
    373393
    374394  return 0;
    375395}
    376396
    377 static int lpc24xx_ssp_read_write(
    378   rtems_libi2c_bus_t *bus,
    379   unsigned char *in,
    380   const unsigned char *out,
    381   int n
    382 )
    383 {
    384   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    385   volatile lpc24xx_ssp *regs = e->regs;
    386   int r = 0;
    387   int w = 0;
    388   int dr = 1;
    389   int dw = 1;
    390   int m = 0;
    391   uint32_t sr = regs->sr;
    392   unsigned char trash = 0;
    393   unsigned char idle_char = (unsigned char) e->idle_char;
    394 
    395   if (lpc24xx_ssp_is_busy(e)) {
    396     return -RTEMS_RESOURCE_IN_USE;
    397   }
    398 
    399   if (n < 0) {
    400     return -RTEMS_INVALID_SIZE;
    401   }
    402 
    403   /* Disable DMA on SSP */
    404   regs->dmacr = 0;
    405 
    406   if (in == NULL) {
    407     dr = 0;
    408     in = &trash;
    409   }
    410 
    411   if (out == NULL) {
    412     dw = 0;
    413     out = &idle_char;
    414   }
    415 
    416   /*
    417    * Assumption: The transmit and receive FIFOs are empty.  If this assumption
    418    * is not true an input buffer overflow may occur or we may never exit the
    419    * loop due to data loss.  This is only possible if entities external to this
    420    * driver operate on the SSP.
    421    */
    422 
    423   while (w < n) {
    424     /* FIFO capacity */
    425     m = w - r;
    426 
    427     /* Write */
    428     if ((sr & SSP_SR_TNF) != 0 && m < LPC24XX_SSP_FIFO_SIZE) {
    429       regs->dr = *out;
    430       ++w;
    431       out += dw;
    432     }
    433 
    434     /* Read */
    435     if ((sr & SSP_SR_RNE) != 0) {
    436       *in = (unsigned char) regs->dr;
    437       ++r;
    438       in += dr;
    439     }
    440 
    441     /* New status */
    442     sr = regs->sr;
    443   }
    444 
    445   /* Read outstanding input */
    446   while (r < n) {
    447     /* Wait */
    448     do {
    449       sr = regs->sr;
    450     } while ((sr & SSP_SR_RNE) == 0);
    451 
    452     /* Read */
    453     *in = (unsigned char) regs->dr;
    454     ++r;
    455     in += dr;
    456   }
    457 
    458   return n;
    459 }
    460 
    461 static int lpc24xx_ssp_read_write_async(
    462   rtems_libi2c_bus_t *bus,
    463   unsigned char *in,
    464   const unsigned char *out,
    465   int n,
    466   rtems_libi2c_read_write_done_t done,
    467   void *arg
    468 )
    469 {
    470   rtems_interrupt_level level;
    471   lpc24xx_ssp_bus_entry *e = (lpc24xx_ssp_bus_entry *) bus;
    472   volatile lpc24xx_ssp *ssp = e->regs;
    473   volatile lpc24xx_dma_channel *receive_channel = GPDMA_CH_BASE_ADDR(0);
    474   volatile lpc24xx_dma_channel *transmit_channel = GPDMA_CH_BASE_ADDR(1);
    475   uint32_t di = GPDMA_CH_CTRL_DI;
    476   uint32_t si = GPDMA_CH_CTRL_SI;
    477 
    478   if (n < 0 || n > (int) GPDMA_CH_CTRL_TSZ_MAX) {
    479     return -RTEMS_INVALID_SIZE;
    480   }
    481 
    482   /* Try to reserve DMA support for this bus */
    483   if (lpc24xx_ssp_dma_data.bus == NULL) {
    484     rtems_interrupt_disable(level);
    485     if (lpc24xx_ssp_dma_data.bus == NULL) {
    486       lpc24xx_ssp_dma_data.bus = e;
    487     }
    488     rtems_interrupt_enable(level);
    489 
    490     /* Try to obtain DMA channels */
    491     if (lpc24xx_ssp_dma_data.bus == e) {
    492       rtems_status_code cs0 = lpc24xx_dma_channel_obtain(0);
    493       rtems_status_code cs1 = lpc24xx_dma_channel_obtain(1);
    494 
    495       if (cs0 != RTEMS_SUCCESSFUL || cs1 != RTEMS_SUCCESSFUL) {
    496         if (cs0 == RTEMS_SUCCESSFUL) {
    497           lpc24xx_dma_channel_release(0);
    498         }
    499         if (cs1 == RTEMS_SUCCESSFUL) {
    500           lpc24xx_dma_channel_release(1);
    501         }
    502         lpc24xx_ssp_dma_data.bus = NULL;
    503       }
    504     }
    505   }
    506 
    507   /* Check if DMA support is available */
    508   if (lpc24xx_ssp_dma_data.bus != e
    509     || lpc24xx_ssp_dma_data.status != LPC24XX_SSP_DMA_AVAILABLE) {
    510     return -RTEMS_RESOURCE_IN_USE;
    511   }
    512 
    513   /* Set DMA support status and parameter */
    514   lpc24xx_ssp_dma_data.status = LPC24XX_SSP_DMA_WAIT;
    515   lpc24xx_ssp_dma_data.done = done;
    516   lpc24xx_ssp_dma_data.n = n;
    517   lpc24xx_ssp_dma_data.arg = arg;
    518 
    519   /* Enable DMA on SSP */
    520   ssp->dmacr = SSP_DMACR_RXDMAE | SSP_DMACR_TXDMAE;
    521 
    522   /* Receive */
    523   if (in != NULL) {
    524     receive_channel->desc.dest = (uint32_t) in;
    525   } else {
    526     receive_channel->desc.dest = (uint32_t) &lpc24xx_ssp_trash;
    527     di = 0;
    528   }
    529   receive_channel->desc.src = (uint32_t) &ssp->dr;
    530   receive_channel->desc.lli = 0;
    531   receive_channel->desc.ctrl = SET_GPDMA_CH_CTRL_TSZ(0, n)
    532     | SET_GPDMA_CH_CTRL_SBSZ(0, GPDMA_CH_CTRL_BSZ_4)
    533     | SET_GPDMA_CH_CTRL_DBSZ(0, GPDMA_CH_CTRL_BSZ_4)
    534     | SET_GPDMA_CH_CTRL_SW(0, GPDMA_CH_CTRL_W_8)
    535     | SET_GPDMA_CH_CTRL_DW(0, GPDMA_CH_CTRL_W_8)
    536     | GPDMA_CH_CTRL_ITC
    537     | di;
    538   receive_channel->cfg = SET_GPDMA_CH_CFG_SRCPER(0, GPDMA_CH_CFG_PER_SSP1_RX)
    539     | SET_GPDMA_CH_CFG_FLOW(0, GPDMA_CH_CFG_FLOW_PER_TO_MEM_DMA)
    540     | GPDMA_CH_CFG_IE
    541     | GPDMA_CH_CFG_ITC
    542     | GPDMA_CH_CFG_EN;
    543 
    544   /* Transmit */
    545   if (out != NULL) {
    546     transmit_channel->desc.src = (uint32_t) out;
    547   } else {
    548     transmit_channel->desc.src = (uint32_t) &e->idle_char;
    549     si = 0;
    550   }
    551   transmit_channel->desc.dest = (uint32_t) &ssp->dr;
    552   transmit_channel->desc.lli = 0;
    553   transmit_channel->desc.ctrl = SET_GPDMA_CH_CTRL_TSZ(0, n)
    554     | SET_GPDMA_CH_CTRL_SBSZ(0, GPDMA_CH_CTRL_BSZ_4)
    555     | SET_GPDMA_CH_CTRL_DBSZ(0, GPDMA_CH_CTRL_BSZ_4)
    556     | SET_GPDMA_CH_CTRL_SW(0, GPDMA_CH_CTRL_W_8)
    557     | SET_GPDMA_CH_CTRL_DW(0, GPDMA_CH_CTRL_W_8)
    558     | GPDMA_CH_CTRL_ITC
    559     | si;
    560   transmit_channel->cfg = SET_GPDMA_CH_CFG_DESTPER(0, GPDMA_CH_CFG_PER_SSP1_TX)
    561     | SET_GPDMA_CH_CFG_FLOW(0, GPDMA_CH_CFG_FLOW_MEM_TO_PER_DMA)
    562     | GPDMA_CH_CFG_IE
    563     | GPDMA_CH_CFG_ITC
    564     | GPDMA_CH_CFG_EN;
    565 
    566   return 0;
    567 }
    568 
    569 static int lpc24xx_ssp_read(rtems_libi2c_bus_t *bus, unsigned char *in, int n)
    570 {
    571   return lpc24xx_ssp_read_write(bus, in, NULL, n);
    572 }
    573 
    574 static int lpc24xx_ssp_write(
    575   rtems_libi2c_bus_t *bus,
    576   unsigned char *out,
    577   int n
    578 )
    579 {
    580   return lpc24xx_ssp_read_write(bus, NULL, out, n);
    581 }
    582 
    583 static int lpc24xx_ssp_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg)
    584 {
    585   int rv = -1;
    586   const rtems_libi2c_tfr_mode_t *tm = (const rtems_libi2c_tfr_mode_t *) arg;
    587   rtems_libi2c_read_write_t *rw = (rtems_libi2c_read_write_t *) arg;
    588   rtems_libi2c_read_write_async_t *rwa =
    589     (rtems_libi2c_read_write_async_t *) arg;
    590 
    591   switch (cmd) {
    592     case RTEMS_LIBI2C_IOCTL_READ_WRITE:
    593       rv = lpc24xx_ssp_read_write(bus, rw->rd_buf, rw->wr_buf, rw->byte_cnt);
    594       break;
    595     case RTEMS_LIBI2C_IOCTL_READ_WRITE_ASYNC:
    596       rv = lpc24xx_ssp_read_write_async(
    597         bus,
    598         rwa->rd_buf,
    599         rwa->wr_buf,
    600         rwa->byte_cnt,
    601         rwa->done,
    602         rwa->arg
    603       );
    604       break;
    605     case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
    606       rv = lpc24xx_ssp_set_transfer_mode(bus, tm);
    607       break;
    608     default:
    609       rv = -RTEMS_NOT_DEFINED;
    610       break;
    611   }
    612 
    613   return rv;
    614 }
    615 
    616 static const rtems_libi2c_bus_ops_t lpc24xx_ssp_ops = {
    617   .init = lpc24xx_ssp_init,
    618   .send_start = lpc24xx_ssp_send_start,
    619   .send_stop = lpc24xx_ssp_send_stop,
    620   .send_addr = lpc24xx_ssp_send_addr,
    621   .read_bytes = lpc24xx_ssp_read,
    622   .write_bytes = lpc24xx_ssp_write,
    623   .ioctl = lpc24xx_ssp_ioctl
    624 };
    625 
    626 static lpc24xx_ssp_bus_entry lpc24xx_ssp_bus_table [LPC24XX_SSP_NUMBER] = {
    627   {
    628     /* SSP 0 */
    629     .bus = {
    630       .ops = &lpc24xx_ssp_ops,
    631       .size = sizeof(lpc24xx_ssp_bus_entry)
    632     },
     397static int spi_bus_register_lpc24xx_ssp(
     398  const char *bus_path,
     399  const lpc24xx_ssp_config *config
     400)
     401{
     402  lpc24xx_ssp_bus *bus;
     403  int eno;
     404
     405  bus = (lpc24xx_ssp_bus *) spi_bus_alloc_and_init(sizeof(*bus));
     406  if (bus == NULL) {
     407    return -1;
     408  }
     409
     410  bus->base.max_speed_hz = LPC24XX_PCLK / 2;
     411  bus->base.bits_per_word = 8;
     412  bus->base.speed_hz = bus->base.max_speed_hz;
     413  bus->regs = config->regs;
     414  bus->module = config->module;
     415  bus->irq = config->irq;
     416
     417  eno = lpc24xx_ssp_init(bus);
     418  if (eno != 0) {
     419    (*bus->base.destroy)(&bus->base);
     420    rtems_set_errno_and_return_minus_one(eno);
     421  }
     422
     423  bus->base.transfer = lpc24xx_ssp_transfer;
     424  bus->base.destroy = lpc24xx_ssp_destroy;
     425  bus->base.setup = lpc24xx_ssp_setup;
     426
     427  return spi_bus_register(&bus->base, bus_path);
     428}
     429
     430int lpc24xx_register_ssp_0(void)
     431{
     432  static const lpc24xx_ssp_config config = {
    633433    .regs = (volatile lpc24xx_ssp *) SSP0_BASE_ADDR,
    634     .clock = 0,
    635     .idle_char = 0xffffffff
    636   }, {
    637     /* SSP 1 */
    638     .bus = {
    639       .ops = &lpc24xx_ssp_ops,
    640       .size = sizeof(lpc24xx_ssp_bus_entry)
    641     },
     434    .module = LPC24XX_MODULE_SSP_0,
     435    .irq = LPC24XX_IRQ_SPI_SSP_0
     436  };
     437
     438  return spi_bus_register_lpc24xx_ssp(
     439    LPC24XX_SSP_0_BUS_PATH,
     440    &config
     441  );
     442}
     443
     444int lpc24xx_register_ssp_1(void)
     445{
     446  static const lpc24xx_ssp_config config = {
    642447    .regs = (volatile lpc24xx_ssp *) SSP1_BASE_ADDR,
    643     .clock = 0,
    644     .idle_char = 0xffffffff
    645   }
    646 };
    647 
    648 rtems_libi2c_bus_t * const lpc24xx_ssp_0 =
    649   (rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [0];
    650 
    651 rtems_libi2c_bus_t * const lpc24xx_ssp_1 =
    652   (rtems_libi2c_bus_t *) &lpc24xx_ssp_bus_table [1];
     448    .module = LPC24XX_MODULE_SSP_1,
     449    .irq = LPC24XX_IRQ_SSP_1
     450  };
     451
     452  return spi_bus_register_lpc24xx_ssp(
     453    LPC24XX_SSP_2_BUS_PATH,
     454    &config
     455  );
     456}
     457
     458#ifdef ARM_MULTILIB_ARCH_V7M
     459int lpc24xx_register_ssp_2(void)
     460{
     461  static const lpc24xx_ssp_config config = {
     462    .regs = (volatile lpc24xx_ssp *) SSP2_BASE_ADDR,
     463    .module = LPC24XX_MODULE_SSP_2,
     464    .irq = LPC24XX_IRQ_SSP_2
     465  };
     466
     467  return spi_bus_register_lpc24xx_ssp(
     468    LPC24XX_SSP_2_BUS_PATH,
     469    &config
     470  );
     471}
     472#endif
Note: See TracChangeset for help on using the changeset viewer.