File indexing completed on 2025-05-11 08:23:03
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
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
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
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
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