Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @brief Console LINFlexD implementation.
0007  */
0008 
0009 /*
0010  * Copyright (C) 2011, 2014 embedded brains GmbH & Co. KG
0011  *
0012  * Redistribution and use in source and binary forms, with or without
0013  * modification, are permitted provided that the following conditions
0014  * are met:
0015  * 1. Redistributions of source code must retain the above copyright
0016  *    notice, this list of conditions and the following disclaimer.
0017  * 2. Redistributions in binary form must reproduce the above copyright
0018  *    notice, this list of conditions and the following disclaimer in the
0019  *    documentation and/or other materials provided with the distribution.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0022  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0025  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0026  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0027  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0030  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0031  * POSSIBILITY OF SUCH DAMAGE.
0032  */
0033 
0034 #include <bsp/console-linflex.h>
0035 
0036 #include <bsp.h>
0037 #include <bsp/fatal.h>
0038 #include <bsp/irq.h>
0039 
0040 #ifdef MPC55XX_HAS_LINFLEX
0041 
0042 mpc55xx_linflex_context mpc55xx_linflex_devices [] = {
0043   {
0044     .regs = &LINFLEX0,
0045     .irq_rxi = MPC55XX_IRQ_LINFLEX_RXI(0),
0046     .irq_txi = MPC55XX_IRQ_LINFLEX_TXI(0),
0047     .irq_err = MPC55XX_IRQ_LINFLEX_ERR(0),
0048     .tx_pcr_register = &((SIU_tag *) &SIUL)->PCR18,
0049     .tx_pa_value = 1,
0050     .rx_pcr_register = &((SIU_tag *) &SIUL)->PCR19,
0051     .rx_psmi_register = &((SIU_tag *) &SIUL)->PSMI31,
0052     .rx_padsel_value = 0
0053   }, {
0054     .regs = &LINFLEX1,
0055     .irq_rxi = MPC55XX_IRQ_LINFLEX_RXI(1),
0056     .irq_txi = MPC55XX_IRQ_LINFLEX_TXI(1),
0057     .irq_err = MPC55XX_IRQ_LINFLEX_ERR(1),
0058     .tx_pcr_register = &((SIU_tag *) &SIUL)->PCR94,
0059     .tx_pa_value = 1,
0060     .rx_pcr_register = &((SIU_tag *) &SIUL)->PCR95,
0061     .rx_psmi_register = &((SIU_tag *) &SIUL)->PSMI32,
0062     .rx_padsel_value = 2
0063   }
0064 };
0065 
0066 static void enter_init_mode(volatile LINFLEX_tag *regs)
0067 {
0068   LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R };
0069   cr1.B.SLEEP = 0;
0070   cr1.B.INIT = 1;
0071   regs->LINCR1.R = cr1.R;
0072 }
0073 
0074 static void enter_active_mode(volatile LINFLEX_tag *regs)
0075 {
0076   LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R };
0077   cr1.B.SLEEP = 0;
0078   cr1.B.INIT = 0;
0079   regs->LINCR1.R = cr1.R;
0080 }
0081 
0082 static void enter_sleep_mode(volatile LINFLEX_tag *regs)
0083 {
0084   LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R };
0085   cr1.B.SLEEP = 1;
0086   cr1.B.INIT = 0;
0087   regs->LINCR1.R = cr1.R;
0088 }
0089 
0090 static void mpc55xx_linflex_poll_write(int minor, char c)
0091 {
0092   mpc55xx_linflex_context *self = console_generic_get_context(minor);
0093   volatile LINFLEX_tag *regs = self->regs;
0094   const LINFLEX_UARTSR_32B_tag clear_dtf = { .B = { .DTF_TFF = 1 } };
0095   rtems_interrupt_level level;
0096   bool done = false;
0097   bool wait_for_transmit_done = false;
0098 
0099   rtems_interrupt_disable(level);
0100   if (self->transmit_nest_level == 0) {
0101     LINFLEX_LINIER_32B_tag ier = { .R = regs->LINIER.R };
0102 
0103     if (ier.B.DTIE != 0) {
0104       ier.B.DTIE = 0;
0105       regs->LINIER.R = ier.R;
0106       wait_for_transmit_done = !self->transmit_in_progress;
0107       self->transmit_nest_level = 1;
0108     }
0109   } else {
0110     ++self->transmit_nest_level;
0111   }
0112   rtems_interrupt_enable(level);
0113 
0114   while (!done) {
0115     rtems_interrupt_disable(level);
0116     bool tx = self->transmit_in_progress;
0117     if (!tx || (tx && regs->UARTSR.B.DTF_TFF)) {
0118       regs->UARTSR.R = clear_dtf.R;
0119       regs->BDRL.B.DATA0 = c;
0120       self->transmit_in_progress = true;
0121       done = true;
0122     }
0123     rtems_interrupt_enable(level);
0124   }
0125 
0126   done = false;
0127   while (!done) {
0128     rtems_interrupt_disable(level);
0129     if (wait_for_transmit_done) {
0130       if (regs->UARTSR.B.DTF_TFF) {
0131         regs->UARTSR.R = clear_dtf.R;
0132         self->transmit_in_progress = false;
0133         done = true;
0134       }
0135     } else {
0136       done = true;
0137     }
0138 
0139     if (done && self->transmit_nest_level > 0) {
0140       --self->transmit_nest_level;
0141 
0142       if (self->transmit_nest_level == 0) {
0143         LINFLEX_LINIER_32B_tag ier = { .R = regs->LINIER.R };
0144 
0145         ier.B.DTIE = 1;
0146         regs->LINIER.R = ier.R;
0147       }
0148     }
0149     rtems_interrupt_enable(level);
0150   }
0151 }
0152 
0153 static void mpc55xx_linflex_rx_interrupt_handler(void *arg)
0154 {
0155   mpc55xx_linflex_context *self = arg;
0156   volatile LINFLEX_tag *regs = self->regs;
0157   char c = regs->BDRM.B.DATA4;
0158   const LINFLEX_UARTSR_32B_tag clear_flags = { .B = { .RMB = 1, .DRF_RFE = 1 } };
0159 
0160   regs->UARTSR.R = clear_flags.R;
0161 
0162   rtems_termios_enqueue_raw_characters(self->tty, &c, 1);
0163 }
0164 
0165 static void mpc55xx_linflex_tx_interrupt_handler(void *arg)
0166 {
0167   mpc55xx_linflex_context *self = arg;
0168   volatile LINFLEX_tag *regs = self->regs;
0169 
0170   regs->UARTSR.B.DTF_TFF = 1;   /* clear flag */
0171   self->transmit_in_progress = false;
0172 
0173   rtems_termios_dequeue_characters(self->tty, 1);
0174 }
0175 
0176 /*
0177 static void mpc55xx_linflex_err_interrupt_handler(void *arg)
0178 {
0179   mpc55xx_linflex_context *self = arg;
0180 }
0181 */
0182 
0183 static int mpc55xx_linflex_set_attributes(int minor, const struct termios *t)
0184 {
0185   mpc55xx_linflex_context *self = console_generic_get_context(minor);
0186   volatile LINFLEX_tag *regs = self->regs;
0187   LINFLEX_UARTCR_32B_tag uartcr = { .R = 0 };
0188   LINFLEX_GCR_32B_tag gcr = { .R = 0 };
0189   LINFLEX_LINIER_32B_tag ier = { .R = 0 };
0190   rtems_termios_baud_t br = rtems_termios_baud_to_number(t->c_ospeed);
0191   LINFLEX_LINFBRR_32B_tag fbrr = { .R = 0 };
0192   LINFLEX_LINIBRR_32B_tag ibrr = { .R = 0 };
0193 
0194   enter_init_mode(regs);
0195 
0196   /* Set to UART-mode */
0197   uartcr.B.UART = 1;
0198   regs->UARTCR.R = uartcr.R;
0199 
0200   /* Set to buffer mode with size 1 */
0201   uartcr.B.TDFL_TFC = 0;
0202   uartcr.B.RDFL_RFC0 = 0;
0203   uartcr.B.RFBM = 0;
0204   uartcr.B.TFBM = 0;
0205 
0206   /* Enable receiver and transmitter */
0207   uartcr.B.RXEN = 1;
0208   uartcr.B.TXEN = 1;
0209 
0210   /* Number of data bits */
0211   uartcr.B.WL1 = 0;
0212   if ((t->c_cflag & CSIZE) == CS8) {
0213     uartcr.B.WL0 = 1;
0214   } else if ((t->c_cflag & CSIZE) == CS7) {
0215     uartcr.B.WL0 = 0;
0216   } else {
0217     return -1;
0218   }
0219 
0220   /* Parity */
0221   uartcr.B.PCE = (t->c_cflag & PARENB) ? 1 : 0;
0222   uartcr.B.PC1 = 0;
0223   uartcr.B.PC0 = (t->c_cflag & PARODD) ? 1 : 0;
0224 
0225   /* Stop bits */
0226   gcr.B.STOP = (t->c_cflag & CSTOPB) ? 1 : 0;
0227 
0228   /* Set control registers */
0229   regs->UARTCR.R = uartcr.R;
0230   regs->GCR.R = gcr.R;
0231 
0232   /* Interrupts */
0233   ier.B.DTIE = 1;
0234   ier.B.DRIE = 1;
0235   regs->LINIER.R = ier.R;
0236 
0237   /* Baud rate */
0238   if (br > 0) {
0239     uint32_t lfdiv_mult_32 = bsp_clock_speed * 2 / br;
0240     if((lfdiv_mult_32 & 0x1) != 0) {
0241         ++lfdiv_mult_32;
0242     }
0243     fbrr.B.FBR = (lfdiv_mult_32 >> 1) & 0xF;
0244     ibrr.B.IBR = lfdiv_mult_32 >> 5;
0245   } else {
0246     return -1;
0247   }
0248   regs->LINFBRR.R = fbrr.R;
0249   regs->LINIBRR.R = ibrr.R;
0250 
0251   enter_active_mode(regs);
0252 
0253   return 0;
0254 }
0255 
0256 static int mpc55xx_linflex_first_open(int major, int minor, void *arg)
0257 {
0258   rtems_status_code sc = RTEMS_SUCCESSFUL;
0259   int rv = 0;
0260   mpc55xx_linflex_context *self = console_generic_get_context(minor);
0261   struct rtems_termios_tty *tty = console_generic_get_tty_at_open(arg);
0262   SIU_PCR_tag pcr = { .R = 0 };
0263   SIUL_PSMI_8B_tag psmi = { .R = 0 };
0264 
0265   self->tty = tty;
0266 
0267   pcr.B.IBE = 1;
0268   self->rx_pcr_register->R = pcr.R;
0269   psmi.B.PADSEL = self->rx_padsel_value;
0270   self->rx_psmi_register->R = psmi.R;
0271   pcr.R = 0;
0272   pcr.B.OBE = 1;
0273   pcr.B.PA = self->tx_pa_value;
0274   self->tx_pcr_register->R = pcr.R;
0275 
0276   rv = rtems_termios_set_initial_baud(tty, BSP_DEFAULT_BAUD_RATE);
0277   if (rv != 0) {
0278     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_BAUD);
0279   }
0280 
0281   rv = mpc55xx_linflex_set_attributes(minor, &tty->termios);
0282   if (rv != 0) {
0283     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ATTRIBUTES);
0284   }
0285 
0286   sc = mpc55xx_interrupt_handler_install(
0287     self->irq_rxi,
0288     "LINFlexD RXI",
0289     RTEMS_INTERRUPT_UNIQUE,
0290     MPC55XX_INTC_DEFAULT_PRIORITY,
0291     mpc55xx_linflex_rx_interrupt_handler,
0292     self
0293   );
0294   if (sc != RTEMS_SUCCESSFUL) {
0295     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_RX_IRQ_INSTALL);
0296   }
0297 
0298   sc = mpc55xx_interrupt_handler_install(
0299     self->irq_txi,
0300     "LINFlexD TXI",
0301     RTEMS_INTERRUPT_UNIQUE,
0302     MPC55XX_INTC_DEFAULT_PRIORITY,
0303     mpc55xx_linflex_tx_interrupt_handler,
0304     self
0305   );
0306   if (sc != RTEMS_SUCCESSFUL) {
0307     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_TX_IRQ_INSTALL);
0308   }
0309 
0310   /*
0311   sc = mpc55xx_interrupt_handler_install(
0312     self->irq_err,
0313     "LINFlexD ERR",
0314     RTEMS_INTERRUPT_UNIQUE,
0315     MPC55XX_INTC_DEFAULT_PRIORITY,
0316     mpc55xx_linflex_err_interrupt_handler,
0317     self
0318   );
0319   if (sc != RTEMS_SUCCESSFUL) {
0320     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ERR_IRQ_INSTALL);
0321   }
0322   */
0323 
0324   return 0;
0325 }
0326 
0327 static int mpc55xx_linflex_last_close(int major, int minor, void* arg)
0328 {
0329   rtems_status_code sc = RTEMS_SUCCESSFUL;
0330   mpc55xx_linflex_context *self = console_generic_get_context(minor);
0331   volatile LINFLEX_tag *regs = self->regs;
0332   SIU_PCR_tag pcr = { .R = 0 };
0333   SIUL_PSMI_8B_tag psmi = { .R = 0 };
0334 
0335   /* enter initialization mode */
0336   enter_init_mode(regs);
0337 
0338   /* disable interrupts */
0339   regs->LINIER.R = 0;
0340 
0341   /* set module to sleep mode */
0342   enter_sleep_mode(regs);
0343 
0344   sc = rtems_interrupt_handler_remove(
0345     self->irq_rxi,
0346     mpc55xx_linflex_rx_interrupt_handler,
0347     self
0348   );
0349   if (sc != RTEMS_SUCCESSFUL) {
0350     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_RX_IRQ_REMOVE);
0351   }
0352 
0353   sc = rtems_interrupt_handler_remove(
0354     self->irq_txi,
0355     mpc55xx_linflex_tx_interrupt_handler,
0356     self
0357   );
0358   if (sc != RTEMS_SUCCESSFUL) {
0359     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_TX_IRQ_REMOVE);
0360   }
0361 
0362   /*
0363   sc = rtems_interrupt_handler_remove(
0364     self->irq_err,
0365     mpc55xx_linflex_err_interrupt_handler,
0366     self
0367   );
0368   if (sc != RTEMS_SUCCESSFUL) {
0369     bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ERR_IRQ_REMOVE);
0370   }
0371   */
0372 
0373   pcr.B.IBE = 1;
0374   self->rx_pcr_register->R = pcr.R;
0375   self->tx_pcr_register->R = pcr.R;
0376   psmi.R = 0;
0377   self->rx_psmi_register->R = psmi.R;
0378 
0379   self->tty = NULL;
0380 
0381   return 0;
0382 }
0383 
0384 static int mpc55xx_linflex_poll_read(int minor)
0385 {
0386   mpc55xx_linflex_context *self = console_generic_get_context(minor);
0387   volatile LINFLEX_tag *regs = self->regs;
0388   rtems_interrupt_level level;
0389   int c = -1;
0390 
0391   rtems_interrupt_disable(level);
0392   if (regs->UARTSR.B.DRF_RFE != 0) {
0393     /* Clear flag */
0394     regs->UARTSR.B.DRF_RFE = 1;
0395 
0396     /* Read */
0397     c = regs->BDRM.B.DATA4;
0398   }
0399   rtems_interrupt_enable(level);
0400 
0401   return c;
0402 }
0403 
0404 static int mpc55xx_linflex_write(int minor, const char *out, size_t n)
0405 {
0406   if (n > 0) {
0407     mpc55xx_linflex_context *self = console_generic_get_context(minor);
0408     volatile LINFLEX_tag *regs = self->regs;
0409 
0410     regs->BDRL.B.DATA0 = out [0];
0411     self->transmit_in_progress = true;
0412     /* TODO: send more then one byte */
0413   }
0414 
0415   return 0;
0416 }
0417 
0418 const console_generic_callbacks mpc55xx_linflex_callbacks = {
0419   .termios_callbacks = {
0420     .firstOpen = mpc55xx_linflex_first_open,
0421     .lastClose = mpc55xx_linflex_last_close,
0422     .write = mpc55xx_linflex_write,
0423     .setAttributes = mpc55xx_linflex_set_attributes,
0424     .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
0425   },
0426   .poll_read = mpc55xx_linflex_poll_read,
0427   .poll_write = mpc55xx_linflex_poll_write
0428 };
0429 
0430 #endif /* MPC55XX_HAS_LINFLEX */