source: rtems/bsps/arm/atsam/spi/atsam_spi_bus.c @ ba34ed9c

5
Last change on this file since ba34ed9c was ba34ed9c, checked in by Sebastian Huber <sebastian.huber@…>, on 03/11/19 at 06:12:38

bsp/atsam: Fix use after free

  • Property mode set to 100644
File size: 16.3 KB
Line 
1/* ---------------------------------------------------------------------------- */
2/*                  Atmel Microcontroller Software Support                      */
3/*                       SAM Software Package License                           */
4/* ---------------------------------------------------------------------------- */
5/* Copyright (c) 2015, Atmel Corporation                                        */
6/* Copyright (c) 2016, embedded brains GmbH                                     */
7/*                                                                              */
8/* All rights reserved.                                                         */
9/*                                                                              */
10/* Redistribution and use in source and binary forms, with or without           */
11/* modification, are permitted provided that the following condition is met:    */
12/*                                                                              */
13/* - Redistributions of source code must retain the above copyright notice,     */
14/* this list of conditions and the disclaimer below.                            */
15/*                                                                              */
16/* Atmel's name may not be used to endorse or promote products derived from     */
17/* this software without specific prior written permission.                     */
18/*                                                                              */
19/* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
20/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
21/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
22/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
23/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
24/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
25/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
26/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
27/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
28/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
29/* ---------------------------------------------------------------------------- */
30
31#include <bsp/atsam-clock-config.h>
32#include <bsp/atsam-spi.h>
33#include <bsp/iocopy.h>
34
35#include <rtems/thread.h>
36
37#include <dev/spi/spi.h>
38
39#include <string.h>
40
41#define MAX_SPI_FREQUENCY 50000000
42
43#define DMA_NR_DESC_PER_DIR 3
44#define DMA_DESC_ALLIGNMENT 4
45
46#define DMA_BUF_RX 0
47#define DMA_BUF_TX 1
48#define DMA_BUF_DIRS 2
49
50struct atsam_spi_xdma_buf {
51  LinkedListDescriporView0 desc[DMA_NR_DESC_PER_DIR];
52  uint8_t leadbuf[CPU_CACHE_LINE_BYTES];
53  uint8_t trailbuf[CPU_CACHE_LINE_BYTES];
54};
55
56typedef struct {
57  spi_bus base;
58  rtems_binary_semaphore sem;
59  const spi_ioc_transfer *msg_current;
60  uint32_t msg_todo;
61  int msg_error;
62  Spi *spi_regs;
63  uint32_t dma_tx_channel;
64  uint32_t dma_rx_channel;
65  struct atsam_spi_xdma_buf *dma_bufs;
66  size_t leadbuf_rx_buffered_len;
67  size_t trailbuf_rx_buffered_len;
68  int transfer_in_progress;
69  bool chip_select_decode;
70  uint8_t spi_id;
71  uint32_t peripheral_clk_per_us;
72  uint32_t spi_mr;
73  uint32_t spi_csr[4];
74} atsam_spi_bus;
75
76static void atsam_spi_wakeup_task(atsam_spi_bus *bus)
77{
78  rtems_binary_semaphore_post(&bus->sem);
79}
80
81static uint8_t atsam_calculate_dlybcs(const atsam_spi_bus *bus)
82{
83  uint32_t dlybcs = bus->base.delay_usecs * bus->peripheral_clk_per_us;
84
85  if (dlybcs > 0xff) {
86    dlybcs = 0xff;
87  }
88
89  return dlybcs;
90}
91
92static uint32_t atsam_calculate_scbr(uint32_t speed_hz)
93{
94  uint32_t scbr;
95
96  scbr = BOARD_MCK / speed_hz;
97  if (scbr > 0x0FF) {
98    /* Best estimation we can offer with the hardware. */
99    scbr = 0x0FF;
100  }
101  if (scbr == 0) {
102    /* SCBR = 0 isn't allowed. */
103    scbr = 1;
104  }
105
106  return scbr;
107}
108
109static void atsam_set_phase_and_polarity(uint32_t mode, uint32_t *csr)
110{
111  uint32_t mode_mask = mode & SPI_MODE_3;
112
113  switch(mode_mask) {
114    case SPI_MODE_0:
115      *csr |= SPI_CSR_NCPHA;
116      break;
117    case SPI_MODE_1:
118      break;
119    case SPI_MODE_2:
120      *csr |= SPI_CSR_NCPHA;
121      *csr |= SPI_CSR_CPOL;
122      break;
123    case SPI_MODE_3:
124      *csr |= SPI_CSR_CPOL;
125      break;
126  }
127  *csr |= SPI_CSR_CSAAT;
128}
129
130static void atsam_configure_spi(atsam_spi_bus *bus)
131{
132  uint32_t scbr;
133  uint32_t csr = 0;
134  uint32_t mr;
135  uint32_t cs = bus->base.cs;
136
137  scbr = atsam_calculate_scbr(bus->base.speed_hz);
138
139  mr = bus->spi_mr;
140
141  if (bus->chip_select_decode) {
142    mr |= SPI_MR_PCS(bus->base.cs);
143    mr |= SPI_MR_PCSDEC;
144    cs /= 4;
145  } else {
146    mr |= SPI_PCS(bus->base.cs);
147  }
148
149  bus->spi_regs->SPI_MR = mr;
150
151  csr = bus->spi_csr[cs]
152    | SPI_CSR_SCBR(scbr)
153    | SPI_CSR_BITS(bus->base.bits_per_word - 8);
154
155  atsam_set_phase_and_polarity(bus->base.mode, &csr);
156
157  SPI_ConfigureNPCS(bus->spi_regs, cs, csr);
158}
159
160static void atsam_reset_spi(atsam_spi_bus *bus)
161{
162  bus->spi_regs->SPI_CR = SPI_CR_SPIDIS;
163  bus->spi_regs->SPI_CR = SPI_CR_SWRST;
164  bus->spi_regs->SPI_CR = SPI_CR_SWRST;
165  bus->spi_regs->SPI_CR = SPI_CR_SPIEN;
166}
167
168static void atsam_spi_check_alignment_and_set_up_dma_descriptors(
169  atsam_spi_bus *bus,
170  struct atsam_spi_xdma_buf *buf,
171  const uint8_t *start,
172  size_t len,
173  bool tx
174)
175{
176  LinkedListDescriporView0 *curdesc = buf->desc;
177  size_t misaligned_begin;
178  size_t misaligned_end;
179  size_t len_main;
180  const uint8_t *start_main;
181  const uint8_t *start_trail;
182
183  /* Check alignments. */
184  if (len < CPU_CACHE_LINE_BYTES) {
185    misaligned_begin = len;
186    misaligned_end = 0;
187    len_main = 0;
188  } else {
189    misaligned_begin = ((uint32_t) start) % CPU_CACHE_LINE_BYTES;
190    misaligned_end = (((uint32_t) start) + len) % CPU_CACHE_LINE_BYTES;
191    len_main = len - misaligned_begin - misaligned_end;
192  }
193  start_main = start + misaligned_begin;
194  start_trail = start_main + len_main;
195
196  /* Store length for copying data back. */
197  if (!tx) {
198    bus->leadbuf_rx_buffered_len = misaligned_begin;
199    bus->trailbuf_rx_buffered_len = misaligned_end;
200  }
201
202  /* Handle misalignment on begin. */
203  if (misaligned_begin != 0) {
204    if (tx) {
205      atsam_copy_to_io(buf->leadbuf, start, misaligned_begin);
206    }
207    curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
208    curdesc->mbr_ta = (uint32_t) buf->leadbuf;
209    curdesc->mbr_ubc = misaligned_begin;
210  }
211
212  /* Main part */
213  if (len_main > 0) {
214    curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
215    curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
216    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
217    ++curdesc;
218
219    curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
220    curdesc->mbr_ta = (uint32_t) start_main;
221    curdesc->mbr_ubc = len_main;
222    if (tx) {
223      rtems_cache_flush_multiple_data_lines(start_main, len_main);
224    } else {
225      rtems_cache_invalidate_multiple_data_lines(start_main, len_main);
226    }
227  }
228
229  /* Handle misalignment on end */
230  if (misaligned_end != 0) {
231    curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
232    curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
233    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
234    ++curdesc;
235
236    if (tx) {
237      atsam_copy_to_io(buf->trailbuf, start_trail, misaligned_end);
238    }
239    curdesc->mbr_nda = 0;
240    curdesc->mbr_ta = (uint32_t) buf->trailbuf;
241    curdesc->mbr_ubc = misaligned_end;
242    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_DIS;
243  }
244}
245
246static void atsam_spi_copy_back_rx_after_dma_transfer(
247  atsam_spi_bus *bus
248)
249{
250  if (bus->leadbuf_rx_buffered_len != 0) {
251    atsam_copy_from_io(
252      bus->msg_current->rx_buf,
253      bus->dma_bufs[DMA_BUF_RX].leadbuf,
254      bus->leadbuf_rx_buffered_len
255    );
256  }
257  if (bus->trailbuf_rx_buffered_len != 0) {
258    atsam_copy_from_io(
259      bus->msg_current->rx_buf + bus->msg_current->len -
260        bus->trailbuf_rx_buffered_len,
261      bus->dma_bufs[DMA_BUF_RX].trailbuf,
262      bus->trailbuf_rx_buffered_len
263    );
264  }
265}
266
267static void atsam_spi_start_dma_transfer(
268  atsam_spi_bus *bus,
269  const spi_ioc_transfer *msg
270)
271{
272  Xdmac *pXdmac = XDMAC;
273
274  atsam_spi_check_alignment_and_set_up_dma_descriptors(
275    bus,
276    &bus->dma_bufs[DMA_BUF_RX],
277    msg->rx_buf,
278    msg->len,
279    false
280  );
281  atsam_spi_check_alignment_and_set_up_dma_descriptors(
282    bus,
283    &bus->dma_bufs[DMA_BUF_TX],
284    msg->tx_buf,
285    msg->len,
286    true
287  );
288
289  XDMAC_SetDescriptorAddr(
290    pXdmac,
291    bus->dma_rx_channel,
292    (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
293    0
294  );
295  XDMAC_SetDescriptorControl(
296    pXdmac,
297    bus->dma_rx_channel,
298    XDMAC_CNDC_NDVIEW_NDV0 |
299    XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
300    XDMAC_CNDC_NDE_DSCR_FETCH_EN
301  );
302  XDMAC_SetDescriptorAddr(
303    pXdmac,
304    bus->dma_tx_channel,
305    (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
306    0
307  );
308  XDMAC_SetDescriptorControl(
309    pXdmac,
310    bus->dma_tx_channel,
311    XDMAC_CNDC_NDVIEW_NDV0 |
312    XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
313    XDMAC_CNDC_NDE_DSCR_FETCH_EN
314  );
315
316  XDMAC_StartTransfer(pXdmac, bus->dma_rx_channel);
317  XDMAC_StartTransfer(pXdmac, bus->dma_tx_channel);
318}
319
320static int atsam_check_configure_spi(atsam_spi_bus *bus, const spi_ioc_transfer *msg)
321{
322  if (
323    msg->mode != bus->base.mode
324      || msg->speed_hz != bus->base.speed_hz
325      || msg->bits_per_word != bus->base.bits_per_word
326      || msg->cs != bus->base.cs
327  ) {
328    if (
329      (msg->bits_per_word != 8 && msg->bits_per_word != 16)
330        || msg->mode > 3
331        || msg->speed_hz > bus->base.max_speed_hz
332    ) {
333      return -EINVAL;
334    }
335
336    bus->base.mode = msg->mode;
337    bus->base.speed_hz = msg->speed_hz;
338    bus->base.bits_per_word = msg->bits_per_word;
339    bus->base.cs = msg->cs;
340    atsam_configure_spi(bus);
341  }
342
343  return 0;
344}
345
346static void atsam_spi_setup_transfer(atsam_spi_bus *bus)
347{
348  uint32_t msg_todo = bus->msg_todo;
349
350  bus->transfer_in_progress = 2;
351
352  if (msg_todo > 0) {
353    const spi_ioc_transfer *msg;
354    int error;
355
356    msg = bus->msg_current;
357    error = atsam_check_configure_spi(bus, msg);
358    if (error == 0) {
359      atsam_spi_start_dma_transfer(bus, msg);
360    } else {
361      bus->msg_error = error;
362      atsam_spi_wakeup_task(bus);
363    }
364  } else {
365    atsam_spi_wakeup_task(bus);
366  }
367}
368
369static void atsam_spi_dma_callback(uint32_t channel, void *arg)
370{
371  atsam_spi_bus *bus = (atsam_spi_bus *)arg;
372
373  --bus->transfer_in_progress;
374
375  if (bus->transfer_in_progress == 0) {
376    const spi_ioc_transfer *msg = bus->msg_current;
377
378    if (msg->delay_usecs != bus->base.delay_usecs) {
379      uint32_t mr;
380      uint32_t mr_dlybcs;
381
382      bus->base.delay_usecs = msg->delay_usecs;
383      mr_dlybcs = SPI_MR_DLYBCS(atsam_calculate_dlybcs(bus));
384      bus->spi_mr = mr_dlybcs | SPI_MR_MSTR | SPI_MR_MODFDIS;
385
386      mr = bus->spi_regs->SPI_MR;
387      mr &= ~SPI_MR_DLYBCS_Msk;
388      mr |= mr_dlybcs;
389      bus->spi_regs->SPI_MR = mr;
390    }
391
392    if (msg->cs_change) {
393      bus->spi_regs->SPI_CR = SPI_CR_LASTXFER;
394    }
395
396    atsam_spi_copy_back_rx_after_dma_transfer(bus);
397
398    bus->msg_current = msg + 1;
399    --bus->msg_todo;
400
401    atsam_spi_setup_transfer(bus);
402  }
403}
404
405static int atsam_spi_transfer(
406  spi_bus *base,
407  const spi_ioc_transfer *msgs,
408  uint32_t msg_count
409)
410{
411  atsam_spi_bus *bus = (atsam_spi_bus *)base;
412
413  bus->msg_current = msgs;
414  bus->msg_todo = msg_count;
415  bus->msg_error = 0;
416  atsam_spi_setup_transfer(bus);
417  rtems_binary_semaphore_wait(&bus->sem);
418  return bus->msg_error;
419}
420
421
422static void atsam_spi_destroy(spi_bus *base)
423{
424  atsam_spi_bus *bus = (atsam_spi_bus *)base;
425  eXdmadRC rc;
426
427  rc = XDMAD_SetCallback(
428    &XDMAD_Instance,
429    bus->dma_rx_channel,
430    XDMAD_DoNothingCallback,
431    NULL
432  );
433  assert(rc == XDMAD_OK);
434
435  rc = XDMAD_SetCallback(
436    &XDMAD_Instance,
437    bus->dma_tx_channel,
438    XDMAD_DoNothingCallback,
439    NULL
440  );
441  assert(rc == XDMAD_OK);
442
443  XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_rx_channel);
444  XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_tx_channel);
445
446  SPI_Disable(bus->spi_regs);
447  PMC_DisablePeripheral(bus->spi_id);
448
449  rtems_cache_coherent_free(bus->dma_bufs);
450  rtems_binary_semaphore_destroy(&bus->sem);
451  spi_bus_destroy_and_free(&bus->base);
452}
453
454static int atsam_spi_setup(spi_bus *base)
455{
456  atsam_spi_bus *bus = (atsam_spi_bus *)base;
457
458  if (
459    bus->base.speed_hz > MAX_SPI_FREQUENCY
460      || (bus->base.bits_per_word != 8 && bus->base.bits_per_word != 16)
461  ) {
462      return -EINVAL;
463  }
464  atsam_configure_spi(bus);
465  return 0;
466}
467
468static void atsam_spi_init_xdma(atsam_spi_bus *bus)
469{
470  sXdmadCfg cfg;
471  uint32_t xdmaInt;
472  uint8_t channel;
473  eXdmadRC rc;
474  uint32_t xdma_cndc;
475
476  bus->dma_bufs = rtems_cache_coherent_allocate(
477    DMA_BUF_DIRS * sizeof(*(bus->dma_bufs)),
478    DMA_DESC_ALLIGNMENT,
479    0
480  );
481  assert(bus->dma_bufs != NULL);
482
483  bus->dma_tx_channel = XDMAD_AllocateChannel(
484    &XDMAD_Instance,
485    XDMAD_TRANSFER_MEMORY,
486    bus->spi_id
487  );
488  assert(bus->dma_tx_channel != XDMAD_ALLOC_FAILED);
489
490  bus->dma_rx_channel = XDMAD_AllocateChannel(
491    &XDMAD_Instance,
492    bus->spi_id,
493    XDMAD_TRANSFER_MEMORY
494  );
495  assert(bus->dma_rx_channel != XDMAD_ALLOC_FAILED);
496
497  rc = XDMAD_SetCallback(
498    &XDMAD_Instance,
499    bus->dma_rx_channel,
500    atsam_spi_dma_callback,
501    bus
502  );
503  assert(rc == XDMAD_OK);
504
505  rc = XDMAD_SetCallback(
506    &XDMAD_Instance,
507    bus->dma_tx_channel,
508    atsam_spi_dma_callback,
509    bus
510  );
511  assert(rc == XDMAD_OK);
512
513  rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_rx_channel);
514  assert(rc == XDMAD_OK);
515
516  rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_tx_channel);
517  assert(rc == XDMAD_OK);
518
519  /* Put all relevant interrupts on */
520  xdmaInt =  (
521    XDMAC_CIE_BIE |
522    XDMAC_CIE_DIE |
523    XDMAC_CIE_FIE |
524    XDMAC_CIE_RBIE |
525    XDMAC_CIE_WBIE |
526    XDMAC_CIE_ROIE);
527
528  /* Setup RX */
529  memset(&cfg, 0, sizeof(cfg));
530  channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_RX);
531  cfg.mbr_sa = (uint32_t)&bus->spi_regs->SPI_RDR;
532  cfg.mbr_cfg =
533    XDMAC_CC_TYPE_PER_TRAN |
534    XDMAC_CC_MBSIZE_SINGLE |
535    XDMAC_CC_DSYNC_PER2MEM |
536    XDMAC_CC_CSIZE_CHK_1 |
537    XDMAC_CC_DWIDTH_BYTE |
538    XDMAC_CC_SIF_AHB_IF1 |
539    XDMAC_CC_DIF_AHB_IF1 |
540    XDMAC_CC_SAM_FIXED_AM |
541    XDMAC_CC_DAM_INCREMENTED_AM |
542    XDMAC_CC_PERID(channel);
543  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
544    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
545    XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
546    XDMAC_CNDC_NDSUP_SRC_PARAMS_UNCHANGED;
547  rc = XDMAD_ConfigureTransfer(
548    &XDMAD_Instance,
549    bus->dma_rx_channel,
550    &cfg,
551    xdma_cndc,
552    (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
553    xdmaInt
554  );
555  assert(rc == XDMAD_OK);
556
557  /* Setup TX  */
558  memset(&cfg, 0, sizeof(cfg));
559  channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_TX);
560  cfg.mbr_da = (uint32_t)&bus->spi_regs->SPI_TDR;
561  cfg.mbr_cfg =
562    XDMAC_CC_TYPE_PER_TRAN |
563    XDMAC_CC_MBSIZE_SINGLE |
564    XDMAC_CC_DSYNC_MEM2PER |
565    XDMAC_CC_CSIZE_CHK_1 |
566    XDMAC_CC_DWIDTH_BYTE |
567    XDMAC_CC_SIF_AHB_IF1 |
568    XDMAC_CC_DIF_AHB_IF1 |
569    XDMAC_CC_SAM_INCREMENTED_AM |
570    XDMAC_CC_DAM_FIXED_AM |
571    XDMAC_CC_PERID(channel);
572  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
573    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
574    XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED |
575    XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED;
576  rc = XDMAD_ConfigureTransfer(
577    &XDMAD_Instance,
578    bus->dma_tx_channel,
579    &cfg,
580    xdma_cndc,
581    (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
582    xdmaInt
583  );
584  assert(rc == XDMAD_OK);
585}
586
587int spi_bus_register_atsam(
588  const char *bus_path,
589  const atsam_spi_config *config
590)
591{
592  atsam_spi_bus *bus;
593  size_t i;
594
595  bus = (atsam_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
596  if (bus == NULL) {
597    return -1;
598  }
599
600  bus->base.transfer = atsam_spi_transfer;
601  bus->base.destroy = atsam_spi_destroy;
602  bus->base.setup = atsam_spi_setup;
603  bus->base.max_speed_hz = MAX_SPI_FREQUENCY;
604  bus->base.bits_per_word = 8;
605  bus->base.speed_hz = bus->base.max_speed_hz;
606  bus->base.cs = 1;
607  bus->spi_id = config->spi_peripheral_id;
608  bus->spi_regs = config->spi_regs;
609  bus->chip_select_decode = config->chip_select_decode;
610  bus->peripheral_clk_per_us = BOARD_MCK / 1000000;
611  bus->spi_mr = SPI_MR_MSTR | SPI_MR_MODFDIS;
612
613  for (i = 0; i < RTEMS_ARRAY_SIZE(bus->spi_csr); ++i) {
614    if (config->dlybs_in_ns[i] != 0) {
615      bus->spi_csr[i] |= SPI_DLYBS(config->dlybs_in_ns[i], BOARD_MCK);
616    }
617
618    if (config->dlybct_in_ns[i] != 0) {
619      bus->spi_csr[i] |= SPI_DLYBCT(config->dlybct_in_ns[i], BOARD_MCK);
620    }
621  }
622
623  rtems_binary_semaphore_init(&bus->sem, "ATSAM SPI");
624  PIO_Configure(config->pins, config->pin_count);
625  PMC_EnablePeripheral(config->spi_peripheral_id);
626  atsam_reset_spi(bus);
627  atsam_configure_spi(bus);
628  atsam_spi_init_xdma(bus);
629
630  return spi_bus_register(&bus->base, bus_path);
631}
Note: See TracBrowser for help on using the repository browser.