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

Last change on this file since 8e6cfcc was 8e6cfcc, checked in by Christian Mauderer <christian.mauderer@…>, on Aug 6, 2018 at 8:29:14 AM

bsp/atsam: Fix handling of slow SPI speeds.

This patch fixes an overflow in the frequency calculation of the SPI
driver for slow SPI speeds.

  • 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 <dev/spi/spi.h>
36
37#include <string.h>
38
39#define MAX_SPI_FREQUENCY 50000000
40
41#define DMA_NR_DESC_PER_DIR 3
42#define DMA_DESC_ALLIGNMENT 4
43
44#define DMA_BUF_RX 0
45#define DMA_BUF_TX 1
46#define DMA_BUF_DIRS 2
47
48struct atsam_spi_xdma_buf {
49  LinkedListDescriporView0 desc[DMA_NR_DESC_PER_DIR];
50  uint8_t leadbuf[CPU_CACHE_LINE_BYTES];
51  uint8_t trailbuf[CPU_CACHE_LINE_BYTES];
52};
53
54typedef struct {
55  spi_bus base;
56  bool msg_cs_change;
57  const spi_ioc_transfer *msg_current;
58  const spi_ioc_transfer *msg_next;
59  uint32_t msg_todo;
60  int msg_error;
61  rtems_id msg_task;
62  Spid spi;
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_active;
70  bool chip_select_decode;
71} atsam_spi_bus;
72
73static void atsam_spi_wakeup_task(atsam_spi_bus *bus)
74{
75  rtems_status_code sc;
76
77  sc = rtems_event_transient_send(bus->msg_task);
78  assert(sc == RTEMS_SUCCESSFUL);
79}
80
81static uint8_t atsam_calculate_dlybcs(uint16_t delay_in_us)
82{
83  return (
84    (BOARD_MCK / delay_in_us) < 0xFF) ?
85    (BOARD_MCK / delay_in_us) : 0xFF;
86}
87
88static uint32_t atsam_calculate_scbr(uint32_t speed_hz)
89{
90  uint32_t scbr;
91
92  scbr = BOARD_MCK / speed_hz;
93  if (scbr > 0x0FF) {
94    /* Best estimation we can offer with the hardware. */
95    scbr = 0x0FF;
96  }
97  if (scbr == 0) {
98    /* SCBR = 0 isn't allowed. */
99    scbr = 1;
100  }
101
102  return scbr;
103}
104
105static void atsam_set_phase_and_polarity(uint32_t mode, uint32_t *csr)
106{
107  uint32_t mode_mask = mode & SPI_MODE_3;
108
109  switch(mode_mask) {
110    case SPI_MODE_0:
111      *csr |= SPI_CSR_NCPHA;
112      break;
113    case SPI_MODE_1:
114      break;
115    case SPI_MODE_2:
116      *csr |= SPI_CSR_NCPHA;
117      *csr |= SPI_CSR_CPOL;
118      break;
119    case SPI_MODE_3:
120      *csr |= SPI_CSR_CPOL;
121      break;
122  }
123  *csr |= SPI_CSR_CSAAT;
124}
125
126static void atsam_configure_spi(atsam_spi_bus *bus)
127{
128  uint8_t delay_cs;
129  uint32_t scbr;
130  uint32_t csr = 0;
131  uint32_t mode = 0;
132  uint32_t cs = bus->base.cs;
133
134  delay_cs = atsam_calculate_dlybcs(bus->base.delay_usecs);
135  scbr = atsam_calculate_scbr(bus->base.speed_hz);
136
137  mode |= SPI_MR_DLYBCS(delay_cs);
138  mode |= SPI_MR_MSTR;
139  mode |= SPI_MR_MODFDIS;
140  if (bus->chip_select_decode) {
141    mode |= SPI_MR_PCS(bus->base.cs);
142    mode |= SPI_MR_PCSDEC;
143    cs /= 4;
144  } else {
145    mode |= SPI_PCS(bus->base.cs);
146  }
147
148  SPID_Configure(
149    &bus->spi,
150    bus->spi.pSpiHw,
151    bus->spi.spiId,
152    mode,
153    &XDMAD_Instance
154  );
155
156  csr =
157    SPI_DLYBCT(1000, BOARD_MCK) |
158    SPI_DLYBS(1000, BOARD_MCK) |
159    SPI_CSR_SCBR(scbr) |
160    SPI_CSR_BITS(bus->base.bits_per_word - 8);
161
162  atsam_set_phase_and_polarity(bus->base.mode, &csr);
163
164  SPI_ConfigureNPCS(bus->spi.pSpiHw, cs, csr);
165}
166
167static void atsam_spi_check_alignment_and_set_up_dma_descriptors(
168  atsam_spi_bus *bus,
169  struct atsam_spi_xdma_buf *buf,
170  const uint8_t *start,
171  size_t len,
172  bool tx
173)
174{
175  LinkedListDescriporView0 *curdesc = buf->desc;
176  size_t misaligned_begin;
177  size_t misaligned_end;
178  size_t len_main;
179  const uint8_t *start_main;
180  const uint8_t *start_trail;
181
182  /* Check alignments. */
183  if (len < CPU_CACHE_LINE_BYTES) {
184    misaligned_begin = len;
185    misaligned_end = 0;
186    len_main = 0;
187  } else {
188    misaligned_begin = ((uint32_t) start) % CPU_CACHE_LINE_BYTES;
189    misaligned_end = (((uint32_t) start) + len) % CPU_CACHE_LINE_BYTES;
190    len_main = len - misaligned_begin - misaligned_end;
191  }
192  start_main = start + misaligned_begin;
193  start_trail = start_main + len_main;
194
195  /* Store length for copying data back. */
196  if (!tx) {
197    bus->leadbuf_rx_buffered_len = misaligned_begin;
198    bus->trailbuf_rx_buffered_len = misaligned_end;
199  }
200
201  /* Handle misalignment on begin. */
202  if (misaligned_begin != 0) {
203    if (tx) {
204      atsam_copy_to_io(buf->leadbuf, start, misaligned_begin);
205    }
206    curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
207    curdesc->mbr_ta = (uint32_t) buf->leadbuf;
208    curdesc->mbr_ubc = misaligned_begin;
209  }
210
211  /* Main part */
212  if (len_main > 0) {
213    curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
214    curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
215    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
216    ++curdesc;
217
218    curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
219    curdesc->mbr_ta = (uint32_t) start_main;
220    curdesc->mbr_ubc = len_main;
221    if (tx) {
222      rtems_cache_flush_multiple_data_lines(start_main, len_main);
223    } else {
224      rtems_cache_invalidate_multiple_data_lines(start_main, len_main);
225    }
226  }
227
228  /* Handle misalignment on end */
229  if (misaligned_end != 0) {
230    curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
231    curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
232    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
233    ++curdesc;
234
235    if (tx) {
236      atsam_copy_to_io(buf->trailbuf, start_trail, misaligned_end);
237    }
238    curdesc->mbr_nda = 0;
239    curdesc->mbr_ta = (uint32_t) buf->trailbuf;
240    curdesc->mbr_ubc = misaligned_end;
241    curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_DIS;
242  }
243}
244
245static void atsam_spi_copy_back_rx_after_dma_transfer(
246  atsam_spi_bus *bus
247)
248{
249  if (bus->leadbuf_rx_buffered_len != 0) {
250    atsam_copy_from_io(
251      bus->msg_current->rx_buf,
252      bus->dma_bufs[DMA_BUF_RX].leadbuf,
253      bus->leadbuf_rx_buffered_len
254    );
255  }
256  if (bus->trailbuf_rx_buffered_len != 0) {
257    atsam_copy_from_io(
258      bus->msg_current->rx_buf + bus->msg_current->len -
259        bus->trailbuf_rx_buffered_len,
260      bus->dma_bufs[DMA_BUF_RX].trailbuf,
261      bus->trailbuf_rx_buffered_len
262    );
263  }
264}
265
266static void atsam_spi_start_dma_transfer(
267  atsam_spi_bus *bus,
268  const spi_ioc_transfer *msg
269)
270{
271  Xdmac *pXdmac = XDMAC;
272  size_t i;
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 *pSpiHw = bus->spi.pSpiHw;
327
328    bus->chip_select_active = true;
329
330    if (bus->chip_select_decode) {
331      pSpiHw->SPI_MR = (pSpiHw->SPI_MR & ~SPI_MR_PCS_Msk) | SPI_MR_PCS(msg->cs);
332    } else {
333      SPI_ChipSelect(pSpiHw, 1 << msg->cs);
334    }
335    SPI_Enable(pSpiHw);
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.pSpiHw);
379    SPI_Disable(bus->spi.pSpiHw);
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  rtems_status_code sc;
422  atsam_spi_bus *bus = (atsam_spi_bus *)base;
423
424  bus->msg_cs_change = false;
425  bus->msg_next = &msgs[0];
426  bus->msg_current = NULL;
427  bus->msg_todo = msg_count;
428  bus->msg_error = 0;
429  bus->msg_task = rtems_task_self();
430  atsam_spi_setup_transfer(bus);
431  sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
432  assert(sc == RTEMS_SUCCESSFUL);
433  return bus->msg_error;
434}
435
436
437static void atsam_spi_destroy(spi_bus *base)
438{
439  atsam_spi_bus *bus = (atsam_spi_bus *)base;
440  eXdmadRC rc;
441
442  rc = XDMAD_SetCallback(
443    bus->spi.pXdmad,
444    bus->dma_rx_channel,
445    XDMAD_DoNothingCallback,
446    NULL
447  );
448  assert(rc == XDMAD_OK);
449
450  rc = XDMAD_SetCallback(
451    bus->spi.pXdmad,
452    bus->dma_tx_channel,
453    XDMAD_DoNothingCallback,
454    NULL
455  );
456  assert(rc == XDMAD_OK);
457
458  XDMAD_FreeChannel(bus->spi.pXdmad, bus->dma_rx_channel);
459  XDMAD_FreeChannel(bus->spi.pXdmad, bus->dma_tx_channel);
460
461  SPI_Disable(bus->spi.pSpiHw);
462  PMC_DisablePeripheral(bus->spi.spiId);
463
464  rtems_cache_coherent_free(bus->dma_bufs);
465
466  spi_bus_destroy_and_free(&bus->base);
467}
468
469static int atsam_spi_setup(spi_bus *base)
470{
471  atsam_spi_bus *bus = (atsam_spi_bus *)base;
472
473  if (
474    bus->base.speed_hz > MAX_SPI_FREQUENCY ||
475    bus->base.bits_per_word < 8 ||
476    bus->base.bits_per_word > 16
477  ) {
478      return -EINVAL;
479  }
480  atsam_configure_spi(bus);
481  return 0;
482}
483
484static void atsam_spi_init_xdma(atsam_spi_bus *bus)
485{
486  sXdmadCfg cfg;
487  uint32_t xdmaInt;
488  uint8_t channel;
489  eXdmadRC rc;
490  uint32_t xdma_cndc;
491
492  bus->dma_bufs = rtems_cache_coherent_allocate(
493    DMA_BUF_DIRS * sizeof(*(bus->dma_bufs)),
494    DMA_DESC_ALLIGNMENT,
495    0
496  );
497  assert(bus->dma_bufs != NULL);
498
499  bus->dma_tx_channel = XDMAD_AllocateChannel(
500    bus->spi.pXdmad,
501    XDMAD_TRANSFER_MEMORY,
502    bus->spi.spiId
503  );
504  assert(bus->dma_tx_channel != XDMAD_ALLOC_FAILED);
505
506  bus->dma_rx_channel = XDMAD_AllocateChannel(
507    bus->spi.pXdmad,
508    bus->spi.spiId,
509    XDMAD_TRANSFER_MEMORY
510  );
511  assert(bus->dma_rx_channel != XDMAD_ALLOC_FAILED);
512
513  rc = XDMAD_SetCallback(
514    bus->spi.pXdmad,
515    bus->dma_rx_channel,
516    atsam_spi_dma_callback,
517    bus
518  );
519  assert(rc == XDMAD_OK);
520
521  rc = XDMAD_SetCallback(
522    bus->spi.pXdmad,
523    bus->dma_tx_channel,
524    atsam_spi_dma_callback,
525    bus
526  );
527  assert(rc == XDMAD_OK);
528
529  rc = XDMAD_PrepareChannel(bus->spi.pXdmad, bus->dma_rx_channel);
530  assert(rc == XDMAD_OK);
531
532  rc = XDMAD_PrepareChannel(bus->spi.pXdmad, bus->dma_tx_channel);
533  assert(rc == XDMAD_OK);
534
535  /* Put all relevant interrupts on */
536  xdmaInt =  (
537    XDMAC_CIE_BIE |
538    XDMAC_CIE_DIE |
539    XDMAC_CIE_FIE |
540    XDMAC_CIE_RBIE |
541    XDMAC_CIE_WBIE |
542    XDMAC_CIE_ROIE);
543
544  /* Setup RX */
545  memset(&cfg, 0, sizeof(cfg));
546  channel = XDMAIF_Get_ChannelNumber(bus->spi.spiId, XDMAD_TRANSFER_RX);
547  cfg.mbr_sa = (uint32_t)&bus->spi.pSpiHw->SPI_RDR;
548  cfg.mbr_cfg =
549    XDMAC_CC_TYPE_PER_TRAN |
550    XDMAC_CC_MBSIZE_SINGLE |
551    XDMAC_CC_DSYNC_PER2MEM |
552    XDMAC_CC_CSIZE_CHK_1 |
553    XDMAC_CC_DWIDTH_BYTE |
554    XDMAC_CC_SIF_AHB_IF1 |
555    XDMAC_CC_DIF_AHB_IF1 |
556    XDMAC_CC_SAM_FIXED_AM |
557    XDMAC_CC_DAM_INCREMENTED_AM |
558    XDMAC_CC_PERID(channel);
559  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
560    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
561    XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
562    XDMAC_CNDC_NDSUP_SRC_PARAMS_UNCHANGED;
563  rc = XDMAD_ConfigureTransfer(
564    bus->spi.pXdmad,
565    bus->dma_rx_channel,
566    &cfg,
567    xdma_cndc,
568    (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
569    xdmaInt
570  );
571  assert(rc == XDMAD_OK);
572
573  /* Setup TX  */
574  memset(&cfg, 0, sizeof(cfg));
575  channel = XDMAIF_Get_ChannelNumber(bus->spi.spiId, XDMAD_TRANSFER_TX);
576  cfg.mbr_da = (uint32_t)&bus->spi.pSpiHw->SPI_TDR;
577  cfg.mbr_cfg =
578    XDMAC_CC_TYPE_PER_TRAN |
579    XDMAC_CC_MBSIZE_SINGLE |
580    XDMAC_CC_DSYNC_MEM2PER |
581    XDMAC_CC_CSIZE_CHK_1 |
582    XDMAC_CC_DWIDTH_BYTE |
583    XDMAC_CC_SIF_AHB_IF1 |
584    XDMAC_CC_DIF_AHB_IF1 |
585    XDMAC_CC_SAM_INCREMENTED_AM |
586    XDMAC_CC_DAM_FIXED_AM |
587    XDMAC_CC_PERID(channel);
588  xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
589    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
590    XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED |
591    XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED;
592  rc = XDMAD_ConfigureTransfer(
593    bus->spi.pXdmad,
594    bus->dma_tx_channel,
595    &cfg,
596    xdma_cndc,
597    (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
598    xdmaInt
599  );
600  assert(rc == XDMAD_OK);
601}
602
603int spi_bus_register_atsam(
604  const char *bus_path,
605  const atsam_spi_config *config
606)
607{
608  atsam_spi_bus *bus;
609
610  bus = (atsam_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
611  if (bus == NULL) {
612    return -1;
613  }
614
615  bus->base.transfer = atsam_spi_transfer;
616  bus->base.destroy = atsam_spi_destroy;
617  bus->base.setup = atsam_spi_setup;
618  bus->base.max_speed_hz = MAX_SPI_FREQUENCY;
619  bus->base.bits_per_word = 8;
620  bus->base.speed_hz = bus->base.max_speed_hz;
621  bus->base.delay_usecs = 1;
622  bus->base.cs = 1;
623  bus->spi.spiId = config->spi_peripheral_id;
624  bus->spi.pSpiHw = config->spi_regs;
625  bus->chip_select_decode = config->chip_select_decode;
626
627  PIO_Configure(config->pins, config->pin_count);
628  PMC_EnablePeripheral(config->spi_peripheral_id);
629  atsam_configure_spi(bus);
630  atsam_spi_init_xdma(bus);
631
632  return spi_bus_register(&bus->base, bus_path);
633}
Note: See TracBrowser for help on using the repository browser.