Skip to content

Commit

Permalink
os/arch/arm/src/amebasmart: Fix UART data loss when wakeup from PG
Browse files Browse the repository at this point in the history
+ Required: This commit has cherry picked #6482 and is required to work

+ Issue is encountered where bytes are lost on UART wakeup source during resume from sleep
+ Analysis: UART FIFO is started but not drained during resume. When the peripheral is initialized again, it causes the FIFO to also be cleared and cause data loss
+ Similarly if more than 64 bytes is passed in together, since there is no mechanism to drain the FIFO into RAM it will cause LSR overrun error
+ This fix drains the FIFO first at KM4, then again at CA32 until it fully wake, then normal Tizen ISR handler resume operation
+ The drained buffer is also passed to application layer
  • Loading branch information
lhenry-realtek committed Nov 11, 2024
1 parent 1171976 commit 5998015
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 7 deletions.
Binary file modified build/tools/amebasmart/gnu_utility/km0_km4_app.bin
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* == "version" + "Realtek git version" + "compile date" + "compile time" == */

== version 69865ced40 2024/11/11-13:19:10 ==
1. Add UART FIFO drain mechanism when receive IPC from CA32
2. Clear UART Rx error status as polling mode in CA32 side does not automatically do it

== version c2b09705dc 2024/10/18-10:30:31 ==
1. Update wifi channel plan version
2. Added new channel plans to meet Samsung requirements
Expand Down
142 changes: 136 additions & 6 deletions os/arch/arm/src/amebasmart/amebasmart_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,11 @@ static uart_dev_t g_uart4port = {
};
#endif

/* FIFO Drain buffer for UART PG wakeup */
static ALIGNMTO(CACHE_LINE_SIZE) u8 g_uart1_buf[256] = { 0 };
static u32 g_uart1_buf_head = &g_uart1_buf;
static u32 g_uart1_dataleft = 0;

/****************************************************************************
* Private Functions
****************************************************************************/
Expand Down Expand Up @@ -1034,8 +1039,28 @@ static int rtl8730e_up_receive(struct uart_dev_s *dev, unsigned int*status)
uint32_t rxd;

DEBUGASSERT(priv);
rxd = serial_getc(sdrv[uart_index_get(priv->tx)]);
*status = rxd;

/* if there is still data in the FIFO drain buffer, read from there, otherwise read from peripheral */
if(g_uart1_dataleft > 0) {
rxd = *((u8 *)g_uart1_buf_head);
g_uart1_buf_head++;
g_uart1_dataleft--;

/* prevent g_uart1_buf_head from overflow */
if(((u32)g_uart1_buf_head - (u32)g_uart1_buf) > sizeof(g_uart1_buf)) {
DiagPrintf("Head pointer exceed buffer size!\n");
g_uart1_buf_head--;
}

/* prevent g_uart1_dataleft from underflow */
if(g_uart1_dataleft == 0xFFFFFFFF) {
g_uart1_dataleft = 0;
}
} else {
/* read from FIFO */
rxd = serial_getc(sdrv[uart_index_get(priv->tx)]);
*status = rxd;
}

return rxd & 0xff;
}
Expand Down Expand Up @@ -1067,7 +1092,12 @@ static bool rtl8730e_up_rxavailable(struct uart_dev_s *dev)
{
struct rtl8730e_up_dev_s *priv = (struct rtl8730e_up_dev_s *)dev->priv;
DEBUGASSERT(priv);
return (serial_readable(sdrv[uart_index_get(priv->tx)]));

/* there is data available if either FIFO DRDY==1 or there is stuff in drain buffer */
u8 fifo_hasdata = serial_readable(sdrv[uart_index_get(priv->tx)]);
u8 buf_hasdata = g_uart1_dataleft > 0;

return (fifo_hasdata || buf_hasdata);
}

/****************************************************************************
Expand Down Expand Up @@ -1183,6 +1213,40 @@ static uint32_t rtk_uart_suspend(uint32_t expected_idle_time, void *param)
{
(void)expected_idle_time;
(void)param;

#if 1
ALIGNMTO(CACHE_LINE_SIZE) u8 flag[CACHE_LINE_ALIGMENT(64)];
ALIGNMTO(CACHE_LINE_SIZE) u32 uart_data[16];
IPC_MSG_STRUCT ipc_req_msg __attribute__((aligned(64)));
ipc_req_msg.msg_type = IPC_USER_POINT;
ipc_req_msg.msg = (u32)uart_data;
ipc_req_msg.msg_len = sizeof(uart_data);
ipc_req_msg.rsvd = (u32)flag;

memset(flag, 0, sizeof(flag));
memset(uart_data, 0, sizeof(uart_data));
/* indicate CA32 is ready to rx, switch back to CA32 */
uart_data[0] = g_uart1priv.tx; //tx
uart_data[1] = g_uart1priv.rx; //rx
uart_data[2] = uart_index_get(g_uart1priv.tx); //uart_idx
uart_data[3] = g_uart1priv.baud; //uart baudrate
uart_data[4] = g_uart1priv.parity; //parity
uart_data[5] = g_uart1priv.bits; //bits
uart_data[6] = g_uart1priv.stopbit; //stop bit
uart_data[7] = 1; //1 switch to KM4, 0 switch to CA32

DCache_Clean((u32)uart_data, sizeof(uart_data));
ipc_send_message(IPC_AP_TO_NP, IPC_A2N_UART, &ipc_req_msg);

while (1) {
DCache_Invalidate((u32)flag, sizeof(flag));
if (flag[0]) {
break;
}
}

#endif

#ifdef CONFIG_RTL8730E_UART1
if (sdrv[uart_index_get(g_uart1priv.tx)] != NULL) {
serial_change_clcksrc(sdrv[uart_index_get(g_uart1priv.tx)], g_uart1priv.baud, 0);
Expand All @@ -1195,6 +1259,74 @@ static uint32_t rtk_uart_resume(uint32_t expected_idle_time, void *param)
{
(void)expected_idle_time;
(void)param;

#if 1
ALIGNMTO(CACHE_LINE_SIZE) u8 flag[CACHE_LINE_ALIGMENT(64)];
ALIGNMTO(CACHE_LINE_SIZE) u32 uart_data[16];

/* reset buffer and head pointer for FIFO drain buffer */
g_uart1_dataleft = 0;
memset(g_uart1_buf, 0, sizeof(g_uart1_buf));
g_uart1_buf_head = &g_uart1_buf;

IPC_MSG_STRUCT ipc_req_msg __attribute__((aligned(64)));
ipc_req_msg.msg_type = IPC_USER_POINT;
ipc_req_msg.msg = (u32)uart_data;
ipc_req_msg.msg_len = sizeof(uart_data);
ipc_req_msg.rsvd = (u32)flag;

memset(flag, 0, sizeof(flag));
memset(uart_data, 0, sizeof(uart_data));

/* indicate CA32 is ready to rx, switch back to CA32 */
uart_data[2] = uart_index_get(g_uart1priv.tx);
uart_data[7] = 0; // 1 switch to KM4, 0 switch to CA32
uart_data[10] = 0; // hold the length of km4 data
uart_data[11] = (u32)g_uart1_buf; // buffer to hold drained FIFO data

/* prepare buffers and notify KM4 to begin resume process */
DCache_Clean((u32)uart_data, sizeof(uart_data));
DCache_Clean((u32)g_uart1_buf, sizeof(g_uart1_buf));
ipc_send_message(IPC_AP_TO_NP, IPC_A2N_UART, &ipc_req_msg);

/* wait for KM4 to finish the drain on its side */
while (1) {
DCache_Invalidate((u32)flag, sizeof(flag));
if (flag[0]) {
/* invalidate the cache to receive the drained FIFO data */
DCache_Invalidate((u32)g_uart1_buf, sizeof(g_uart1_buf));
DCache_Invalidate((u32)uart_data, sizeof(uart_data));

/* null terminate for safety */
g_uart1_buf[uart_data[10]] = 0;
break;
}
}

/*
* control has switched back from KM4 to CA32.
* KM4 has stopped reading the FIFO, so now we can drain it in CA32
* no extra config on the peripheral should be done except detach attach irq as required
*/
u8 ch = 0;
g_uart1_dataleft = uart_data[10];
UART_TypeDef* uartx = UART_DEV_TABLE[uart_index_get(g_uart1priv.tx)].UARTx;

/* prevent PM transition while fetching remainder FIFO */
pm_timedsuspend(pm_domain_register("UART_PG_WAKE"), 5000);

/* drain the remainder FIFO from CA32 side */
while(UART_Readable(uartx) == 1) {
UART_CharGet(uartx, &ch);
g_uart1_buf[g_uart1_dataleft++] = ch;
}

UART_INT_Clear(uartx, RUART_BIT_RLSICF);

//DiagPrintf("@total bufsz after resume: %d\n", g_uart1_dataleft);

#endif

#ifdef CONFIG_RTL8730E_UART1
if (sdrv[uart_index_get(g_uart1priv.tx)] != NULL) {
serial_change_clcksrc(sdrv[uart_index_get(g_uart1priv.tx)], g_uart1priv.baud, 1);
Expand Down Expand Up @@ -1399,6 +1531,4 @@ int up_getc(void)
ch = up_lowgetc();
return ch;
}
#endif /* USE_SERIALDRIVER */


#endif /* USE_SERIALDRIVER */
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ typedef struct _IPC_INIT_TABLE_ {
#define IPC_A2L_UARTBRIDGE 2
#define IPC_A2L_DISLOGUART 3
#define IPC_A2L_WIFI_FW_INFO 4 /*!< AP --> LP Get stats info from WIFI FW */
#define IPC_A2L_UART 5 /*! < AP --> NP UART data receive during PG*/
//#define IPC_A2L_Channel5 5
//#define IPC_A2L_Channel6 6
#define IPC_A2L_IMQ_TRX_TRAN 7 /*!< AP --> LP IMQ Message Exchange */
Expand All @@ -335,7 +336,8 @@ typedef struct _IPC_INIT_TABLE_ {
#define IPC_A2N_FLASHPG_REQ 2 /*!< AP --> NP Flash Program Request*/
#define IPC_A2N_BT_API_TRAN 3 /*!< AP --> NP BT API Exchange */
#define IPC_A2N_BT_DRC_TRAN 4 /*!< AP --> NP BT DATA Message Exchange */
#define IPC_A2N_802154_TRAN 5
//#define IPC_A2N_802154_TRAN 5
#define IPC_A2N_UART 5 /*! < AP --> NP UART data receive during PG*/
#define IPC_A2N_OTP_RX_TRAN 6
#define IPC_A2N_LOGUART_RX_SWITCH 7 /*!< AP --> NP Loguart Message Exchange for Linux*/
#define IPC_A2N_IMQ_TRX_TRAN 7 /*!< AP --> NP IMQ Message Exchange for RTOS*/
Expand Down

0 comments on commit 5998015

Please sign in to comment.