File indexing completed on 2025-05-11 08:24:05
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 #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
0097 regs->txthreshold = 1;
0098
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
0165 div = MAX(2, div);
0166
0167
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
0251 regs->txthreshold = 1;
0252
0253 } else {
0254
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
0273
0274
0275
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
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
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
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
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 }