source: rtems/bsps/arm/lpc24xx/spi/ssp.c

Last change on this file was bcef89f2, checked in by Sebastian Huber <sebastian.huber@…>, on 05/19/23 at 06:18:25

Update company name

The embedded brains GmbH & Co. KG is the legal successor of embedded
brains GmbH.

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup RTEMSBSPsARMLPC24XXSSP
5 */
6
7/*
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * Copyright (C) 2008, 2019 embedded brains GmbH & Co. KG
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.
32 */
33
34#include <bsp/ssp.h>
35#include <bsp.h>
36#include <bsp/io.h>
37#include <bsp/irq.h>
38#include <bsp/lpc24xx.h>
39
40#include <rtems/score/assert.h>
41
42#include <dev/spi/spi.h>
43
44typedef struct {
45  spi_bus base;
46  volatile lpc24xx_ssp *regs;
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;
60
61typedef struct {
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;
292        }
293      } else {
294        lpc24xx_ssp_done(bus, 0);
295        break;
296      }
297    } else {
298      break;
299    }
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
374  sc = rtems_interrupt_handler_install(
375    bus->irq,
376    "SSP",
377    RTEMS_INTERRUPT_UNIQUE,
378    lpc24xx_ssp_interrupt,
379    bus
380  );
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;
393
394  return 0;
395}
396
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 = {
433    .regs = (volatile lpc24xx_ssp *) SSP0_BASE_ADDR,
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 = {
447    .regs = (volatile lpc24xx_ssp *) SSP1_BASE_ADDR,
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 TracBrowser for help on using the repository browser.