meson_uart.c revision 1.4 1 /* $NetBSD: meson_uart.c,v 1.4 2021/01/15 18:42:40 ryo Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "opt_console.h"
33 #include "locators.h"
34
35 #include <sys/cdefs.h>
36
37 __KERNEL_RCSID(1, "$NetBSD: meson_uart.c,v 1.4 2021/01/15 18:42:40 ryo Exp $");
38
39 #define cn_trap() \
40 do { \
41 console_debugger(); \
42 cn_trapped = 1; \
43 } while (/* CONSTCOND */ 0)
44
45 #include <sys/param.h>
46 #include <sys/bus.h>
47 #include <sys/device.h>
48 #include <sys/conf.h>
49 #include <sys/intr.h>
50 #include <sys/systm.h>
51 #include <sys/time.h>
52 #include <sys/termios.h>
53 #include <sys/kauth.h>
54 #include <sys/lwp.h>
55 #include <sys/tty.h>
56
57 #include <dev/cons.h>
58
59 #include <dev/fdt/fdtvar.h>
60
61 #include <arm/amlogic/meson_uart.h>
62
63 static int meson_uart_match(device_t, cfdata_t, void *);
64 static void meson_uart_attach(device_t, device_t, void *);
65
66 static int meson_uart_intr(void *);
67 static void meson_uart_rxsoft(void *);
68
69 static int meson_uart_cngetc(dev_t);
70 static void meson_uart_cnputc(dev_t, int);
71 static void meson_uart_cnpollc(dev_t, int);
72
73 static void meson_uart_start(struct tty *);
74 static int meson_uart_param(struct tty *, struct termios *);
75
76 extern struct cfdriver mesonuart_cd;
77
78 static const char * const compatible[] = {
79 "amlogic,meson6-uart",
80 "amlogic,meson8-uart",
81 "amlogic,meson8b-uart",
82 "amlogic,meson-gx-uart",
83 NULL
84 };
85
86 struct meson_uart_softc {
87 device_t sc_dev;
88 bus_space_tag_t sc_bst;
89 bus_space_handle_t sc_bsh;
90 kmutex_t sc_intr_lock;
91 void *sc_ih;
92 void *sc_sih;
93
94 struct tty *sc_tty;
95
96 int sc_ospeed;
97 unsigned int sc_rbuf_w; /* write ptr of sc_rbuf[] */
98 unsigned int sc_rbuf_r; /* read ptr of sc_rbuf[] */
99 tcflag_t sc_cflag;
100
101 u_char sc_buf[1024];
102 #define MESON_RBUFSZ 128 /* must be 2^n */
103 u_char sc_rbuf[MESON_RBUFSZ]; /* good enough for sizeof RXFIFO */
104 };
105
106 static int meson_uart_console_phandle = -1;
107
108 static struct meson_uart_softc meson_uart_cnsc;
109
110 static struct cnm_state meson_uart_cnm_state;
111
112 struct consdev meson_uart_consdev = {
113 .cn_getc = meson_uart_cngetc,
114 .cn_putc = meson_uart_cnputc,
115 .cn_pollc = meson_uart_cnpollc,
116 .cn_dev = NODEV,
117 .cn_pri = CN_NORMAL,
118 };
119
120 static dev_type_open(meson_uart_open);
121 static dev_type_open(meson_uart_close);
122 static dev_type_read(meson_uart_read);
123 static dev_type_write(meson_uart_write);
124 static dev_type_ioctl(meson_uart_ioctl);
125 static dev_type_tty(meson_uart_tty);
126 static dev_type_poll(meson_uart_poll);
127 static dev_type_stop(meson_uart_stop);
128
129 const struct cdevsw mesonuart_cdevsw = {
130 .d_open = meson_uart_open,
131 .d_close = meson_uart_close,
132 .d_read = meson_uart_read,
133 .d_write = meson_uart_write,
134 .d_ioctl = meson_uart_ioctl,
135 .d_stop = meson_uart_stop,
136 .d_tty = meson_uart_tty,
137 .d_poll = meson_uart_poll,
138 .d_mmap = nommap,
139 .d_kqfilter = ttykqfilter,
140 .d_discard = nodiscard,
141 .d_flag = D_TTY
142 };
143
144 static int meson_uart_cmajor = -1;
145
146 CFATTACH_DECL_NEW(meson_uart, sizeof(struct meson_uart_softc),
147 meson_uart_match, meson_uart_attach, NULL, NULL);
148
149 static int
150 meson_uart_match(device_t parent, cfdata_t cf, void *aux)
151 {
152 struct fdt_attach_args * const faa = aux;
153
154 return of_match_compatible(faa->faa_phandle, compatible);
155 }
156
157 static void
158 meson_uart_attach(device_t parent, device_t self, void *aux)
159 {
160 struct meson_uart_softc * const sc = device_private(self);
161 struct fdt_attach_args * const faa = aux;
162 const int phandle = faa->faa_phandle;
163 char intrstr[128];
164 bus_addr_t addr;
165 bus_size_t size;
166 struct tty *tp;
167 int major, minor, error;
168 uint32_t misc, control;
169
170 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
171 aprint_error(": couldn't get registers\n");
172 return;
173 }
174
175 sc->sc_dev = self;
176 sc->sc_bst = faa->faa_bst;
177
178 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
179 if (error != 0) {
180 aprint_error(": couldn't map registers\n");
181 return;
182 }
183
184 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
185 aprint_error(": failed to decode interrupt\n");
186 return;
187 }
188
189 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SERIAL);
190 sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SERIAL,
191 FDT_INTR_MPSAFE, meson_uart_intr, sc, device_xname(self));
192 if (sc->sc_ih == NULL) {
193 aprint_error(": failed to establish interrupt on %s\n",
194 intrstr);
195 return;
196 }
197
198 sc->sc_sih = softint_establish(SOFTINT_SERIAL, meson_uart_rxsoft, sc);
199 if (sc->sc_sih == NULL) {
200 aprint_error(": failed to establish softint\n");
201 return;
202 }
203
204 if (meson_uart_cmajor == -1) {
205 /* allocate a major number */
206 int bmajor = -1, cmajor = -1;
207 error = devsw_attach("mesonuart", NULL, &bmajor,
208 &mesonuart_cdevsw, &cmajor);
209 if (error) {
210 aprint_error(": couldn't allocate major number\n");
211 return;
212 }
213 meson_uart_cmajor = cmajor;
214 }
215
216 major = cdevsw_lookup_major(&mesonuart_cdevsw);
217 minor = device_unit(self);
218
219 tp = sc->sc_tty = tty_alloc();
220 tp->t_oproc = meson_uart_start;
221 tp->t_param = meson_uart_param;
222 tp->t_dev = makedev(major, minor);
223 tp->t_sc = sc;
224 tty_attach(tp);
225
226 aprint_naive("\n");
227 if (meson_uart_console_phandle == phandle) {
228 cn_tab->cn_dev = tp->t_dev;
229 aprint_normal(": console");
230 }
231 aprint_normal("\n");
232
233 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
234
235 misc = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG);
236 misc &= ~UART_MISC_TX_IRQ_CNT;
237 misc |= __SHIFTIN(0, UART_MISC_TX_IRQ_CNT);
238 misc &= ~UART_MISC_RX_IRQ_CNT;
239 misc |= __SHIFTIN(1, UART_MISC_RX_IRQ_CNT);
240 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG, misc);
241
242 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG);
243 control &= ~(UART_CONTROL_TX_INT_EN|UART_CONTROL_RX_INT_EN);
244 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control);
245 }
246
247 static int
248 meson_uart_cngetc(dev_t dev)
249 {
250 bus_space_tag_t bst = meson_uart_cnsc.sc_bst;
251 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh;
252 uint32_t status;
253 int s, c;
254
255 s = splserial();
256
257 status = bus_space_read_4(bst, bsh, UART_STATUS_REG);
258 if (status & UART_STATUS_RX_EMPTY) {
259 splx(s);
260 return -1;
261 }
262
263 c = bus_space_read_4(bst, bsh, UART_RFIFO_REG);
264 #if defined(DDB)
265 extern int db_active;
266 if (!db_active)
267 #endif
268 {
269 int cn_trapped __unused = 0;
270 cn_check_magic(dev, c, meson_uart_cnm_state);
271 }
272
273 splx(s);
274
275 return c & 0xff;
276 }
277
278 static void
279 meson_uart_cnputc(dev_t dev, int c)
280 {
281 bus_space_tag_t bst = meson_uart_cnsc.sc_bst;
282 bus_space_handle_t bsh = meson_uart_cnsc.sc_bsh;
283 int s;
284
285 s = splserial();
286
287 while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0)
288 ;
289
290 bus_space_write_4(bst, bsh, UART_WFIFO_REG, c);
291
292 splx(s);
293 }
294
295
296 static void
297 meson_uart_cnpollc(dev_t dev, int on)
298 {
299 }
300
301 static int
302 meson_uart_open(dev_t dev, int flag, int mode, lwp_t *l)
303 {
304 struct meson_uart_softc *sc =
305 device_lookup_private(&mesonuart_cd, minor(dev));
306 struct tty *tp = sc->sc_tty;
307 uint32_t control;
308
309 if (kauth_authorize_device_tty(l->l_cred,
310 KAUTH_DEVICE_TTY_OPEN, tp) != 0) {
311 return EBUSY;
312 }
313
314 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
315 tp->t_dev = dev;
316 ttychars(tp);
317 tp->t_iflag = TTYDEF_IFLAG;
318 tp->t_oflag = TTYDEF_OFLAG;
319 tp->t_cflag = TTYDEF_CFLAG;
320 tp->t_lflag = TTYDEF_LFLAG;
321 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
322 ttsetwater(tp);
323
324 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG);
325 control |= UART_CONTROL_RX_INT_EN;
326 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control);
327 }
328 tp->t_state |= TS_CARR_ON;
329
330 return tp->t_linesw->l_open(dev, tp);
331 }
332
333 static int
334 meson_uart_close(dev_t dev, int flag, int mode, lwp_t *l)
335 {
336 struct meson_uart_softc *sc =
337 device_lookup_private(&mesonuart_cd, minor(dev));
338 struct tty *tp = sc->sc_tty;
339 uint32_t control;
340
341 tp->t_linesw->l_close(tp, flag);
342 ttyclose(tp);
343
344 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
345 control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG);
346 control &= ~UART_CONTROL_RX_INT_EN;
347 bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control);
348 }
349
350 return 0;
351 }
352
353 static int
354 meson_uart_read(dev_t dev, struct uio *uio, int flag)
355 {
356 struct meson_uart_softc *sc =
357 device_lookup_private(&mesonuart_cd, minor(dev));
358 struct tty *tp = sc->sc_tty;
359
360 return tp->t_linesw->l_read(tp, uio, flag);
361 }
362
363 static int
364 meson_uart_write(dev_t dev, struct uio *uio, int flag)
365 {
366 struct meson_uart_softc *sc =
367 device_lookup_private(&mesonuart_cd, minor(dev));
368 struct tty *tp = sc->sc_tty;
369
370 return tp->t_linesw->l_write(tp, uio, flag);
371 }
372
373 static int
374 meson_uart_poll(dev_t dev, int events, lwp_t *l)
375 {
376 struct meson_uart_softc *sc =
377 device_lookup_private(&mesonuart_cd, minor(dev));
378 struct tty *tp = sc->sc_tty;
379
380 return tp->t_linesw->l_poll(tp, events, l);
381 }
382
383 static int
384 meson_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
385 {
386 struct meson_uart_softc *sc =
387 device_lookup_private(&mesonuart_cd, minor(dev));
388 struct tty *tp = sc->sc_tty;
389 int error;
390
391 error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
392 if (error != EPASSTHROUGH)
393 return error;
394
395 return ttioctl(tp, cmd, data, flag, l);
396 }
397
398 static struct tty *
399 meson_uart_tty(dev_t dev)
400 {
401 struct meson_uart_softc *sc =
402 device_lookup_private(&mesonuart_cd, minor(dev));
403
404 return sc->sc_tty;
405 }
406
407 static void
408 meson_uart_stop(struct tty *tp, int flag)
409 {
410 }
411
412 static void
413 meson_uart_start(struct tty *tp)
414 {
415 struct meson_uart_softc *sc = tp->t_sc;
416 u_char *p = sc->sc_buf;
417 int s, brem;
418
419 s = spltty();
420
421 if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) {
422 splx(s);
423 return;
424 }
425 tp->t_state |= TS_BUSY;
426
427 splx(s);
428
429 for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf));
430 brem > 0;
431 brem--, p++) {
432 while ((bus_space_read_4(sc->sc_bst, sc->sc_bsh,
433 UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0)
434 ;
435
436 bus_space_write_4(sc->sc_bst, sc->sc_bsh,
437 UART_WFIFO_REG, *p);
438 }
439
440 s = spltty();
441 tp->t_state &= ~TS_BUSY;
442 if (ttypull(tp)) {
443 tp->t_state |= TS_TIMEOUT;
444 callout_schedule(&tp->t_rstrt_ch, 1);
445 }
446 splx(s);
447 }
448
449 static int
450 meson_uart_param(struct tty *tp, struct termios *t)
451 {
452
453 tp->t_ispeed = t->c_ispeed;
454 tp->t_ospeed = t->c_ospeed;
455 tp->t_cflag = t->c_cflag;
456
457 return 0;
458 }
459
460 static int
461 meson_uart_intr(void *priv)
462 {
463 struct meson_uart_softc *sc = priv;
464 struct tty *tp = sc->sc_tty;
465 uint32_t status, c;
466
467 mutex_spin_enter(&sc->sc_intr_lock);
468 for (;;) {
469 int cn_trapped = 0;
470 status = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
471 UART_STATUS_REG);
472 if (status & UART_STATUS_RX_EMPTY) {
473 break;
474 }
475 if (status & UART_STATUS_BREAK) {
476 cn_check_magic(tp->t_dev, CNC_BREAK,
477 meson_uart_cnm_state);
478 if (cn_trapped)
479 continue;
480 }
481
482 c = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_RFIFO_REG);
483 cn_check_magic(tp->t_dev, c & 0xff, meson_uart_cnm_state);
484 if (cn_trapped)
485 continue;
486
487 if ((sc->sc_rbuf_w - sc->sc_rbuf_r) >= (MESON_RBUFSZ - 1))
488 continue;
489 sc->sc_rbuf[sc->sc_rbuf_w++ & (MESON_RBUFSZ - 1)] = c;
490 }
491 mutex_spin_exit(&sc->sc_intr_lock);
492
493 if (sc->sc_rbuf_w != sc->sc_rbuf_r)
494 softint_schedule(sc->sc_sih);
495
496 return 0;
497 }
498
499 static void
500 meson_uart_rxsoft(void *priv)
501 {
502 struct meson_uart_softc *sc = priv;
503 struct tty *tp = sc->sc_tty;
504 int c;
505
506 while (sc->sc_rbuf_w != sc->sc_rbuf_r) {
507 c = sc->sc_rbuf[sc->sc_rbuf_r++ & (MESON_RBUFSZ - 1)];
508 if (tp->t_linesw->l_rint(c & 0xff, tp) == -1)
509 break;
510 }
511 }
512
513 static int
514 meson_uart_console_match(int phandle)
515 {
516 return of_match_compatible(phandle, compatible);
517 }
518
519 static void
520 meson_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
521 {
522 struct meson_uart_softc *sc = &meson_uart_cnsc;
523 const int phandle = faa->faa_phandle;
524 bus_addr_t addr;
525 bus_size_t size;
526 int error;
527
528 fdtbus_get_reg(phandle, 0, &addr, &size);
529
530 sc->sc_bst = faa->faa_bst;
531 sc->sc_ospeed = fdtbus_get_stdout_speed();
532 if (sc->sc_ospeed < 0)
533 sc->sc_ospeed = 115200;
534 sc->sc_cflag = fdtbus_get_stdout_flags();
535
536 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
537 if (error != 0)
538 panic("failed to map console, error = %d", error);
539
540 cn_tab = &meson_uart_consdev;
541 cn_init_magic(&meson_uart_cnm_state);
542 cn_set_magic("\047\001");
543
544 meson_uart_console_phandle = phandle;
545 }
546
547 static const struct fdt_console meson_uart_console = {
548 .match = meson_uart_console_match,
549 .consinit = meson_uart_console_consinit,
550 };
551
552 FDT_CONSOLE(meson_uart, &meson_uart_console);
553