source: rtems/cpukit/dev/serial/sc16is752.c @ bf70702

5
Last change on this file since bf70702 was bf70702, checked in by Christian Mauderer <christian.mauderer@…>, on 02/09/18 at 07:59:27

dev/sc16is752: Add GPIO access via ioctl.

  • Property mode set to 100644
File size: 7.5 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 & IER_SLEEP_MODE) != 0;
61}
62
63static void set_sleep_mode(sc16is752_context *ctx, bool enable)
64{
65  if (enable) {
66    ctx->ier |= IER_SLEEP_MODE;
67  } else {
68    ctx->ier &= ~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 |= 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 &= ~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 |= MCR_PRESCALE_NEEDED;
131    }
132  } else {
133    mcr &= ~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  bool baud_successful;
147  rtems_termios_baud_t baud;
148
149  ctx->lcr = 0;
150
151  baud = rtems_termios_baud_to_number(term->c_ospeed);
152  baud_successful = set_baud(ctx, baud);
153  if (!baud_successful){
154    return false;
155  }
156
157  if ((term->c_cflag & CREAD) == 0){
158    ctx->efcr |= EFCR_RX_DISABLE;
159  } else {
160    ctx->efcr &= ~EFCR_RX_DISABLE;
161  }
162
163  write_reg(ctx, SC16IS752_EFCR, &ctx->efcr, 1);
164
165  switch (term->c_cflag & CSIZE) {
166    case CS5:
167      ctx->lcr |= LCR_CHRL_5_BIT;
168      break;
169    case CS6:
170      ctx->lcr |= LCR_CHRL_6_BIT;
171      break;
172    case CS7:
173      ctx->lcr |= LCR_CHRL_7_BIT;
174      break;
175    case CS8:
176      ctx->lcr |= LCR_CHRL_8_BIT;
177      break;
178  }
179
180  if ((term->c_cflag & PARENB) != 0){
181    if ((term->c_cflag & PARODD) != 0) {
182      ctx->lcr &= ~LCR_EVEN_PARITY;
183    } else {
184      ctx->lcr |= LCR_EVEN_PARITY;
185    }
186  } else {
187    ctx->lcr &= ~LCR_SET_PARITY;
188  }
189
190  if ((term->c_cflag & CSTOPB) != 0) {
191    ctx->lcr |= LCR_2_STOP_BIT;
192  } else {
193    ctx->lcr &= ~LCR_2_STOP_BIT;
194  }
195
196  write_reg(ctx, SC16IS752_LCR, &ctx->lcr, 1);
197  return true;
198}
199
200static bool sc16is752_first_open(
201  rtems_termios_tty *tty,
202  rtems_termios_device_context *base,
203  struct termios *term,
204  rtems_libio_open_close_args_t *args
205)
206{
207  bool ok;
208  uint8_t fcr;
209
210  (void)args;
211  sc16is752_context *ctx = (sc16is752_context *)base;
212
213  ctx->tty = tty;
214
215  ok = (*ctx->first_open)(ctx);
216  if (!ok) {
217    return ok;
218  }
219
220  if (ctx->mode == SC16IS752_MODE_RS485) {
221    ctx->efcr = EFCR_RS485_ENABLE;
222  } else {
223    ctx->efcr = 0;
224  }
225
226  write_reg(ctx, SC16IS752_FCR, &ctx->efcr, 1);
227
228  fcr = FCR_FIFO_EN | FCR_RX_FIFO_RST | FCR_TX_FIFO_RST
229    | FCR_RX_FIFO_TRG_16 | FCR_TX_FIFO_TRG_32;
230  write_reg(ctx, SC16IS752_FCR, &fcr, 1);
231
232  ctx->ier = IER_RHR;
233  write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
234  set_efr(ctx, EFR_ENHANCED_FUNC_ENABLE);
235
236  rtems_termios_set_initial_baud(tty, 115200);
237  sc16is752_set_attributes(base, term);
238
239  (*ctx->install_irq)(ctx);
240
241  return true;
242}
243
244static void sc16is752_last_close(
245  rtems_termios_tty *tty,
246  rtems_termios_device_context *base,
247  rtems_libio_open_close_args_t *args
248)
249{
250  sc16is752_context *ctx = (sc16is752_context *)base;
251
252  (void)tty;
253  (void)args;
254  (*ctx->last_close)(ctx);
255}
256
257static void sc16is752_write(
258  rtems_termios_device_context *base,
259  const char *buf,
260  size_t len
261)
262{
263  sc16is752_context *ctx = (sc16is752_context *)base;
264
265  if (len > 0) {
266    ctx->ier |= IER_THR;
267    len = MIN(len, 32);
268    ctx->tx_in_progress = (uint8_t)len;
269    write_reg(ctx, SC16IS752_THR, (const uint8_t *)&buf[0], len);
270    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
271  } else {
272    ctx->tx_in_progress = 0;
273    ctx->ier &= ~IER_THR;
274    write_reg(ctx, SC16IS752_IER, &ctx->ier, 1);
275  }
276}
277
278static int sc16is752_ioctl(
279  rtems_termios_device_context *base,
280  ioctl_command_t               request,
281  void                         *buffer
282)
283{
284  sc16is752_context *ctx = (sc16is752_context *)base;
285  uint8_t regval;
286
287  switch (request) {
288    case SC16IS752_SET_SLEEP_MODE:
289      set_sleep_mode(ctx, *(int *)buffer != 0);
290      break;
291    case SC16IS752_GET_SLEEP_MODE:
292      *(int *)buffer = is_sleep_mode_enabled(ctx);
293      break;
294    case SC16IS752_SET_IOCONTROL:
295      regval = (*(uint8_t *)buffer) & ~SC16IS752_IOCONTROL_SRESET;
296      write_reg(ctx, SC16IS752_IOCONTROL, &regval, 1);
297      break;
298    case SC16IS752_GET_IOCONTROL:
299      read_reg(ctx, SC16IS752_IOCONTROL, (uint8_t *)buffer, 1);
300      break;
301    case SC16IS752_SET_IODIR:
302      write_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
303      break;
304    case SC16IS752_GET_IODIR:
305      read_reg(ctx, SC16IS752_IODIR, (uint8_t *)buffer, 1);
306      break;
307    case SC16IS752_SET_IOSTATE:
308      write_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
309      break;
310    case SC16IS752_GET_IOSTATE:
311      read_reg(ctx, SC16IS752_IOSTATE, (uint8_t *)buffer, 1);
312      break;
313    default:
314      rtems_set_errno_and_return_minus_one(EINVAL);
315  }
316
317  return 0;
318}
319
320const rtems_termios_device_handler sc16is752_termios_handler = {
321  .first_open = sc16is752_first_open,
322  .last_close = sc16is752_last_close,
323  .write = sc16is752_write,
324  .set_attributes = sc16is752_set_attributes,
325  .ioctl = sc16is752_ioctl,
326  .mode = TERMIOS_IRQ_SERVER_DRIVEN
327};
328
329void sc16is752_interrupt_handler(void *arg)
330{
331  sc16is752_context *ctx = (sc16is752_context *)arg;
332  uint8_t data[2];
333  uint8_t iir;
334
335  read_2_reg(ctx, SC16IS752_IIR, SC16IS752_RXLVL, data);
336  iir = data[0];
337
338  if ((iir & IIR_TX_INTERRUPT) != 0 && ctx->tx_in_progress > 0) {
339    rtems_termios_dequeue_characters(ctx->tty, ctx->tx_in_progress);
340  }
341
342  if ((iir & IIR_RX_INTERRUPT) != 0) {
343    uint8_t buf[SC16IS752_FIFO_DEPTH];
344    uint8_t rxlvl = data[1];
345
346    rxlvl = MIN(rxlvl, SC16IS752_FIFO_DEPTH);
347    read_reg(ctx, SC16IS752_RHR, &buf[0], rxlvl);
348    rtems_termios_enqueue_raw_characters(ctx->tty, (const char *)&buf[0], rxlvl);
349  }
350}
Note: See TracBrowser for help on using the repository browser.