diff --git a/multi/imxrt-multi/gpio.c b/multi/imxrt-multi/gpio.c index cdcb7be8..b0756e06 100644 --- a/multi/imxrt-multi/gpio.c +++ b/multi/imxrt-multi/gpio.c @@ -21,6 +21,24 @@ #include "common.h" #include "gpio.h" +#include "board_config.h" + +#ifndef GPIO_SUPPORT_ASYNC_IRQ_WAIT +#define GPIO_SUPPORT_ASYNC_IRQ_WAIT 0 +#endif + + +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT +#include +#include + +#define GPIO_IRQ_THREAD_PRIO 2 +#define GPIO_IRQ_THREAD_STACKSZ 2048 + +#define GPIO_IRQ_REQ_CACHE_SIZE GPIO_PORTS +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ + + /* clang-format off */ enum { gpio_dr = 0, gpio_gdir, gpio_psr, gpio_icr1, gpio_icr2, gpio_imr, @@ -28,9 +46,33 @@ enum { gpio_dr = 0, gpio_gdir, gpio_psr, gpio_icr1, gpio_icr2, gpio_imr, /* clang-format on */ -struct { + +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT +typedef struct gpio_async_req { + struct gpio_async_req *next; + msg_t msg; + msg_rid_t rid; + int port; /* -1: free, != -1: taken */ +} gpio_async_req_t; +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ + + +static struct { volatile uint32_t *base[GPIO_PORTS]; handle_t lock; + +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT + struct { + struct { + gpio_async_req_t *head; + size_t len; + handle_t lock; + } req_list; + handle_t lock, cond; + uint8_t stack[GPIO_IRQ_THREAD_STACKSZ]; + } irq; + +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ } gpio_common; @@ -99,7 +141,126 @@ int gpio_getDir(int port, uint32_t *val) } -static void gpio_handleDevCtl(msg_t *msg, int port) +int gpio_setIrqConf(int port, unsigned int pin, int type) +{ + int reg; + uint32_t t; + + if ((port <= 0) || (port > GPIO_PORTS)) { + return -EINVAL; + } + if (pin > 31) { + return -EINVAL; + } + if ((type < gpio_irq_active_low) || (type > gpio_irq_active_falling)) { + return -EINVAL; + } + + port -= 1; + reg = (pin < 16) ? gpio_icr1 : gpio_icr2; + pin = (pin & (16 - 1)) * 2; + + mutexLock(gpio_common.lock); + t = *(gpio_common.base[port] + reg); + t &= ~(3u << pin); + t |= (type << pin); + *(gpio_common.base[port] + reg) = t; + mutexUnlock(gpio_common.lock); + + return EOK; +} + + +int gpio_getIrqConf(int port, unsigned int pin, uint32_t *val) +{ + int reg; + + if ((port <= 0) || (port > GPIO_PORTS)) { + return -EINVAL; + } + if (pin > 31) { + return -EINVAL; + } + + port -= 1; + reg = (pin < 16) ? gpio_icr1 : gpio_icr2; + pin = (pin & (16 - 1)) * 2; + + *val = (*(gpio_common.base[port] + reg) >> pin) & 0x3; + + return EOK; +} + + +int gpio_waitIrq(msg_t *msg, msg_rid_t rid, int port, uint32_t mask, uint32_t *val) +{ + if ((port <= 0) || (port > GPIO_PORTS)) { + return -EINVAL; + } + if (mask == 0) { + return -EINVAL; + } + port -= 1; + +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT + + mutexLock(gpio_common.irq.req_list.lock); + gpio_async_req_t *req = gpio_common.irq.req_list.head; + + while (req != NULL) { + if (req->port == -1) { + break; + } + req = req->next; + } + + if (req == NULL || req->port != -1) { + req = malloc(sizeof(*req)); + if (req == NULL) { + return -ENOMEM; + } + req->next = gpio_common.irq.req_list.head; + gpio_common.irq.req_list.head = req; + gpio_common.irq.req_list.len++; + } + + memcpy(&req->msg, msg, sizeof(*msg)); + req->rid = rid; + req->port = port; + + /* clear status & enable requested interrupt(s) */ + mutexLock(gpio_common.lock); + *(gpio_common.base[port] + gpio_isr) = mask; + *(gpio_common.base[port] + gpio_imr) |= mask; + mutexUnlock(gpio_common.lock); + + mutexUnlock(gpio_common.irq.req_list.lock); + + return EWOULDBLOCK; + +#else + + uint32_t status = *(gpio_common.base[port] + gpio_isr) & mask; + + mutexLock(gpio_common.lock); + *(gpio_common.base[port] + gpio_isr) = mask; + /* no need to enable interrupts - ISR is always asserted + when interrupt condition occurs */ + mutexUnlock(gpio_common.lock); + + if (status == 0) { + /* interrupt did not occur: return -EWOULDBLOCK */ + return -EWOULDBLOCK; + } + + *val = status; + return EOK; + +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ +} + + +static void gpio_handleDevCtl(msg_t *msg, msg_rid_t rid, int port) { multi_i_t *imsg = (multi_i_t *)msg->i.raw; multi_o_t *omsg = (multi_o_t *)msg->o.raw; @@ -123,6 +284,18 @@ static void gpio_handleDevCtl(msg_t *msg, int port) msg->o.err = gpio_getDir(port, &omsg->val); break; + case gpio_set_irq_conf: + msg->o.err = gpio_setIrqConf(port, imsg->gpio.irq_conf.pin, imsg->gpio.irq_conf.type); + break; + + case gpio_get_irq_conf: + msg->o.err = gpio_getIrqConf(port, imsg->gpio.irq_conf.pin, &omsg->val); + break; + + case gpio_wait_irq: + msg->o.err = gpio_waitIrq(msg, rid, port, imsg->gpio.wait_irq.mask, &omsg->val); + break; + default: msg->o.err = -ENOSYS; break; @@ -130,7 +303,7 @@ static void gpio_handleDevCtl(msg_t *msg, int port) } -int gpio_handleMsg(msg_t *msg, int dev) +int gpio_handleMsg(msg_t *msg, msg_rid_t rid, int dev) { dev -= id_gpio1 - 1; /* Port number will be verified later */ @@ -145,7 +318,7 @@ int gpio_handleMsg(msg_t *msg, int dev) break; case mtDevCtl: - gpio_handleDevCtl(msg, dev); + gpio_handleDevCtl(msg, rid, dev); break; default: @@ -157,6 +330,163 @@ int gpio_handleMsg(msg_t *msg, int dev) } +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT +static void _gpio_disableInterrupts(void) +{ + for (int i = 0; i < sizeof(gpio_common.base) / sizeof(gpio_common.base[0]); i++) { + *(gpio_common.base[i] + gpio_imr) = 0x0; + } +} + + +static int gpio_handleIntr(unsigned int n, void *arg) +{ + (void)arg; + _gpio_disableInterrupts(); + return 0; +} + + +static void gpio_intrThread(void *arg) +{ + (void)arg; + gpio_async_req_t *curr; + gpio_async_req_t *prev; + gpio_async_req_t *del; + multi_i_t *imsg; + multi_o_t *omsg; + uint32_t status; + + mutexLock(gpio_common.irq.lock); + for (;;) { + condWait(gpio_common.irq.cond, gpio_common.irq.lock, 0); + + mutexLock(gpio_common.irq.req_list.lock); + if (gpio_common.irq.req_list.head == NULL) { + mutexUnlock(gpio_common.irq.req_list.lock); + continue; + } + + curr = gpio_common.irq.req_list.head; + prev = NULL; + + while (curr != NULL) { + if (curr->port == -1) { + prev = curr; + curr = curr->next; + continue; + } + + imsg = (multi_i_t *)curr->msg.i.raw; + omsg = (multi_o_t *)curr->msg.o.raw; + + status = *(gpio_common.base[curr->port] + gpio_isr) & imsg->gpio.wait_irq.mask; + + if (status == 0) { + /* re-enable IRQ mask*/ + mutexLock(gpio_common.lock); + *(gpio_common.base[curr->port] + gpio_imr) |= imsg->gpio.wait_irq.mask; + mutexUnlock(gpio_common.lock); + + prev = curr; + curr = curr->next; + continue; + } + + /* reset IRQ status */ + mutexLock(gpio_common.lock); + *(gpio_common.base[curr->port] + gpio_isr) = status; + mutexUnlock(gpio_common.lock); + + /* respond with bitmask of all IRQs requested that happened */ + omsg->val = status; + curr->msg.o.err = EOK; + msgRespond(multi_port, &curr->msg, curr->rid); + + if (gpio_common.irq.req_list.len > GPIO_IRQ_REQ_CACHE_SIZE) { + del = curr; + + if (prev == NULL) { + gpio_common.irq.req_list.head = curr->next; + } + else { + prev->next = curr->next; + } + + curr = curr->next; + free(del); + gpio_common.irq.req_list.len--; + } + else { + curr->port = -1; + prev = curr; + curr = curr->next; + } + } + + mutexUnlock(gpio_common.irq.req_list.lock); + } + mutexUnlock(gpio_common.irq.lock); + endthread(); +} + + +static int gpio_initInterrupts(void) +{ + int i; + + mutexLock(gpio_common.lock); + _gpio_disableInterrupts(); + mutexUnlock(gpio_common.lock); + + for (i = 0; i < sizeof(gpio_interrupts) / sizeof(gpio_interrupts[0]); i++) { + if (interrupt(gpio_interrupts[i].num, gpio_handleIntr, NULL, gpio_common.irq.cond, &gpio_interrupts[i].handle) < 0) { + return -ENOMEM; + } + } + + return 0; +} + + +static int gpio_initAsync(void) +{ + int err; + + if (mutexCreate(&gpio_common.irq.req_list.lock) < 0) { + return -ENOMEM; + } + if (mutexCreate(&gpio_common.irq.lock) < 0) { + resourceDestroy(gpio_common.irq.req_list.lock); + return -ENOMEM; + } + if (condCreate(&gpio_common.irq.cond) < 0) { + resourceDestroy(gpio_common.irq.req_list.lock); + resourceDestroy(gpio_common.irq.lock); + return -ENOMEM; + } + + err = gpio_initInterrupts(); + if (err < 0) { + resourceDestroy(gpio_common.irq.req_list.lock); + resourceDestroy(gpio_common.irq.lock); + resourceDestroy(gpio_common.irq.cond); + return err; + } + + err = beginthread(gpio_intrThread, GPIO_IRQ_THREAD_PRIO, gpio_common.irq.stack, sizeof(gpio_common.irq.stack), NULL); + if (err < 0) { + resourceDestroy(gpio_common.irq.req_list.lock); + resourceDestroy(gpio_common.irq.lock); + resourceDestroy(gpio_common.irq.cond); + return err; + } + + return 0; +} +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ + + int gpio_init(void) { int i; @@ -175,17 +505,30 @@ int gpio_init(void) pctl.devclock.state = clk_state_run; for (i = 0; i < sizeof(clocks) / sizeof(clocks[0]); ++i) { - if (clocks[i] < 0) + if (clocks[i] < 0) { continue; + } pctl.devclock.dev = clocks[i]; - if (platformctl(&pctl) < 0) + if (platformctl(&pctl) < 0) { return -1; + } } #endif - for (i = 0; i < sizeof(gpio_common.base) / sizeof(gpio_common.base[0]); ++i) + for (i = 0; i < sizeof(gpio_common.base) / sizeof(gpio_common.base[0]); ++i) { gpio_common.base[i] = (void *)addresses[i]; + } + + if (mutexCreate(&gpio_common.lock) < 0) { + return -1; + } +#if GPIO_SUPPORT_ASYNC_IRQ_WAIT + if (gpio_initAsync() < 0) { + resourceDestroy(gpio_common.lock); + return -1; + } +#endif /* GPIO_SUPPORT_ASYNC_IRQ_WAIT */ return 0; } diff --git a/multi/imxrt-multi/gpio.h b/multi/imxrt-multi/gpio.h index 1e3dde4b..bbf61475 100644 --- a/multi/imxrt-multi/gpio.h +++ b/multi/imxrt-multi/gpio.h @@ -17,7 +17,7 @@ #include -int gpio_handleMsg(msg_t *msg, int dev); +int gpio_handleMsg(msg_t *msg, msg_rid_t rid, int dev); int gpio_init(void); @@ -35,4 +35,13 @@ int gpio_setDir(int port, uint32_t mask, uint32_t val); int gpio_getDir(int port, uint32_t *val); +int gpio_setIrqConf(int port, unsigned int pin, int type); + + +int gpio_getIrqConf(int port, unsigned int pin, uint32_t *val); + + +int gpio_waitIrq(msg_t *msg, msg_rid_t rid, int port, uint32_t mask, uint32_t *val); + + #endif diff --git a/multi/imxrt-multi/imxrt-multi.c b/multi/imxrt-multi/imxrt-multi.c index 531bdeaf..11360a7c 100644 --- a/multi/imxrt-multi/imxrt-multi.c +++ b/multi/imxrt-multi/imxrt-multi.c @@ -49,14 +49,14 @@ #endif #define MULTI_THREADS_NO 2 -#define UART_THREADS_NO 2 +#define UART_THREADS_NO 2 #define STACKSZ 1024 struct { uint32_t uart_port; - char stack[MULTI_THREADS_NO + UART_THREADS_NO - 1][STACKSZ] __attribute__ ((aligned(8))); + char stack[MULTI_THREADS_NO + UART_THREADS_NO - 1][STACKSZ] __attribute__((aligned(8))); } common; @@ -78,7 +78,7 @@ static inline int multi2pseudo(id_t id) #endif -static void multi_dispatchMsg(msg_t *msg) +static void multi_dispatchMsg(msg_t *msg, msg_rid_t rid) { id_t id = msg->oid.id; @@ -98,7 +98,7 @@ static void multi_dispatchMsg(msg_t *msg) case id_gpio12: case id_gpio13: #endif - gpio_handleMsg(msg, id); + gpio_handleMsg(msg, rid, id); break; case id_spi1: @@ -496,7 +496,11 @@ static void multi_thread(void *arg) case mtGetAttr: case mtSetAttr: case mtDevCtl: - multi_dispatchMsg(&msg); + multi_dispatchMsg(&msg, rid); + if (msg.o.err == EWOULDBLOCK) { + /* msgRespond in driver */ + continue; + } break; case mtCreate: @@ -571,18 +575,48 @@ extern int posixsrv_start(void); #endif +static void multi_cleanup(void) +{ + oid_t oid; + if (common.uart_port != 0) { + portDestroy(common.uart_port); + } + if (multi_port != 0) { + portDestroy(multi_port); + } + if (lookup(_PATH_CONSOLE, &oid, NULL) >= 0) { + remove(_PATH_CONSOLE); + } +} + + int main(void) { int i; oid_t oid; - priority(IMXRT_MULTI_PRIO); + (void)priority(IMXRT_MULTI_PRIO); + + common.uart_port = 0; + multi_port = 0; - portCreate(&common.uart_port); - portCreate(&multi_port); + if (portCreate(&common.uart_port) < 0) { + debug("imxrt-multi: Failed to create UART port\n"); + return EXIT_FAILURE; + } + + if (portCreate(&multi_port) < 0) { + debug("imxrt-multi: Failed to create multi port\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #if BUILTIN_DUMMYFS - fs_init(); + if (fs_init() != EOK) { + debug("imxrt-multi: Failed to initialize filesystem\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #else /* Wait for the filesystem */ while (lookup("/", NULL, &oid) < 0) { @@ -590,26 +624,73 @@ int main(void) } #endif - gpio_init(); - uart_init(); - rtt_init(); - spi_init(); - i2c_init(); + if (gpio_init() != EOK) { + debug("imxrt-multi: Failed to initialize GPIO\n"); + multi_cleanup(); + return EXIT_FAILURE; + } + + if (uart_init() != EOK) { + debug("imxrt-multi: Failed to initialize UART\n"); + multi_cleanup(); + return EXIT_FAILURE; + } + +#if defined(RTT_ENABLED) && RTT_ENABLED + if (rtt_init() != EOK) { + debug("imxrt-multi: Failed to initialize RTT\n"); + multi_cleanup(); + return EXIT_FAILURE; + } +#endif + + if (spi_init() != EOK) { + debug("imxrt-multi: Failed to initialize SPI\n"); + multi_cleanup(); + return EXIT_FAILURE; + } + + if (i2c_init() != EOK) { + debug("imxrt-multi: Failed to initialize I2C\n"); + multi_cleanup(); + return EXIT_FAILURE; + } oid.port = common.uart_port; oid.id = id_console; - create_dev(&oid, _PATH_CONSOLE); + if (create_dev(&oid, _PATH_CONSOLE)) { + debug("imxrt-multi: Failed to create device file\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #if !ISEMPTY(RTT_CONSOLE_USER) - libklog_init(rtt_klogCblk); + if (libklog_init(rtt_klogCblk) != EOK) { + debug("imxrt-multi: Failed to initialize klog\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #else - libklog_init(uart_klogCblk); + if (libklog_init(uart_klogCblk) != EOK) { + debug("imxrt-multi: Failed to initialize klog\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #endif oid_t kmsgctrl = { .port = common.uart_port, .id = id_kmsgctrl }; - libklog_ctrlRegister(&kmsgctrl); + if (libklog_ctrlRegister(&kmsgctrl) != EOK) { + // FIXME: no way to remove file created by libklog_ctrlRegister + debug("imxrt-multi: Failed to register klog ctrl\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #if TRNG - trng_init(); + if (trng_init() != EOK) { + debug("imxrt-multi: Failed to initialize TRNG\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #endif #if CM4 @@ -617,7 +698,11 @@ int main(void) #endif #if BUILTIN_POSIXSRV - posixsrv_start(); + if (posixsrv_start() != EOK) { + debug("imxrt-multi: Failed to start posixsrv\n"); + multi_cleanup(); + return EXIT_FAILURE; + } #endif #if PSEUDODEV @@ -625,11 +710,19 @@ int main(void) #endif for (i = 0; i < UART_THREADS_NO; ++i) { - beginthread(uart_thread, IMXRT_MULTI_PRIO, common.stack[i], STACKSZ, (void *)i); + if (beginthread(uart_thread, IMXRT_MULTI_PRIO, common.stack[i], STACKSZ, (void *)i) < 0) { + debug("imxrt-multi: Failed to start UART thread\n"); + multi_cleanup(); + return EXIT_FAILURE; + } } for (; i < (MULTI_THREADS_NO + UART_THREADS_NO - 1); ++i) { - beginthread(multi_thread, IMXRT_MULTI_PRIO, common.stack[i], STACKSZ, (void *)i); + if (beginthread(multi_thread, IMXRT_MULTI_PRIO, common.stack[i], STACKSZ, (void *)i) < 0) { + debug("imxrt-multi: Failed to start MULTI thread\n"); + multi_cleanup(); + return EXIT_FAILURE; + } } if (createDevFiles() < 0) { diff --git a/multi/imxrt-multi/imxrt-multi.h b/multi/imxrt-multi/imxrt-multi.h index ac954cbc..37578b25 100644 --- a/multi/imxrt-multi/imxrt-multi.h +++ b/multi/imxrt-multi/imxrt-multi.h @@ -44,7 +44,13 @@ enum { id_console = 0, id_uart1, id_uart2, id_uart3, id_uart4, id_uart5, id_uart typedef struct { - enum { gpio_set_port = 0, gpio_get_port, gpio_set_dir, gpio_get_dir } type; + enum { gpio_set_port = 0, + gpio_get_port, + gpio_set_dir, + gpio_get_dir, + gpio_set_irq_conf, + gpio_get_irq_conf, + gpio_wait_irq } type; union { struct { @@ -56,11 +62,23 @@ typedef struct { unsigned int mask; unsigned int val; } dir; + + struct { + unsigned int pin; + enum { gpio_irq_active_low = 0, + gpio_irq_active_high, + gpio_irq_active_rising, + gpio_irq_active_falling, + } type; + } irq_conf; + + struct { + unsigned int mask; + } wait_irq; }; } gpio_t; - /* SPI */ /* clang-format off */ diff --git a/multi/imxrt-multi/imxrt1060.h b/multi/imxrt-multi/imxrt1060.h index c1a6c6be..10af2265 100644 --- a/multi/imxrt-multi/imxrt1060.h +++ b/multi/imxrt-multi/imxrt1060.h @@ -16,6 +16,7 @@ #define _IMXRT1060_H_ #include +#include #include #define UART_CLK 80000000 @@ -70,9 +71,9 @@ #define GPIO4_BASE ((void *)0x401c4000) #define GPIO5_BASE ((void *)0x400c0000) #define GPIO6_BASE ((void *)0x42000000) -#define GPIO7_BASE ((void *)0x42040000) -#define GPIO8_BASE ((void *)0x42080000) -#define GPIO9_BASE ((void *)0x420c0000) +#define GPIO7_BASE ((void *)0x42004000) +#define GPIO8_BASE ((void *)0x42008000) +#define GPIO9_BASE ((void *)0x4200c000) #define GPIO10_BASE NULL #define GPIO11_BASE NULL @@ -266,6 +267,23 @@ #endif +static union { + unsigned int num; + handle_t handle; +} gpio_interrupts[] __attribute__((unused)) = { + { .num = gpio1_0_15_irq }, + { .num = gpio1_16_31_irq }, + { .num = gpio2_0_15_irq }, + { .num = gpio2_16_31_irq }, + { .num = gpio3_0_15_irq }, + { .num = gpio3_16_31_irq }, + { .num = gpio4_0_15_irq }, + { .num = gpio4_16_31_irq }, + { .num = gpio5_0_15_irq }, + { .num = gpio5_16_31_irq }, +}; + + static inline int common_setClock(int dev, unsigned int state) { platformctl_t pctl; diff --git a/multi/imxrt-multi/imxrt1170.h b/multi/imxrt-multi/imxrt1170.h index 10c2b6e9..b72a4407 100644 --- a/multi/imxrt-multi/imxrt1170.h +++ b/multi/imxrt-multi/imxrt1170.h @@ -15,6 +15,7 @@ #ifndef _IMXRT1170_H_ #define _IMXRT1170_H_ +#include #include #include @@ -288,6 +289,23 @@ #define SPI6_PCS3_DEFAULT +static union { + unsigned int num; + handle_t handle; +} gpio_interrupts[] __attribute__((unused)) = { + { .num = gpio1_int0_irq }, + { .num = gpio1_int1_irq }, + { .num = gpio2_int0_irq }, + { .num = gpio2_int1_irq }, + { .num = gpio3_int0_irq }, + { .num = gpio3_int1_irq }, + { .num = gpio4_int0_irq }, + { .num = gpio4_int1_irq }, + { .num = gpio5_int0_irq }, + { .num = gpio5_int1_irq }, +}; + + static inline int common_setClock(int clock, int div, int mux, int mfd, int mfn, int state) { int res;