Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR)
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 
0029 #include <bsp.h>
0030 
0031 #include <rtems/irq-extension.h>
0032 #include <sys/param.h> /* MAX() */
0033 
0034 #include <dev/spi/spi.h>
0035 #include <dev/spi/xilinx-axi-spi.h>
0036 #include <dev/spi/xilinx-axi-spi-regs.h>
0037 
0038 #define XILINX_AXI_SPI_CS_NONE 0xFF
0039 
0040 typedef struct xilinx_axi_spi_bus xilinx_axi_spi_bus;
0041 
0042 struct xilinx_axi_spi_bus {
0043   spi_bus base;
0044   volatile xilinx_axi_spi *regs;
0045   uint32_t fifo_size;
0046   uint32_t num_cs;
0047   uint32_t msg_todo;
0048   const spi_ioc_transfer *msg;
0049   uint32_t todo;
0050   uint32_t in_transfer;
0051   uint8_t *rx_buf;
0052   const uint8_t *tx_buf;
0053   rtems_id task_id;
0054   rtems_vector_number irq;
0055 };
0056 
0057 
0058 static void xilinx_axi_spi_disable_interrupts(volatile xilinx_axi_spi *regs)
0059 {
0060   regs->globalirq = 0;
0061   regs->irqenable = 0;
0062 }
0063 
0064 static bool xilinx_axi_spi_rx_fifo_not_empty(volatile xilinx_axi_spi *regs)
0065 {
0066   return (regs->status & XILINX_AXI_SPI_STATUS_RXEMPTY) == 0;
0067 }
0068 
0069 static void xilinx_axi_spi_reset(xilinx_axi_spi_bus *bus)
0070 {
0071   volatile xilinx_axi_spi *regs = bus->regs;
0072   uint32_t control;
0073 
0074   /* Initiate soft reset for initial state */
0075   regs->reset = XILINX_AXI_SPI_RESET;
0076 
0077   /* Configure as master */
0078   control = regs->control;
0079   control |= XILINX_AXI_SPI_CONTROL_MSTREN;
0080   regs->control = control;
0081 }
0082 
0083 static void xilinx_axi_spi_done(xilinx_axi_spi_bus *bus)
0084 {
0085   volatile xilinx_axi_spi *regs = bus->regs;
0086   uint32_t control = regs->control;
0087   control &= ~XILINX_AXI_SPI_CONTROL_SPIEN;
0088   regs->control = control;
0089 
0090   xilinx_axi_spi_disable_interrupts(regs);
0091   rtems_event_transient_send(bus->task_id);
0092 }
0093 
0094 static void xilinx_axi_spi_push(xilinx_axi_spi_bus *bus, volatile xilinx_axi_spi *regs)
0095 {
0096   while (bus->todo > 0 && bus->in_transfer < bus->fifo_size) {
0097     uint8_t val = 0;
0098     if (bus->tx_buf != NULL) {
0099         val = *bus->tx_buf;
0100         ++bus->tx_buf;
0101     }
0102 
0103     --bus->todo;
0104     regs->txdata = val;
0105     ++bus->in_transfer;
0106   }
0107 }
0108 
0109 static void
0110 xilinx_axi_spi_set_chipsel(xilinx_axi_spi_bus *bus, uint32_t cs)
0111 {
0112 
0113    volatile xilinx_axi_spi *regs = bus->regs;
0114    uint32_t cs_bit = XILINX_AXI_SPI_CS_NONE;
0115 
0116   if (cs != SPI_NO_CS && cs < bus->num_cs) {
0117       cs_bit &= ~(1<<cs);
0118   }
0119   bus->base.cs = cs;
0120 
0121   regs->cs = cs_bit;
0122 }
0123 
0124 static void xilinx_axi_spi_config(
0125   xilinx_axi_spi_bus *bus,
0126   volatile xilinx_axi_spi *regs,
0127   uint32_t mode,
0128   uint8_t  cs
0129 )
0130 {
0131   spi_bus *base = &bus->base;
0132   uint32_t control = regs->control;
0133 
0134   control &= ~XILINX_AXI_SPI_CONTROL_SPIEN;
0135   regs->control = control;
0136   
0137   if ((mode & SPI_CPHA) != 0) {
0138     control |= XILINX_AXI_SPI_CONTROL_CPHA;
0139   } else {
0140     control &= ~XILINX_AXI_SPI_CONTROL_CPHA;
0141   }
0142 
0143   if ((mode & SPI_CPOL) != 0) {
0144     control |= XILINX_AXI_SPI_CONTROL_CPOL;
0145   } else {
0146     control &= ~XILINX_AXI_SPI_CONTROL_CPOL;
0147   }
0148 
0149   if ((mode & SPI_LOOP) != 0) {
0150     control |= XILINX_AXI_SPI_CONTROL_LOOP;
0151   } else {
0152     control &= ~XILINX_AXI_SPI_CONTROL_LOOP;
0153   }
0154 
0155   regs->control = control;
0156   xilinx_axi_spi_set_chipsel(bus, cs);
0157 
0158   base->mode = mode;
0159   base->cs = cs;
0160 
0161 }
0162 
0163 static void
0164 xilinx_axi_spi_next_msg(xilinx_axi_spi_bus *bus, volatile xilinx_axi_spi *regs)
0165 {
0166     uint32_t control = regs->control;
0167     control |= XILINX_AXI_SPI_CONTROL_MST_TRANS_INHIBIT
0168                | XILINX_AXI_SPI_CONTROL_RX_FIFO_RESET
0169                | XILINX_AXI_SPI_CONTROL_TX_FIFO_RESET;
0170     regs->control = control;
0171 
0172   if (bus->msg_todo > 0) {
0173     const spi_ioc_transfer *msg;
0174     spi_bus *base = &bus->base;
0175 
0176     msg = bus->msg;
0177 
0178     if (
0179         msg->mode != base->mode
0180         || msg->cs != base->cs
0181     ) {
0182       xilinx_axi_spi_config(
0183         bus,
0184         regs,
0185         msg->mode,
0186         msg->cs
0187       );
0188     }
0189 
0190     if ((msg->mode & SPI_NO_CS) != 0) {
0191       xilinx_axi_spi_set_chipsel(bus, XILINX_AXI_SPI_CS_NONE);
0192     }
0193 
0194     bus->todo = msg->len;
0195     bus->rx_buf = msg->rx_buf;
0196     bus->tx_buf = msg->tx_buf;
0197     xilinx_axi_spi_push(bus, regs);
0198     
0199     xilinx_axi_spi_disable_interrupts(regs);
0200     if (
0201         bus->todo < bus->fifo_size
0202         || bus->fifo_size == 1) {
0203         /* if the msg fits into the FIFO, wait for empty TX buffer */
0204         regs->irqenable = XILINX_AXI_SPI_IRQ_TXEMPTY;
0205 
0206     } else {
0207         /* if the msg does not fit, refill tx_buf when the tx FIFO is half empty */
0208         regs->irqenable = XILINX_AXI_SPI_IRQ_TXHALF;
0209     }
0210     regs->globalirq = XILINX_AXI_SPI_GLOBAL_IRQ_ENABLE;
0211     control = regs->control;
0212     control |= XILINX_AXI_SPI_CONTROL_SPIEN;
0213     control &= ~XILINX_AXI_SPI_CONTROL_MST_TRANS_INHIBIT;
0214     regs->control = control;
0215   } else {
0216     xilinx_axi_spi_done(bus);
0217   }
0218 }
0219 
0220 static void xilinx_axi_spi_interrupt(void *arg)
0221 {
0222   xilinx_axi_spi_bus *bus;
0223   volatile xilinx_axi_spi *regs;
0224 
0225   bus = arg;
0226   regs = bus->regs;
0227 
0228   /* Clear interrupt flag. It's safe, since only one IRQ active at a time */
0229   regs->irqstatus = regs->irqenable;
0230 
0231   while (xilinx_axi_spi_rx_fifo_not_empty(regs)) {
0232     uint32_t val = regs->rxdata;
0233     if (bus->rx_buf != NULL) {
0234         *bus->rx_buf = (uint8_t)val;
0235         ++bus->rx_buf;
0236     }
0237     --bus->in_transfer;
0238   }
0239 
0240   if (bus->todo > 0) {
0241     xilinx_axi_spi_push(bus, regs);
0242   } else if (bus->in_transfer > 0) {
0243     /* Wait until all bytes have been transfered */
0244     regs->irqenable = XILINX_AXI_SPI_IRQ_TXEMPTY;
0245   } else {
0246     --bus->msg_todo;
0247     ++bus->msg;
0248     xilinx_axi_spi_next_msg(bus, regs);
0249   }
0250 }
0251 
0252 static int xilinx_axi_spi_check_messages(
0253   xilinx_axi_spi_bus *bus,
0254   const spi_ioc_transfer *msg,
0255   uint32_t size)
0256 {
0257   while(size > 0) {
0258     if (msg->bits_per_word != 8) {
0259       return -EINVAL;
0260     }
0261     if ((msg->mode &
0262         ~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0) {
0263       return -EINVAL;
0264     }
0265     if ((msg->mode & SPI_NO_CS) == 0 &&
0266         (msg->cs > bus->num_cs)) {
0267       return -EINVAL;
0268     }
0269 
0270     ++msg;
0271     --size;
0272   }
0273 
0274   return 0;
0275 }
0276 
0277 static int xilinx_axi_spi_transfer(
0278   spi_bus *base,
0279   const spi_ioc_transfer *msgs,
0280   uint32_t n
0281 )
0282 {
0283   xilinx_axi_spi_bus *bus;
0284   int rv;
0285 
0286   bus = (xilinx_axi_spi_bus *) base;
0287 
0288   rv = xilinx_axi_spi_check_messages(bus, msgs, n);
0289 
0290   if (rv == 0) {
0291     bus->msg_todo = n;
0292     bus->msg = &msgs[0];
0293     bus->task_id = rtems_task_self();
0294 
0295     xilinx_axi_spi_next_msg(bus, bus->regs);
0296     rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
0297     xilinx_axi_spi_set_chipsel(bus, XILINX_AXI_SPI_CS_NONE);
0298   }
0299   return rv;
0300 }
0301 
0302 static void xilinx_axi_spi_destroy(spi_bus *base)
0303 {
0304   xilinx_axi_spi_bus *bus;
0305 
0306   bus = (xilinx_axi_spi_bus *) base;
0307   rtems_interrupt_handler_remove(bus->irq, xilinx_axi_spi_interrupt, bus);
0308   spi_bus_destroy_and_free(&bus->base);
0309 }
0310 
0311 static int xilinx_axi_spi_setup(spi_bus *base)
0312 {
0313   xilinx_axi_spi_bus *bus;
0314   uint32_t mode = base->mode;
0315 
0316   bus = (xilinx_axi_spi_bus *) base;
0317 
0318   if (bus->base.bits_per_word > 8) {
0319     return -EINVAL;
0320   }
0321 
0322   /* SPI_CS_HIGH not supported */
0323   if (mode & SPI_CS_HIGH) {
0324       return -EINVAL;
0325   }
0326 
0327   xilinx_axi_spi_config(
0328     bus,
0329     bus->regs,
0330     base->mode,
0331     base->cs
0332   );
0333   return 0;
0334 }
0335 
0336 int spi_bus_register_xilinx_axi(
0337         const char *bus_path,
0338         uintptr_t register_base,
0339         uint32_t fifo_size,
0340         uint32_t num_cs,
0341         rtems_vector_number irq)
0342 {
0343   xilinx_axi_spi_bus *bus;
0344   spi_bus *base;
0345   int sc;
0346 
0347   if (fifo_size != 0 && fifo_size != 16 && fifo_size != 256) {
0348       return -1;
0349   }
0350 
0351   if (num_cs > 32) {
0352       return -1;
0353   }
0354 
0355   bus = (xilinx_axi_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
0356   if (bus == NULL){
0357     return -1;
0358   }
0359 
0360   base = &bus->base;
0361   bus->regs = (volatile xilinx_axi_spi *) register_base;
0362   bus->irq = irq;
0363   bus->num_cs = num_cs;
0364 
0365   /* For operation without FIFO set fifo_size to 1
0366    * so that comparison operators work
0367    */
0368   if (fifo_size == 0) {
0369     bus->fifo_size = 1;
0370   } else {
0371     bus->fifo_size = fifo_size;
0372   }
0373 
0374   base->cs = SPI_NO_CS;
0375 
0376   xilinx_axi_spi_reset(bus);
0377   xilinx_axi_spi_config(
0378     bus,
0379     bus->regs,
0380     base->mode,
0381     base->cs
0382     );
0383 
0384 
0385   sc = rtems_interrupt_handler_install(
0386     bus->irq,
0387     "XSPI",
0388     RTEMS_INTERRUPT_UNIQUE,
0389     xilinx_axi_spi_interrupt,
0390     bus
0391   );
0392   if (sc != RTEMS_SUCCESSFUL) {
0393     (*bus->base.destroy)(&bus->base);
0394     rtems_set_errno_and_return_minus_one(EAGAIN);
0395   }
0396 
0397   bus->base.transfer = xilinx_axi_spi_transfer;
0398   bus->base.destroy = xilinx_axi_spi_destroy;
0399   bus->base.setup = xilinx_axi_spi_setup;
0400 
0401   return spi_bus_register(&bus->base, bus_path);
0402 }