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 #include <dev/serial/arm-pl011.h>
0029 #include <bspopts.h>
0030
0031 static inline int arm_pl011_read_char(volatile pl011_base *regs_base)
0032 {
0033 return PL011_UARTDR_DATA_GET(regs_base->uartdr);
0034 }
0035
0036 static inline void arm_pl011_write_char(
0037 volatile pl011_base *regs_base,
0038 const char ch
0039 )
0040 {
0041 regs_base->uartdr = PL011_UARTDR_DATA_SET(regs_base->uartdr, ch);
0042 }
0043
0044 static inline bool arm_pl011_is_rxfifo_empty(volatile pl011_base *regs_base)
0045 {
0046 return (regs_base->uartfr & PL011_UARTFR_RXFE) != 0;
0047 }
0048
0049 static inline bool arm_pl011_is_txfifo_full(volatile pl011_base *regs_base)
0050 {
0051 return (regs_base->uartfr & PL011_UARTFR_TXFF) != 0;
0052 }
0053
0054 volatile arm_pl011_uart *arm_pl011_get_regs(rtems_termios_device_context *base)
0055 {
0056 return ((arm_pl011_context *)base)->regs;
0057 }
0058
0059 bool arm_pl011_probe(rtems_termios_device_context *base)
0060 {
0061 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0062
0063 regs->base.uartlcr_h = PL011_UARTLCR_H_WLEN(PL011_UARTLCR_H_WLEN_8);
0064 regs->base.uartcr = PL011_UARTCR_RXE
0065 | PL011_UARTCR_TXE
0066 | PL011_UARTCR_UARTEN;
0067
0068 return true;
0069 }
0070
0071 static void arm_pl011_flush_fifos(const arm_pl011_context *context)
0072 {
0073 volatile pl011_base *regs = (volatile pl011_base *) context->regs;
0074
0075 regs->uartlcr_h &= ~PL011_UARTLCR_H_FEN;
0076 regs->uartlcr_h |= PL011_UARTLCR_H_FEN;
0077 }
0078
0079 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0080 static inline void arm_pl011_clear_irq(
0081 volatile pl011_base *regs_base,
0082 const uint32_t irq_mask
0083 )
0084 {
0085
0086 regs_base->uarticr = irq_mask;
0087 }
0088
0089 static inline void arm_pl011_enable_irq(
0090 volatile pl011_base *regs_base,
0091 const uint32_t irq_mask
0092 )
0093 {
0094 regs_base->uartimsc |= irq_mask;
0095 }
0096 #endif
0097
0098 static inline void arm_pl011_disable_irq(
0099 volatile pl011_base *regs_base,
0100 uint32_t irq_mask
0101 )
0102 {
0103 regs_base->uartimsc &= ~irq_mask;
0104 }
0105
0106 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0107 static void arm_pl011_irq_handler(void *arg)
0108 {
0109 arm_pl011_context *context = (void *) arg;
0110 volatile arm_pl011_uart *regs =
0111 arm_pl011_get_regs((rtems_termios_device_context *) context);
0112 const uint32_t irqs = regs->base.uartmis;
0113 char buffer[ARM_PL011_FIFO_DEPTH];
0114
0115 uint32_t rx_irq_mask = PL011_UARTI_RTI | PL011_UARTI_RXI;
0116 if ((irqs & rx_irq_mask) != 0) {
0117 arm_pl011_clear_irq(®s->base, rx_irq_mask);
0118
0119 unsigned int i = 0;
0120 while (i < sizeof(buffer) && !arm_pl011_is_rxfifo_empty(®s->base)) {
0121 buffer[i] = arm_pl011_read_char(®s->base);
0122 i++;
0123 }
0124
0125 (void) rtems_termios_enqueue_raw_characters(context->tty, buffer, i);
0126 }
0127
0128
0129
0130
0131
0132 if ((irqs & PL011_UARTI_TXI) != 0) {
0133
0134
0135
0136
0137 if (context->needs_sw_triggered_tx_irq == true) {
0138 context->needs_sw_triggered_tx_irq = false;
0139 }
0140
0141 (void) rtems_termios_dequeue_characters(context->tty,
0142 context->tx_queued_chars);
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152 arm_pl011_clear_irq(®s->base, PL011_UARTI_TXI);
0153 }
0154 }
0155
0156 static bool arm_pl011_first_open_irq(
0157 rtems_termios_device_context *base,
0158 arm_pl011_context *context
0159 )
0160 {
0161 rtems_status_code sc;
0162 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0163
0164 regs->base.uartifls =
0165 PL011_UARTIFLS_TXIFLSEL_SET(regs->base.uartifls, TXFIFO_IRQ_TRIGGER_LEVEL);
0166 regs->base.uartifls =
0167 PL011_UARTIFLS_RXIFLSEL_SET(regs->base.uartifls, RXFIFO_IRQ_TRIGGER_LEVEL);
0168 regs->base.uartlcr_h |= PL011_UARTLCR_H_FEN;
0169 arm_pl011_disable_irq(®s->base, PL011_UARTI_MASK);
0170 sc = rtems_interrupt_handler_install(
0171 context->irq,
0172 "UART",
0173 RTEMS_INTERRUPT_SHARED,
0174 arm_pl011_irq_handler,
0175 context
0176 );
0177 if (sc != RTEMS_SUCCESSFUL) {
0178 return false;
0179 }
0180
0181
0182 arm_pl011_clear_irq(®s->base, PL011_UARTI_MASK);
0183 arm_pl011_enable_irq(®s->base, PL011_UARTI_RTI | PL011_UARTI_RXI);
0184 return RTEMS_SUCCESSFUL;
0185 }
0186 #endif
0187
0188 int arm_pl011_compute_baudrate_params(
0189 uint32_t *ibrd,
0190 uint32_t *fbrd,
0191 const uint32_t baudrate,
0192 const uint32_t clock,
0193 const unsigned short max_error
0194 )
0195 {
0196 uint16_t scalar;
0197 uint32_t computed_bauddiv, computed_baudrate, baud_error;
0198 uint64_t scaled_bauddiv;
0199 unsigned short round_off, percent_error;
0200
0201
0202
0203
0204
0205 *ibrd = clock / 16 / baudrate;
0206 if (*ibrd < 1 || *ibrd > PL011_UARTIBRD_BAUD_DIVINT_MASK) {
0207 return 2;
0208 }
0209
0210
0211 scalar = 1 << (PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH + 1);
0212 scaled_bauddiv =
0213 ((uint64_t) clock * scalar) / 16 / baudrate;
0214 round_off = scaled_bauddiv & 0x1;
0215 *fbrd =
0216 ((scaled_bauddiv >> 1) & PL011_UARTFBRD_BAUD_DIVFRAC_MASK) + round_off;
0217
0218
0219 computed_bauddiv =
0220 (*ibrd << PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH) | *fbrd;
0221 computed_baudrate =
0222 ((uint64_t) clock << PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH)
0223 / 16 / computed_bauddiv;
0224
0225 baud_error = computed_baudrate - baudrate;
0226 if (baudrate > computed_baudrate) {
0227 baud_error = baudrate - computed_baudrate;
0228 }
0229
0230 percent_error = (baud_error * 100) / baudrate;
0231 if (percent_error >= max_error) {
0232 return 1;
0233 }
0234
0235 return 0;
0236 }
0237
0238 static uint32_t arm_pl011_set_lcrh(const struct termios *term)
0239 {
0240
0241 uint32_t lcrh = PL011_UARTLCR_H_FEN;
0242
0243
0244 if ((term->c_cflag & PARENB) != 0) {
0245 lcrh |= PL011_UARTLCR_H_PEN;
0246 if ((term->c_cflag & PARODD) == 0) {
0247 lcrh |= PL011_UARTLCR_H_EPS;
0248 }
0249 }
0250
0251
0252 switch (term->c_cflag & CSIZE) {
0253 case CS5:
0254 lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_5);
0255 break;
0256 case CS6:
0257 lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_6);
0258 break;
0259 case CS7:
0260 lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_7);
0261 break;
0262 case CS8:
0263 default:
0264 lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_8);
0265 }
0266
0267
0268 if ((term->c_cflag & CSTOPB) != 0) {
0269
0270 lcrh |= PL011_UARTLCR_H_STP2;
0271 }
0272 return lcrh;
0273 }
0274
0275 static uint32_t arm_pl011_set_cr(const struct termios *term, volatile pl011_base *regs)
0276 {
0277 uint32_t cr = regs->uartcr;
0278
0279
0280
0281
0282 cr &= ~(PL011_UARTCR_CTSEN | PL011_UARTCR_RTSEN);
0283 if ((term->c_cflag & CCTS_OFLOW) != 0) {
0284 cr |= PL011_UARTCR_CTSEN;
0285 }
0286 if ((term->c_cflag & CRTS_IFLOW) != 0) {
0287 cr |= PL011_UARTCR_RTSEN;
0288 }
0289
0290
0291 if ((term->c_cflag & CREAD) != 0) {
0292 cr |= PL011_UARTCR_RXE;
0293 }
0294
0295
0296 cr |= PL011_UARTCR_UARTEN | PL011_UARTCR_TXE;
0297
0298 return cr;
0299 }
0300
0301 static bool arm_pl011_set_attributes(
0302 rtems_termios_device_context *base,
0303 const struct termios *term
0304 )
0305 {
0306 uint32_t ibrd, fbrd, lcrh, baud, cr;
0307 int err;
0308 arm_pl011_context *context = (arm_pl011_context *) base;
0309 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0310
0311
0312 baud = rtems_termios_number_to_baud(term->c_ospeed);
0313 if (baud == B0) {
0314 return false;
0315 }
0316
0317 err = arm_pl011_compute_baudrate_params(&ibrd,&fbrd,baud,context->clock,3);
0318 if (err != 0) {
0319 return false;
0320 }
0321
0322 lcrh = arm_pl011_set_lcrh(term);
0323
0324
0325 arm_pl011_disable_irq(®s->base, PL011_UARTI_MASK);
0326
0327
0328
0329
0330
0331 while (
0332 (regs->base.uartfr & PL011_UARTFR_TXFE) == 0 ||
0333 (regs->base.uartfr & PL011_UARTFR_BUSY) != 0
0334 ) {
0335
0336 }
0337
0338 regs->base.uartcr = PL011_UARTCR_UARTEN;
0339
0340
0341 regs->base.uartibrd = ibrd;
0342 regs->base.uartfbrd = fbrd;
0343
0344
0345
0346
0347
0348
0349
0350 regs->base.uartlcr_h = lcrh;
0351
0352
0353 regs->base.uartcr &=
0354 ~(PL011_UARTCR_UARTEN | PL011_UARTCR_RXE | PL011_UARTCR_TXE);
0355 cr = arm_pl011_set_cr(term, ®s->base);
0356 arm_pl011_flush_fifos(context);
0357
0358 regs->base.uartcr = cr;
0359
0360 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0361
0362 arm_pl011_clear_irq(®s->base, PL011_UARTI_MASK);
0363
0364
0365 if ((term->c_cflag & CREAD) != 0) {
0366 arm_pl011_enable_irq(®s->base, PL011_UARTI_RTI | PL011_UARTI_RXI);
0367 }
0368
0369
0370
0371
0372
0373 context->needs_sw_triggered_tx_irq = true;
0374 #endif
0375 return true;
0376 }
0377
0378 static bool arm_pl011_first_open(
0379 struct rtems_termios_tty *tty,
0380 rtems_termios_device_context *base,
0381 struct termios *term,
0382 rtems_libio_open_close_args_t *args
0383 )
0384 {
0385 arm_pl011_context *context = (arm_pl011_context *) base;
0386 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0387 rtems_status_code sc;
0388
0389 context->tty = tty;
0390 #endif
0391
0392 if (rtems_termios_set_initial_baud(tty, context->initial_baud) != 0) {
0393 return false;
0394 }
0395
0396 if (!arm_pl011_set_attributes(base, term)) {
0397 return false;
0398 }
0399
0400 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0401 sc = arm_pl011_first_open_irq(base, context);
0402 if (sc != RTEMS_SUCCESSFUL) {
0403 return false;
0404 }
0405 #endif
0406
0407 return true;
0408 }
0409
0410 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0411 static void arm_pl011_last_close(
0412 rtems_termios_tty *tty,
0413 rtems_termios_device_context *base,
0414 rtems_libio_open_close_args_t *args
0415 )
0416 {
0417 const arm_pl011_context *context = (void *) base;
0418 (void) rtems_interrupt_handler_remove(
0419 context->irq,
0420 arm_pl011_irq_handler,
0421 tty
0422 );
0423 }
0424 #endif
0425
0426 int arm_pl011_read_polled(rtems_termios_device_context *base)
0427 {
0428 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0429
0430 if (arm_pl011_is_rxfifo_empty(®s->base)) {
0431 return -1;
0432 } else {
0433 return arm_pl011_read_char(®s->base);
0434 }
0435 }
0436
0437 void arm_pl011_write_polled(rtems_termios_device_context *base, char c)
0438 {
0439 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0440
0441
0442 while (arm_pl011_is_txfifo_full(®s->base)) {
0443
0444 }
0445
0446 arm_pl011_write_char(®s->base, c);
0447 }
0448
0449 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0450 static void arm_pl011_write_support_interrupt(
0451 rtems_termios_device_context *base,
0452 const char *buffer,
0453 size_t buffer_len
0454 )
0455 {
0456 arm_pl011_context *context = (arm_pl011_context *) base;
0457 volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0458 size_t i = 0;
0459
0460 if (buffer_len > 0) {
0461 arm_pl011_enable_irq(®s->base, PL011_UARTI_TXI);
0462
0463
0464
0465
0466
0467 while (arm_pl011_is_txfifo_full(®s->base)) {
0468
0469 }
0470 while (!arm_pl011_is_txfifo_full(®s->base) && i < buffer_len) {
0471 arm_pl011_write_char(®s->base, buffer[i]);
0472 i++;
0473 }
0474 context->tx_queued_chars = i;
0475
0476 if (context->needs_sw_triggered_tx_irq) {
0477
0478
0479
0480
0481
0482 (void) rtems_termios_dequeue_characters(context->tty,
0483 context->tx_queued_chars);
0484 }
0485 } else {
0486
0487
0488
0489
0490
0491 arm_pl011_disable_irq(®s->base, PL011_UARTI_TXI);
0492 }
0493 }
0494 #else
0495 static void arm_pl011_write_support_polled(
0496 rtems_termios_device_context *base,
0497 const char *s,
0498 size_t n
0499 )
0500 {
0501 size_t i;
0502
0503 for (i = 0; i < n; ++i) {
0504 arm_pl011_write_polled(base, s[i]);
0505 }
0506 }
0507 #endif
0508
0509 static void arm_pl011_write_buffer(
0510 rtems_termios_device_context *base,
0511 const char *buffer,
0512 size_t n
0513 )
0514 {
0515 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0516 arm_pl011_write_support_interrupt(base, buffer, n);
0517 #else
0518 arm_pl011_write_support_polled(base, buffer, n);
0519 #endif
0520 }
0521
0522 const rtems_termios_device_handler arm_pl011_fns = {
0523 .first_open = arm_pl011_first_open,
0524 .write = arm_pl011_write_buffer,
0525 .set_attributes = arm_pl011_set_attributes,
0526 .ioctl = NULL,
0527 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0528 .last_close = arm_pl011_last_close,
0529 .poll_read = NULL,
0530 .mode = TERMIOS_IRQ_DRIVEN,
0531 #else
0532 .last_close = NULL,
0533 .poll_read = arm_pl011_read_polled,
0534 .mode = TERMIOS_POLLED,
0535 #endif
0536 };