From 71b542c7f7e9615ab1d254b69c4080d34bf5154f Mon Sep 17 00:00:00 2001 From: Aleksander Kaminski Date: Fri, 28 Jun 2024 16:57:01 +0200 Subject: [PATCH] Add MCX N94x multidrv JIRA: RTOS-881 --- _targets/Makefile.armv8m33-mcxn94x | 9 + multi/mcxn94x-multi/Makefile | 16 + multi/mcxn94x-multi/common.h | 45 +++ multi/mcxn94x-multi/dev.c | 102 ++++++ multi/mcxn94x-multi/dev.h | 36 ++ multi/mcxn94x-multi/dummyfs.c | 184 ++++++++++ multi/mcxn94x-multi/mcxn94x-multi.c | 126 +++++++ multi/mcxn94x-multi/uart.c | 538 ++++++++++++++++++++++++++++ multi/mcxn94x-multi/uart.h | 21 ++ 9 files changed, 1077 insertions(+) create mode 100644 _targets/Makefile.armv8m33-mcxn94x create mode 100644 multi/mcxn94x-multi/Makefile create mode 100644 multi/mcxn94x-multi/common.h create mode 100644 multi/mcxn94x-multi/dev.c create mode 100644 multi/mcxn94x-multi/dev.h create mode 100644 multi/mcxn94x-multi/dummyfs.c create mode 100644 multi/mcxn94x-multi/mcxn94x-multi.c create mode 100644 multi/mcxn94x-multi/uart.c create mode 100644 multi/mcxn94x-multi/uart.h diff --git a/_targets/Makefile.armv8m33-mcxn94x b/_targets/Makefile.armv8m33-mcxn94x new file mode 100644 index 00000000..339bd075 --- /dev/null +++ b/_targets/Makefile.armv8m33-mcxn94x @@ -0,0 +1,9 @@ +# +# Makefile for Phoenix-RTOS 3 device drivers +# +# MCX N94x drivers +# +# Copyright 2019, 2020, 2024 Phoenix Systems +# + +DEFAULT_COMPONENTS := mcxn94x-multi diff --git a/multi/mcxn94x-multi/Makefile b/multi/mcxn94x-multi/Makefile new file mode 100644 index 00000000..5878c744 --- /dev/null +++ b/multi/mcxn94x-multi/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for Phoenix-RTOS mcxn94x-multi +# +# Copyright 2024 Phoenix Systems +# + +NAME := mcxn94x-multi + +LOCAL_PATH := $(call my-dir) + +LOCAL_SRCS = mcxn94x-multi.c dev.c uart.c dummyfs.c +DEP_LIBS := libtty libklog libpseudodev +LIBS := libdummyfs libklog libpseudodev libposixsrv +LOCAL_HEADERS := mcxn94x-multi.h + +include $(binary.mk) diff --git a/multi/mcxn94x-multi/common.h b/multi/mcxn94x-multi/common.h new file mode 100644 index 00000000..25b39b87 --- /dev/null +++ b/multi/mcxn94x-multi/common.h @@ -0,0 +1,45 @@ +/* + * Phoenix-RTOS + * + * MCX N94x common + * + * Copyright 2017, 2018, 2024 Phoenix Systems + * Author: Aleksander Kaminski, Hubert Buczynski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include +#include +#include + +#include "mcxn94x-multi.h" + + +#define NELEMS(x) (sizeof(x) / sizeof(*(x))) + + +static inline void common_dataBarrier(void) +{ + __asm__ volatile ("dmb"); +} + + +static inline void common_dataSyncBarrier(void) +{ + __asm__ volatile ("dsb"); +} + + +static inline void common_instrBarrier(void) +{ + __asm__ volatile ("isb"); +} + + +#endif diff --git a/multi/mcxn94x-multi/dev.c b/multi/mcxn94x-multi/dev.c new file mode 100644 index 00000000..250cc5e9 --- /dev/null +++ b/multi/mcxn94x-multi/dev.c @@ -0,0 +1,102 @@ +/* + * Phoenix-RTOS + * + * Multidriver device manager + * + * Copyright 2024 Phoenix Systems + * Author: Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include + +#include "common.h" +#include "dev.h" + + +#define MAJOR_MAX 16 + + +static struct { + oid_t oid; + devHandler *devices[MAJOR_MAX]; +} dev_common = { .devices = { NULL } }; + + +static void dev_oid2mm(oid_t *oid, unsigned int *major, unsigned int *minor) +{ + *major = (oid->id >> 16) & 0xffff; + *minor = oid->id & 0xffff; +} + + +static void dev_mm2oid(oid_t *oid, unsigned int major, unsigned int minor) +{ + oid->id = (major << 16) | minor; +} + + +/* To be used only in constructors - no lock needed */ +int dev_allocMajor(unsigned int *major) +{ + static unsigned int counter = 0; + + if (counter == NELEMS(dev_common.devices)) { + return -1; + } + + *major = counter++; + + return 0; +} + + +int dev_registerFile(const char *fname, unsigned int major, unsigned int minor) +{ + oid_t oid = { .port = dev_common.oid.port }; + + dev_mm2oid(&oid, major, minor); + + return create_dev(&oid, fname); +} + + +int dev_msgReceive(msg_t *msg, msg_rid_t *rid) +{ + return msgRecv(dev_common.oid.port, msg, rid); +} + + +void dev_handle(msg_t *msg, msg_rid_t rid) +{ + unsigned int major, minor; + + dev_oid2mm(&msg->oid, &major, &minor); + + if ((major < NELEMS(dev_common.devices)) && (dev_common.devices[major] != NULL)) { + dev_common.devices[major](msg, rid, major, minor); + } + else { + msg->o.err = -ENOSYS; + msgRespond(dev_common.oid.port, msg, rid); + } +} + + +void dev_register(devHandler *handler, unsigned int major) +{ + dev_common.devices[major] = handler; +} + + +/* Has to be executed first! */ +static void __attribute__((constructor(101))) dev_init(void) +{ + portCreate(&dev_common.oid.port); +} diff --git a/multi/mcxn94x-multi/dev.h b/multi/mcxn94x-multi/dev.h new file mode 100644 index 00000000..a548bd64 --- /dev/null +++ b/multi/mcxn94x-multi/dev.h @@ -0,0 +1,36 @@ +/* + * Phoenix-RTOS + * + * Multidriver device manager + * + * Copyright 2024 Phoenix Systems + * Author: Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef MULTI_DEV_H_ +#define MULTI_DEV_H_ + +#include + +typedef void devHandler(msg_t *msg, msg_rid_t rid, unsigned int major, unsigned int minor); + + +int dev_allocMajor(unsigned int *major); + + +int dev_registerFile(const char *fname, unsigned int major, unsigned int minor); + + +int dev_msgReceive(msg_t *msg, msg_rid_t *rid); + + +void dev_handle(msg_t *msg, msg_rid_t rid); + + +void dev_register(devHandler *handler, unsigned int major); + +#endif /* MULTI_DEV_H_ */ diff --git a/multi/mcxn94x-multi/dummyfs.c b/multi/mcxn94x-multi/dummyfs.c new file mode 100644 index 00000000..3890c60f --- /dev/null +++ b/multi/mcxn94x-multi/dummyfs.c @@ -0,0 +1,184 @@ +/* + * Phoenix-RTOS + * + * MCX N94x multi driver buit-in dummyds + * + * Copyright 2021, 2024 Phoenix Systems + * Author: Maciej Purski, Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + +#if defined(BUILTIN_DUMMYFS) && BUILTIN_DUMMYFS != 0 + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static struct { + char stack[2048] __attribute__((aligned(8))); + unsigned port; +} dummy_common; + + +static int syspage_create(void *ctx, oid_t *root) +{ + oid_t sysoid = { 0 }; + + int progsz = syspageprog(NULL, -1); + if (progsz < 0) { + return progsz; + } + + if (dummyfs_create(ctx, root, "syspage", &sysoid, 0666, otDir, NULL) != 0) { + return -ENOMEM; + } + + for (int i = 0; i < progsz; i++) { + syspageprog_t prog; + oid_t toid; + + if (syspageprog(&prog, i) != 0) { + continue; + } + + dummyfs_createMapped(ctx, &sysoid, prog.name, (void *)prog.addr, prog.size, &toid); + } + + return 0; +} + + +static void msgthr(void *ctx) +{ + msg_t msg; + msg_rid_t rid; + + for (;;) { + if (msgRecv(dummy_common.port, &msg, &rid) < 0) { + continue; + } + + switch (msg.type) { + + case mtOpen: + msg.o.err = dummyfs_open(ctx, &msg.oid); + break; + + case mtClose: + msg.o.err = dummyfs_close(ctx, &msg.oid); + break; + + case mtRead: + msg.o.err = dummyfs_read(ctx, &msg.oid, msg.i.io.offs, msg.o.data, msg.o.size); + break; + + case mtWrite: + msg.o.err = dummyfs_write(ctx, &msg.oid, msg.i.io.offs, msg.i.data, msg.i.size); + break; + + case mtTruncate: + msg.o.err = dummyfs_truncate(ctx, &msg.oid, msg.i.io.len); + break; + + case mtDevCtl: + msg.o.err = -ENOSYS; + break; + + case mtCreate: + msg.o.err = dummyfs_create(ctx, &msg.oid, msg.i.data, &msg.o.create.oid, msg.i.create.mode, msg.i.create.type, &msg.i.create.dev); + break; + + case mtDestroy: + msg.o.err = dummyfs_destroy(ctx, &msg.oid); + break; + + case mtSetAttr: + msg.o.err = dummyfs_setattr(ctx, &msg.oid, msg.i.attr.type, msg.i.attr.val, msg.i.data, msg.i.size); + break; + + case mtGetAttr: + msg.o.err = dummyfs_getattr(ctx, &msg.oid, msg.i.attr.type, &msg.o.attr.val); + break; + + case mtGetAttrAll: { + struct _attrAll *attrs = msg.o.data; + if ((attrs == NULL) || (msg.o.size < sizeof(struct _attrAll))) { + msg.o.err = -EINVAL; + break; + } + msg.o.err = dummyfs_getattrAll(ctx, &msg.oid, attrs); + break; + } + + case mtLookup: + msg.o.err = dummyfs_lookup(ctx, &msg.oid, msg.i.data, &msg.o.lookup.fil, &msg.o.lookup.dev); + break; + + case mtLink: + msg.o.err = dummyfs_link(ctx, &msg.oid, msg.i.data, &msg.i.ln.oid); + break; + + case mtUnlink: + msg.o.err = dummyfs_unlink(ctx, &msg.oid, msg.i.data); + break; + + case mtReaddir: + msg.o.err = dummyfs_readdir(ctx, &msg.oid, msg.i.readdir.offs, msg.o.data, msg.o.size); + break; + + default: + msg.o.err = -EINVAL; + break; + } + msgRespond(dummy_common.port, &msg, rid); + } +} + + +void __attribute__((constructor(102))) dummyfs_init(void) +{ + void *ctx; + oid_t root = { 0 }; + + if (portCreate(&dummy_common.port) != 0) { + return; + } + + if (portRegister(dummy_common.port, "/", &root) != 0) { + portDestroy(dummy_common.port); + return; + } + + root.port = dummy_common.port; + if (dummyfs_mount(&ctx, NULL, 0, &root) != 0) { + printf("dummyfs mount failed\n"); + portDestroy(dummy_common.port); + return; + } + + if (syspage_create(ctx, &root) != 0) { + dummyfs_unmount(ctx); + portDestroy(dummy_common.port); + return; + } + + if (beginthread(msgthr, 4, dummy_common.stack, sizeof(dummy_common.stack), ctx) != 0) { + dummyfs_unmount(ctx); + portDestroy(dummy_common.port); + return; + } +} + +#endif /* BUILTIN_DUMMYFS */ diff --git a/multi/mcxn94x-multi/mcxn94x-multi.c b/multi/mcxn94x-multi/mcxn94x-multi.c new file mode 100644 index 00000000..0f119440 --- /dev/null +++ b/multi/mcxn94x-multi/mcxn94x-multi.c @@ -0,0 +1,126 @@ +/* + * Phoenix-RTOS + * + * MCX N94x Multi driver server + * + * Copyright 2024 Phoenix Systems + * Author: Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "uart.h" + +#define THREAD_POOL 4 +#define THREAD_STACKSZ 800 +#define THREAD_PRIORITY 3 + +#ifndef UART_CONSOLE +#define UART_CONSOLE 4 +#endif + +static struct { + unsigned char stack[THREAD_POOL - 1][THREAD_STACKSZ]; +} common; + + +static void threadPool(void *arg) +{ + msg_t msg; + msg_rid_t rid; + + for (;;) { + int err = dev_msgReceive(&msg, &rid); + switch (err) { + case 0: + dev_handle(&msg, rid); + /* msgRespond handled in dev_handle() */ + break; + + case -EINTR: + case -ENOMEM: + case -EAGAIN: + /* Temporary errors */ + break; + + default: + /* TODO report? end? restart? */ + break; + } + } +} + + +static void usage(const char *p) +{ + printf("usage: %s [-t ttyno]\n", p); +} + + +int main(int argc, char *argv[]) +{ + int ttyno = UART_CONSOLE; + bool ttyinit = false; + + for (;;) { + int c = getopt(argc, argv, "t:"); + if (c == -1) { + break; + } + + switch (c) { + case 't': + ttyno = atoi(optarg); + ttyinit = true; + break; + + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + + if (!ttyinit) { + printf("%s: No TTY selected, fallback to default (uart%d)\n", argv[0], ttyno); + } + + int err = uart_ttyInit(ttyno); + if (err < 0) { + fprintf(stderr, "%s: TTY%d init failed (%d), fallback to default (uart%d)\n", + argv[0], ttyno, err, UART_CONSOLE); + + err = uart_ttyInit(UART_CONSOLE); + if (err < 0) { + fprintf(stderr, "%s: Default TTY #%d init failed (%d)\n", argv[0], UART_CONSOLE, err); + + /* Let's keep going, not the end of the world */ + } + } + + /* Everything is already initialized by constructors */ + printf("%s: Initialized\n", argv[0]); + + for (int i = 0; i < THREAD_POOL - 1; ++i) { + if (beginthread(threadPool, THREAD_PRIORITY, common.stack[i], THREAD_STACKSZ, (void *)i) < 0) { + /* Not really that critical of the error, in the worst case we still have the main thread */ + fprintf(stderr, "%s: Thread pool initialization failed\n", argv[0]); + break; + } + } + + threadPool((void *)THREAD_POOL); + + /* Never reached */ + return 0; +} diff --git a/multi/mcxn94x-multi/uart.c b/multi/mcxn94x-multi/uart.c new file mode 100644 index 00000000..3268014f --- /dev/null +++ b/multi/mcxn94x-multi/uart.c @@ -0,0 +1,538 @@ +/* + * Phoenix-RTOS + * + * MCX N94x UART driver + * + * Copyright 2017-2019, 2024 Phoenix Systems + * Author: Kamil Amanowicz, Marek Bialowas, Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "uart.h" +#include "dev.h" + + +#define FLEXCOMM_CAT(x) FLEXCOMM##x##_SEL +#define UARTN_ACTIVE(x) (FLEXCOMM_CAT(x) == FLEXCOMM_UART) + +#define UART1_POS 0 +#define UART2_POS (UART1_POS + UARTN_ACTIVE(0)) +#define UART3_POS (UART2_POS + UARTN_ACTIVE(1)) +#define UART4_POS (UART3_POS + UARTN_ACTIVE(2)) +#define UART5_POS (UART4_POS + UARTN_ACTIVE(3)) +#define UART6_POS (UART5_POS + UARTN_ACTIVE(4)) +#define UART7_POS (UART6_POS + UARTN_ACTIVE(5)) +#define UART8_POS (UART7_POS + UARTN_ACTIVE(6)) +#define UART9_POS (UART8_POS + UARTN_ACTIVE(7)) + +#define UART_MAX 10 +#define UART_CNT (UARTN_ACTIVE(0) + UARTN_ACTIVE(1) + UARTN_ACTIVE(2) + \ + UARTN_ACTIVE(3) + UARTN_ACTIVE(4) + UARTN_ACTIVE(5) + UARTN_ACTIVE(6) + \ + UARTN_ACTIVE(7) + UARTN_ACTIVE(8)) + + +#ifndef UART_RXFIFOSIZE +#define UART_RXFIFOSIZE 128 +#endif + + +typedef struct uart_s { + char stack[1024] __attribute__ ((aligned(8))); + + volatile uint32_t *base; + uint32_t mode; + + handle_t cond; + handle_t inth; + handle_t lock; + + size_t rxFifoSz; + size_t txFifoSz; + + libtty_common_t tty_common; + + /* fifo between irq and thread */ + lf_fifo_t rxFifoCtx; + uint8_t rxFifoData[UART_RXFIFOSIZE]; + + /* statistics */ + struct { + /* NOTE: to read statistics use debugger */ + size_t hw_overrunCntr; + size_t sw_overrunCntr; + } stat; +} uart_t; + + +static struct { + uart_t uarts[UART_CNT]; + unsigned int major; + unsigned int ttyminor; +} uart_common; + + +static const int uartConfig[] = { UARTN_ACTIVE(0), UARTN_ACTIVE(1), UARTN_ACTIVE(2), UARTN_ACTIVE(3), + UARTN_ACTIVE(4), UARTN_ACTIVE(5), UARTN_ACTIVE(6), UARTN_ACTIVE(7), UARTN_ACTIVE(8), UARTN_ACTIVE(9) }; + + +static const int uartPos[] = { UART1_POS, UART2_POS, UART3_POS, UART4_POS, UART5_POS, UART6_POS, + UART7_POS, UART8_POS, UART9_POS }; + + +/* clang-format off */ +enum { veridr = 0, paramr, globalr, pincfgr, baudr, statr, ctrlr, datar, matchr, modirr, fifor, waterr }; +/* clang-format on */ + + +static inline int uart_getRXcount(uart_t *uart) +{ + return (*(uart->base + waterr) >> 24) & 0xff; +} + + +static inline int uart_getTXcount(uart_t *uart) +{ + return (*(uart->base + waterr) >> 8) & 0xff; +} + + +static int uart_handleIntr(unsigned int n, void *arg) +{ + uart_t *uart = (uart_t *)arg; + uint32_t status = *(uart->base + statr); + + /* Disable interrupts, enabled in uart_intrThread */ + *(uart->base + ctrlr) &= ~((1 << 27) | (1 << 26) | (1 << 25) | (1 << 23) | (1 << 21)); + + /* check for RX overrun */ + if ((status & (1uL << 19u)) != 0u) { + /* invalidate fifo */ + *(uart->base + fifor) |= 1uL << 14u; + uart->stat.hw_overrunCntr++; + } + + /* Process received data */ + while (uart_getRXcount(uart) != 0) { + uint8_t data = *(uart->base + datar); + if (lf_fifo_push(&uart->rxFifoCtx, data) == 0) { + uart->stat.sw_overrunCntr++; + } + } + + /* Clear errors: parity, framing, noise, overrun and idle flag */ + *(uart->base + statr) = (status & (0x1fuL << 16)); + + return 1; +} + + +static void uart_intrThread(void *arg) +{ + uart_t *uart = (uart_t *)arg; + uint8_t mask; + uint8_t c; + + for (;;) { + /* wait for character or transmit data */ + mutexLock(uart->lock); + while (lf_fifo_empty(&uart->rxFifoCtx) != 0) { /* nothing to RX */ + if (libtty_txready(&uart->tty_common)) { /* something to TX */ + if (uart_getTXcount(uart) < uart->txFifoSz) { /* TX ready */ + break; + } + else { + *(uart->base + ctrlr) |= 1 << 23; + } + } + + *(uart->base + ctrlr) |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 21); + + condWait(uart->cond, uart->lock, 0); + } + + if ((uart->tty_common.term.c_cflag & CSIZE) == CS7) { + mask = 0x7f; + } + else { + mask = 0xff; + } + + mutexUnlock(uart->lock); + + /* RX */ + while (lf_fifo_pop(&uart->rxFifoCtx, &c) != 0) { + libtty_putchar(&uart->tty_common, c & mask, NULL); + } + + /* TX */ + while (libtty_txready(&uart->tty_common) && uart_getTXcount(uart) < uart->txFifoSz) { + *(uart->base + datar) = libtty_getchar(&uart->tty_common, NULL); + } + } +} + + +static void signal_txready(void *_uart) +{ + uart_t *uartptr = (uart_t *)_uart; + condSignal(uartptr->cond); +} + + +static void set_cflag(void *_uart, tcflag_t *cflag) +{ + uart_t *uartptr = (uart_t *)_uart; + uint32_t t; + + /* disable TX and RX */ + *(uartptr->base + ctrlr) &= ~((1 << 19) | (1 << 18)); + + /* CSIZE only CS7 and CS8 (default) is supported */ + if ((*cflag & CSIZE) != CS7) { /* CS8 */ + *cflag &= ~CSIZE; + *cflag |= CS8; + } + + /* If parity bit is enabled data character length must be incremented */ + t = *(uartptr->base + ctrlr) & ~(1 << 4 | 1 << 11); + if ((*cflag & CSIZE) == CS7) { + if ((*cflag & PARENB) == 0) { + t |= 1 << 11; + } + } + else if ((*cflag & PARENB) != 0) { + t |= 1 << 4; + } + *(uartptr->base + ctrlr) = t; + + /* parity */ + t = *(uartptr->base + ctrlr) & ~3; + if ((*cflag & PARENB) != 0) { + t |= 1 << 1; + if ((*cflag & PARODD) != 0) { + t |= 1; + } + } + *(uartptr->base + ctrlr) = t; + + /* stop bits */ + if ((*cflag & CSTOPB) != 0) { + *(uartptr->base + baudr) |= (1 << 13); + } + else { + *(uartptr->base + baudr) &= ~(1 << 13); + } + + /* re-enable TX and RX */ + *(uartptr->base + ctrlr) |= (1 << 19) | (1 << 18); +} + + +static uint32_t calculate_baudrate(uint32_t baud) +{ + uint32_t osr, sbr, bestSbr = 0, bestOsr = 0, bestDiff, t, tDiff; + + if (baud == 0) { + return 0; + } + + bestDiff = baud; + + for (osr = 4; osr <= 32; ++osr) { + /* find sbr value in range between 1 and 8191 */ + sbr = (UART_CLK / (baud * osr)) & 0x1fff; + sbr = (sbr == 0) ? 1 : sbr; + + /* baud rate difference based on temporary osr and sbr */ + tDiff = UART_CLK / (osr * sbr) - baud; + t = UART_CLK / (osr * (sbr + 1)); + + /* select best values between sbr and sbr+1 */ + if (tDiff > baud - t) { + tDiff = baud - t; + sbr += (sbr < 0x1fff); + } + + if (tDiff <= bestDiff) { + bestDiff = tDiff; + bestOsr = osr - 1; + bestSbr = sbr; + } + } + + return (bestOsr << 24) | ((bestOsr <= 6) << 17) | (bestSbr & 0x1fff); +} + + +static void set_baudrate(void *_uart, speed_t baud) +{ + uint32_t reg = 0, t; + int b; + uart_t *uartptr = (uart_t *)_uart; + int b = libtty_baudrate_to_int(baud); + + if (b > 0) { + reg = calculate_baudrate((uint32_t)b); + } + + /* disable TX and RX */ + *(uartptr->base + ctrlr) &= ~((1 << 19) | (1 << 18)); + + t = *(uartptr->base + baudr) & ~((0x1f << 24) | (1 << 17) | 0x1fff); + *(uartptr->base + baudr) = t | reg; + + /* re-enable TX and RX */ + *(uartptr->base + ctrlr) |= (1 << 19) | (1 << 18); +} + + +static void uart_handleMsg(msg_t *msg, msg_rid_t rid, unsigned int major, unsigned int minor) +{ + unsigned long request; + const void *in_data, *out_data = NULL; + pid_t pid; + int err; + uart_t *uart; + + if ((minor >= NELEMS(uartConfig)) || (uartConfig[minor] == 0)) { + msg->o.err = -EINVAL; + msgRespond(msg->oid.port, msg, rid); + return; + } + + uart = &uart_common.uarts[uartPos[minor]]; + + switch (msg->type) { + case mtOpen: + case mtClose: + msg->o.err = 0; + break; + + case mtWrite: + msg->o.err = libtty_write(&uart->tty_common, msg->i.data, msg->i.size, msg->i.io.mode); + break; + + case mtRead: + msg->o.err = libtty_read(&uart->tty_common, msg->o.data, msg->o.size, msg->i.io.mode); + break; + + case mtGetAttr: + if (msg->i.attr.type != atPollStatus) { + msg->o.err = -ENOSYS; + break; + } + msg->o.attr.val = libtty_poll_status(&uart->tty_common); + msg->o.err = 0; + break; + + case mtDevCtl: + in_data = ioctl_unpack(msg, &request, NULL); + pid = ioctl_getSenderPid(msg); + err = libtty_ioctl(&uart->tty_common, pid, request, in_data, &out_data); + ioctl_setResponse(msg, request, err, out_data); + break; + + default: + msg->o.err = -ENOSYS; + break; + } + + msgRespond(msg->oid.port, msg, rid); +} + + +static void uart_klogCblk(const char *data, size_t size) +{ + libtty_write(&uart_common.uarts[uartPos[uart_common.ttyminor]].tty_common, data, size, 0); +} + + +int uart_ttyInit(int ttyno) +{ + char path[24]; + oid_t dev; + + if (ttyno < 0) { + return -EINVAL; + } + + snprintf(path, sizeof(path) - 1, "/dev/uart%d", ttyno); + path[sizeof(path) - 1] = '\0'; + + if (lookup(path, NULL, &dev) < 0) { + return -ENOENT; + } + + uart_common.ttyminor = ttyno; + + int err = symlink(path, _PATH_TTY); + if (err < 0) { + return err; + } + + err = symlink(path, _PATH_CONSOLE); + if (err < 0) { + return err; + } + + libklog_init(uart_klogCblk); + + return 0; +} + + +static int uart_init(unsigned int minor) +{ + uint32_t t; + uart_t *uart; + libtty_callbacks_t callbacks; + static const size_t fifoSzLut[] = { 1, 4, 8, 16, 32, 64, 128, 256 }; + static const struct { + volatile uint32_t *base; + int tx; + int rx; + int txalt; + int rxalt; + int irq; + } info[10] = { + { .base = FLEXCOMM0_BASE, .tx = UART0_TX_PIN, .rx = UART0_RX_PIN, .txalt = UART0_TX_ALT, .rxalt = UART0_RX_ALT, .irq = FLEXCOMM0_IRQ }, + { .base = FLEXCOMM1_BASE, .tx = UART1_TX_PIN, .rx = UART1_RX_PIN, .txalt = UART1_TX_ALT, .rxalt = UART1_RX_ALT, .irq = FLEXCOMM1_IRQ }, + { .base = FLEXCOMM2_BASE, .tx = UART2_TX_PIN, .rx = UART2_RX_PIN, .txalt = UART2_TX_ALT, .rxalt = UART2_RX_ALT, .irq = FLEXCOMM2_IRQ }, + { .base = FLEXCOMM3_BASE, .tx = UART3_TX_PIN, .rx = UART3_RX_PIN, .txalt = UART3_TX_ALT, .rxalt = UART3_RX_ALT, .irq = FLEXCOMM3_IRQ }, + { .base = FLEXCOMM4_BASE, .tx = UART4_TX_PIN, .rx = UART4_RX_PIN, .txalt = UART4_TX_ALT, .rxalt = UART4_RX_ALT, .irq = FLEXCOMM4_IRQ }, + { .base = FLEXCOMM5_BASE, .tx = UART5_TX_PIN, .rx = UART5_RX_PIN, .txalt = UART5_TX_ALT, .rxalt = UART5_RX_ALT, .irq = FLEXCOMM5_IRQ }, + { .base = FLEXCOMM6_BASE, .tx = UART6_TX_PIN, .rx = UART6_RX_PIN, .txalt = UART6_TX_ALT, .rxalt = UART6_RX_ALT, .irq = FLEXCOMM6_IRQ }, + { .base = FLEXCOMM7_BASE, .tx = UART7_TX_PIN, .rx = UART7_RX_PIN, .txalt = UART7_TX_ALT, .rxalt = UART7_RX_ALT, .irq = FLEXCOMM7_IRQ }, + { .base = FLEXCOMM8_BASE, .tx = UART8_TX_PIN, .rx = UART8_RX_PIN, .txalt = UART8_TX_ALT, .rxalt = UART8_RX_ALT, .irq = FLEXCOMM8_IRQ }, + { .base = FLEXCOMM9_BASE, .tx = UART9_TX_PIN, .rx = UART9_RX_PIN, .txalt = UART9_TX_ALT, .rxalt = UART9_RX_ALT, .irq = FLEXCOMM9_IRQ }, + }; + + static const uint32_t default_baud[] = { UART0_BAUDRATE, UART1_BAUDRATE, UART2_BAUDRATE, UART3_BAUDRATE, + UART4_BAUDRATE, UART5_BAUDRATE, UART6_BAUDRATE, UART7_BAUDRATE, UART8_BAUDRATE, UART9_BAUDRATE }; + static const uint32_t tty_bufsz[] = { UART0_BUFFSZ, UART1_BUFFSZ, UART2_BUFFSZ, UART3_BUFFSZ, + UART4_BUFFSZ, UART5_BUFFSZ, UART6_BUFFSZ, UART7_BUFFSZ, UART8_BUFFSZ, UART9_BUFFSZ }; + + uart = &uart_common.uarts[uartPos[minor]]; + uart->base = info[minor].base; + + if (condCreate(&uart->cond) < 0) { + return -1; + } + + if (mutexCreate(&uart->lock) < 0) { + resourceDestroy(uart->cond); + return -1; + } + + callbacks.arg = uart; + callbacks.set_baudrate = set_baudrate; + callbacks.set_cflag = set_cflag; + callbacks.signal_txready = signal_txready; + + if (libtty_init(&uart->tty_common, &callbacks, tty_bufsz[minor], libtty_int_to_baudrate(default_baud[minor])) < 0) { + resourceDestroy(uart->cond); + resourceDestroy(uart->lock); + return -1; + } + + lf_fifo_init(&uart->rxFifoCtx, uart->rxFifoData, sizeof(uart->rxFifoData)); + + /* Disable TX and RX */ + *(uart->base + ctrlr) &= ~((1 << 19) | (1 << 18)); + + /* Reset all internal logic and registers, except the Global Register */ + *(uart->base + globalr) |= 1 << 1; + common_dataBarrier(); + *(uart->base + globalr) &= ~(1 << 1); + common_dataBarrier(); + + /* Disable input trigger */ + *(uart->base + pincfgr) &= ~3; + + /* Set 115200 default baudrate */ + t = *(uart->base + baudr) & ~((0x1f << 24) | (1 << 17) | 0x1fff); + *(uart->base + baudr) = t | calculate_baudrate(default_baud[minor]); + + /* Set 8 bit and no parity mode */ + *(uart->base + ctrlr) &= ~0x117; + + /* One stop bit */ + *(uart->base + baudr) &= ~(1 << 13); + + *(uart->base + waterr) = 0; + + /* Enable FIFO */ + *(uart->base + fifor) |= (1 << 7) | (1 << 3); + *(uart->base + fifor) |= 0x3 << 14; + + /* Clear all status flags */ + *(uart->base + statr) |= 0xc01fc000; + + uart->rxFifoSz = fifoSzLut[*(uart->base + fifor) & 0x7]; + uart->txFifoSz = fifoSzLut[(*(uart->base + fifor) >> 4) & 0x7]; + + /* Enable overrun, noise, framing error and receiver interrupts */ + *(uart->base + ctrlr) |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 21); + + /* Enable TX and RX */ + *(uart->base + ctrlr) |= (1 << 19) | (1 << 18); + + beginthread(uart_intrThread, 1, &uart->stack, sizeof(uart->stack), uart); + interrupt(info[minor].irq, uart_handleIntr, (void *)uart, uart->cond, NULL); + + return 0; +} + + +static void __attribute__((constructor)) uart_register(void) +{ + if (dev_allocMajor(&uart_common.major) < 0) { + /* TODO - exit the driver? trigger the reset? report the error? */ + return; + } + + dev_register(uart_handleMsg, uart_common.major); + + for (unsigned int minor = 0; minor < UART_MAX; ++minor) { + char fname[8]; + + if (uartConfig[minor] == 0) { + continue; + } + + if (uart_init(minor) < 0) { + /* TODO - exit the driver? trigger the reset? report the error? */ + return; + } + + /* FIXME: Perhaps numerate UNIX way, not by physical device id? */ + snprintf(fname, sizeof(fname) - 1, "uart%u", minor); + fname[sizeof(fname) - 1] = '\0'; + + if (dev_registerFile(fname, uart_common.major, minor) < 0) { + /* TODO - exit the driver? trigger the reset? report the error? */ + return; + } + } +} diff --git a/multi/mcxn94x-multi/uart.h b/multi/mcxn94x-multi/uart.h new file mode 100644 index 00000000..bfafdd54 --- /dev/null +++ b/multi/mcxn94x-multi/uart.h @@ -0,0 +1,21 @@ +/* + * Phoenix-RTOS + * + * MCX N94x UART driver + * + * Copyright 2017-2019, 2024 Phoenix Systems + * Author: Kamil Amanowicz, Marek Bialowas, Aleksander Kaminski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef MCXN94X_UART_H_ +#define MCXN94X_UART_H_ + + +int uart_ttyInit(int ttyno); + + +#endif