File indexing completed on 2025-05-11 08:24:11
0001
0002
0003
0004
0005
0006
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
0241 case SC16IS752_MODE_RS485_RTS:
0242 efcr |= SC16IS752_EFCR_RTSCON;
0243
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, ®val, 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 }