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
This commit is contained in:
CTCaer 2022-05-08 05:45:16 +03:00
parent ae394d9f37
commit f7bf4af3ec
4 changed files with 78 additions and 45 deletions

View file

@ -227,7 +227,7 @@ static u8 jc_crc(u8 *data, u16 len)
void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size) void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
{ {
uart_send(uart_port, buf, 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) 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); 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_invert(jc->uart, true, UART_INVERT_TXD);
uart_set_IIR(jc->uart); uart_set_IIR(jc->uart);

View file

@ -102,7 +102,7 @@ static void _config_gpios(bool nx_hoag)
PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0; PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0;
PINMUX_AUX(PINMUX_AUX_UART3_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 #if !defined (DEBUG_UART_PORT) || DEBUG_UART_PORT != UART_B
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
#endif #endif
@ -179,7 +179,7 @@ static void _mbist_workaround()
I2S(I2S5_CG) &= ~I2S_CG_SLCG_ENABLE; I2S(I2S5_CG) &= ~I2S_CG_SLCG_ENABLE;
DISPLAY_A(_DIREG(DC_COM_DSC_TOP_CTL)) |= 4; // DSC_SLCG_OVERRIDE. DISPLAY_A(_DIREG(DC_COM_DSC_TOP_CTL)) |= 4; // DSC_SLCG_OVERRIDE.
VIC(0x8C) = 0xFFFFFFFF; VIC(0x8C) = 0xFFFFFFFF; // NV_PVIC_THI_CONFIG0.
usleep(2); usleep(2);
// Set per-clock reset for APE/VIC/HOST1X/DISP1. // Set per-clock reset for APE/VIC/HOST1X/DISP1.
@ -361,7 +361,7 @@ void hw_init()
#ifdef DEBUG_UART_PORT #ifdef DEBUG_UART_PORT
clock_enable_uart(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); uart_invert(DEBUG_UART_PORT, DEBUG_UART_INVERT, UART_INVERT_TXD);
#endif #endif
@ -378,7 +378,7 @@ void hw_init()
// Initialize I2C5, mandatory for PMIC. // Initialize I2C5, mandatory for PMIC.
i2c_init(I2C_5); 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) if (nx_hoag)
{ {
max7762x_regulator_set_voltage(REGULATOR_LDO8, 2800000); max7762x_regulator_set_voltage(REGULATOR_LDO8, 2800000);

View file

@ -23,12 +23,13 @@
/* UART A, B, C, D and E. */ /* UART A, B, C, D and E. */
static const u32 uart_baseoff[5] = { 0, 0x40, 0x200, 0x300, 0x400 }; 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]); uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]);
// Make sure no data is being sent. // 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. // Set clock.
bool clk_type = clock_uart_use_src_div(idx, baud); 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_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_THR_DLAB = (u8)div; // Divisor latch LSB.
uart->UART_IER_DLAB = (u8)(div >> 8); // Divisor latch MSB. 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; (void)uart->UART_SPR;
// Setup and flush fifo. // Setup and flush fifo.
@ -50,12 +52,14 @@ void uart_init(u32 idx, u32 baud)
usleep(96); usleep(96);
uart->UART_IIR_FCR = UART_IIR_FCR_EN_FIFO | UART_IIR_FCR_TX_CLR | UART_IIR_FCR_RX_CLR; 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. // Wait 3 symbols for baudrate change.
usleep(3 * ((baud + 999999) / baud)); 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]); uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]);
if (UART_TX_IDLE & which) if (UART_TX_IDLE & which)
@ -63,10 +67,10 @@ void uart_wait_idle(u32 idx, u32 which)
while (!(uart->UART_LSR & UART_LSR_TMTY)) while (!(uart->UART_LSR & UART_LSR_TMTY))
; ;
} }
if (UART_RX_IDLE & which) if (UART_RX_RDYR & which)
{ {
while (uart->UART_LSR & UART_LSR_RDR) 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) u32 uart_recv(u32 idx, u8 *buf, u32 len)
{ {
uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); 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 timeout = get_tmr_us() + 250;
u32 i; u32 i;
if (manual_mode)
uart->UART_MCR &= ~UART_MCR_RTS;
for (i = 0; ; i++) for (i = 0; ; i++)
{ {
while (!(uart->UART_LSR & UART_LSR_RDR)) if (len && len <= i)
{
if (timeout < get_tmr_us())
break;
if (len && len < i)
break;
}
if (timeout < get_tmr_us())
break; break;
while (!(uart->UART_LSR & UART_LSR_RDR))
if (timeout < get_tmr_us())
goto out;
buf[i] = uart->UART_THR_DLAB; buf[i] = uart->UART_THR_DLAB;
timeout = get_tmr_us() + 250; 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) 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->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) u32 uart_get_IIR(u32 idx)
{ {
uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]);

View file

@ -28,33 +28,33 @@
#define BAUD_115200 115200 #define BAUD_115200 115200
#define UART_TX_IDLE 0x1 #define UART_TX_IDLE BIT(0)
#define UART_RX_IDLE 0x2 #define UART_RX_RDYR BIT(1)
#define UART_TX_FIFO_FULL 0x100 #define UART_TX_FIFO_FULL BIT(8)
#define UART_RX_FIFO_EMPTY 0x200 #define UART_RX_FIFO_EMPTY BIT(9)
#define UART_INVERT_RXD 0x01 #define UART_INVERT_RXD BIT(0)
#define UART_INVERT_TXD 0x02 #define UART_INVERT_TXD BIT(1)
#define UART_INVERT_CTS 0x04 #define UART_INVERT_CTS BIT(2)
#define UART_INVERT_RTS 0x08 #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_WORD_LENGTH_8 0x3
#define UART_LCR_STOP BIT(2)
#define UART_LCR_DLAB BIT(7)
#define UART_LSR_RDR 0x1 #define UART_LSR_RDR BIT(0)
#define UART_LSR_THRE 0x20 #define UART_LSR_THRE BIT(5)
#define UART_LSR_TMTY 0x40 #define UART_LSR_TMTY BIT(6)
#define UART_LSR_FIFOE 0x80 #define UART_LSR_FIFOE BIT(7)
#define UART_IIR_FCR_TX_CLR 0x4 #define UART_IIR_FCR_EN_FIFO BIT(0)
#define UART_IIR_FCR_RX_CLR 0x2 #define UART_IIR_FCR_RX_CLR BIT(1)
#define UART_IIR_FCR_EN_FIFO 0x1 #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 #define UART_IIR_INT_MASK 0xF
/* Custom returned interrupt results. Actual interrupts are -1 */ /* Custom returned interrupt results. Actual interrupts are -1 */
#define UART_IIR_NOI 0 // No interrupt. #define UART_IIR_NOI 0 // No interrupt.
@ -65,8 +65,10 @@
#define UART_IIR_REDI 5 // Receiver end of data interrupt. #define UART_IIR_REDI 5 // Receiver end of data interrupt.
#define UART_IIR_RDTI 7 // Receiver data timeout interrupt. #define UART_IIR_RDTI 7 // Receiver data timeout interrupt.
#define UART_MCR_RTS 0x2 #define UART_MCR_DTR BIT(0)
#define UART_MCR_DTR 0x1 #define UART_MCR_RTS BIT(1)
#define UART_MCR_CTS_EN BIT(5)
#define UART_MCR_RTS_EN BIT(6)
typedef struct _uart_t typedef struct _uart_t
{ {
@ -86,11 +88,24 @@ typedef struct _uart_t
/* 0x3C */ vu32 UART_ASR; /* 0x3C */ vu32 UART_ASR;
} uart_t; } uart_t;
void uart_init(u32 idx, u32 baud); //! TODO: Commented out modes are not supported yet.
void uart_wait_idle(u32 idx, u32 which); 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); void uart_send(u32 idx, const u8 *buf, u32 len);
u32 uart_recv(u32 idx, 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_invert(u32 idx, bool enable, u32 invert_mask);
void uart_set_mode(u32 idx, u32 mode);
u32 uart_get_IIR(u32 idx); u32 uart_get_IIR(u32 idx);
void uart_set_IIR(u32 idx); void uart_set_IIR(u32 idx);
void uart_empty_fifo(u32 idx, u32 which); void uart_empty_fifo(u32 idx, u32 which);