x1000: add basic UART implementation

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]> Tue, 26 May 2026 07:14:28 -0400
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit 0c8365a28684037c317841191b344c68bd9fa104
Author: Skye <[email protected]>
Date:   Wed Apr 29 19:31:42 2026 +0900

    x1000: add basic UART implementation
    
    Change-Id: Ic5a6d66b7280b924e6a106f07849a057c72d5c4c

diff --git a/firmware/SOURCES b/firmware/SOURCES
index d552df7426..20158f8489 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1754,6 +1754,7 @@ target/mips/ingenic_x1000/pwm-x1000.c
 target/mips/ingenic_x1000/sfc-x1000.c
 target/mips/ingenic_x1000/system-x1000.c
 target/mips/ingenic_x1000/timer-x1000.c
+target/mips/ingenic_x1000/uart-x1000.c
 #ifndef USB_NONE
 target/mips/ingenic_x1000/usb-x1000.c
 #endif
diff --git a/firmware/target/mips/ingenic_x1000/uart-x1000.c b/firmware/target/mips/ingenic_x1000/uart-x1000.c
new file mode 100644
index 0000000000..8aed0aca50
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/uart-x1000.c
@@ -0,0 +1,246 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2026 Skye Green
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "kernel.h"
+#include "panic.h"
+#include "semaphore.h"
+#include "system.h"
+#include "uart-x1000.h"
+#include "thread.h"
+#include "x1000/macro.h"
+#include "x1000/uart.h"
+#include "x1000/cpm.h"
+#include <assert.h>
+
+const uint8_t *tx_buf[PORT_MAX];
+size_t tx_available[PORT_MAX];
+
+struct semaphore tx_done_sem[PORT_MAX];
+
+uint8_t *rx_buf[PORT_MAX];
+size_t rx_available[PORT_MAX];
+
+struct semaphore rx_done_sem[PORT_MAX];
+
+void uart_init(uart_port_t port, int baud) {
+    switch (port) {
+        case PORT_UART0:
+            jz_writef(CPM_CLKGR, UART0(0));
+            system_enable_irq(IRQ_UART0);
+            break;
+        case PORT_UART1:
+            jz_writef(CPM_CLKGR, UART1(0));
+            system_enable_irq(IRQ_UART1);
+            break;
+        case PORT_UART2:
+            jz_writef(CPM_CLKGR, UART2(0));
+            system_enable_irq(IRQ_UART2);
+            break;
+        default:
+            panicf("invalid UART port %d", port);
+    }
+
+    tx_available[port] = 0;
+    rx_available[port] = 0;
+
+    semaphore_init(&tx_done_sem[port], 1, 0);
+    semaphore_init(&rx_done_sem[port], 1, 0);
+
+    jz_write(UART_ULCR(port), 0);
+    jz_write(UART_UIER(port), 0);
+
+    jz_overwritef(UART_ULCR(port), WLS(0b11));
+    uart_set_baud(port, baud);
+    jz_overwritef(UART_UMCR(port), MDCE(1), FCM(1));
+    jz_overwritef(UART_UFCR(port), RDTR(0b11), UME(1), TFRT(1), RFRT(1), FME(1));
+}
+
+void uart_deinit(uart_port_t port) {
+    jz_write(UART_ULCR(port), 0);
+    jz_write(UART_UIER(port), 0);
+    jz_writef(UART_UFCR(port), UME(0));
+
+    if (tx_available[port] > 0) {
+        semaphore_release(&tx_done_sem[port]);
+    }
+
+    if (rx_available[port] > 0) {
+        semaphore_release(&rx_done_sem[port]);
+    }
+
+    switch (port) {
+        case PORT_UART0:
+            system_disable_irq(IRQ_UART0);
+            jz_writef(CPM_CLKGR, UART0(1));
+            break;
+        case PORT_UART1:
+            system_disable_irq(IRQ_UART1);
+            jz_writef(CPM_CLKGR, UART1(1));
+            break;
+        case PORT_UART2:
+            system_disable_irq(IRQ_UART2);
+            jz_writef(CPM_CLKGR, UART2(1));
+            break;
+        default:
+            panicf("invalid UART port %d", port);
+    }
+}
+
+void uart_set_baud(uart_port_t port, int baud) {
+    uint32_t udllr, udlhr, umr, uacr;
+    switch (baud) {
+#if X1000_EXCLK_FREQ == 24000000
+        case 115200:
+            udllr = 13;
+            udlhr = 0;
+            umr = 16;
+            uacr = 0;
+            break;
+        case 1500000:
+            udllr = 1;
+            udlhr = 0;
+            umr = 16;
+            uacr = 0;
+            break;
+        case 2000000:
+            udllr = 1;
+            udlhr = 0;
+            umr = 12;
+            uacr = 0;
+            break;
+        case 3000000:
+            udllr = 1;
+            udlhr = 0;
+            umr = 8;
+            uacr = 0;
+            break;
+        case 4000000:
+            udllr = 1;
+            udlhr = 0;
+            umr = 6;
+            uacr = 0;
+            break;
+#endif
+        default:
+            panicf("UART(%d): unsupported baud %d", port, baud);
+    }
+
+    jz_writef(UART_ULCR(port), DLAB(1));
+    jz_write(UART_UDLLR(port), udllr);
+    jz_write(UART_UDLHR(port), udlhr);
+    jz_writef(UART_ULCR(port), DLAB(0));
+    jz_write(UART_UMR(port), umr);
+    jz_write(UART_UACR(port), uacr);
+}
+
+void uart_refill_tx(uart_port_t port) {
+    for (int i = 0; i < 32 && tx_available[port] > 0; i++) {
+        uint8_t byte = *tx_buf[port];
+        jz_write(UART_UTHR(port), byte);
+        tx_available[port]--;
+        tx_buf[port]++;
+    }
+}
+
+void uart_empty_rx(uart_port_t port) {
+    int slots = jz_read(UART_URCR(port));
+    while (slots > 0 && rx_available[port] > 0) {
+        *rx_buf[port] = jz_read(UART_URBR(port));
+        rx_available[port]--;
+        rx_buf[port]++;
+        slots--;
+    }
+}
+
+void uart_tx(uart_port_t port, const uint8_t *buf, size_t len) {
+    assert(buf != NULL);
+    assert(len > 0);
+    tx_buf[port] = buf;
+    tx_available[port] = len;
+    jz_writef(UART_UIER(port), TDRIE(1));
+    semaphore_wait(&tx_done_sem[port], TIMEOUT_BLOCK);
+    while (jz_readf(UART_ULSR(port), TEMP) == 0) {
+        yield();
+    }
+}
+
+size_t uart_rx(uart_port_t port, uint8_t *buf, size_t len) {
+    rx_buf[port] = buf;
+    rx_available[port] = len;
+    jz_writef(UART_UIER(port), RTOIE(1), RDRIE(1));
+    semaphore_wait(&rx_done_sem[port], TIMEOUT_BLOCK);
+    size_t rem = rx_available[port];
+    rx_available[port] = 0;
+    return len - rem;
+}
+
+bool uart_pending_rx(uart_port_t port) {
+    return jz_read(UART_URCR(port)) > 0;
+}
+
+void uart_irq(uart_port_t port) {
+    uint32_t uiir = jz_read(UART_UIIR(port));
+    if (jz_vreadf(uiir, UART_UIIR, INPEND) != 0) {
+        return;
+    }
+
+    uint32_t ulsr = jz_read(UART_ULSR(port));
+
+    if (jz_vreadf(ulsr, UART_ULSR, TDRQ) != 0) {
+        uart_refill_tx(port);
+        if (tx_available[port] == 0) {
+            jz_writef(UART_UIER(port), TDRIE(0));
+            semaphore_release(&tx_done_sem[port]);
+        }
+    }
+
+    if (jz_vreadf(ulsr, UART_ULSR, DRY) != 0) {
+        bool was_avail = rx_available[port] > 0;
+        uart_empty_rx(port);
+        if (was_avail && rx_available[port] == 0) {
+            jz_writef(UART_UIER(port), RDRIE(0), RLSIE(0), RTOIE(0));
+            semaphore_release(&rx_done_sem[port]);
+            return;
+        }
+    }
+
+    if (jz_vreadf(uiir, UART_UIIR, INID) == 0b110) {
+        jz_writef(UART_UIER(port), RDRIE(0), RLSIE(0), RTOIE(0));
+        semaphore_release(&rx_done_sem[port]);
+    }
+}
+
+void UART0(void)
+{
+    uart_irq(PORT_UART0);
+}
+
+void UART1(void)
+{
+    uart_irq(PORT_UART1);
+}
+
+void UART2(void)
+{
+    uart_irq(PORT_UART2);
+}
\ No newline at end of file
diff --git a/firmware/target/mips/ingenic_x1000/uart-x1000.h b/firmware/target/mips/ingenic_x1000/uart-x1000.h
new file mode 100644
index 0000000000..327c5e9e94
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/uart-x1000.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2026 Skye Green
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __UART_X1000_H__
+#define __UART_X1000_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef enum {
+    PORT_UART0 = 0,
+    PORT_UART1 = 1,
+    PORT_UART2 = 2,
+    PORT_MAX = 3,
+} uart_port_t;
+
+extern void uart_init(uart_port_t port, int baud);
+extern void uart_tx(uart_port_t port, const uint8_t *buf, size_t len);
+extern size_t uart_rx(uart_port_t port, uint8_t *buf, size_t len);
+extern bool uart_pending_rx(uart_port_t port);
+extern void uart_set_baud(uart_port_t port, int baud);
+extern void uart_deinit(uart_port_t port);
+
+#endif /* __UART_X1000_H__ */
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs