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/cadence-spi.h>
0036 #include <dev/spi/cadence-spi-regs.h>
0037 
0038 #define CADENCE_SPI_FIFO_SIZE 128
0039 #define CADENCE_SPI_MAX_CHIPSELECTS 3
0040 #define CADENCE_SPI_CS_NONE 0xF
0041 
0042 typedef struct cadence_spi_bus cadence_spi_bus;
0043 
0044 struct cadence_spi_bus {
0045   spi_bus base;
0046   volatile cadence_spi *regs;
0047   uint32_t clk_in;
0048   uint16_t clk_per_usecs;
0049   uint32_t msg_todo;
0050   const spi_ioc_transfer *msg;
0051   uint32_t todo;
0052   uint32_t in_transfer;
0053   uint8_t *rx_buf;
0054   const uint8_t *tx_buf;
0055   rtems_id task_id;
0056   rtems_vector_number irq;
0057 };
0058 
0059 
0060 static void cadence_spi_disable_interrupts(volatile cadence_spi *regs)
0061 {
0062   regs->irqdisable = 0xffff;
0063 }
0064 
0065 static void cadence_spi_clear_irq_status(volatile cadence_spi *regs)
0066 {
0067   regs->irqstatus = regs->irqstatus;
0068 }
0069 
0070 static bool cadence_spi_rx_fifo_not_empty(volatile cadence_spi *regs)
0071 {
0072   return (regs->irqstatus & CADENCE_SPI_IXR_RXNEMPTY) != 0;
0073 }
0074 
0075 static void cadence_spi_reset(cadence_spi_bus *bus)
0076 {
0077   volatile cadence_spi *regs = bus->regs;
0078   uint32_t val;
0079 
0080   cadence_spi_disable_interrupts(regs);
0081 
0082   regs->spienable = 0;
0083 
0084   val = regs->config;
0085   val &= ~(CADENCE_SPI_CONFIG_MODEFAIL_EN
0086          | CADENCE_SPI_CONFIG_MANSTRT_EN
0087          | CADENCE_SPI_CONFIG_MANUAL_CS
0088          | CADENCE_SPI_CONFIG_PERI_SEL
0089          | CADENCE_SPI_CONFIG_REF_CLK);
0090 
0091   val |= CADENCE_SPI_CONFIG_CS(CADENCE_SPI_CS_NONE)
0092     | CADENCE_SPI_CONFIG_MSTREN
0093     | CADENCE_SPI_CONFIG_MANSTRT;
0094   regs->config = val;
0095 
0096   /* Initialize here, will be set for every transfer */
0097   regs->txthreshold = 1;
0098   /* Set to 1, so we can check when the rx buffer is empty */
0099   regs->rxthreshold = 1;
0100 
0101   while (cadence_spi_rx_fifo_not_empty(regs)) {
0102     regs->rxdata;
0103   }
0104   cadence_spi_clear_irq_status(regs);
0105 }
0106 
0107 static void cadence_spi_done(cadence_spi_bus *bus)
0108 {
0109   volatile cadence_spi *regs = bus->regs;
0110   regs->spienable = 0;
0111   cadence_spi_disable_interrupts(regs);
0112   cadence_spi_clear_irq_status(regs);
0113   rtems_event_transient_send(bus->task_id);
0114 }
0115 
0116 static void cadence_spi_push(cadence_spi_bus *bus, volatile cadence_spi *regs)
0117 {
0118   while (bus->todo > 0 && bus->in_transfer < CADENCE_SPI_FIFO_SIZE) {
0119     uint8_t val = 0;
0120     if (bus->tx_buf != NULL) {
0121         val = *bus->tx_buf;
0122         ++bus->tx_buf;
0123     }
0124 
0125     --bus->todo;
0126     regs->txdata = val;
0127     ++bus->in_transfer;
0128   }
0129 }
0130 
0131 static void
0132 cadence_spi_set_chipsel(cadence_spi_bus *bus, uint32_t cs)
0133 {
0134 
0135    volatile cadence_spi *regs = bus->regs;
0136    uint32_t cs_bit = CADENCE_SPI_CS_NONE;
0137    uint32_t config = regs->config;
0138 
0139   if (cs != SPI_NO_CS && cs < CADENCE_SPI_MAX_CHIPSELECTS) {
0140       cs_bit >>= (CADENCE_SPI_MAX_CHIPSELECTS - cs + 1);
0141   }
0142   config = CADENCE_SPI_CONFIG_CS_SET(config, cs_bit);
0143   bus->base.cs = cs;
0144 
0145   regs->config = config;
0146 }
0147 
0148 static uint32_t
0149 cadence_spi_baud_divider(cadence_spi_bus *bus, 
0150                 uint32_t speed_hz)
0151 {
0152   uint32_t div;
0153   uint32_t clk_in;
0154 
0155   clk_in = bus->clk_in;
0156 
0157   div = clk_in / speed_hz;
0158   div = fls((int) div);
0159 
0160   if (clk_in > (speed_hz << div)) {
0161       ++div;
0162   }
0163 
0164   /* The baud divider needs to be at least 4, i.e. 2^2 */
0165   div = MAX(2, div);
0166   
0167   /* 0b111 is the maximum possible divider value */
0168   div =  MIN(7, div-1);
0169   return div;
0170 }
0171 
0172 static void cadence_spi_config(
0173   cadence_spi_bus *bus,
0174   volatile cadence_spi *regs,
0175   uint32_t speed_hz,
0176   uint32_t mode,
0177   uint16_t delay_usecs,
0178   uint8_t  cs
0179 )
0180 {
0181   spi_bus *base = &bus->base;
0182   uint32_t config = regs->config;
0183   uint32_t delay;
0184 
0185   regs->spienable = 0;
0186   
0187   if ((mode & SPI_CPHA) != 0) {
0188     config |= CADENCE_SPI_CONFIG_CLK_PH;
0189   } else {
0190     config &= ~CADENCE_SPI_CONFIG_CLK_PH;
0191   }
0192 
0193   if ((mode & SPI_CPOL) != 0) {
0194     config |= CADENCE_SPI_CONFIG_CLK_POL;
0195   } else {
0196     config &= ~CADENCE_SPI_CONFIG_CLK_POL;
0197   }
0198 
0199   config = CADENCE_SPI_CONFIG_BAUD_DIV_SET(config,
0200           cadence_spi_baud_divider(bus, speed_hz));
0201 
0202   regs->config = config;
0203   cadence_spi_set_chipsel(bus, cs);
0204 
0205   delay = regs->delay;
0206   delay = CADENCE_SPI_DELAY_DBTWN_SET(delay, delay_usecs * bus->clk_per_usecs);
0207   regs->delay = delay;
0208 
0209   base->speed_hz = speed_hz;
0210   base->mode = mode;
0211   base->cs = cs;
0212 
0213 }
0214 
0215 static void
0216 cadence_spi_next_msg(cadence_spi_bus *bus, volatile cadence_spi *regs)
0217 {
0218   if (bus->msg_todo > 0) {
0219     const spi_ioc_transfer *msg;
0220     spi_bus *base = &bus->base;
0221 
0222     msg = bus->msg;
0223 
0224     if (
0225       msg->speed_hz != base->speed_hz
0226         || msg->mode != base->mode
0227         || msg->cs != base->cs
0228     ) {
0229       cadence_spi_config(
0230         bus,
0231         regs,
0232         msg->speed_hz,
0233         msg->mode,
0234         msg->delay_usecs,
0235         msg->cs
0236       );
0237     }
0238 
0239     if ((msg->mode & SPI_NO_CS) != 0) {
0240       cadence_spi_set_chipsel(bus, CADENCE_SPI_CS_NONE);
0241     }
0242 
0243     bus->todo = msg->len;
0244     bus->rx_buf = msg->rx_buf;
0245     bus->tx_buf = msg->tx_buf;
0246     cadence_spi_push(bus, regs);
0247     
0248     cadence_spi_disable_interrupts(regs);
0249     if (bus->todo < CADENCE_SPI_FIFO_SIZE) {
0250         /* if the msg fits into the FIFO for empty TX buffer */
0251         regs->txthreshold = 1;
0252 
0253     } else {
0254         /* if the msg does not fit refill tx_buf when the threshold is hit */
0255         regs->txthreshold = CADENCE_SPI_FIFO_SIZE / 2;
0256     }
0257     regs->irqenable = CADENCE_SPI_IXR_TXOW;
0258     regs->spienable = 1;
0259   } else {
0260     cadence_spi_done(bus);
0261   }
0262 }
0263 
0264 static void cadence_spi_interrupt(void *arg)
0265 {
0266   cadence_spi_bus *bus;
0267   volatile cadence_spi *regs;
0268 
0269   bus = arg;
0270   regs = bus->regs;
0271 
0272   /* The RXNEMPTY flag is sometimes not cleared fast
0273    * enough between 2 reads which could lead to
0274    * reading an extra byte erroneously. Therefore,
0275    * also check the in_transfer counter
0276    */
0277   while (cadence_spi_rx_fifo_not_empty(regs) 
0278          && bus->in_transfer > 0) {
0279     uint32_t val = regs->rxdata;
0280     if (bus->rx_buf != NULL) {
0281         *bus->rx_buf = (uint8_t)val;
0282         ++bus->rx_buf;
0283     }
0284     --bus->in_transfer;
0285   }
0286 
0287   if (bus->todo > 0) {
0288     cadence_spi_push(bus, regs);
0289   } else if (bus->in_transfer > 0) {
0290     /* Wait until all bytes have been transfered */
0291     regs->txthreshold = 1;
0292   } else {
0293     --bus->msg_todo;
0294     ++bus->msg;
0295     cadence_spi_next_msg(bus, regs);
0296   }
0297 }
0298 
0299 static int cadence_spi_check_messages(
0300   cadence_spi_bus *bus,
0301   const spi_ioc_transfer *msg,
0302   uint32_t size)
0303 {
0304   while(size > 0) {
0305     if (msg->bits_per_word != 8) {
0306       return -EINVAL;
0307     }
0308     if ((msg->mode &
0309         ~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0) {
0310       return -EINVAL;
0311     }
0312     if ((msg->mode & SPI_NO_CS) == 0 &&
0313         (msg->cs > CADENCE_SPI_MAX_CHIPSELECTS)) {
0314       return -EINVAL;
0315     }
0316 
0317     ++msg;
0318     --size;
0319   }
0320 
0321   return 0;
0322 }
0323 
0324 static int cadence_spi_transfer(
0325   spi_bus *base,
0326   const spi_ioc_transfer *msgs,
0327   uint32_t n
0328 )
0329 {
0330   cadence_spi_bus *bus;
0331   int rv;
0332 
0333   bus = (cadence_spi_bus *) base;
0334 
0335   rv = cadence_spi_check_messages(bus, msgs, n);
0336 
0337   if (rv == 0) {
0338     bus->msg_todo = n;
0339     bus->msg = &msgs[0];
0340     bus->task_id = rtems_task_self();
0341 
0342     cadence_spi_next_msg(bus, bus->regs);
0343     rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
0344     cadence_spi_set_chipsel(bus, CADENCE_SPI_CS_NONE);
0345   }
0346   return rv;
0347 }
0348 
0349 static void cadence_spi_destroy(spi_bus *base)
0350 {
0351   cadence_spi_bus *bus;
0352 
0353   bus = (cadence_spi_bus *) base;
0354   rtems_interrupt_handler_remove(bus->irq, cadence_spi_interrupt, bus);
0355   spi_bus_destroy_and_free(&bus->base);
0356 }
0357 
0358 static int cadence_spi_setup(spi_bus *base)
0359 {
0360   cadence_spi_bus *bus;
0361   uint32_t mode = base->mode;
0362 
0363   bus = (cadence_spi_bus *) base;
0364 
0365   /* Baud rate divider is at least 4 and at most 256 */
0366   if (
0367     base->speed_hz > base->max_speed_hz
0368       || base->speed_hz < (bus->clk_in / 256)
0369       || bus->base.bits_per_word > 8
0370   ) {
0371     return -EINVAL;
0372   }
0373 
0374   /* SPI_CS_HIGH and SPI_LOOP not supported */
0375   if ((mode & SPI_CS_HIGH) || (mode & SPI_LOOP)) {
0376       return -EINVAL;
0377   }
0378 
0379   cadence_spi_config(
0380     bus,
0381     bus->regs,
0382     base->speed_hz,
0383     base->mode,
0384     base->delay_usecs,
0385     base->cs
0386   );
0387   return 0;
0388 }
0389 
0390 int spi_bus_register_cadence(const char *bus_path,
0391         uintptr_t register_base,
0392         uint32_t input_clock,
0393         rtems_vector_number irq)
0394 {
0395   cadence_spi_bus *bus;
0396   spi_bus *base;
0397   int sc;
0398 
0399   bus = (cadence_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
0400   if (bus == NULL){
0401     return -1;
0402   }
0403 
0404   base = &bus->base;
0405   bus->regs = (volatile cadence_spi *) register_base;
0406   bus->clk_in = input_clock;
0407   bus->clk_per_usecs = input_clock / 1000000;
0408   bus->irq = irq;
0409 
0410   /* The minimum clock divider is 4 */
0411   base->max_speed_hz = (input_clock + 3) / 4;
0412   base->delay_usecs = 0;
0413   base->speed_hz = base->max_speed_hz;
0414   base->cs = SPI_NO_CS;
0415 
0416   cadence_spi_reset(bus);
0417   cadence_spi_config(
0418     bus,
0419     bus->regs,
0420     base->speed_hz,
0421     base->mode,
0422     base->delay_usecs,
0423     base->cs
0424     );
0425 
0426 
0427   sc = rtems_interrupt_handler_install(
0428     bus->irq,
0429     "CASPI",
0430     RTEMS_INTERRUPT_UNIQUE,
0431     cadence_spi_interrupt,
0432     bus
0433   );
0434   if (sc != RTEMS_SUCCESSFUL) {
0435     (*bus->base.destroy)(&bus->base);
0436     rtems_set_errno_and_return_minus_one(EAGAIN);
0437   }
0438 
0439   bus->base.transfer = cadence_spi_transfer;
0440   bus->base.destroy = cadence_spi_destroy;
0441   bus->base.setup = cadence_spi_setup;
0442 
0443   return spi_bus_register(&bus->base, bus_path);
0444 }