source: rtems/cpukit/dev/serial/sc16is752.c @ 255fe43

Last change on this file since 255fe43 was 255fe43, checked in by Joel Sherrill <joel@…>, on 03/01/22 at 20:40:44

cpukit/: Scripted embedded brains header file clean up

Updates #4625.

  • Property mode set to 100644
File size: 10.5 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 * The license and distribution terms for this file may be
5 * found in the file LICENSE in this distribution or at
6 * http://www.rtems.org/license/LICENSE.
7 */
8
9
10#include <dev/serial/sc16is752.h>
11
12#include <sys/param.h>
13
14#include <assert.h>
15#include <stdio.h>
16#include <fcntl.h>
17
18#include <rtems/seterr.h>
19
20#include "sc16is752-regs.h"
21
22static void write_reg(
23  sc16is752_context *ctx,
24  uint8_t addr,
25  const uint8_t *data,
26  size_t len
27)
28{
29  (*ctx->write_reg)(ctx, addr, data, len);
30}
31
32static void read_reg(
33  sc16is752_context *ctx,
34  uint8_t addr,
35  uint8_t *data,
36  size_t len
37)
38{
39  (*ctx->read_reg)(ctx, addr, data, len);
40}
41
42static void read_2_reg(
43  sc16is752_context *ctx,
44  uint8_t addr_0,
45  uint8_t addr_1,
46  uint8_t data[2]
47)
48{
49  (*ctx->read_2_reg)(ctx, addr_0, addr_1, data);
50}
51
52static bool is_sleep_mode_enabled(sc16is752_context *ctx)
53{
54  return (ctx->ier & SC16IS752_IER_SLEEP_MODE) != 0;
55}
56
57static void set_sleep_mode(sc16is752_context *ctx, bool enable)
58{
59  if (enable) {
60    ctx->ier |= SC16IS752_IER_SLEEP_MODE;
61  } else {
62    ctx->ier &= ~SC16IS752_IER_SLEEP_MODE;
63  }
64
65  write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
66}
67
68static void set_mcr_dll_dlh(
69  sc16is752_context *ctx,
70  uint8_t mcr,
71  uint32_t divisor
72)
73{
74  bool sleep_mode = is_sleep_mode_enabled(ctx);
75  uint8_t dll = (uint8_t)divisor;
76  uint8_t dlh = (uint8_t)(divisor >> 8);
77
78  if (sleep_mode) {
79    set_sleep_mode(ctx, false);
80  }
81
82  ctx->lcr |= SC16IS752_LCR_ENABLE_DIVISOR;
83  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
84
85  write_reg(ctx, SC16IS752_MCR, &mcr, 1);
86  write_reg(ctx, SC16IS752_DLH, &dlh, 1);
87  write_reg(ctx, SC16IS752_DLL, &dll, 1);
88
89  ctx->lcr &= ~SC16IS752_LCR_ENABLE_DIVISOR;
90  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
91
92  if (sleep_mode) {
93    set_sleep_mode(ctx, true);
94  }
95}
96
97static void set_efr(sc16is752_context *ctx, uint8_t efr)
98{
99  uint8_t lcr = ctx->lcr;
100
101  ctx->lcr = 0xbf;
102  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
103
104  write_reg(ctx, SC16IS752_EFR, &efr, 1);
105
106  ctx->lcr = lcr;
107  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
108}
109
110static void set_tlr(sc16is752_context *ctx, uint8_t tlr)
111{
112  uint8_t mcr;
113
114  read_reg(ctx, SC16IS752_MCR, &mcr, 1);
115  mcr |= SC16IS752_MCR_TCR_TLR;
116  write_reg(ctx, SC16IS752_MCR, &mcr, 1);
117  write_reg(ctx, SC16IS752_TLR, &tlr, 1);
118  mcr &= ~SC16IS752_MCR_TCR_TLR;
119  write_reg(ctx, SC16IS752_MCR, &mcr, 1);
120}
121
122static bool set_baud(sc16is752_context *ctx, rtems_termios_baud_t baud)
123{
124  uint32_t freq = ctx->input_frequency;
125  uint8_t mcr;
126  uint32_t divisor;
127
128  read_reg(ctx, SC16IS752_MCR, &mcr, 1);
129
130  divisor = freq / baud / 16;
131  if (divisor > 0xFFFF){
132    divisor = (freq / (4 * baud)) / 16;
133    if (divisor > 0xFFFF){
134      return false;
135    } else {
136      mcr |= SC16IS752_MCR_PRESCALE_NEEDED;
137    }
138  } else {
139    mcr &= ~SC16IS752_MCR_PRESCALE_NEEDED;
140  }
141
142  set_mcr_dll_dlh(ctx, mcr, divisor);
143  return true;
144}
145
146static bool sc16is752_set_attributes(
147  rtems_termios_device_context *base,
148  const struct termios *term
149)
150{
151  sc16is752_context *ctx = (sc16is752_context *)base;
152  rtems_termios_baud_t baud;
153
154  ctx->lcr = 0;
155
156  baud = rtems_termios_baud_to_number(term->c_ospeed);
157
158  if (baud > 0) {
159    if (!set_baud(ctx, baud)){
160      return false;
161    }
162
163    ctx->efcr &= ~SC16IS752_EFCR_TX_DISABLE;
164
165    if ((term->c_cflag & CREAD) == 0){
166      ctx->efcr |= SC16IS752_EFCR_RX_DISABLE;
167    } else {
168      ctx->efcr &= ~SC16IS752_EFCR_RX_DISABLE;
169    }
170  } else {
171    ctx->efcr |= SC16IS752_EFCR_RX_DISABLE | SC16IS752_EFCR_TX_DISABLE;
172  }
173
174  write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
175
176  switch (term->c_cflag & CSIZE) {
177    case CS5:
178      ctx->lcr |= SC16IS752_LCR_CHRL_5_BIT;
179      break;
180    case CS6:
181      ctx->lcr |= SC16IS752_LCR_CHRL_6_BIT;
182      break;
183    case CS7:
184      ctx->lcr |= SC16IS752_LCR_CHRL_7_BIT;
185      break;
186    case CS8:
187      ctx->lcr |= SC16IS752_LCR_CHRL_8_BIT;
188      break;
189  }
190
191  if ((term->c_cflag & PARENB) != 0){
192    ctx->lcr |= SC16IS752_LCR_SET_PARITY;
193    if ((term->c_cflag & PARODD) != 0) {
194      ctx->lcr &= ~SC16IS752_LCR_EVEN_PARITY;
195    } else {
196      ctx->lcr |= SC16IS752_LCR_EVEN_PARITY;
197    }
198  } else {
199    ctx->lcr &= ~SC16IS752_LCR_SET_PARITY;
200  }
201
202  if ((term->c_cflag & CSTOPB) != 0) {
203    ctx->lcr |= SC16IS752_LCR_2_STOP_BIT;
204  } else {
205    ctx->lcr &= ~SC16IS752_LCR_2_STOP_BIT;
206  }
207
208  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
209  return true;
210}
211
212static bool sc16is752_first_open(
213  rtems_termios_tty *tty,
214  rtems_termios_device_context *base,
215  struct termios *term,
216  rtems_libio_open_close_args_t *args
217)
218{
219  bool ok;
220  uint8_t fcr;
221  uint8_t efcr;
222
223  (void)args;
224  sc16is752_context *ctx = (sc16is752_context *)base;
225
226  ctx->tty = tty;
227
228  ok = (*ctx->first_open)(ctx);
229  if (!ok) {
230    return ok;
231  }
232
233  set_efr(ctx, SC16IS752_EFR_ENHANCED_FUNC_ENABLE);
234
235  efcr = 0;
236
237  switch (ctx->mode) {
238    case SC16IS752_MODE_RS485_RTS_INV:
239      efcr |= SC16IS752_EFCR_RTSINVER;
240      /* Fall through */
241    case SC16IS752_MODE_RS485_RTS:
242      efcr |= SC16IS752_EFCR_RTSCON;
243      /* Fall through */
244    case SC16IS752_MODE_RS485:
245      efcr |= SC16IS752_EFCR_RS485_ENABLE;
246      break;
247    default:
248      break;
249  }
250
251  ctx->efcr = efcr;
252  write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
253
254  fcr = SC16IS752_FCR_FIFO_EN
255    | SC16IS752_FCR_RX_FIFO_RST
256    | SC16IS752_FCR_TX_FIFO_RST
257    | SC16IS752_FCR_RX_FIFO_TRG_8
258    | SC16IS752_FCR_TX_FIFO_TRG_32;
259  write_reg(ctx, SC16IS752_FCR, &fcr, 1);
260
261  fcr = SC16IS752_FCR_FIFO_EN
262    | SC16IS752_FCR_RX_FIFO_TRG_8
263    | SC16IS752_FCR_TX_FIFO_TRG_32;
264  write_reg(ctx, SC16IS752_FCR, &fcr, 1);
265
266  set_tlr(ctx, 0);
267
268  ctx->ier = SC16IS752_IER_RHR;
269  write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
270
271  rtems_termios_set_initial_baud(tty, 115200);
272  ok = sc16is752_set_attributes(base, term);
273  if (!ok) {
274    return ok;
275  }
276
277  ok = (*ctx->install_irq)(ctx);
278  return ok;
279}
280
281static void sc16is752_last_close(
282  rtems_termios_tty *tty,
283  rtems_termios_device_context *base,
284  rtems_libio_open_close_args_t *args
285)
286{
287  sc16is752_context *ctx = (sc16is752_context *)base;
288
289  (void)tty;
290  (void)args;
291  (*ctx->last_close)(ctx);
292}
293
294static void sc16is752_write(
295  rtems_termios_device_context *base,
296  const char *buf,
297  size_t len
298)
299{
300  sc16is752_context *ctx = (sc16is752_context *)base;
301
302  if (len > 0) {
303    ctx->ier |= SC16IS752_IER_THR;
304    len = MIN(len, 32);
305    ctx->tx_in_progress = (uint8_t)len;
306    write_reg(ctx, SC16IS752_THR, (const uint8_t *)&buf[0], len);
307    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
308  } else {
309    ctx->tx_in_progress = 0;
310    ctx->ier &= ~SC16IS752_IER_THR;
311    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
312  }
313}
314
315static void sc16is752_get_modem_bits(sc16is752_context *ctx, int *bits)
316{
317  *bits = 0;
318  uint8_t msr;
319  uint8_t mcr;
320
321  read_reg(ctx, SC16IS752_MSR, &msr, 1);
322  read_reg(ctx, SC16IS752_MCR, &mcr, 1);
323
324  if (msr & SC16IS752_MSR_CTS) {
325    *bits |= TIOCM_CTS;
326  }
327  if (msr & SC16IS752_MSR_DSR) {
328    *bits |= TIOCM_DSR;
329  }
330  if (msr & SC16IS752_MSR_RI) {
331    *bits |= TIOCM_RI;
332  }
333  if (msr & SC16IS752_MSR_CD) {
334    *bits |= TIOCM_CD;
335  }
336  if ((mcr & SC16IS752_MCR_DTR) == 0) {
337    *bits |= TIOCM_DTR;
338  }
339  if ((mcr & SC16IS752_MCR_RTS) == 0) {
340    *bits |= TIOCM_RTS;
341  }
342}
343
344static void sc16is752_set_modem_bits(
345  sc16is752_context *ctx, int *bits, int set, int clear
346)
347{
348  uint8_t mcr;
349
350  read_reg(ctx, SC16IS752_MCR, &mcr, 1);
351
352  if (bits != NULL) {
353    if ((*bits & TIOCM_DTR) == 0) {
354      mcr |= SC16IS752_MCR_DTR;
355    } else {
356      mcr &= ~SC16IS752_MCR_DTR;
357    }
358
359    if ((*bits & TIOCM_RTS) == 0) {
360      mcr |= SC16IS752_MCR_RTS;
361    } else {
362      mcr &= ~SC16IS752_MCR_RTS;
363    }
364  }
365
366  if ((set & TIOCM_DTR) != 0) {
367    mcr &= ~SC16IS752_MCR_DTR;
368  }
369  if ((set & TIOCM_RTS) != 0) {
370    mcr &= ~SC16IS752_MCR_RTS;
371  }
372  if ((clear & TIOCM_DTR) != 0) {
373    mcr |= SC16IS752_MCR_DTR;
374  }
375  if ((clear & TIOCM_RTS) != 0) {
376    mcr |= SC16IS752_MCR_RTS;
377  }
378
379  write_reg(ctx, SC16IS752_MCR, &mcr, 1);
380}
381
382static int sc16is752_ioctl(
383  rtems_termios_device_context *base,
384  ioctl_command_t               request,
385  void                         *buffer
386)
387{
388  sc16is752_context *ctx = (sc16is752_context *)base;
389  uint8_t regval;
390
391  switch (request) {
392    case SC16IS752_SET_SLEEP_MODE:
393      set_sleep_mode(ctx, *(int *)buffer != 0);
394      break;
395    case SC16IS752_GET_SLEEP_MODE:
396      *(int *)buffer = is_sleep_mode_enabled(ctx);
397      break;
398    case SC16IS752_SET_IOCONTROL:
399      regval = (*(uint8_t *)buffer) & ~SC16IS752_IOCONTROL_SRESET;
400      write_reg(ctx, SC16IS752_IOCONTROL, &regval, 1);
401      break;
402    case SC16IS752_GET_IOCONTROL:
403      read_reg(ctx, SC16IS752_IOCONTROL, (uint8_t *)buffer, 1);
404      break;
405    case SC16IS752_SET_IODIR:
406      write_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
407      break;
408    case SC16IS752_GET_IODIR:
409      read_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
410      break;
411    case SC16IS752_SET_IOSTATE:
412      write_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
413      break;
414    case SC16IS752_GET_IOSTATE:
415      read_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
416      break;
417    case SC16IS752_SET_EFCR:
418      write_reg(ctx, SC16IS752_EFCR, (uint8_t *)buffer, 1);
419      break;
420    case SC16IS752_GET_EFCR:
421      read_reg(ctx, SC16IS752_EFCR, (uint8_t *)buffer, 1);
422      break;
423    case TIOCMGET:
424      sc16is752_get_modem_bits(ctx, (int *)buffer);
425      break;
426    case TIOCMSET:
427      sc16is752_set_modem_bits(ctx, (int *)buffer, 0, 0);
428      break;
429    case TIOCMBIS:
430      sc16is752_set_modem_bits(ctx, NULL, *(int *)buffer, 0);
431      break;
432    case TIOCMBIC:
433      sc16is752_set_modem_bits(ctx, NULL, 0, *(int *)buffer);
434      break;
435    default:
436      rtems_set_errno_and_return_minus_one(EINVAL);
437  }
438
439  return 0;
440}
441
442const rtems_termios_device_handler sc16is752_termios_handler = {
443  .first_open = sc16is752_first_open,
444  .last_close = sc16is752_last_close,
445  .write = sc16is752_write,
446  .set_attributes = sc16is752_set_attributes,
447  .ioctl = sc16is752_ioctl,
448  .mode = TERMIOS_IRQ_SERVER_DRIVEN
449};
450
451void sc16is752_interrupt_handler(void *arg)
452{
453  sc16is752_context *ctx = (sc16is752_context *)arg;
454  uint8_t data[2];
455  uint8_t iir;
456
457  read_2_reg(ctx, SC16IS752_IIR, SC16IS752_RXLVL, data);
458  iir = data[0];
459
460  if ((iir & SC16IS752_IIR_TX_INTERRUPT) != 0 && ctx->tx_in_progress > 0) {
461    rtems_termios_dequeue_characters(ctx->tty, ctx->tx_in_progress);
462  }
463
464  if ((iir & SC16IS752_IIR_RX_INTERRUPT) != 0) {
465    uint8_t buf[SC16IS752_FIFO_DEPTH];
466    uint8_t rxlvl = data[1];
467
468    rxlvl = MIN(rxlvl, SC16IS752_FIFO_DEPTH);
469    read_reg(ctx, SC16IS752_RHR, &buf[0], rxlvl);
470    rtems_termios_enqueue_raw_characters(ctx->tty, (const char *)&buf[0], rxlvl);
471  }
472}
Note: See TracBrowser for help on using the repository browser.