From f7bf4af3ec6c71ed04959842dcbaaea6e4d4d771 Mon Sep 17 00:00:00 2001 From: CTCaer Date: Sun, 8 May 2022 05:45:16 +0300 Subject: [PATCH] bdk: uart: refactor and add new functionality - Allow to set CTS/RTS mode (only specific combos supported for now) - Support the above modes in receiving - Set 2 stop bits to decreases errors on high baudrates --- bdk/input/joycon.c | 5 ++-- bdk/soc/hw_init.c | 8 +++--- bdk/soc/uart.c | 49 +++++++++++++++++++++++++------------ bdk/soc/uart.h | 61 +++++++++++++++++++++++++++++----------------- 4 files changed, 78 insertions(+), 45 deletions(-) diff --git a/bdk/input/joycon.c b/bdk/input/joycon.c index 825e347..1707aba 100644 --- a/bdk/input/joycon.c +++ b/bdk/input/joycon.c @@ -227,7 +227,7 @@ static u8 jc_crc(u8 *data, u16 len) void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size) { uart_send(uart_port, buf, size); - uart_wait_idle(uart_port, UART_TX_IDLE); + uart_wait_xfer(uart_port, UART_TX_IDLE); } static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size, bool crc) @@ -741,7 +741,8 @@ static void jc_init_conn(joycon_ctxt_t *jc) gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); } - uart_init(jc->uart, 1000000); + // Initialize uart to 1 megabaud and manual RTS. + uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX); uart_invert(jc->uart, true, UART_INVERT_TXD); uart_set_IIR(jc->uart); diff --git a/bdk/soc/hw_init.c b/bdk/soc/hw_init.c index c57b653..081fd57 100644 --- a/bdk/soc/hw_init.c +++ b/bdk/soc/hw_init.c @@ -102,7 +102,7 @@ static void _config_gpios(bool nx_hoag) PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0; PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0; - // Set pin mode for UARTB/C TX pins. + // Turn Joy-Con detect on. (GPIO mode for UARTB/C TX pins.) #if !defined (DEBUG_UART_PORT) || DEBUG_UART_PORT != UART_B gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); #endif @@ -179,7 +179,7 @@ static void _mbist_workaround() I2S(I2S5_CG) &= ~I2S_CG_SLCG_ENABLE; DISPLAY_A(_DIREG(DC_COM_DSC_TOP_CTL)) |= 4; // DSC_SLCG_OVERRIDE. - VIC(0x8C) = 0xFFFFFFFF; + VIC(0x8C) = 0xFFFFFFFF; // NV_PVIC_THI_CONFIG0. usleep(2); // Set per-clock reset for APE/VIC/HOST1X/DISP1. @@ -361,7 +361,7 @@ void hw_init() #ifdef DEBUG_UART_PORT clock_enable_uart(DEBUG_UART_PORT); - uart_init(DEBUG_UART_PORT, DEBUG_UART_BAUDRATE); + uart_init(DEBUG_UART_PORT, DEBUG_UART_BAUDRATE, UART_AO_TX_AO_RX); uart_invert(DEBUG_UART_PORT, DEBUG_UART_INVERT, UART_INVERT_TXD); #endif @@ -378,7 +378,7 @@ void hw_init() // Initialize I2C5, mandatory for PMIC. i2c_init(I2C_5); - //! TODO: Why? Device is NFC MCU on Lite. + // Power up Joycon MCU (Sio) on Hoag as it's required for I2C1 communication. if (nx_hoag) { max7762x_regulator_set_voltage(REGULATOR_LDO8, 2800000); diff --git a/bdk/soc/uart.c b/bdk/soc/uart.c index 00ee1ad..4b72944 100644 --- a/bdk/soc/uart.c +++ b/bdk/soc/uart.c @@ -23,12 +23,13 @@ /* UART A, B, C, D and E. */ static const u32 uart_baseoff[5] = { 0, 0x40, 0x200, 0x300, 0x400 }; -void uart_init(u32 idx, u32 baud) +void uart_init(u32 idx, u32 baud, u32 mode) { uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); // Make sure no data is being sent. - uart_wait_idle(idx, UART_TX_IDLE); + if (!(mode & (UART_MCR_CTS_EN | UART_MCR_DTR))) + uart_wait_xfer(idx, UART_TX_IDLE); // Set clock. bool clk_type = clock_uart_use_src_div(idx, baud); @@ -39,7 +40,8 @@ void uart_init(u32 idx, u32 baud) uart->UART_LCR = UART_LCR_DLAB | UART_LCR_WORD_LENGTH_8; // Enable DLAB & set 8n1 mode. uart->UART_THR_DLAB = (u8)div; // Divisor latch LSB. uart->UART_IER_DLAB = (u8)(div >> 8); // Divisor latch MSB. - uart->UART_LCR = UART_LCR_WORD_LENGTH_8; // Disable DLAB. + // Disable DLAB and set 2 STOP bits (reduced efficiency but less errors on high baudrates). + uart->UART_LCR = UART_LCR_STOP | UART_LCR_WORD_LENGTH_8; (void)uart->UART_SPR; // Setup and flush fifo. @@ -50,12 +52,14 @@ void uart_init(u32 idx, u32 baud) usleep(96); uart->UART_IIR_FCR = UART_IIR_FCR_EN_FIFO | UART_IIR_FCR_TX_CLR | UART_IIR_FCR_RX_CLR; + uart->UART_MCR = mode; + // Wait 3 symbols for baudrate change. usleep(3 * ((baud + 999999) / baud)); - uart_wait_idle(idx, UART_TX_IDLE | UART_RX_IDLE); + uart_wait_xfer(idx, UART_TX_IDLE | UART_RX_RDYR); } -void uart_wait_idle(u32 idx, u32 which) +void uart_wait_xfer(u32 idx, u32 which) { uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); if (UART_TX_IDLE & which) @@ -63,10 +67,10 @@ void uart_wait_idle(u32 idx, u32 which) while (!(uart->UART_LSR & UART_LSR_TMTY)) ; } - if (UART_RX_IDLE & which) + if (UART_RX_RDYR & which) { while (uart->UART_LSR & UART_LSR_RDR) - ; + (void)uart->UART_THR_DLAB; } } @@ -85,26 +89,31 @@ void uart_send(u32 idx, const u8 *buf, u32 len) u32 uart_recv(u32 idx, u8 *buf, u32 len) { uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + bool manual_mode = uart->UART_MCR & UART_MCR_RTS; u32 timeout = get_tmr_us() + 250; u32 i; + if (manual_mode) + uart->UART_MCR &= ~UART_MCR_RTS; + for (i = 0; ; i++) { - while (!(uart->UART_LSR & UART_LSR_RDR)) - { - if (timeout < get_tmr_us()) - break; - if (len && len < i) - break; - } - if (timeout < get_tmr_us()) + if (len && len <= i) break; + while (!(uart->UART_LSR & UART_LSR_RDR)) + if (timeout < get_tmr_us()) + goto out; + buf[i] = uart->UART_THR_DLAB; timeout = get_tmr_us() + 250; } - return i ? (len ? (i - 1) : i) : 0; +out: + if (manual_mode) + uart->UART_MCR |= UART_MCR_RTS; + + return i; } void uart_invert(u32 idx, bool enable, u32 invert_mask) @@ -118,6 +127,14 @@ void uart_invert(u32 idx, bool enable, u32 invert_mask) (void)uart->UART_SPR; } +void uart_set_mode(u32 idx, u32 mode) +{ + uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + + uart->UART_MCR = mode; + (void)uart->UART_SPR; +} + u32 uart_get_IIR(u32 idx) { uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); diff --git a/bdk/soc/uart.h b/bdk/soc/uart.h index 103db13..26a4c7f 100644 --- a/bdk/soc/uart.h +++ b/bdk/soc/uart.h @@ -28,33 +28,33 @@ #define BAUD_115200 115200 -#define UART_TX_IDLE 0x1 -#define UART_RX_IDLE 0x2 +#define UART_TX_IDLE BIT(0) +#define UART_RX_RDYR BIT(1) -#define UART_TX_FIFO_FULL 0x100 -#define UART_RX_FIFO_EMPTY 0x200 +#define UART_TX_FIFO_FULL BIT(8) +#define UART_RX_FIFO_EMPTY BIT(9) -#define UART_INVERT_RXD 0x01 -#define UART_INVERT_TXD 0x02 -#define UART_INVERT_CTS 0x04 -#define UART_INVERT_RTS 0x08 +#define UART_INVERT_RXD BIT(0) +#define UART_INVERT_TXD BIT(1) +#define UART_INVERT_CTS BIT(2) +#define UART_INVERT_RTS BIT(3) -#define UART_IER_DLAB_IE_EORD 0x20 +#define UART_IER_DLAB_IE_EORD BIT(5) -#define UART_LCR_DLAB 0x80 -#define UART_LCR_STOP 0x4 #define UART_LCR_WORD_LENGTH_8 0x3 +#define UART_LCR_STOP BIT(2) +#define UART_LCR_DLAB BIT(7) -#define UART_LSR_RDR 0x1 -#define UART_LSR_THRE 0x20 -#define UART_LSR_TMTY 0x40 -#define UART_LSR_FIFOE 0x80 +#define UART_LSR_RDR BIT(0) +#define UART_LSR_THRE BIT(5) +#define UART_LSR_TMTY BIT(6) +#define UART_LSR_FIFOE BIT(7) -#define UART_IIR_FCR_TX_CLR 0x4 -#define UART_IIR_FCR_RX_CLR 0x2 -#define UART_IIR_FCR_EN_FIFO 0x1 +#define UART_IIR_FCR_EN_FIFO BIT(0) +#define UART_IIR_FCR_RX_CLR BIT(1) +#define UART_IIR_FCR_TX_CLR BIT(2) -#define UART_IIR_NO_INT BIT(0) +#define UART_IIR_NO_INT BIT(0) #define UART_IIR_INT_MASK 0xF /* Custom returned interrupt results. Actual interrupts are -1 */ #define UART_IIR_NOI 0 // No interrupt. @@ -65,8 +65,10 @@ #define UART_IIR_REDI 5 // Receiver end of data interrupt. #define UART_IIR_RDTI 7 // Receiver data timeout interrupt. -#define UART_MCR_RTS 0x2 -#define UART_MCR_DTR 0x1 +#define UART_MCR_DTR BIT(0) +#define UART_MCR_RTS BIT(1) +#define UART_MCR_CTS_EN BIT(5) +#define UART_MCR_RTS_EN BIT(6) typedef struct _uart_t { @@ -86,11 +88,24 @@ typedef struct _uart_t /* 0x3C */ vu32 UART_ASR; } uart_t; -void uart_init(u32 idx, u32 baud); -void uart_wait_idle(u32 idx, u32 which); +//! TODO: Commented out modes are not supported yet. +typedef enum _uart_mode_t +{ + UART_AO_TX_AO_RX = 0, + //UART_MN_TX_AO_RX = UART_MCR_RTS | UART_MCR_DTR, + UART_AO_TX_MN_RX = UART_MCR_RTS, // Up to 36 bytes read. + //UART_MN_TX_AO_RX = UART_MCR_DTR, + //UART_HW_TX_HW_RX = UART_MCR_RTS_EN | UART_MCR_CTS_EN, + UART_AO_TX_HW_RX = UART_MCR_RTS_EN, + //UART_HW_TX_AO_RX = UART_MCR_CTS_EN, +} uart_mode_t; + +void uart_init(u32 idx, u32 baud, u32 mode); +void uart_wait_xfer(u32 idx, u32 which); void uart_send(u32 idx, const u8 *buf, u32 len); u32 uart_recv(u32 idx, u8 *buf, u32 len); void uart_invert(u32 idx, bool enable, u32 invert_mask); +void uart_set_mode(u32 idx, u32 mode); u32 uart_get_IIR(u32 idx); void uart_set_IIR(u32 idx); void uart_empty_fifo(u32 idx, u32 which);