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

Last change on this file since b934898 was b934898, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 6, 2019 at 8:48:41 AM

bsp/atsam: Make SPI CS delays configurable

  • Property mode set to 100644
File size: 16.7 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  bool msg_cs_change;
60  const spi_ioc_transfer *msg_current;
61  const spi_ioc_transfer *msg_next;
62  uint32_t msg_todo;
63  int msg_error;
64  Spi *spi_regs;
65  uint32_t dma_tx_channel;
66  uint32_t dma_rx_channel;
67  struct atsam_spi_xdma_buf *dma_bufs;
68  size_t leadbuf_rx_buffered_len;
69  size_t trailbuf_rx_buffered_len;
70  int transfer_in_progress;
71  bool chip_select_active;
72  bool chip_select_decode;
73  uint8_t spi_id;
74  uint32_t spi_csr[4];
75} atsam_spi_bus;
76
77static void atsam_spi_wakeup_task(atsam_spi_bus *bus)
78{
79  rtems_binary_semaphore_post(&bus->sem);
80}
81
82static uint8_t atsam_calculate_dlybcs(uint16_t delay_in_us)
83{
84  return (
85    (BOARD_MCK / delay_in_us) < 0xFF) ?
86    (BOARD_MCK / delay_in_us) : 0xFF;
87}
88
89static uint32_t atsam_calculate_scbr(uint32_t speed_hz)
90{
91  uint32_t scbr;
92
93  scbr = BOARD_MCK / speed_hz;
94  if (scbr > 0x0FF) {
95    /* Best estimation we can offer with the hardware. */
96    scbr = 0x0FF;
97  }
98  if (scbr == 0) {
99    /* SCBR = 0 isn't allowed. */
100    scbr = 1;
101  }
102
103  return scbr;
104}
105
106static void atsam_set_phase_and_polarity(uint32_t mode, uint32_t *csr)
107{
108  uint32_t mode_mask = mode & SPI_MODE_3;
109
110  switch(mode_mask) {
111    case SPI_MODE_0:
112      *csr |= SPI_CSR_NCPHA;
113      break;
114    case SPI_MODE_1:
115      break;
116    case SPI_MODE_2:
117      *csr |= SPI_CSR_NCPHA;
118      *csr |= SPI_CSR_CPOL;
119      break;
120    case SPI_MODE_3:
121      *csr |= SPI_CSR_CPOL;
122      break;
123  }
124  *csr |= SPI_CSR_CSAAT;
125}
126
127static void atsam_configure_spi(atsam_spi_bus *bus)
128{
129  uint8_t delay_cs;
130  uint32_t scbr;
131  uint32_t csr = 0;
132  uint32_t mode = 0;
133  uint32_t cs = bus->base.cs;
134
135  delay_cs = atsam_calculate_dlybcs(bus->base.delay_usecs);
136  scbr = atsam_calculate_scbr(bus->base.speed_hz);
137
138  mode |= SPI_MR_DLYBCS(delay_cs);
139  mode |= SPI_MR_MSTR;
140  mode |= SPI_MR_MODFDIS;
141  if (bus->chip_select_decode) {
142    mode |= SPI_MR_PCS(bus->base.cs);
143    mode |= SPI_MR_PCSDEC;
144    cs /= 4;
145  } else {
146    mode |= SPI_PCS(bus->base.cs);
147  }
148
149  bus->spi_regs->SPI_MR = mode;
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 void atsam_spi_do_transfer(
321  atsam_spi_bus *bus,
322  const spi_ioc_transfer *msg
323)
324{
325  if (!bus->chip_select_active){
326    Spi *spi_regs = bus->spi_regs;
327
328    bus->chip_select_active = true;
329
330    if (bus->chip_select_decode) {
331      spi_regs->SPI_MR = (spi_regs->SPI_MR & ~SPI_MR_PCS_Msk) | SPI_MR_PCS(msg->cs);
332    } else {
333      SPI_ChipSelect(spi_regs, 1 << msg->cs);
334    }
335    SPI_Enable(spi_regs);
336  }
337
338  atsam_spi_start_dma_transfer(bus, msg);
339}
340
341static int atsam_check_configure_spi(atsam_spi_bus *bus, const spi_ioc_transfer *msg)
342{
343  if (
344    msg->mode != bus->base.mode
345      || msg->speed_hz != bus->base.speed_hz
346      || msg->bits_per_word != bus->base.bits_per_word
347      || msg->cs != bus->base.cs
348      || msg->delay_usecs != bus->base.delay_usecs
349  ) {
350    if (
351      msg->bits_per_word < 8
352        || msg->bits_per_word > 16
353        || msg->mode > 3
354        || msg->speed_hz > bus->base.max_speed_hz
355    ) {
356      return -EINVAL;
357    }
358
359    bus->base.mode = msg->mode;
360    bus->base.speed_hz = msg->speed_hz;
361    bus->base.bits_per_word = msg->bits_per_word;
362    bus->base.cs = msg->cs;
363    bus->base.delay_usecs = msg->delay_usecs;
364    atsam_configure_spi(bus);
365  }
366
367  return 0;
368}
369
370static void atsam_spi_setup_transfer(atsam_spi_bus *bus)
371{
372  uint32_t msg_todo = bus->msg_todo;
373
374  bus->transfer_in_progress = 2;
375
376  if (bus->msg_cs_change) {
377    bus->chip_select_active = false;
378    SPI_ReleaseCS(bus->spi_regs);
379    SPI_Disable(bus->spi_regs);
380  }
381
382  if (msg_todo > 0) {
383    const spi_ioc_transfer *msg = bus->msg_next;
384    int error;
385
386    bus->msg_cs_change = msg->cs_change;
387    bus->msg_next = msg + 1;
388    bus->msg_current = msg;
389    bus->msg_todo = msg_todo - 1;
390
391    error = atsam_check_configure_spi(bus, msg);
392    if (error == 0) {
393      atsam_spi_do_transfer(bus, msg);
394    } else {
395      bus->msg_error = error;
396      atsam_spi_wakeup_task(bus);
397    }
398  } else {
399    atsam_spi_wakeup_task(bus);
400  }
401}
402
403static void atsam_spi_dma_callback(uint32_t channel, void *arg)
404{
405  atsam_spi_bus *bus = (atsam_spi_bus *)arg;
406
407  --bus->transfer_in_progress;
408
409  if (bus->transfer_in_progress == 0) {
410    atsam_spi_copy_back_rx_after_dma_transfer(bus);
411    atsam_spi_setup_transfer(bus);
412  }
413}
414
415static int atsam_spi_transfer(
416  spi_bus *base,
417  const spi_ioc_transfer *msgs,
418  uint32_t msg_count
419)
420{
421  atsam_spi_bus *bus = (atsam_spi_bus *)base;
422
423  bus->msg_cs_change = false;
424  bus->msg_next = &msgs[0];
425  bus->msg_current = NULL;
426  bus->msg_todo = msg_count;
427  bus->msg_error = 0;
428  atsam_spi_setup_transfer(bus);
429  rtems_binary_semaphore_wait(&bus->sem);
430  return bus->msg_error;
431}
432
433
434static void atsam_spi_destroy(spi_bus *base)
435{
436  atsam_spi_bus *bus = (atsam_spi_bus *)base;
437  eXdmadRC rc;
438
439  rc = XDMAD_SetCallback(
440    &XDMAD_Instance,
441    bus->dma_rx_channel,
442    XDMAD_DoNothingCallback,
443    NULL
444  );
445  assert(rc == XDMAD_OK);
446
447  rc = XDMAD_SetCallback(
448    &XDMAD_Instance,
449    bus->dma_tx_channel,
450    XDMAD_DoNothingCallback,
451    NULL
452  );
453  assert(rc == XDMAD_OK);
454
455  XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_rx_channel);
456  XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_tx_channel);
457
458  SPI_Disable(bus->spi_regs);
459  PMC_DisablePeripheral(bus->spi_id);
460
461  rtems_cache_coherent_free(bus->dma_bufs);
462
463  spi_bus_destroy_and_free(&bus->base);
464  rtems_binary_semaphore_destroy(&bus->sem);
465}
466
467static int atsam_spi_setup(spi_bus *base)
468{
469  atsam_spi_bus *bus = (atsam_spi_bus *)base;
470
471  if (
472    bus->base.speed_hz > MAX_SPI_FREQUENCY ||
473    bus->base.bits_per_word < 8 ||
474    bus->base.bits_per_word > 16
475  ) {
476      return -EINVAL;
477  }
478  atsam_configure_spi(bus);
479  return 0;
480}
481
482static void atsam_spi_init_xdma(atsam_spi_bus *bus)
483{
484  sXdmadCfg cfg;
485  uint32_t xdmaInt;
486  uint8_t channel;
487  eXdmadRC rc;
488  uint32_t xdma_cndc;
489
490  bus->dma_bufs = rtems_cache_coherent_allocate(
491    DMA_BUF_DIRS * sizeof(*(bus->dma_bufs)),
492    DMA_DESC_ALLIGNMENT,
493    0
494  );
495  assert(bus->dma_bufs != NULL);
496
497  bus->dma_tx_channel = XDMAD_AllocateChannel(
498    &XDMAD_Instance,
499    XDMAD_TRANSFER_MEMORY,
500    bus->spi_id
501  );
502  assert(bus->dma_tx_channel != XDMAD_ALLOC_FAILED);
503
504  bus->dma_rx_channel = XDMAD_AllocateChannel(
505    &XDMAD_Instance,
506    bus->spi_id,
507    XDMAD_TRANSFER_MEMORY
508  );
509  assert(bus->dma_rx_channel != XDMAD_ALLOC_FAILED);
510
511  rc = XDMAD_SetCallback(
512    &XDMAD_Instance,
513    bus->dma_rx_channel,
514    atsam_spi_dma_callback,
515    bus
516  );
517  assert(rc == XDMAD_OK);
518
519  rc = XDMAD_SetCallback(
520    &XDMAD_Instance,
521    bus->dma_tx_channel,
522    atsam_spi_dma_callback,
523    bus
524  );
525  assert(rc == XDMAD_OK);
526
527  rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_rx_channel);
528  assert(rc == XDMAD_OK);
529
530  rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_tx_channel);
531  assert(rc == XDMAD_OK);
532
533  /* Put all relevant interrupts on */
534  xdmaInt =  (
535    XDMAC_CIE_BIE |
536    XDMAC_CIE_DIE |
537    XDMAC_CIE_FIE |
538    XDMAC_CIE_RBIE |
539    XDMAC_CIE_WBIE |
540    XDMAC_CIE_ROIE);
541
542  /* Setup RX */
543  memset(&cfg, 0, sizeof(cfg));
544  channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_RX);
545  cfg.mbr_sa = (uint32_t)&bus->spi_regs->SPI_RDR;
546  cfg.mbr_cfg =
547    XDMAC_CC_TYPE_PER_TRAN |
548    XDMAC_CC_MBSIZE_SINGLE |
549    XDMAC_CC_DSYNC_PER2MEM |
550    XDMAC_CC_CSIZE_CHK_1 |
551    XDMAC_CC_DWIDTH_BYTE |
552    XDMAC_CC_SIF_AHB_IF1 |
553    XDMAC_CC_DIF_AHB_IF1 |
554    XDMAC_CC_SAM_FIXED_AM |
555    XDMAC_CC_DAM_INCREMENTED_AM |
556    XDMAC_CC_PERID(channel);
557  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
558    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
559    XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
560    XDMAC_CNDC_NDSUP_SRC_PARAMS_UNCHANGED;
561  rc = XDMAD_ConfigureTransfer(
562    &XDMAD_Instance,
563    bus->dma_rx_channel,
564    &cfg,
565    xdma_cndc,
566    (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
567    xdmaInt
568  );
569  assert(rc == XDMAD_OK);
570
571  /* Setup TX  */
572  memset(&cfg, 0, sizeof(cfg));
573  channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_TX);
574  cfg.mbr_da = (uint32_t)&bus->spi_regs->SPI_TDR;
575  cfg.mbr_cfg =
576    XDMAC_CC_TYPE_PER_TRAN |
577    XDMAC_CC_MBSIZE_SINGLE |
578    XDMAC_CC_DSYNC_MEM2PER |
579    XDMAC_CC_CSIZE_CHK_1 |
580    XDMAC_CC_DWIDTH_BYTE |
581    XDMAC_CC_SIF_AHB_IF1 |
582    XDMAC_CC_DIF_AHB_IF1 |
583    XDMAC_CC_SAM_INCREMENTED_AM |
584    XDMAC_CC_DAM_FIXED_AM |
585    XDMAC_CC_PERID(channel);
586  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
587    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
588    XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED |
589    XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED;
590  rc = XDMAD_ConfigureTransfer(
591    &XDMAD_Instance,
592    bus->dma_tx_channel,
593    &cfg,
594    xdma_cndc,
595    (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
596    xdmaInt
597  );
598  assert(rc == XDMAD_OK);
599}
600
601int spi_bus_register_atsam(
602  const char *bus_path,
603  const atsam_spi_config *config
604)
605{
606  atsam_spi_bus *bus;
607  size_t i;
608
609  bus = (atsam_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
610  if (bus == NULL) {
611    return -1;
612  }
613
614  bus->base.transfer = atsam_spi_transfer;
615  bus->base.destroy = atsam_spi_destroy;
616  bus->base.setup = atsam_spi_setup;
617  bus->base.max_speed_hz = MAX_SPI_FREQUENCY;
618  bus->base.bits_per_word = 8;
619  bus->base.speed_hz = bus->base.max_speed_hz;
620  bus->base.delay_usecs = 1;
621  bus->base.cs = 1;
622  bus->spi_id = config->spi_peripheral_id;
623  bus->spi_regs = config->spi_regs;
624  bus->chip_select_decode = config->chip_select_decode;
625
626  for (i = 0; i < RTEMS_ARRAY_SIZE(bus->spi_csr); ++i) {
627    if (config->dlybs_in_ns[i] != 0) {
628      bus->spi_csr[i] |= SPI_DLYBS(config->dlybs_in_ns[i], BOARD_MCK);
629    }
630
631    if (config->dlybct_in_ns[i] != 0) {
632      bus->spi_csr[i] |= SPI_DLYBCT(config->dlybct_in_ns[i], BOARD_MCK);
633    }
634  }
635
636  rtems_binary_semaphore_init(&bus->sem, "ATSAM SPI");
637  PIO_Configure(config->pins, config->pin_count);
638  PMC_EnablePeripheral(config->spi_peripheral_id);
639  atsam_reset_spi(bus);
640  atsam_configure_spi(bus);
641  atsam_spi_init_xdma(bus);
642
643  return spi_bus_register(&bus->base, bus_path);
644}
Note: See TracBrowser for help on using the repository browser.