Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:11

0001 /*
0002  * Copyright (c) 2016 embedded brains GmbH & Co. KG
0003  *
0004  * The license and distribution terms for this file may be
0005  * found in the file LICENSE in this distribution or at
0006  * http://www.rtems.org/license/LICENSE.
0007  */
0008 
0009 
0010 #include <dev/serial/sc16is752.h>
0011 
0012 #include <sys/param.h>
0013 
0014 #include <assert.h>
0015 #include <stdio.h>
0016 #include <fcntl.h>
0017 
0018 #include <rtems/seterr.h>
0019 
0020 #include "sc16is752-regs.h"
0021 
0022 static void write_reg(
0023   sc16is752_context *ctx,
0024   uint8_t addr,
0025   const uint8_t *data,
0026   size_t len
0027 )
0028 {
0029   (*ctx->write_reg)(ctx, addr, data, len);
0030 }
0031 
0032 static void read_reg(
0033   sc16is752_context *ctx,
0034   uint8_t addr,
0035   uint8_t *data,
0036   size_t len
0037 )
0038 {
0039   (*ctx->read_reg)(ctx, addr, data, len);
0040 }
0041 
0042 static void read_2_reg(
0043   sc16is752_context *ctx,
0044   uint8_t addr_0,
0045   uint8_t addr_1,
0046   uint8_t data[2]
0047 )
0048 {
0049   (*ctx->read_2_reg)(ctx, addr_0, addr_1, data);
0050 }
0051 
0052 static bool is_sleep_mode_enabled(sc16is752_context *ctx)
0053 {
0054   return (ctx->ier & SC16IS752_IER_SLEEP_MODE) != 0;
0055 }
0056 
0057 static void set_sleep_mode(sc16is752_context *ctx, bool enable)
0058 {
0059   if (enable) {
0060     ctx->ier |= SC16IS752_IER_SLEEP_MODE;
0061   } else {
0062     ctx->ier &= ~SC16IS752_IER_SLEEP_MODE;
0063   }
0064 
0065   write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
0066 }
0067 
0068 static void set_mcr_dll_dlh(
0069   sc16is752_context *ctx,
0070   uint8_t mcr,
0071   uint32_t divisor
0072 )
0073 {
0074   bool sleep_mode = is_sleep_mode_enabled(ctx);
0075   uint8_t dll = (uint8_t)divisor;
0076   uint8_t dlh = (uint8_t)(divisor >> 8);
0077 
0078   if (sleep_mode) {
0079     set_sleep_mode(ctx, false);
0080   }
0081 
0082   ctx->lcr |= SC16IS752_LCR_ENABLE_DIVISOR;
0083   write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
0084 
0085   write_reg(ctx, SC16IS752_MCR, &mcr, 1);
0086   write_reg(ctx, SC16IS752_DLH, &dlh, 1);
0087   write_reg(ctx, SC16IS752_DLL, &dll, 1);
0088 
0089   ctx->lcr &= ~SC16IS752_LCR_ENABLE_DIVISOR;
0090   write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
0091 
0092   if (sleep_mode) {
0093     set_sleep_mode(ctx, true);
0094   }
0095 }
0096 
0097 static void set_efr(sc16is752_context *ctx, uint8_t efr)
0098 {
0099   uint8_t lcr = ctx->lcr;
0100 
0101   ctx->lcr = 0xbf;
0102   write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
0103 
0104   write_reg(ctx, SC16IS752_EFR, &efr, 1);
0105 
0106   ctx->lcr = lcr;
0107   write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
0108 }
0109 
0110 static void set_tlr(sc16is752_context *ctx, uint8_t tlr)
0111 {
0112   uint8_t mcr;
0113 
0114   read_reg(ctx, SC16IS752_MCR, &mcr, 1);
0115   mcr |= SC16IS752_MCR_TCR_TLR;
0116   write_reg(ctx, SC16IS752_MCR, &mcr, 1);
0117   write_reg(ctx, SC16IS752_TLR, &tlr, 1);
0118   mcr &= ~SC16IS752_MCR_TCR_TLR;
0119   write_reg(ctx, SC16IS752_MCR, &mcr, 1);
0120 }
0121 
0122 static bool set_baud(sc16is752_context *ctx, rtems_termios_baud_t baud)
0123 {
0124   uint32_t freq = ctx->input_frequency;
0125   uint8_t mcr;
0126   uint32_t divisor;
0127 
0128   read_reg(ctx, SC16IS752_MCR, &mcr, 1);
0129 
0130   divisor = freq / baud / 16;
0131   if (divisor > 0xFFFF){
0132     divisor = (freq / (4 * baud)) / 16;
0133     if (divisor > 0xFFFF){
0134       return false;
0135     } else {
0136       mcr |= SC16IS752_MCR_PRESCALE_NEEDED;
0137     }
0138   } else {
0139     mcr &= ~SC16IS752_MCR_PRESCALE_NEEDED;
0140   }
0141 
0142   set_mcr_dll_dlh(ctx, mcr, divisor);
0143   return true;
0144 }
0145 
0146 static bool sc16is752_set_attributes(
0147   rtems_termios_device_context *base,
0148   const struct termios *term
0149 )
0150 {
0151   sc16is752_context *ctx = (sc16is752_context *)base;
0152   rtems_termios_baud_t baud;
0153 
0154   ctx->lcr = 0;
0155 
0156   baud = rtems_termios_baud_to_number(term->c_ospeed);
0157 
0158   if (baud > 0) {
0159     if (!set_baud(ctx, baud)){
0160       return false;
0161     }
0162 
0163     ctx->efcr &= ~SC16IS752_EFCR_TX_DISABLE;
0164 
0165     if ((term->c_cflag & CREAD) == 0){
0166       ctx->efcr |= SC16IS752_EFCR_RX_DISABLE;
0167     } else {
0168       ctx->efcr &= ~SC16IS752_EFCR_RX_DISABLE;
0169     }
0170   } else {
0171     ctx->efcr |= SC16IS752_EFCR_RX_DISABLE | SC16IS752_EFCR_TX_DISABLE;
0172   }
0173 
0174   write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
0175 
0176   switch (term->c_cflag & CSIZE) {
0177     case CS5:
0178       ctx->lcr |= SC16IS752_LCR_CHRL_5_BIT;
0179       break;
0180     case CS6:
0181       ctx->lcr |= SC16IS752_LCR_CHRL_6_BIT;
0182       break;
0183     case CS7:
0184       ctx->lcr |= SC16IS752_LCR_CHRL_7_BIT;
0185       break;
0186     case CS8:
0187       ctx->lcr |= SC16IS752_LCR_CHRL_8_BIT;
0188       break;
0189   }
0190 
0191   if ((term->c_cflag & PARENB) != 0){
0192     ctx->lcr |= SC16IS752_LCR_SET_PARITY;
0193     if ((term->c_cflag & PARODD) != 0) {
0194       ctx->lcr &= ~SC16IS752_LCR_EVEN_PARITY;
0195     } else {
0196       ctx->lcr |= SC16IS752_LCR_EVEN_PARITY;
0197     }
0198   } else {
0199     ctx->lcr &= ~SC16IS752_LCR_SET_PARITY;
0200   }
0201 
0202   if ((term->c_cflag & CSTOPB) != 0) {
0203     ctx->lcr |= SC16IS752_LCR_2_STOP_BIT;
0204   } else {
0205     ctx->lcr &= ~SC16IS752_LCR_2_STOP_BIT;
0206   }
0207 
0208   write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
0209   return true;
0210 }
0211 
0212 static bool sc16is752_first_open(
0213   rtems_termios_tty *tty,
0214   rtems_termios_device_context *base,
0215   struct termios *term,
0216   rtems_libio_open_close_args_t *args
0217 )
0218 {
0219   bool ok;
0220   uint8_t fcr;
0221   uint8_t efcr;
0222 
0223   (void)args;
0224   sc16is752_context *ctx = (sc16is752_context *)base;
0225 
0226   ctx->tty = tty;
0227 
0228   ok = (*ctx->first_open)(ctx);
0229   if (!ok) {
0230     return ok;
0231   }
0232 
0233   set_efr(ctx, SC16IS752_EFR_ENHANCED_FUNC_ENABLE);
0234 
0235   efcr = 0;
0236 
0237   switch (ctx->mode) {
0238     case SC16IS752_MODE_RS485_RTS_INV:
0239       efcr |= SC16IS752_EFCR_RTSINVER;
0240       /* Fall through */
0241     case SC16IS752_MODE_RS485_RTS:
0242       efcr |= SC16IS752_EFCR_RTSCON;
0243       /* Fall through */
0244     case SC16IS752_MODE_RS485:
0245       efcr |= SC16IS752_EFCR_RS485_ENABLE;
0246       break;
0247     default:
0248       break;
0249   }
0250 
0251   ctx->efcr = efcr;
0252   write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
0253 
0254   fcr = SC16IS752_FCR_FIFO_EN
0255     | SC16IS752_FCR_RX_FIFO_RST
0256     | SC16IS752_FCR_TX_FIFO_RST
0257     | SC16IS752_FCR_RX_FIFO_TRG_8
0258     | SC16IS752_FCR_TX_FIFO_TRG_32;
0259   write_reg(ctx, SC16IS752_FCR, &fcr, 1);
0260 
0261   fcr = SC16IS752_FCR_FIFO_EN
0262     | SC16IS752_FCR_RX_FIFO_TRG_8
0263     | SC16IS752_FCR_TX_FIFO_TRG_32;
0264   write_reg(ctx, SC16IS752_FCR, &fcr, 1);
0265 
0266   set_tlr(ctx, 0);
0267 
0268   ctx->ier = SC16IS752_IER_RHR;
0269   write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
0270 
0271   rtems_termios_set_initial_baud(tty, 115200);
0272   ok = sc16is752_set_attributes(base, term);
0273   if (!ok) {
0274     return ok;
0275   }
0276 
0277   ok = (*ctx->install_irq)(ctx);
0278   return ok;
0279 }
0280 
0281 static void sc16is752_last_close(
0282   rtems_termios_tty *tty,
0283   rtems_termios_device_context *base,
0284   rtems_libio_open_close_args_t *args
0285 )
0286 {
0287   sc16is752_context *ctx = (sc16is752_context *)base;
0288 
0289   (void)tty;
0290   (void)args;
0291   (*ctx->last_close)(ctx);
0292 }
0293 
0294 static void sc16is752_write(
0295   rtems_termios_device_context *base,
0296   const char *buf,
0297   size_t len
0298 )
0299 {
0300   sc16is752_context *ctx = (sc16is752_context *)base;
0301 
0302   if (len > 0) {
0303     ctx->ier |= SC16IS752_IER_THR;
0304     len = MIN(len, 32);
0305     ctx->tx_in_progress = (uint8_t)len;
0306     write_reg(ctx, SC16IS752_THR, (const uint8_t *)&buf[0], len);
0307     write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
0308   } else {
0309     ctx->tx_in_progress = 0;
0310     ctx->ier &= ~SC16IS752_IER_THR;
0311     write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
0312   }
0313 }
0314 
0315 static void sc16is752_get_modem_bits(sc16is752_context *ctx, int *bits)
0316 {
0317   *bits = 0;
0318   uint8_t msr;
0319   uint8_t mcr;
0320 
0321   read_reg(ctx, SC16IS752_MSR, &msr, 1);
0322   read_reg(ctx, SC16IS752_MCR, &mcr, 1);
0323 
0324   if (msr & SC16IS752_MSR_CTS) {
0325     *bits |= TIOCM_CTS;
0326   }
0327   if (msr & SC16IS752_MSR_DSR) {
0328     *bits |= TIOCM_DSR;
0329   }
0330   if (msr & SC16IS752_MSR_RI) {
0331     *bits |= TIOCM_RI;
0332   }
0333   if (msr & SC16IS752_MSR_CD) {
0334     *bits |= TIOCM_CD;
0335   }
0336   if ((mcr & SC16IS752_MCR_DTR) == 0) {
0337     *bits |= TIOCM_DTR;
0338   }
0339   if ((mcr & SC16IS752_MCR_RTS) == 0) {
0340     *bits |= TIOCM_RTS;
0341   }
0342 }
0343 
0344 static void sc16is752_set_modem_bits(
0345   sc16is752_context *ctx, int *bits, int set, int clear
0346 )
0347 {
0348   uint8_t mcr;
0349 
0350   read_reg(ctx, SC16IS752_MCR, &mcr, 1);
0351 
0352   if (bits != NULL) {
0353     if ((*bits & TIOCM_DTR) == 0) {
0354       mcr |= SC16IS752_MCR_DTR;
0355     } else {
0356       mcr &= ~SC16IS752_MCR_DTR;
0357     }
0358 
0359     if ((*bits & TIOCM_RTS) == 0) {
0360       mcr |= SC16IS752_MCR_RTS;
0361     } else {
0362       mcr &= ~SC16IS752_MCR_RTS;
0363     }
0364   }
0365 
0366   if ((set & TIOCM_DTR) != 0) {
0367     mcr &= ~SC16IS752_MCR_DTR;
0368   }
0369   if ((set & TIOCM_RTS) != 0) {
0370     mcr &= ~SC16IS752_MCR_RTS;
0371   }
0372   if ((clear & TIOCM_DTR) != 0) {
0373     mcr |= SC16IS752_MCR_DTR;
0374   }
0375   if ((clear & TIOCM_RTS) != 0) {
0376     mcr |= SC16IS752_MCR_RTS;
0377   }
0378 
0379   write_reg(ctx, SC16IS752_MCR, &mcr, 1);
0380 }
0381 
0382 static int sc16is752_ioctl(
0383   rtems_termios_device_context *base,
0384   ioctl_command_t               request,
0385   void                         *buffer
0386 )
0387 {
0388   sc16is752_context *ctx = (sc16is752_context *)base;
0389   uint8_t regval;
0390 
0391   switch (request) {
0392     case SC16IS752_SET_SLEEP_MODE:
0393       set_sleep_mode(ctx, *(int *)buffer != 0);
0394       break;
0395     case SC16IS752_GET_SLEEP_MODE:
0396       *(int *)buffer = is_sleep_mode_enabled(ctx);
0397       break;
0398     case SC16IS752_SET_IOCONTROL:
0399       regval = (*(uint8_t *)buffer) & ~SC16IS752_IOCONTROL_SRESET;
0400       write_reg(ctx, SC16IS752_IOCONTROL, &regval, 1);
0401       break;
0402     case SC16IS752_GET_IOCONTROL:
0403       read_reg(ctx, SC16IS752_IOCONTROL, (uint8_t *)buffer, 1);
0404       break;
0405     case SC16IS752_SET_IODIR:
0406       write_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
0407       break;
0408     case SC16IS752_GET_IODIR:
0409       read_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
0410       break;
0411     case SC16IS752_SET_IOSTATE:
0412       write_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
0413       break;
0414     case SC16IS752_GET_IOSTATE:
0415       read_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
0416       break;
0417     case SC16IS752_SET_EFCR:
0418       write_reg(ctx, SC16IS752_EFCR, (uint8_t *)buffer, 1);
0419       break;
0420     case SC16IS752_GET_EFCR:
0421       read_reg(ctx, SC16IS752_EFCR, (uint8_t *)buffer, 1);
0422       break;
0423     case TIOCMGET:
0424       sc16is752_get_modem_bits(ctx, (int *)buffer);
0425       break;
0426     case TIOCMSET:
0427       sc16is752_set_modem_bits(ctx, (int *)buffer, 0, 0);
0428       break;
0429     case TIOCMBIS:
0430       sc16is752_set_modem_bits(ctx, NULL, *(int *)buffer, 0);
0431       break;
0432     case TIOCMBIC:
0433       sc16is752_set_modem_bits(ctx, NULL, 0, *(int *)buffer);
0434       break;
0435     default:
0436       rtems_set_errno_and_return_minus_one(EINVAL);
0437   }
0438 
0439   return 0;
0440 }
0441 
0442 const rtems_termios_device_handler sc16is752_termios_handler = {
0443   .first_open = sc16is752_first_open,
0444   .last_close = sc16is752_last_close,
0445   .write = sc16is752_write,
0446   .set_attributes = sc16is752_set_attributes,
0447   .ioctl = sc16is752_ioctl,
0448   .mode = TERMIOS_IRQ_SERVER_DRIVEN
0449 };
0450 
0451 void sc16is752_interrupt_handler(void *arg)
0452 {
0453   sc16is752_context *ctx = (sc16is752_context *)arg;
0454   uint8_t data[2];
0455   uint8_t iir;
0456 
0457   read_2_reg(ctx, SC16IS752_IIR, SC16IS752_RXLVL, data);
0458   iir = data[0];
0459 
0460   if ((iir & SC16IS752_IIR_TX_INTERRUPT) != 0 && ctx->tx_in_progress > 0) {
0461     rtems_termios_dequeue_characters(ctx->tty, ctx->tx_in_progress);
0462   }
0463 
0464   if ((iir & SC16IS752_IIR_RX_INTERRUPT) != 0) {
0465     uint8_t buf[SC16IS752_FIFO_DEPTH];
0466     uint8_t rxlvl = data[1];
0467 
0468     rxlvl = MIN(rxlvl, SC16IS752_FIFO_DEPTH);
0469     read_reg(ctx, SC16IS752_RHR, &buf[0], rxlvl);
0470     rtems_termios_enqueue_raw_characters(ctx->tty, (const char *)&buf[0], rxlvl);
0471   }
0472 }