Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:03

0001 /**
0002  * @file
0003  *
0004  * @ingroup RTEMSBSPsARMLPC24XXSSP
0005  */
0006 
0007 /*
0008  * SPDX-License-Identifier: BSD-2-Clause
0009  *
0010  * Copyright (C) 2008, 2019 embedded brains GmbH & Co. KG
0011  *
0012  * Redistribution and use in source and binary forms, with or without
0013  * modification, are permitted provided that the following conditions
0014  * are met:
0015  * 1. Redistributions of source code must retain the above copyright
0016  *    notice, this list of conditions and the following disclaimer.
0017  * 2. Redistributions in binary form must reproduce the above copyright
0018  *    notice, this list of conditions and the following disclaimer in the
0019  *    documentation and/or other materials provided with the distribution.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0022  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0025  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0026  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0027  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0030  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0031  * POSSIBILITY OF SUCH DAMAGE.
0032  */
0033 
0034 #include <bsp/ssp.h>
0035 #include <bsp.h>
0036 #include <bsp/io.h>
0037 #include <bsp/irq.h>
0038 #include <bsp/lpc24xx.h>
0039 
0040 #include <rtems/score/assert.h>
0041 
0042 #include <dev/spi/spi.h>
0043 
0044 typedef struct {
0045   spi_bus base;
0046   volatile lpc24xx_ssp *regs;
0047   size_t tx_todo;
0048   const uint8_t *tx_buf;
0049   size_t tx_inc;
0050   size_t rx_todo;
0051   uint8_t *rx_buf;
0052   size_t rx_inc;
0053   const spi_ioc_transfer *msg;
0054   uint32_t msg_todo;
0055   int msg_error;
0056   rtems_binary_semaphore sem;
0057   lpc24xx_module module;
0058   rtems_vector_number irq;
0059 } lpc24xx_ssp_bus;
0060 
0061 typedef struct {
0062   volatile lpc24xx_ssp *regs;
0063   lpc24xx_module module;
0064   rtems_vector_number irq;
0065 } lpc24xx_ssp_config;
0066 
0067 static uint8_t lpc24xx_ssp_trash;
0068 
0069 static const uint8_t lpc24xx_ssp_idle = 0xff;
0070 
0071 static void lpc24xx_ssp_done(lpc24xx_ssp_bus *bus, int error)
0072 {
0073   bus->msg_error = error;
0074   rtems_binary_semaphore_post(&bus->sem);
0075 }
0076 
0077 static int lpc24xx_ssp_do_setup(
0078   lpc24xx_ssp_bus *bus,
0079   uint32_t speed_hz,
0080   uint32_t mode
0081 )
0082 {
0083   volatile lpc24xx_ssp *regs;
0084   uint32_t clk;
0085   uint32_t scr_plus_one;
0086   uint32_t cr0;
0087 
0088   if (speed_hz > bus->base.max_speed_hz || speed_hz == 0) {
0089     return -EINVAL;
0090   }
0091 
0092   if ((mode & ~(SPI_CPOL | SPI_CPHA)) != 0) {
0093     return -EINVAL;
0094   }
0095 
0096   regs = bus->regs;
0097   clk = bus->base.max_speed_hz;
0098   scr_plus_one = (clk + speed_hz - 1) / speed_hz;
0099 
0100   if (scr_plus_one > 256) {
0101     uint32_t pre;
0102 
0103     pre = (scr_plus_one + 255) / 256;
0104 
0105     if (pre <= 127) {
0106       scr_plus_one = (clk / pre + speed_hz - 1) / speed_hz;
0107     } else {
0108       pre = 127;
0109       scr_plus_one = 256;
0110     }
0111 
0112     regs->cpsr = 2 * pre;
0113   }
0114 
0115   cr0 = SET_SSP_CR0_DSS(0, 0x7) | SET_SSP_CR0_SCR(0, scr_plus_one - 1);
0116 
0117   if ((mode & SPI_CPOL) != 0) {
0118     cr0 |= SSP_CR0_CPOL;
0119   }
0120 
0121   if ((mode & SPI_CPHA) != 0) {
0122     cr0 |= SSP_CR0_CPHA;
0123   }
0124 
0125   regs->cr0 = cr0;
0126 
0127   bus->base.speed_hz = speed_hz;
0128   bus->base.mode = mode;
0129   return 0;
0130 }
0131 
0132 static bool lpc24xx_ssp_msg_setup(
0133   lpc24xx_ssp_bus *bus,
0134   const spi_ioc_transfer *msg
0135 )
0136 {
0137   if (msg->cs_change == 0 || msg->bits_per_word != 8) {
0138     lpc24xx_ssp_done(bus, -EINVAL);
0139     return false;
0140   }
0141 
0142   if (msg->speed_hz != bus->base.speed_hz || msg->mode != bus->base.mode) {
0143     int error;
0144 
0145     error = lpc24xx_ssp_do_setup(bus, msg->speed_hz, msg->mode);
0146     if (error != 0) {
0147       lpc24xx_ssp_done(bus, error);
0148       return false;
0149     }
0150   }
0151 
0152   bus->tx_todo = msg->len;
0153   bus->rx_todo = msg->len;
0154 
0155   if (msg->tx_buf != NULL) {
0156     bus->tx_buf = msg->tx_buf;
0157     bus->tx_inc = 1;
0158   } else {
0159     bus->tx_buf = &lpc24xx_ssp_idle;
0160     bus->tx_inc = 0;
0161   }
0162 
0163   if (msg->rx_buf != NULL) {
0164     bus->rx_buf = msg->rx_buf;
0165     bus->rx_inc = 1;
0166   } else {
0167     bus->rx_buf = &lpc24xx_ssp_trash;
0168     bus->rx_inc = 0;
0169   }
0170 
0171   return true;
0172 }
0173 
0174 static bool lpc24xx_ssp_do_tx_and_rx(
0175   lpc24xx_ssp_bus *bus,
0176   volatile lpc24xx_ssp *regs,
0177   uint32_t sr
0178 )
0179 {
0180   size_t tx_todo;
0181   const uint8_t *tx_buf;
0182   size_t tx_inc;
0183   size_t rx_todo;
0184   uint8_t *rx_buf;
0185   size_t rx_inc;
0186   uint32_t imsc;
0187 
0188   tx_todo = bus->tx_todo;
0189   tx_buf = bus->tx_buf;
0190   tx_inc = bus->tx_inc;
0191   rx_todo = bus->rx_todo;
0192   rx_buf = bus->rx_buf;
0193   rx_inc = bus->rx_inc;
0194 
0195   while (tx_todo > 0 && (sr & SSP_SR_TNF) != 0) {
0196     regs->dr = *tx_buf;
0197     --tx_todo;
0198     tx_buf += tx_inc;
0199 
0200     if (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
0201       *rx_buf = regs->dr;
0202       --rx_todo;
0203       rx_buf += rx_inc;
0204     }
0205 
0206     sr = regs->sr;
0207   }
0208 
0209   while (rx_todo > 0 && (sr & SSP_SR_RNE) != 0) {
0210     *rx_buf = regs->dr;
0211     --rx_todo;
0212     rx_buf += rx_inc;
0213 
0214     sr = regs->sr;
0215   }
0216 
0217   bus->tx_todo = tx_todo;
0218   bus->tx_buf = tx_buf;
0219   bus->rx_todo = rx_todo;
0220   bus->rx_buf = rx_buf;
0221 
0222   imsc = 0;
0223 
0224   if (tx_todo > 0) {
0225     imsc |= SSP_IMSC_TXIM;
0226   } else if (rx_todo > 0) {
0227     imsc |= SSP_IMSC_RXIM | SSP_IMSC_RTIM;
0228     regs->icr = SSP_ICR_RTRIS;
0229   }
0230 
0231   regs->imsc = imsc;
0232 
0233   return tx_todo == 0 && rx_todo == 0;
0234 }
0235 
0236 static void lpc24xx_ssp_start(
0237   lpc24xx_ssp_bus *bus,
0238   const spi_ioc_transfer *msg
0239 )
0240 {
0241   while (true) {
0242     if (lpc24xx_ssp_msg_setup(bus, msg)) {
0243       volatile lpc24xx_ssp *regs;
0244       uint32_t sr;
0245       bool next_msg;
0246 
0247       regs = bus->regs;
0248       sr = regs->sr;
0249 
0250       if ((sr & (SSP_SR_RNE | SSP_SR_TFE)) != SSP_SR_TFE) {
0251         lpc24xx_ssp_done(bus, -EIO);
0252         break;
0253       }
0254 
0255       next_msg = lpc24xx_ssp_do_tx_and_rx(bus, regs, sr);
0256       if (!next_msg) {
0257         break;
0258       }
0259 
0260       --bus->msg_todo;
0261 
0262       if (bus->msg_todo == 0) {
0263         lpc24xx_ssp_done(bus, 0);
0264         break;
0265       }
0266 
0267       ++msg;
0268       bus->msg = msg;
0269     } else {
0270       break;
0271     }
0272   }
0273 }
0274 
0275 static void lpc24xx_ssp_interrupt(void *arg)
0276 {
0277   lpc24xx_ssp_bus *bus;
0278   volatile lpc24xx_ssp *regs;
0279 
0280   bus = arg;
0281   regs = bus->regs;
0282 
0283   while (true) {
0284     if (lpc24xx_ssp_do_tx_and_rx(bus, regs, regs->sr)) {
0285       --bus->msg_todo;
0286 
0287       if (bus->msg_todo > 0) {
0288         ++bus->msg;
0289 
0290         if (!lpc24xx_ssp_msg_setup(bus, bus->msg)) {
0291           break;
0292         }
0293       } else {
0294         lpc24xx_ssp_done(bus, 0);
0295         break;
0296       }
0297     } else {
0298       break;
0299     }
0300   }
0301 }
0302 
0303 static int lpc24xx_ssp_transfer(
0304   spi_bus *base,
0305   const spi_ioc_transfer *msgs,
0306   uint32_t msg_count
0307 )
0308 {
0309   lpc24xx_ssp_bus *bus;
0310 
0311   if (msg_count == 0) {
0312     return 0;
0313   }
0314 
0315   bus = (lpc24xx_ssp_bus *) base;
0316   bus->msg = msgs;
0317   bus->msg_todo = msg_count;
0318   lpc24xx_ssp_start(bus, msgs);
0319   rtems_binary_semaphore_wait(&bus->sem);
0320 
0321   return bus->msg_error;
0322 }
0323 
0324 static void lpc24xx_ssp_destroy(spi_bus *base)
0325 {
0326   lpc24xx_ssp_bus *bus;
0327   rtems_status_code sc;
0328 
0329   bus = (lpc24xx_ssp_bus *) base;
0330 
0331   sc = rtems_interrupt_handler_remove(
0332     bus->irq,
0333     lpc24xx_ssp_interrupt,
0334     bus
0335   );
0336   _Assert(sc == RTEMS_SUCCESSFUL);
0337   (void) sc;
0338 
0339   /* Disable SSP module */
0340   bus->regs->cr1 = 0;
0341 
0342   sc = lpc24xx_module_disable(bus->module);
0343   _Assert(sc == RTEMS_SUCCESSFUL);
0344   (void) sc;
0345 
0346   rtems_binary_semaphore_destroy(&bus->sem);
0347   spi_bus_destroy_and_free(&bus->base);
0348 }
0349 
0350 static int lpc24xx_ssp_setup(spi_bus *base)
0351 {
0352   lpc24xx_ssp_bus *bus;
0353 
0354   bus = (lpc24xx_ssp_bus *) base;
0355 
0356   if (bus->base.bits_per_word != 8) {
0357     return -EINVAL;
0358   }
0359 
0360   return lpc24xx_ssp_do_setup(bus, bus->base.speed_hz, bus->base.mode);
0361 }
0362 
0363 static int lpc24xx_ssp_init(lpc24xx_ssp_bus *bus)
0364 {
0365   rtems_status_code sc;
0366 
0367   sc = lpc24xx_module_enable(bus->module, LPC24XX_MODULE_PCLK_DEFAULT);
0368   _Assert(sc == RTEMS_SUCCESSFUL);
0369   (void) sc;
0370 
0371   /* Disable SSP module */
0372   bus->regs->cr1 = 0;
0373 
0374   sc = rtems_interrupt_handler_install(
0375     bus->irq,
0376     "SSP",
0377     RTEMS_INTERRUPT_UNIQUE,
0378     lpc24xx_ssp_interrupt,
0379     bus
0380   );
0381   if (sc != RTEMS_SUCCESSFUL) {
0382     return EAGAIN;
0383   }
0384 
0385   rtems_binary_semaphore_init(&bus->sem, "SSP");
0386 
0387   /* Initialize SSP module */
0388   bus->regs->dmacr = 0;
0389   bus->regs->imsc = 0;
0390   bus->regs->cpsr = 2;
0391   bus->regs->cr0 = SET_SSP_CR0_DSS(0, 0x7);
0392   bus->regs->cr1 = SSP_CR1_SSE;
0393 
0394   return 0;
0395 }
0396 
0397 static int spi_bus_register_lpc24xx_ssp(
0398   const char *bus_path,
0399   const lpc24xx_ssp_config *config
0400 )
0401 {
0402   lpc24xx_ssp_bus *bus;
0403   int eno;
0404 
0405   bus = (lpc24xx_ssp_bus *) spi_bus_alloc_and_init(sizeof(*bus));
0406   if (bus == NULL) {
0407     return -1;
0408   }
0409 
0410   bus->base.max_speed_hz = LPC24XX_PCLK / 2;
0411   bus->base.bits_per_word = 8;
0412   bus->base.speed_hz = bus->base.max_speed_hz;
0413   bus->regs = config->regs;
0414   bus->module = config->module;
0415   bus->irq = config->irq;
0416 
0417   eno = lpc24xx_ssp_init(bus);
0418   if (eno != 0) {
0419     (*bus->base.destroy)(&bus->base);
0420     rtems_set_errno_and_return_minus_one(eno);
0421   }
0422 
0423   bus->base.transfer = lpc24xx_ssp_transfer;
0424   bus->base.destroy = lpc24xx_ssp_destroy;
0425   bus->base.setup = lpc24xx_ssp_setup;
0426 
0427   return spi_bus_register(&bus->base, bus_path);
0428 }
0429 
0430 int lpc24xx_register_ssp_0(void)
0431 {
0432   static const lpc24xx_ssp_config config = {
0433     .regs = (volatile lpc24xx_ssp *) SSP0_BASE_ADDR,
0434     .module = LPC24XX_MODULE_SSP_0,
0435     .irq = LPC24XX_IRQ_SPI_SSP_0
0436   };
0437 
0438   return spi_bus_register_lpc24xx_ssp(
0439     LPC24XX_SSP_0_BUS_PATH,
0440     &config
0441   );
0442 }
0443 
0444 int lpc24xx_register_ssp_1(void)
0445 {
0446   static const lpc24xx_ssp_config config = {
0447     .regs = (volatile lpc24xx_ssp *) SSP1_BASE_ADDR,
0448     .module = LPC24XX_MODULE_SSP_1,
0449     .irq = LPC24XX_IRQ_SSP_1
0450   };
0451 
0452   return spi_bus_register_lpc24xx_ssp(
0453     LPC24XX_SSP_2_BUS_PATH,
0454     &config
0455   );
0456 }
0457 
0458 #ifdef ARM_MULTILIB_ARCH_V7M
0459 int lpc24xx_register_ssp_2(void)
0460 {
0461   static const lpc24xx_ssp_config config = {
0462     .regs = (volatile lpc24xx_ssp *) SSP2_BASE_ADDR,
0463     .module = LPC24XX_MODULE_SSP_2,
0464     .irq = LPC24XX_IRQ_SSP_2
0465   };
0466 
0467   return spi_bus_register_lpc24xx_ssp(
0468     LPC24XX_SSP_2_BUS_PATH,
0469     &config
0470   );
0471 }
0472 #endif