Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:48

0001 /* ---------------------------------------------------------------------------- */
0002 /*                  Atmel Microcontroller Software Support                      */
0003 /*                       SAM Software Package License                           */
0004 /* ---------------------------------------------------------------------------- */
0005 /* Copyright (c) 2015, Atmel Corporation                                        */
0006 /* Copyright (c) 2016, embedded brains GmbH & Co. KG                            */
0007 /*                                                                              */
0008 /* All rights reserved.                                                         */
0009 /*                                                                              */
0010 /* Redistribution and use in source and binary forms, with or without           */
0011 /* modification, are permitted provided that the following condition is met:    */
0012 /*                                                                              */
0013 /* - Redistributions of source code must retain the above copyright notice,     */
0014 /* this list of conditions and the disclaimer below.                            */
0015 /*                                                                              */
0016 /* Atmel's name may not be used to endorse or promote products derived from     */
0017 /* this software without specific prior written permission.                     */
0018 /*                                                                              */
0019 /* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
0020 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
0021 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
0022 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
0023 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
0024 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
0025 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
0026 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
0027 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
0028 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
0029 /* ---------------------------------------------------------------------------- */
0030 
0031 #include <bsp/atsam-clock-config.h>
0032 #include <bsp/atsam-spi.h>
0033 #include <bsp/iocopy.h>
0034 
0035 #include <rtems/thread.h>
0036 
0037 #include <dev/spi/spi.h>
0038 
0039 #include <string.h>
0040 
0041 #define MAX_SPI_FREQUENCY 50000000
0042 
0043 typedef struct {
0044   volatile LinkedListDescriporView2 tx_desc;
0045   volatile LinkedListDescriporView2 rx_desc[3];
0046   uint8_t rx_bounce_head_buf[CPU_CACHE_LINE_BYTES];
0047   uint8_t rx_bounce_tail_buf[CPU_CACHE_LINE_BYTES];
0048 } atsam_spi_dma;
0049 
0050 typedef struct {
0051   spi_bus base;
0052   rtems_binary_semaphore sem;
0053   const spi_ioc_transfer *msg_current;
0054   uint32_t msg_todo;
0055   int error;
0056   Spi *spi_regs;
0057   uint32_t dma_tx_channel;
0058   uint32_t dma_rx_channel;
0059   atsam_spi_dma *dma;
0060   size_t rx_bounce_head_len;
0061   size_t rx_bounce_tail_len;
0062   int transfer_in_progress;
0063   bool chip_select_decode;
0064   uint8_t spi_id;
0065   uint32_t peripheral_clk_per_us;
0066   uint32_t spi_mr;
0067   uint32_t spi_csr[4];
0068 } atsam_spi_bus;
0069 
0070 static const uint32_t atsam_spi_dummy_write_data = 0xffffffff;
0071 
0072 static void atsam_spi_wakeup_task(atsam_spi_bus *bus)
0073 {
0074   rtems_binary_semaphore_post(&bus->sem);
0075 }
0076 
0077 static uint8_t atsam_calculate_dlybcs(const atsam_spi_bus *bus)
0078 {
0079   uint32_t dlybcs = bus->base.delay_usecs * bus->peripheral_clk_per_us;
0080 
0081   if (dlybcs > 0xff) {
0082     dlybcs = 0xff;
0083   }
0084 
0085   return dlybcs;
0086 }
0087 
0088 static uint32_t atsam_calculate_scbr(uint32_t speed_hz)
0089 {
0090   uint32_t scbr;
0091 
0092   scbr = BOARD_MCK / speed_hz;
0093   if (scbr > 0x0FF) {
0094     /* Best estimation we can offer with the hardware. */
0095     scbr = 0x0FF;
0096   }
0097   if (scbr == 0) {
0098     /* SCBR = 0 isn't allowed. */
0099     scbr = 1;
0100   }
0101 
0102   return scbr;
0103 }
0104 
0105 static void atsam_set_phase_and_polarity(uint32_t mode, uint32_t *csr)
0106 {
0107   uint32_t mode_mask = mode & SPI_MODE_3;
0108 
0109   switch(mode_mask) {
0110     case SPI_MODE_0:
0111       *csr |= SPI_CSR_NCPHA;
0112       break;
0113     case SPI_MODE_1:
0114       break;
0115     case SPI_MODE_2:
0116       *csr |= SPI_CSR_NCPHA;
0117       *csr |= SPI_CSR_CPOL;
0118       break;
0119     case SPI_MODE_3:
0120       *csr |= SPI_CSR_CPOL;
0121       break;
0122   }
0123   *csr |= SPI_CSR_CSAAT;
0124 }
0125 
0126 static void atsam_configure_spi(atsam_spi_bus *bus)
0127 {
0128   uint32_t scbr;
0129   uint32_t csr = 0;
0130   uint32_t mr;
0131   uint32_t cs = bus->base.cs;
0132 
0133   scbr = atsam_calculate_scbr(bus->base.speed_hz);
0134 
0135   mr = bus->spi_mr;
0136 
0137   if (bus->chip_select_decode) {
0138     mr |= SPI_MR_PCS(bus->base.cs);
0139     mr |= SPI_MR_PCSDEC;
0140     cs /= 4;
0141   } else {
0142     mr |= SPI_PCS(bus->base.cs);
0143   }
0144 
0145   bus->spi_regs->SPI_MR = mr;
0146 
0147   csr = bus->spi_csr[cs]
0148     | SPI_CSR_SCBR(scbr)
0149     | SPI_CSR_BITS(bus->base.bits_per_word - 8);
0150 
0151   atsam_set_phase_and_polarity(bus->base.mode, &csr);
0152 
0153   SPI_ConfigureNPCS(bus->spi_regs, cs, csr);
0154 }
0155 
0156 static void atsam_reset_spi(atsam_spi_bus *bus)
0157 {
0158   bus->spi_regs->SPI_CR = SPI_CR_SPIDIS;
0159   bus->spi_regs->SPI_CR = SPI_CR_SWRST;
0160   bus->spi_regs->SPI_CR = SPI_CR_SWRST;
0161   bus->spi_regs->SPI_CR = SPI_CR_SPIEN;
0162 }
0163 
0164 static void atsam_spi_copy_rx_bounce_bufs(
0165   const atsam_spi_bus *bus,
0166   const spi_ioc_transfer *msg
0167 )
0168 {
0169   if (bus->rx_bounce_head_len > 0) {
0170     atsam_copy_from_io(
0171       msg->rx_buf,
0172       bus->dma->rx_bounce_head_buf,
0173       bus->rx_bounce_head_len
0174     );
0175   }
0176 
0177   if (bus->rx_bounce_tail_len > 0) {
0178     atsam_copy_from_io(
0179       msg->rx_buf + msg->len - bus->rx_bounce_tail_len,
0180       bus->dma->rx_bounce_tail_buf,
0181       bus->rx_bounce_tail_len
0182     );
0183   }
0184 }
0185 
0186 static void atsam_spi_setup_real_rx_dma_desc(
0187   atsam_spi_bus *bus,
0188   atsam_spi_dma *dma,
0189   const uint8_t *buf,
0190   size_t n
0191 )
0192 {
0193   volatile LinkedListDescriporView2 *desc;
0194   uintptr_t m;
0195   uintptr_t b;
0196   uintptr_t a;
0197   uintptr_t ae;
0198   uintptr_t e;
0199 
0200   desc = &dma->rx_desc[0];
0201   m = CPU_CACHE_LINE_BYTES - 1;
0202   b = (uintptr_t) buf;
0203   e = b + n;
0204   a = (b + m) & ~m;
0205   ae = e & ~m;
0206 
0207   /* An earlier dummy read maybe has set the DAM to FIXED_AM. Reset it. */
0208   desc[0].mbr_cfg =
0209       (desc[0].mbr_cfg & ~XDMAC_CC_DAM_Msk) | XDMAC_CC_DAM_INCREMENTED_AM;
0210   if (n <= m) {
0211     bus->rx_bounce_head_len = n;
0212     bus->rx_bounce_tail_len = 0;
0213 
0214     desc[0].mbr_da = (uint32_t) dma->rx_bounce_head_buf;
0215     desc[0].mbr_ubc = n | XDMA_UBC_NVIEW_NDV2;
0216   } else {
0217     bus->rx_bounce_head_len = a - b;
0218     bus->rx_bounce_tail_len = e & m;
0219 
0220     if ((b & m) == 0) {
0221       if ((n & m) == 0) {
0222         desc[0].mbr_da = a;
0223         desc[0].mbr_ubc = n | XDMA_UBC_NVIEW_NDV2;
0224       } else {
0225         desc[0].mbr_da = a;
0226         desc[0].mbr_ubc = (ae - a) | XDMA_UBC_NDEN_UPDATED
0227           | XDMA_UBC_NVIEW_NDV2
0228           | XDMA_UBC_NDE_FETCH_EN;
0229         desc[1].mbr_da = (uint32_t) dma->rx_bounce_tail_buf;
0230         desc[1].mbr_ubc = n & m | XDMA_UBC_NVIEW_NDV2;
0231       }
0232     } else {
0233       if ((e & m) == 0) {
0234         desc[0].mbr_da = (uint32_t) dma->rx_bounce_head_buf;
0235         desc[0].mbr_ubc = (a - b) | XDMA_UBC_NDEN_UPDATED
0236           | XDMA_UBC_NVIEW_NDV2
0237           | XDMA_UBC_NDE_FETCH_EN;
0238         desc[1].mbr_da = a;
0239         desc[1].mbr_ubc = ae - a | XDMA_UBC_NVIEW_NDV2;
0240       } else if ((ae - a) == 0) {
0241         bus->rx_bounce_head_len = n;
0242         bus->rx_bounce_tail_len = 0;
0243 
0244         desc[0].mbr_da = (uint32_t) dma->rx_bounce_head_buf;
0245         desc[0].mbr_ubc = n | XDMA_UBC_NVIEW_NDV2;
0246       } else {
0247         desc[0].mbr_da = (uint32_t) dma->rx_bounce_head_buf;
0248         desc[0].mbr_ubc = (a - b) | XDMA_UBC_NDEN_UPDATED
0249           | XDMA_UBC_NVIEW_NDV2
0250           | XDMA_UBC_NDE_FETCH_EN;
0251         desc[1].mbr_da = a;
0252         desc[1].mbr_ubc = (ae - a) | XDMA_UBC_NDEN_UPDATED
0253           | XDMA_UBC_NVIEW_NDV2
0254           | XDMA_UBC_NDE_FETCH_EN;
0255         desc[2].mbr_da = (uint32_t) dma->rx_bounce_tail_buf;
0256         desc[2].mbr_ubc = e - ae | XDMA_UBC_NVIEW_NDV2;
0257       }
0258     }
0259 
0260     rtems_cache_invalidate_multiple_data_lines((void *) a, ae - a);
0261   }
0262 }
0263 
0264 static void atsam_spi_setup_dummy_rx_dma_desc(
0265   atsam_spi_bus *bus,
0266   atsam_spi_dma *dma,
0267   size_t n
0268 )
0269 {
0270   volatile LinkedListDescriporView2 *desc;
0271 
0272   desc = &dma->rx_desc[0];
0273 
0274   /* Avoid copying bounce buffers after receive. */
0275   bus->rx_bounce_head_len = 0;
0276   bus->rx_bounce_tail_len = 0;
0277 
0278   /* But use one of the bounce buffers to write dummy data to. */
0279   desc[0].mbr_da = (uint32_t) dma->rx_bounce_head_buf;
0280   desc[0].mbr_ubc = n | XDMA_UBC_NVIEW_NDV2;
0281   desc[0].mbr_cfg =
0282       (desc[0].mbr_cfg & ~XDMAC_CC_DAM_Msk) | XDMAC_CC_DAM_FIXED_AM;
0283 }
0284 
0285 static void atsam_spi_setup_rx_dma_desc(
0286   atsam_spi_bus *bus,
0287   atsam_spi_dma *dma,
0288   const uint8_t *buf,
0289   size_t n
0290 )
0291 {
0292   if (buf != NULL) {
0293     atsam_spi_setup_real_rx_dma_desc(bus, dma, buf, n);
0294   } else {
0295     atsam_spi_setup_dummy_rx_dma_desc(bus, dma, n);
0296   }
0297 }
0298 
0299 static void atsam_spi_setup_tx_dma_desc(
0300   atsam_spi_dma *dma,
0301   const uint8_t *buf,
0302   size_t n
0303 )
0304 {
0305   volatile LinkedListDescriporView2 *desc;
0306 
0307   desc = &dma->tx_desc;
0308   desc->mbr_ubc = n | XDMA_UBC_NVIEW_NDV2;
0309   if (buf != NULL) {
0310     desc->mbr_sa = (uint32_t) buf;
0311     desc->mbr_cfg =
0312         (desc->mbr_cfg & ~XDMAC_CC_SAM_Msk) | XDMAC_CC_SAM_INCREMENTED_AM;
0313     rtems_cache_flush_multiple_data_lines(buf, n);
0314   } else {
0315     desc->mbr_sa = (uint32_t)&atsam_spi_dummy_write_data;
0316     desc->mbr_cfg =
0317         (desc->mbr_cfg & ~XDMAC_CC_SAM_Msk) | XDMAC_CC_SAM_FIXED_AM;
0318   }
0319 }
0320 
0321 static void atsam_spi_start_dma_transfer(
0322   atsam_spi_bus *bus,
0323   const spi_ioc_transfer *msg
0324 )
0325 {
0326   atsam_spi_dma *dma;
0327   Xdmac *pXdmac;
0328 
0329   dma = bus->dma;
0330   pXdmac = XDMAC;
0331 
0332   bus->transfer_in_progress = 2;
0333 
0334   atsam_spi_setup_rx_dma_desc(bus, dma, msg->rx_buf, msg->len);
0335   atsam_spi_setup_tx_dma_desc(dma, msg->tx_buf, msg->len);
0336 
0337   XDMAC_SetDescriptorAddr(
0338     pXdmac,
0339     bus->dma_rx_channel,
0340     (uint32_t) &dma->rx_desc[0],
0341     0
0342   );
0343   XDMAC_SetDescriptorControl(
0344     pXdmac,
0345     bus->dma_rx_channel,
0346     XDMAC_CNDC_NDVIEW_NDV2 |
0347     XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
0348     XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
0349     XDMAC_CNDC_NDE_DSCR_FETCH_EN
0350   );
0351   XDMAC_SetDescriptorAddr(
0352     pXdmac,
0353     bus->dma_tx_channel,
0354     (uint32_t) &dma->tx_desc,
0355     0
0356   );
0357   XDMAC_SetDescriptorControl(
0358     pXdmac,
0359     bus->dma_tx_channel,
0360     XDMAC_CNDC_NDVIEW_NDV2 |
0361     XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
0362     XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
0363     XDMAC_CNDC_NDE_DSCR_FETCH_EN
0364   );
0365 
0366   XDMAC_StartTransfer(pXdmac, bus->dma_rx_channel);
0367   XDMAC_StartTransfer(pXdmac, bus->dma_tx_channel);
0368 }
0369 
0370 static int atsam_check_configure_spi(atsam_spi_bus *bus, const spi_ioc_transfer *msg)
0371 {
0372   if (
0373     msg->mode != bus->base.mode
0374       || msg->speed_hz != bus->base.speed_hz
0375       || msg->bits_per_word != bus->base.bits_per_word
0376       || msg->cs != bus->base.cs
0377   ) {
0378     if (
0379       msg->bits_per_word != 8
0380         || msg->mode > 3
0381         || msg->speed_hz > bus->base.max_speed_hz
0382     ) {
0383       return -EINVAL;
0384     }
0385 
0386     bus->base.mode = msg->mode;
0387     bus->base.speed_hz = msg->speed_hz;
0388     bus->base.bits_per_word = msg->bits_per_word;
0389     bus->base.cs = msg->cs;
0390     atsam_configure_spi(bus);
0391   }
0392 
0393   return 0;
0394 }
0395 
0396 static void atsam_spi_setup_transfer(atsam_spi_bus *bus)
0397 {
0398   uint32_t msg_todo = bus->msg_todo;
0399 
0400   if (msg_todo > 0) {
0401     const spi_ioc_transfer *msg;
0402     int error;
0403 
0404     msg = bus->msg_current;
0405     error = atsam_check_configure_spi(bus, msg);
0406     if (error == 0) {
0407       atsam_spi_start_dma_transfer(bus, msg);
0408     } else {
0409       bus->error = error;
0410       atsam_spi_wakeup_task(bus);
0411     }
0412   } else {
0413     atsam_spi_wakeup_task(bus);
0414   }
0415 }
0416 
0417 static void atsam_spi_dma_callback(uint32_t ch, void *arg, uint32_t status)
0418 {
0419   atsam_spi_bus *bus;
0420   uint32_t dma_errors;
0421 
0422   bus = arg;
0423   dma_errors = XDMAC_CIE_DIE | XDMAC_CIE_FIE | XDMAC_CIE_RBIE | XDMAC_CIE_WBIE
0424     | XDMAC_CIE_ROIE;
0425 
0426   if ((status & dma_errors) != 0) {
0427     bus->error = -EIO;
0428   }
0429 
0430   --bus->transfer_in_progress;
0431 
0432   if (bus->transfer_in_progress == 0) {
0433     const spi_ioc_transfer *msg = bus->msg_current;
0434 
0435     if (msg->delay_usecs != bus->base.delay_usecs) {
0436       uint32_t mr;
0437       uint32_t mr_dlybcs;
0438 
0439       bus->base.delay_usecs = msg->delay_usecs;
0440       mr_dlybcs = SPI_MR_DLYBCS(atsam_calculate_dlybcs(bus));
0441       bus->spi_mr = mr_dlybcs | SPI_MR_MSTR | SPI_MR_MODFDIS;
0442 
0443       mr = bus->spi_regs->SPI_MR;
0444       mr &= ~SPI_MR_DLYBCS_Msk;
0445       mr |= mr_dlybcs;
0446       bus->spi_regs->SPI_MR = mr;
0447     }
0448 
0449     if (msg->cs_change) {
0450       bus->spi_regs->SPI_CR = SPI_CR_LASTXFER;
0451     }
0452 
0453     atsam_spi_copy_rx_bounce_bufs(bus, msg);
0454 
0455     bus->msg_current = msg + 1;
0456     --bus->msg_todo;
0457 
0458     if (bus->error == 0) {
0459       atsam_spi_setup_transfer(bus);
0460     } else {
0461       atsam_spi_wakeup_task(bus);
0462     }
0463   }
0464 }
0465 
0466 static int atsam_spi_transfer(
0467   spi_bus *base,
0468   const spi_ioc_transfer *msgs,
0469   uint32_t msg_count
0470 )
0471 {
0472   atsam_spi_bus *bus = (atsam_spi_bus *)base;
0473 
0474   bus->msg_current = msgs;
0475   bus->msg_todo = msg_count;
0476   bus->error = 0;
0477   atsam_spi_setup_transfer(bus);
0478   rtems_binary_semaphore_wait(&bus->sem);
0479   return bus->error;
0480 }
0481 
0482 
0483 static void atsam_spi_destroy(spi_bus *base)
0484 {
0485   atsam_spi_bus *bus = (atsam_spi_bus *)base;
0486   eXdmadRC rc;
0487 
0488   rc = XDMAD_SetCallback(
0489     &XDMAD_Instance,
0490     bus->dma_rx_channel,
0491     XDMAD_DoNothingCallback,
0492     NULL
0493   );
0494   assert(rc == XDMAD_OK);
0495 
0496   rc = XDMAD_SetCallback(
0497     &XDMAD_Instance,
0498     bus->dma_tx_channel,
0499     XDMAD_DoNothingCallback,
0500     NULL
0501   );
0502   assert(rc == XDMAD_OK);
0503 
0504   XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_rx_channel);
0505   XDMAD_FreeChannel(&XDMAD_Instance, bus->dma_tx_channel);
0506 
0507   SPI_Disable(bus->spi_regs);
0508   PMC_DisablePeripheral(bus->spi_id);
0509 
0510   rtems_cache_coherent_free(bus->dma);
0511   rtems_binary_semaphore_destroy(&bus->sem);
0512   spi_bus_destroy_and_free(&bus->base);
0513 }
0514 
0515 static int atsam_spi_setup(spi_bus *base)
0516 {
0517   atsam_spi_bus *bus = (atsam_spi_bus *)base;
0518 
0519   if (
0520     bus->base.speed_hz > MAX_SPI_FREQUENCY
0521       || bus->base.bits_per_word != 8
0522   ) {
0523       return -EINVAL;
0524   }
0525   atsam_configure_spi(bus);
0526   return 0;
0527 }
0528 
0529 static void atsam_spi_init_xdma(atsam_spi_bus *bus)
0530 {
0531   atsam_spi_dma *dma;
0532   sXdmadCfg cfg;
0533   uint32_t xdmaInt;
0534   uint8_t channel;
0535   eXdmadRC rc;
0536   uint32_t xdma_cndc;
0537   uint32_t tx_da;
0538   uint32_t rx_sa;
0539   uint32_t tx_cfg;
0540   uint32_t rx_cfg;
0541 
0542   tx_da = (uint32_t)&bus->spi_regs->SPI_TDR;
0543   rx_sa = (uint32_t)&bus->spi_regs->SPI_RDR;
0544 
0545   channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_TX);
0546   tx_cfg =
0547     XDMAC_CC_TYPE_PER_TRAN |
0548     XDMAC_CC_MBSIZE_SINGLE |
0549     XDMAC_CC_DSYNC_MEM2PER |
0550     XDMAC_CC_CSIZE_CHK_1 |
0551     XDMAC_CC_DWIDTH_BYTE |
0552     XDMAC_CC_SIF_AHB_IF1 |
0553     XDMAC_CC_DIF_AHB_IF1 |
0554     XDMAC_CC_SAM_INCREMENTED_AM |
0555     XDMAC_CC_DAM_FIXED_AM |
0556     XDMAC_CC_PERID(channel);
0557 
0558   channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_RX);
0559   rx_cfg =
0560     XDMAC_CC_TYPE_PER_TRAN |
0561     XDMAC_CC_MBSIZE_SINGLE |
0562     XDMAC_CC_DSYNC_PER2MEM |
0563     XDMAC_CC_CSIZE_CHK_1 |
0564     XDMAC_CC_DWIDTH_BYTE |
0565     XDMAC_CC_SIF_AHB_IF1 |
0566     XDMAC_CC_DIF_AHB_IF1 |
0567     XDMAC_CC_SAM_FIXED_AM |
0568     XDMAC_CC_DAM_INCREMENTED_AM |
0569     XDMAC_CC_PERID(channel);
0570 
0571   dma = rtems_cache_coherent_allocate(sizeof(*dma), 0, 0);
0572   assert(dma != NULL);
0573   bus->dma = dma;
0574 
0575   dma->tx_desc.mbr_nda = 0;
0576   dma->tx_desc.mbr_sa = 0;
0577   dma->tx_desc.mbr_da = tx_da;
0578   dma->tx_desc.mbr_cfg = tx_cfg;
0579   dma->rx_desc[0].mbr_nda = (uint32_t) &dma->rx_desc[1];
0580   dma->rx_desc[0].mbr_sa = rx_sa;
0581   dma->rx_desc[0].mbr_da = 0;
0582   dma->rx_desc[0].mbr_cfg = rx_cfg;
0583   dma->rx_desc[1].mbr_nda = (uint32_t) &dma->rx_desc[2];
0584   dma->rx_desc[1].mbr_sa = rx_sa;
0585   dma->rx_desc[1].mbr_da = 0;
0586   dma->rx_desc[1].mbr_cfg = rx_cfg;
0587   dma->rx_desc[2].mbr_nda = 0;
0588   dma->rx_desc[2].mbr_sa = rx_sa;
0589   dma->rx_desc[2].mbr_da = 0;
0590   dma->rx_desc[2].mbr_cfg = rx_cfg;
0591 
0592   bus->dma_tx_channel = XDMAD_AllocateChannel(
0593     &XDMAD_Instance,
0594     XDMAD_TRANSFER_MEMORY,
0595     bus->spi_id
0596   );
0597   assert(bus->dma_tx_channel != XDMAD_ALLOC_FAILED);
0598 
0599   bus->dma_rx_channel = XDMAD_AllocateChannel(
0600     &XDMAD_Instance,
0601     bus->spi_id,
0602     XDMAD_TRANSFER_MEMORY
0603   );
0604   assert(bus->dma_rx_channel != XDMAD_ALLOC_FAILED);
0605 
0606   rc = XDMAD_SetCallback(
0607     &XDMAD_Instance,
0608     bus->dma_rx_channel,
0609     atsam_spi_dma_callback,
0610     bus
0611   );
0612   assert(rc == XDMAD_OK);
0613 
0614   rc = XDMAD_SetCallback(
0615     &XDMAD_Instance,
0616     bus->dma_tx_channel,
0617     atsam_spi_dma_callback,
0618     bus
0619   );
0620   assert(rc == XDMAD_OK);
0621 
0622   rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_rx_channel);
0623   assert(rc == XDMAD_OK);
0624 
0625   rc = XDMAD_PrepareChannel(&XDMAD_Instance, bus->dma_tx_channel);
0626   assert(rc == XDMAD_OK);
0627 
0628   /* Put all relevant interrupts on */
0629   xdmaInt = XDMAC_CIE_LIE | XDMAC_CIE_DIE | XDMAC_CIE_FIE | XDMAC_CIE_RBIE
0630     | XDMAC_CIE_WBIE | XDMAC_CIE_ROIE;
0631 
0632   /* Setup RX */
0633   memset(&cfg, 0, sizeof(cfg));
0634   channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_RX);
0635   cfg.mbr_sa = rx_sa;
0636   cfg.mbr_cfg = rx_cfg;
0637   xdma_cndc = XDMAC_CNDC_NDVIEW_NDV2 |
0638     XDMAC_CNDC_NDE_DSCR_FETCH_EN |
0639     XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
0640     XDMAC_CNDC_NDSUP_SRC_PARAMS_UNCHANGED;
0641   rc = XDMAD_ConfigureTransfer(
0642     &XDMAD_Instance,
0643     bus->dma_rx_channel,
0644     &cfg,
0645     xdma_cndc,
0646     (uint32_t) &bus->dma->rx_desc[0],
0647     xdmaInt
0648   );
0649   assert(rc == XDMAD_OK);
0650 
0651   /* Setup TX  */
0652   memset(&cfg, 0, sizeof(cfg));
0653   channel = XDMAIF_Get_ChannelNumber(bus->spi_id, XDMAD_TRANSFER_TX);
0654   cfg.mbr_da = tx_da;
0655   cfg.mbr_cfg = tx_cfg;
0656   xdma_cndc = XDMAC_CNDC_NDVIEW_NDV2 |
0657     XDMAC_CNDC_NDE_DSCR_FETCH_EN |
0658     XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED |
0659     XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED;
0660   rc = XDMAD_ConfigureTransfer(
0661     &XDMAD_Instance,
0662     bus->dma_tx_channel,
0663     &cfg,
0664     xdma_cndc,
0665     (uint32_t) &bus->dma->tx_desc,
0666     xdmaInt
0667   );
0668   assert(rc == XDMAD_OK);
0669 }
0670 
0671 int spi_bus_register_atsam(
0672   const char *bus_path,
0673   const atsam_spi_config *config
0674 )
0675 {
0676   atsam_spi_bus *bus;
0677   size_t i;
0678 
0679   bus = (atsam_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
0680   if (bus == NULL) {
0681     return -1;
0682   }
0683 
0684   bus->base.transfer = atsam_spi_transfer;
0685   bus->base.destroy = atsam_spi_destroy;
0686   bus->base.setup = atsam_spi_setup;
0687   bus->base.max_speed_hz = MAX_SPI_FREQUENCY;
0688   bus->base.bits_per_word = 8;
0689   bus->base.speed_hz = bus->base.max_speed_hz;
0690   bus->base.cs = 1;
0691   bus->spi_id = config->spi_peripheral_id;
0692   bus->spi_regs = config->spi_regs;
0693   bus->chip_select_decode = config->chip_select_decode;
0694   bus->peripheral_clk_per_us = BOARD_MCK / 1000000;
0695   bus->spi_mr = SPI_MR_MSTR | SPI_MR_MODFDIS;
0696 
0697   for (i = 0; i < RTEMS_ARRAY_SIZE(bus->spi_csr); ++i) {
0698     if (config->dlybs_in_ns[i] != 0) {
0699       bus->spi_csr[i] |= SPI_DLYBS(config->dlybs_in_ns[i], BOARD_MCK);
0700     }
0701 
0702     if (config->dlybct_in_ns[i] != 0) {
0703       bus->spi_csr[i] |= SPI_DLYBCT(config->dlybct_in_ns[i], BOARD_MCK);
0704     }
0705   }
0706 
0707   rtems_binary_semaphore_init(&bus->sem, "ATSAM SPI");
0708   PIO_Configure(config->pins, config->pin_count);
0709   PMC_EnablePeripheral(config->spi_peripheral_id);
0710   atsam_reset_spi(bus);
0711   atsam_configure_spi(bus);
0712   atsam_spi_init_xdma(bus);
0713 
0714   return spi_bus_register(&bus->base, bus_path);
0715 }