source: rtems/cpukit/dev/serial/sc16is752.c @ 6ff1da40

5
Last change on this file since 6ff1da40 was 6ff1da40, checked in by Sebastian Huber <sebastian.huber@…>, on 06/14/19 at 05:35:21

dev/sc16is752: Add RS485 mode variants

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