File indexing completed on 2025-05-11 08:23:04
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include <bsp.h>
0025 #include <bsp/raspberrypi.h>
0026 #include <bsp/gpio.h>
0027 #include <bsp/rpi-gpio.h>
0028 #include <bsp/irq.h>
0029 #include <bsp/spi.h>
0030 #include <assert.h>
0031
0032 #define SPI_POLLING(condition) \
0033 while ( condition ) { \
0034 ; \
0035 }
0036
0037
0038
0039
0040
0041
0042 typedef struct
0043 {
0044 int initialized;
0045 uint8_t bytes_per_char;
0046
0047
0048
0049 uint8_t bit_shift;
0050 uint32_t dummy_char;
0051 uint32_t current_slave_addr;
0052 rtems_id task_id;
0053 int irq_write;
0054 } rpi_spi_softc_t;
0055
0056
0057
0058
0059
0060
0061 typedef struct
0062 {
0063 rtems_libi2c_bus_t bus_desc;
0064 rpi_spi_softc_t softc;
0065 } rpi_spi_desc_t;
0066
0067
0068
0069
0070 static bool bidirectional = false;
0071
0072
0073
0074 static rtems_status_code rpi_spi_calculate_clock_divider(
0075 uint32_t clock_hz,
0076 uint16_t *clock_divider
0077 ) {
0078 uint16_t divider;
0079 uint32_t clock_rate;
0080
0081 assert( clock_hz > 0 );
0082
0083
0084 divider = GPU_CORE_CLOCK_RATE / clock_hz;
0085
0086
0087
0088 --divider;
0089
0090 divider |= (divider >> 1);
0091 divider |= (divider >> 2);
0092 divider |= (divider >> 4);
0093 divider |= (divider >> 8);
0094
0095 ++divider;
0096
0097 clock_rate = GPU_CORE_CLOCK_RATE / divider;
0098
0099
0100
0101 while ( clock_rate > clock_hz ) {
0102 divider = (divider << 1);
0103
0104 clock_rate = GPU_CORE_CLOCK_RATE / divider;
0105 }
0106
0107 *clock_divider = divider;
0108
0109 return RTEMS_SUCCESSFUL;
0110 }
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 static rtems_status_code rpi_spi_set_tfr_mode(
0125 rtems_libi2c_bus_t *bushdl,
0126 const rtems_libi2c_tfr_mode_t *tfr_mode
0127 ) {
0128 rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
0129 rtems_status_code sc = RTEMS_SUCCESSFUL;
0130 uint16_t clock_divider;
0131
0132
0133 softc_ptr->dummy_char = tfr_mode->idle_char;
0134
0135
0136 sc = rpi_spi_calculate_clock_divider(tfr_mode->baudrate, &clock_divider);
0137
0138 if ( sc != RTEMS_SUCCESSFUL ) {
0139 return sc;
0140 }
0141
0142
0143 BCM2835_REG(BCM2835_SPI_CLK) = clock_divider;
0144
0145
0146
0147 switch ( tfr_mode->bits_per_char ) {
0148 case 8:
0149 case 16:
0150 case 24:
0151 case 32:
0152 softc_ptr->bytes_per_char = tfr_mode->bits_per_char / 8;
0153 break;
0154
0155 default:
0156 return RTEMS_INVALID_NUMBER;
0157 }
0158
0159
0160
0161 if ( tfr_mode->lsb_first ) {
0162 softc_ptr->bit_shift = 32 - tfr_mode->bits_per_char;
0163 }
0164
0165 else {
0166 softc_ptr->bit_shift = 0;
0167 }
0168
0169
0170
0171 if ( tfr_mode->clock_inv ) {
0172
0173 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 3);
0174 }
0175 else {
0176
0177 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 3);
0178 }
0179
0180
0181
0182
0183 if ( tfr_mode->clock_phs ) {
0184
0185 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 2);
0186 }
0187 else {
0188
0189 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 2);
0190 }
0191
0192 return sc;
0193 }
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208 static int rpi_spi_read_write(
0209 rtems_libi2c_bus_t * bushdl,
0210 unsigned char *rd_buf,
0211 const unsigned char *wr_buf,
0212 int buffer_size
0213 ) {
0214 rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
0215
0216 uint8_t bytes_per_char = softc_ptr->bytes_per_char;
0217 uint8_t bit_shift = softc_ptr->bit_shift;
0218 uint32_t dummy_char = softc_ptr->dummy_char;
0219
0220 uint32_t bytes_sent = buffer_size;
0221 uint32_t fifo_data;
0222
0223
0224 BCM2835_REG(BCM2835_SPI_CS) |= (3 << 4);
0225
0226
0227 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 7);
0228
0229
0230 #if SPI_IO_MODE == 1
0231 softc_ptr->irq_write = 1;
0232
0233 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
0234
0235 if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
0236 rtems_event_transient_clear();
0237
0238 return -1;
0239 }
0240
0241
0242 #else
0243
0244
0245 SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) == 0);
0246 #endif
0247
0248
0249 while ( buffer_size >= bytes_per_char ) {
0250
0251 if ( rd_buf != NULL ) {
0252 BCM2835_REG(BCM2835_SPI_FIFO) = dummy_char;
0253 }
0254
0255 else {
0256 switch ( bytes_per_char ) {
0257 case 1:
0258 BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFF) << bit_shift);
0259 break;
0260
0261 case 2:
0262 BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFF) << bit_shift);
0263 break;
0264
0265 case 3:
0266 BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFFFF) << bit_shift);
0267 break;
0268
0269 case 4:
0270 BCM2835_REG(BCM2835_SPI_FIFO) = ((*wr_buf) << bit_shift);
0271 break;
0272
0273 default:
0274 return -1;
0275 }
0276
0277 wr_buf += bytes_per_char;
0278
0279 buffer_size -= bytes_per_char;
0280 }
0281
0282
0283 if ( bidirectional ) {
0284
0285 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 12);
0286 }
0287
0288
0289 #if SPI_IO_MODE == 1
0290 softc_ptr->irq_write = 0;
0291
0292 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
0293
0294 if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
0295 rtems_event_transient_clear();
0296
0297 return -1;
0298 }
0299
0300
0301 #else
0302
0303 SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0);
0304
0305
0306
0307 SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) == 0);
0308 #endif
0309
0310
0311 if ( rd_buf == NULL ) {
0312 fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF;
0313 }
0314
0315
0316
0317 if ( rd_buf != NULL ) {
0318 switch ( bytes_per_char ) {
0319 case 1:
0320 fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF;
0321 (*rd_buf) = (fifo_data >> bit_shift);
0322 break;
0323
0324 case 2:
0325 fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFF;
0326 (*rd_buf) = (fifo_data >> bit_shift);
0327 break;
0328
0329 case 3:
0330 fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFFFF;
0331 (*rd_buf) = (fifo_data >> bit_shift);
0332 break;
0333
0334 case 4:
0335 fifo_data = BCM2835_REG(BCM2835_SPI_FIFO);
0336 (*rd_buf) = (fifo_data >> bit_shift);
0337 break;
0338
0339 default:
0340 return -1;
0341 }
0342
0343 rd_buf += bytes_per_char;
0344
0345 buffer_size -= bytes_per_char;
0346 }
0347
0348
0349 if ( bidirectional ) {
0350
0351 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 12);
0352 }
0353 }
0354
0355
0356 #if SPI_IO_MODE == 1
0357 softc_ptr->irq_write = 1;
0358
0359 BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
0360
0361 if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
0362 rtems_event_transient_clear();
0363
0364 return -1;
0365 }
0366
0367
0368 #else
0369
0370 SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0);
0371 #endif
0372
0373 bytes_sent -= buffer_size;
0374
0375 return bytes_sent;
0376 }
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402 #if SPI_IO_MODE == 1
0403 static void spi_handler(void* arg)
0404 {
0405 rpi_spi_softc_t *softc_ptr = (rpi_spi_softc_t *) arg;
0406
0407
0408
0409
0410 if (
0411 ( softc_ptr->irq_write == 1 &&
0412 (BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) != 0
0413 ) ||
0414 ( softc_ptr->irq_write == 0 &&
0415 (BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) != 0
0416 )
0417 ) {
0418
0419 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 9);
0420
0421
0422 rtems_event_transient_send(softc_ptr->task_id);
0423 }
0424 }
0425 #endif
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435
0436 static rtems_status_code rpi_libi2c_spi_init(rtems_libi2c_bus_t * bushdl)
0437 {
0438 rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
0439 rtems_status_code sc = RTEMS_SUCCESSFUL;
0440
0441 if ( softc_ptr->initialized == 1 ) {
0442 return sc;
0443 }
0444
0445 softc_ptr->initialized = 1;
0446
0447
0448 #if SPI_IO_MODE == 1
0449 softc_ptr->task_id = rtems_task_self();
0450
0451 sc = rtems_interrupt_handler_install(
0452 BCM2835_IRQ_ID_SPI,
0453 NULL,
0454 RTEMS_INTERRUPT_UNIQUE,
0455 (rtems_interrupt_handler) spi_handler,
0456 softc_ptr
0457 );
0458 #endif
0459
0460 return sc;
0461 }
0462
0463
0464
0465
0466
0467
0468
0469
0470
0471
0472 static rtems_status_code rpi_libi2c_spi_send_start(rtems_libi2c_bus_t * bushdl)
0473 {
0474 return RTEMS_SUCCESSFUL;
0475 }
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487 static rtems_status_code rpi_libi2c_spi_stop(rtems_libi2c_bus_t * bushdl)
0488 {
0489 rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
0490
0491 uint32_t addr = softc_ptr->current_slave_addr;
0492 uint32_t chip_select_bit = 21 + addr;
0493
0494
0495 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 7);
0496
0497
0498 switch ( addr ) {
0499 case 0:
0500 case 1:
0501 BCM2835_REG(BCM2835_SPI_CS) |= (1 << chip_select_bit);
0502
0503 break;
0504
0505 default:
0506 return RTEMS_INVALID_ADDRESS;
0507 }
0508
0509 return RTEMS_SUCCESSFUL;
0510 }
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524 static rtems_status_code rpi_libi2c_spi_send_addr(
0525 rtems_libi2c_bus_t * bushdl,
0526 uint32_t addr,
0527 int rw
0528 ) {
0529 rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
0530
0531
0532
0533 uint32_t chip_select_bit = 21 + addr;
0534
0535
0536
0537 softc_ptr->current_slave_addr = addr;
0538
0539
0540 switch ( addr ) {
0541 case 0:
0542 case 1:
0543 BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << chip_select_bit);
0544 break;
0545
0546 default:
0547 return RTEMS_INVALID_ADDRESS;
0548 }
0549
0550 return RTEMS_SUCCESSFUL;
0551 }
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564 static int rpi_libi2c_spi_read_bytes(
0565 rtems_libi2c_bus_t * bushdl,
0566 unsigned char *bytes,
0567 int nbytes
0568 ) {
0569 return rpi_spi_read_write(bushdl, bytes, NULL, nbytes);
0570 }
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584 static int rpi_libi2c_spi_write_bytes(
0585 rtems_libi2c_bus_t * bushdl,
0586 unsigned char *bytes,
0587 int nbytes
0588 ) {
0589 return rpi_spi_read_write(bushdl, NULL, bytes, nbytes);
0590 }
0591
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604
0605 static int rpi_libi2c_spi_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg)
0606 {
0607 switch ( cmd ) {
0608 case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
0609 return rpi_spi_set_tfr_mode(
0610 bushdl,
0611 (const rtems_libi2c_tfr_mode_t *)arg
0612 );
0613 default:
0614 return -1;
0615 }
0616
0617 return 0;
0618 }
0619
0620 static rtems_libi2c_bus_ops_t rpi_spi_ops = {
0621 .init = rpi_libi2c_spi_init,
0622 .send_start = rpi_libi2c_spi_send_start,
0623 .send_stop = rpi_libi2c_spi_stop,
0624 .send_addr = rpi_libi2c_spi_send_addr,
0625 .read_bytes = rpi_libi2c_spi_read_bytes,
0626 .write_bytes = rpi_libi2c_spi_write_bytes,
0627 .ioctl = rpi_libi2c_spi_ioctl
0628 };
0629
0630 static rpi_spi_desc_t rpi_spi_bus_desc = {
0631 {
0632 .ops = &rpi_spi_ops,
0633 .size = sizeof(rpi_spi_bus_desc)
0634 },
0635 {
0636 .initialized = 0
0637 }
0638 };
0639
0640 int rpi_spi_init(bool bidirectional_mode)
0641 {
0642
0643 rtems_libi2c_initialize();
0644
0645
0646 rtems_gpio_initialize();
0647
0648 assert ( rpi_gpio_select_spi() == RTEMS_SUCCESSFUL );
0649
0650 bidirectional = bidirectional_mode;
0651
0652
0653 BCM2835_REG(BCM2835_SPI_CS) = (3 << 4);
0654
0655
0656 return rtems_libi2c_register_bus("/dev/spi", &(rpi_spi_bus_desc.bus_desc));
0657 }