Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsARMTMS570
0007  *
0008  * @brief This source file contains the Console Driver implementation using the
0009  *   Serial Communication Interface (SCI).
0010  */
0011 
0012 /*
0013  * Copyright (C) 2014 Premysl Houdek <kom541000@gmail.com>
0014  *
0015  * Google Summer of Code 2014 at
0016  * Czech Technical University in Prague
0017  * Zikova 1903/4
0018  * 166 36 Praha 6
0019  * Czech Republic
0020  *
0021  * Redistribution and use in source and binary forms, with or without
0022  * modification, are permitted provided that the following conditions
0023  * are met:
0024  * 1. Redistributions of source code must retain the above copyright
0025  *    notice, this list of conditions and the following disclaimer.
0026  * 2. Redistributions in binary form must reproduce the above copyright
0027  *    notice, this list of conditions and the following disclaimer in the
0028  *    documentation and/or other materials provided with the distribution.
0029  *
0030  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0031  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0032  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0033  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0034  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0035  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0036  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0037  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0038  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0039  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0040  * POSSIBILITY OF SUCH DAMAGE.
0041  */
0042 
0043 #include <bspopts.h>
0044 #include <termios.h>
0045 #include <rtems/termiostypes.h>
0046 #include <bsp/tms570-sci-driver.h>
0047 #include <rtems/console.h>
0048 #include <bsp.h>
0049 #include <bsp/fatal.h>
0050 #include <bsp/irq.h>
0051 
0052 /**
0053  * @brief Table including all serial drivers
0054  *
0055  * Definitions of all serial drivers
0056  */
0057 tms570_sci_context driver_context_table[] = {
0058   {
0059     .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI1"),
0060     .device_name = "/dev/console",
0061     /* TMS570 UART peripheral use subset of LIN registers which are equivalent
0062      * to SCI ones
0063      */
0064     .regs = (volatile tms570_sci_t *) &TMS570_LIN,
0065     .irq = TMS570_IRQ_SCI_LEVEL_0,
0066   },
0067   {
0068     .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI2"),
0069     .device_name = "/dev/ttyS1",
0070     .regs = &TMS570_SCI,
0071     .irq = TMS570_IRQ_SCI2_LEVEL_0,
0072   }
0073 };
0074 
0075 void tms570_sci_initialize(tms570_sci_context *ctx)
0076 {
0077   uint32_t rx_pin = 1 << 1;
0078   uint32_t tx_pin = 1 << 2;
0079 
0080   /* Resec SCI peripheral */
0081   ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 0;
0082   ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 1;
0083 
0084   /* Clear all interrupt sources */
0085   ctx->regs->CLEARINT = 0xffffffff;
0086 
0087   /* Map all interrupts to SCI INT0 line */
0088   ctx->regs->CLEARINTLVL = 0xffffffff;
0089 
0090   ctx->regs->GCR1 = TMS570_SCI_GCR1_TXENA * 0 |
0091                     TMS570_SCI_GCR1_RXENA * 0 |
0092                     TMS570_SCI_GCR1_CONT * 0 | /* continue operation when debugged */
0093                     TMS570_SCI_GCR1_LOOP_BACK * 0 |
0094                     TMS570_SCI_GCR1_POWERDOWN * 0 |
0095                     TMS570_SCI_GCR1_SLEEP * 0 |
0096                     TMS570_SCI_GCR1_SWnRST * 0 | /* reset state */
0097                     TMS570_SCI_GCR1_CLOCK * 1 | /* internal clock */
0098                     TMS570_SCI_GCR1_TIMING_MODE * 1 |
0099                     TMS570_SCI_GCR1_COMM_MODE * 0;
0100 
0101   /* Setup connection of SCI peripheral Rx and Tx  pins */
0102   ctx->regs->PIO0 = rx_pin * 1 | tx_pin * 1; /* Rx and Tx pins are not GPIO */
0103   ctx->regs->PIO3 = rx_pin * 0 | tx_pin * 0; /* Default output low  */
0104   ctx->regs->PIO1 = rx_pin * 0 | tx_pin * 0; /* Input when not used by SCI */
0105   ctx->regs->PIO6 = rx_pin * 0 | tx_pin * 0; /* No open drain */
0106   ctx->regs->PIO7 = rx_pin * 0 | tx_pin * 0; /* Pull-up/down enabled */
0107   ctx->regs->PIO8 = rx_pin * 1 | tx_pin * 1; /* Select pull-up */
0108 
0109   /* Bring device out of software reset */
0110   ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST;
0111 }
0112 
0113 /**
0114  * @brief Serial drivers init function
0115  *
0116  * Initialize all serial drivers specified in driver_context_table
0117  *
0118  * @param[in] major
0119  * @param[in] minor
0120  * @param[in] arg
0121  * @retval RTEMS_SUCCESSFUL Initialization completed
0122  */
0123 rtems_device_driver console_initialize(
0124   rtems_device_major_number  major,
0125   rtems_device_minor_number  minor,
0126   void                      *arg
0127 )
0128 {
0129   rtems_status_code sc;
0130 #if CONSOLE_USE_INTERRUPTS
0131   const rtems_termios_device_handler *handler = &tms570_sci_handler_interrupt;
0132 #else
0133   const rtems_termios_device_handler *handler = &tms570_sci_handler_polled;
0134 #endif
0135 
0136   /*
0137    * Initialize the Termios infrastructure.  If Termios has already
0138    * been initialized by another device driver, then this call will
0139    * have no effect.
0140    */
0141   rtems_termios_initialize();
0142 
0143   /* Initialize each device */
0144   for (
0145     minor = 0;
0146     minor < RTEMS_ARRAY_SIZE(driver_context_table);
0147     ++minor
0148   ) {
0149     tms570_sci_context *ctx = &driver_context_table[minor];
0150 
0151     tms570_sci_initialize(ctx);
0152 
0153     /*
0154      * Install this device in the file system and Termios.  In order
0155      * to use the console (i.e. being able to do printf, scanf etc.
0156      * on stdin, stdout and stderr), one device must be registered as
0157      * "/dev/console" (CONSOLE_DEVICE_NAME).
0158      */
0159     sc = rtems_termios_device_install(
0160         ctx->device_name,
0161         handler,
0162         NULL,
0163         &ctx->base
0164     );
0165     if ( sc != RTEMS_SUCCESSFUL ) {
0166       bsp_fatal(BSP_FATAL_CONSOLE_NO_DEV);
0167     }
0168   }
0169   return RTEMS_SUCCESSFUL;
0170 }
0171 
0172 /**
0173  * @brief Enables RX interrupt
0174  *
0175  * Enables RX interrupt source of SCI peripheral
0176  * specified in the driver context.
0177  *
0178  * @param[in] ctx context of the driver
0179  * @retval Void
0180  */
0181 static void tms570_sci_enable_interrupts(tms570_sci_context * ctx)
0182 {
0183   ctx->regs->SETINT = TMS570_SCI_SETINT_SET_RX_INT;
0184 }
0185 
0186 /**
0187  * @brief Disables RX interrupt
0188  *
0189  * Disables RX interrupt source of SCI peripheral specified in the driver
0190  * context.
0191  *
0192  * @param[in] ctx context of the driver
0193  * @retval Void
0194  */
0195 static void tms570_sci_disable_interrupts(tms570_sci_context * ctx)
0196 {
0197   ctx->regs->CLEARINT = TMS570_SCI_CLEARINT_CLR_RX_INT;
0198 }
0199 
0200 /**
0201  * @brief Check whether driver has put char in HW
0202  *
0203  * Check whether driver has put char in HW.
0204  * This information is read from the driver context not from a peripheral.
0205  * TMS570 does not have write data buffer asociated with SCI
0206  * so the return can be only 0 or 1.
0207  *
0208  * @param[in] ctx context of the driver
0209  * @retval x
0210  */
0211 static int tms570_sci_transmitted_chars(tms570_sci_context * ctx)
0212 {
0213   int ret;
0214 
0215   ret = ctx->tx_chars_in_hw;
0216   if ( ret == 1 ) {
0217     ctx->tx_chars_in_hw = 0;
0218     return 1;
0219   }
0220   return ret;
0221 }
0222 
0223 /**
0224  * @brief Set attributes of the HW peripheral
0225  *
0226  * Sets attributes of the HW peripheral (parity, baud rate, etc.)
0227  *
0228  * @param[in] base context of the driver
0229  * @param[in] t termios driver
0230  * @retval true peripheral setting is changed
0231  */
0232 bool tms570_sci_set_attributes(
0233   rtems_termios_device_context *base,
0234   const struct termios *t
0235 )
0236 {
0237   tms570_sci_context *ctx = (tms570_sci_context *) base;
0238   rtems_interrupt_lock_context lock_context;
0239   int32_t bauddiv;
0240   int32_t baudrate;
0241   uint32_t flr_tx_ready = TMS570_SCI_FLR_TX_EMPTY;
0242   /*
0243    * Test for TMS570_SCI_FLR_TXRDY is not necessary
0244    * because both SCITD and SCITXSHF has to be empty
0245    * to TX_EMPTY be asserted. But there is no interrupt
0246    * option for TX_EMPTY. Polling is used isntead.
0247    */
0248 
0249   /* Baud rate */
0250   baudrate = rtems_termios_baud_to_number(cfgetospeed(t));
0251 
0252   rtems_termios_device_lock_acquire(base, &lock_context);
0253 
0254   while ( (ctx->regs->GCR1 & TMS570_SCI_GCR1_TXENA) &&
0255           (ctx->regs->FLR & flr_tx_ready) != flr_tx_ready) {
0256     /*
0257      * There are pending characters in the hardware,
0258      * change in the middle of the character Tx leads
0259      * to disturb of the character and SCI engine
0260      */
0261     rtems_interval tw;
0262 
0263     rtems_termios_device_lock_release(base, &lock_context);
0264 
0265     tw = rtems_clock_get_ticks_per_second();
0266     tw = tw * 5 / baudrate + 1;
0267     rtems_task_wake_after( tw );
0268 
0269     rtems_termios_device_lock_acquire(base, &lock_context);
0270   }
0271 
0272   ctx->regs->GCR1 &= ~( TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
0273                         TMS570_SCI_GCR1_RXENA );
0274 
0275   ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_STOP;    /*one stop bit*/
0276   ctx->regs->FORMAT = TMS570_SCI_FORMAT_CHAR(0x7);
0277 
0278   switch ( t->c_cflag & ( PARENB|PARODD ) ) {
0279     case ( PARENB|PARODD ):
0280       /* Odd parity */
0281       ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY;
0282       ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
0283       break;
0284 
0285     case PARENB:
0286       /* Even parity */
0287       ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY;
0288       ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
0289       break;
0290 
0291     default:
0292     case 0:
0293     case PARODD:
0294       /* No Parity */
0295       ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY_ENA;
0296   }
0297 
0298   /* Apply baudrate to the hardware */
0299   baudrate *= 16;
0300   bauddiv = (TMS570_VCLK_HZ + baudrate / 2) / baudrate;
0301   ctx->regs->BRS = bauddiv? bauddiv - 1: 0;
0302 
0303   ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
0304                      TMS570_SCI_GCR1_RXENA;
0305 
0306   rtems_termios_device_lock_release(base, &lock_context);
0307 
0308   return true;
0309 }
0310 
0311 /**
0312  * @brief sci interrupt handler
0313  *
0314  * Handler checks which interrupt occured and provides nessesary maintenance
0315  * dequeue characters in termios driver whether character is send succesfully
0316  * enqueue characters in termios driver whether character is recieved
0317  *
0318  * @param[in] arg rtems_termios_tty
0319  * @retval Void
0320  */
0321 static void tms570_sci_interrupt_handler(void * arg)
0322 {
0323   rtems_termios_tty *tty = arg;
0324   tms570_sci_context *ctx = rtems_termios_get_device_context(tty);
0325 
0326   /*
0327    * Check if we have received something.
0328    */
0329    if ( (ctx->regs->FLR & TMS570_SCI_FLR_RXRDY ) == TMS570_SCI_FLR_RXRDY ) {
0330       char buf[1];
0331 
0332       /* Read the received byte */
0333       buf[0] = ctx->regs->RD & 0x000000FF;
0334 
0335       /* Hand the data over to the Termios infrastructure */
0336       rtems_termios_enqueue_raw_characters(tty, buf, 1);
0337     }
0338   /*
0339    * Check if we have something transmitted.
0340    */
0341   if ( (ctx->regs->FLR & TMS570_SCI_FLR_TXRDY ) == TMS570_SCI_FLR_TXRDY ) {
0342     size_t n;
0343 
0344     n = tms570_sci_transmitted_chars(ctx);
0345     if ( n > 0 ) {
0346       /*
0347        * Notify Termios that we have transmitted some characters.  It
0348        * will call now the interrupt write function if more characters
0349        * are ready for transmission.
0350        */
0351       rtems_termios_dequeue_characters(tty, n);
0352     }
0353   }
0354 }
0355 
0356 /**
0357  * @brief sci write function called from interrupt
0358  *
0359  * Nonblocking write function. Writes characters to HW peripheral
0360  * TMS570 does not have write data buffer asociated with SCI
0361  * so only one character can be written.
0362  *
0363  * @param[in] base context of the driver
0364  * @param[in] buf buffer of characters pending to send
0365  * @param[in] len size of the buffer
0366  * @retval Void
0367  */
0368 static void tms570_sci_interrupt_write(
0369   rtems_termios_device_context *base,
0370   const char *buf,
0371   size_t len
0372 )
0373 {
0374   tms570_sci_context *ctx = (tms570_sci_context *) base;
0375 
0376   if ( len > 0 ) {
0377     /* start UART TX, this will result in an interrupt when done */
0378     ctx->regs->TD = *buf;
0379     /* character written - raise count*/
0380     ctx->tx_chars_in_hw = 1;
0381     /* Enable TX interrupt (interrupt is edge-triggered) */
0382     ctx->regs->SETINT = (1<<8);
0383 
0384   } else {
0385     /* No more to send, disable TX interrupts */
0386     ctx->regs->CLEARINT = (1<<8);
0387     /* Tell close that we sent everything */
0388   }
0389 }
0390 
0391 /**
0392  * @brief sci write function
0393  *
0394  * Blocking write function. Waits until HW peripheral is ready and then writes
0395  * character to HW peripheral. Writes all characters in the buffer.
0396  *
0397  * @param[in] base context of the driver
0398  * @param[in] buf buffer of characters pending to send
0399  * @param[in] len size of the buffer
0400  * @retval Void
0401  */
0402 static void tms570_sci_poll_write(
0403   rtems_termios_device_context *base,
0404   const char *buf,
0405   size_t n
0406 )
0407 {
0408   tms570_sci_context *ctx = (tms570_sci_context *) base;
0409   size_t i;
0410 
0411   /* Write */
0412 
0413   for ( i = 0; i < n; ++i ) {
0414     while ( (ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0) {
0415       ;
0416     }
0417     ctx->regs->TD = buf[i];
0418   }
0419 }
0420 
0421 /**
0422  * @brief See if there is recieved charakter to read
0423  *
0424  * read the RX flag from peripheral specified in context
0425  *
0426  * @param[in] ctx context of the driver
0427  * @retval 0 No character to read
0428  * @retval x Character ready to read
0429  */
0430 static int TMS570_sci_can_read_char(
0431   tms570_sci_context * ctx
0432 )
0433 {
0434   return ctx->regs->FLR & TMS570_SCI_FLR_RXRDY;
0435 }
0436 
0437 /**
0438  * @brief reads character from peripheral
0439  *
0440  * reads the recieved character from peripheral specified in context
0441  *
0442  * @param[in] ctx context of the driver
0443  * @retval x Character
0444  */
0445 static char TMS570_sci_read_char(
0446   tms570_sci_context * ctx
0447 )
0448 {
0449   return ctx->regs->RD;
0450 }
0451 
0452 /**
0453  * @brief sci read function
0454  *
0455  * check if there is recieved character to be read and reads it.
0456  *
0457  * @param[in] base context of the driver
0458  * @retval -1 No character to be read
0459  * @retval x Read character
0460  */
0461 static int tms570_sci_poll_read(rtems_termios_device_context *base)
0462 {
0463   tms570_sci_context *ctx = (tms570_sci_context *) base;
0464 
0465   /* Check if a character is available */
0466   if ( TMS570_sci_can_read_char(ctx) ) {
0467     return TMS570_sci_read_char(ctx);
0468   } else {
0469     return -1;
0470   }
0471 }
0472 
0473 /**
0474  * @brief initialization of the driver
0475  *
0476  * initialization of the HW peripheral specified in contex of the driver.
0477  * This function is called only once when opening the driver.
0478  *
0479  * @param[in] tty Termios control
0480  * @param[in] ctx context of the driver
0481  * @param[in] term Termios attributes
0482  * @param[in] args
0483  * @retval false Error occured during initialization
0484  * @retval true Driver is open and ready
0485  */
0486 static bool tms570_sci_poll_first_open(
0487   rtems_termios_tty             *tty,
0488   rtems_termios_device_context  *ctx,
0489   struct termios                *term,
0490   rtems_libio_open_close_args_t *args
0491 )
0492 {
0493   bool ok;
0494 
0495   rtems_termios_set_best_baud(term, TMS570_SCI_BAUD_RATE);
0496   ok = tms570_sci_set_attributes(ctx, term);
0497   if ( !ok ) {
0498     return false;
0499   }
0500   return true;
0501 }
0502 
0503 /**
0504  * @brief initialization of the interrupt driven driver
0505  *
0506  * calls tms570_sci_poll_first_open function.
0507  * install and enables interrupts.
0508  *
0509  * @param[in] tty Termios control
0510  * @param[in] base context of the driver
0511  * @param[in] args
0512  * @retval false Error occured during initialization
0513  * @retval true Driver is open and ready
0514  */
0515 static bool tms570_sci_interrupt_first_open(
0516   rtems_termios_tty             *tty,
0517   rtems_termios_device_context  *base,
0518   struct termios                *term,
0519   rtems_libio_open_close_args_t *args
0520 )
0521 {
0522   tms570_sci_context *ctx = (tms570_sci_context *) base;
0523   rtems_status_code sc;
0524   bool ret;
0525 
0526   ret = tms570_sci_poll_first_open(tty, base, term, args);
0527   if ( ret == false ) {
0528     return false;
0529   }
0530 
0531   /* Register Interrupt handler */
0532   sc = rtems_interrupt_handler_install(ctx->irq,
0533       ctx->device_name,
0534       RTEMS_INTERRUPT_SHARED,
0535       tms570_sci_interrupt_handler,
0536       tty
0537   );
0538   if ( sc != RTEMS_SUCCESSFUL ) {
0539     return false;
0540   }
0541   tms570_sci_enable_interrupts(ctx);
0542   return true;
0543 }
0544 
0545 /**
0546  * @brief closes sci peripheral
0547  *
0548  * @param[in] tty Termios control
0549  * @param[in] base context of the driver
0550  * @param[in] args
0551  * @retval false Error occured during initialization
0552  * @retval true Driver is open and ready
0553  */
0554 static void tms570_sci_poll_last_close(
0555   rtems_termios_tty             *tty,
0556   rtems_termios_device_context  *base,
0557   rtems_libio_open_close_args_t *args
0558 )
0559 {
0560   ;
0561 }
0562 
0563 /**
0564  * @brief closes sci peripheral of interrupt driven driver
0565  *
0566  * calls tms570_sci_poll_last_close and disables interrupts
0567  *
0568  * @param[in] tty Termios control
0569  * @param[in] base context of the driver
0570  * @param[in] args
0571  * @retval false Error occured during initialization
0572  * @retval true Driver is open and ready
0573  */
0574 static void tms570_sci_interrupt_last_close(
0575   rtems_termios_tty             *tty,
0576   rtems_termios_device_context  *base,
0577   rtems_libio_open_close_args_t *args
0578 )
0579 {
0580   tms570_sci_context *ctx = (tms570_sci_context *) base;
0581   rtems_interrupt_lock_context lock_context;
0582   rtems_interval tw;
0583   int32_t baudrate;
0584 
0585   /* Turn off RX interrupts */
0586   rtems_termios_device_lock_acquire(base, &lock_context);
0587   tms570_sci_disable_interrupts(ctx);
0588   rtems_termios_device_lock_release(base, &lock_context);
0589 
0590   tw = rtems_clock_get_ticks_per_second();
0591   baudrate = rtems_termios_baud_to_number(cfgetospeed(&tty->termios));
0592   tw = tw * 10 / baudrate + 1;
0593   while ( ( ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0 ) {
0594      rtems_task_wake_after(tw);
0595   }
0596 
0597   /* uninstall ISR */
0598   rtems_interrupt_handler_remove(ctx->irq, tms570_sci_interrupt_handler, tty);
0599 
0600   tms570_sci_poll_last_close(tty, base, args);
0601 }
0602 
0603 /**
0604  * @brief Struct containing definitions of polled driver functions.
0605  *
0606  * Encapsulates polled driver functions.
0607  * Use of this table is determited by not defining TMS570_USE_INTERRUPTS
0608  */
0609 const rtems_termios_device_handler tms570_sci_handler_polled = {
0610   .first_open = tms570_sci_poll_first_open,
0611   .last_close = tms570_sci_poll_last_close,
0612   .poll_read = tms570_sci_poll_read,
0613   .write = tms570_sci_poll_write,
0614   .set_attributes = tms570_sci_set_attributes,
0615   .mode = TERMIOS_POLLED
0616 };
0617 
0618 /**
0619  * @brief Struct containing definitions of interrupt driven driver functions.
0620  *
0621  * Encapsulates interrupt driven driver functions.
0622  * Use of this table is determited by defining TMS570_USE_INTERRUPTS
0623  */
0624 const rtems_termios_device_handler tms570_sci_handler_interrupt  = {
0625   .first_open = tms570_sci_interrupt_first_open,
0626   .last_close = tms570_sci_interrupt_last_close,
0627   .poll_read = NULL,
0628   .write = tms570_sci_interrupt_write,
0629   .set_attributes = tms570_sci_set_attributes,
0630   .mode = TERMIOS_IRQ_DRIVEN
0631 };