diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 9d9a1dc84558..09189b7c90e5 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -152,6 +152,30 @@ #define DMA_RX_BUF_SIZE (2048) #define UART_CONSOLE_RX_WM (2) +enum uart_error_code { + UART_ERROR_DEFAULT, + UART_ERROR_INVALID_FW_LOADED, + UART_ERROR_CLK_GET_FAIL, + UART_ERROR_SE_CLK_RATE_FIND_FAIL, + UART_ERROR_SE_RESOURCES_INIT_FAIL, + UART_ERROR_SE_RESOURCES_ON_FAIL, + UART_ERROR_SE_RESOURCES_OFF_FAIL, + UART_ERROR_TX_DMA_MAP_FAIL, + UART_ERROR_TX_CANCEL_FAIL, + UART_ERROR_TX_ABORT_FAIL, + UART_ERROR_TX_FSM_RESET_FAIL, + UART_ERROR_RX_CANCEL_FAIL, + UART_ERROR_RX_ABORT_FAIL, + UART_ERROR_RX_FSM_RESET_FAIL, + UART_ERROR_RX_TTY_INSERT_FAIL, + UART_ERROR_ILLEGAL_INTERRUPT, + UART_ERROR_BUFFER_OVERRUN, + UART_ERROR_RX_PARITY_ERROR, + UART_ERROR_RX_BREAK_ERROR, + UART_ERROR_RX_SBE_ERROR, + SOC_ERROR_START_TX_IOS_SOC_RFR_HIGH +}; + struct msm_geni_serial_ver_info { int hw_major_ver; int hw_minor_ver; @@ -212,6 +236,7 @@ struct msm_geni_serial_port { struct completion s_cmd_timeout; spinlock_t rx_lock; bool bypass_flow_control; + enum uart_error_code uart_error; }; static const struct uart_ops msm_geni_serial_pops; @@ -248,6 +273,19 @@ static struct msm_geni_serial_port msm_geni_console_port; static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS]; static void msm_geni_serial_handle_isr(struct uart_port *uport, unsigned long *flags, bool is_irq_masked); +/* + * The below API is required to pass UART error code to BT HOST. + */ +static void msm_geni_update_uart_error_code(struct msm_geni_serial_port *port, + enum uart_error_code uart_error_code) +{ + if (!port->is_console && !port->uart_error) { + port->uart_error = uart_error_code; + IPC_LOG_MSG(port->ipc_log_misc, + "%s: uart_error_code:%d\n", __func__, port->uart_error); + } +} + /* * The below API is required to check if uport->lock (spinlock) @@ -557,6 +595,7 @@ static int msm_geni_serial_ioctl(struct uart_port *uport, unsigned int cmd, { int ret = -ENOIOCTLCMD; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + enum uart_error_code uart_error; switch (cmd) { case TIOCPMGET: { @@ -572,13 +611,12 @@ static int msm_geni_serial_ioctl(struct uart_port *uport, unsigned int cmd, break; } case TIOCFAULT: { - geni_se_dump_dbg_regs(&port->serial_rsc, - uport->membase, port->ipc_log_misc); - port->ipc_log_rx = port->ipc_log_single; - port->ipc_log_tx = port->ipc_log_single; - port->ipc_log_misc = port->ipc_log_single; - port->ipc_log_pwr = port->ipc_log_single; - ret = 0; + uart_error = port->uart_error; + port->uart_error = UART_ERROR_DEFAULT; + IPC_LOG_MSG(port->ipc_log_misc, + "%s:TIOCFAULT - uart_error_set:%d new_uart_error:%d\n", + __func__, uart_error, port->uart_error); + ret = uart_error; break; } default: @@ -629,6 +667,10 @@ static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport) geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS); if (!(geni_ios & IO2_DATA_IN)) mctrl |= TIOCM_CTS; + else + msm_geni_update_uart_error_code(port, + SOC_ERROR_START_TX_IOS_SOC_RFR_HIGH); + IPC_LOG_MSG(port->ipc_log_misc, "%s: geni_ios:0x%x, mctrl:0x%x\n", __func__, geni_ios, mctrl); return mctrl; @@ -1111,6 +1153,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) } else { IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: TX DMA map Fail %d\n", __func__, ret); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_TX_DMA_MAP_FAIL); geni_write_reg_nolog(0, uport->membase, SE_UART_TX_TRANS_LEN); msm_port->m_cmd_done = false; @@ -1133,6 +1177,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: tx_cancel failed 0x%x\n", __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_TX_CANCEL_FAIL); msm_port->m_cmd_done = false; reinit_completion(&msm_port->m_cmd_timeout); @@ -1150,6 +1196,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) "%s: tx abort failed 0x%x\n", __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_TX_ABORT_FAIL); } } @@ -1164,9 +1212,12 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) timeout = geni_wait_for_cmd_done(uport, is_irq_masked); - if (timeout) + if (timeout) { IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: tx fsm reset failed\n", __func__); + msm_geni_update_uart_error_code( + msm_port, UART_ERROR_TX_FSM_RESET_FAIL); + } } if (msm_port->tx_dma) { @@ -1275,6 +1326,8 @@ static void stop_tx_sequencer(struct uart_port *uport) __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); IPC_LOG_MSG(port->ipc_log_misc, "%s: tx_cancel failed 0x%x\n", __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + msm_geni_update_uart_error_code(port, + UART_ERROR_TX_CANCEL_FAIL); port->m_cmd_done = false; reinit_completion(&port->m_cmd_timeout); @@ -1288,6 +1341,8 @@ static void stop_tx_sequencer(struct uart_port *uport) IPC_LOG_MSG(port->ipc_log_misc, "%s: tx abort failed 0x%x\n", __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + msm_geni_update_uart_error_code(port, + UART_ERROR_TX_ABORT_FAIL); } } @@ -1301,9 +1356,12 @@ static void stop_tx_sequencer(struct uart_port *uport) timeout = geni_wait_for_cmd_done(uport, is_irq_masked); - if (timeout) + if (timeout) { IPC_LOG_MSG(port->ipc_log_misc, "%s: tx fsm reset failed\n", __func__); + msm_geni_update_uart_error_code(port, + UART_ERROR_TX_FSM_RESET_FAIL); + } } if (port->tx_dma) { @@ -1522,6 +1580,9 @@ static int stop_rx_sequencer(struct uart_port *uport) __func__, timeout, is_rx_active, geni_status); geni_se_dump_dbg_regs(&port->serial_rsc, uport->membase, port->ipc_log_misc); + msm_geni_update_uart_error_code(port, + UART_ERROR_RX_CANCEL_FAIL); + /* * Possible that stop_rx is called from system resume context * for console usecase. In early resume, irq remains disabled @@ -1551,6 +1612,8 @@ static int stop_rx_sequencer(struct uart_port *uport) IPC_LOG_MSG(port->console_log, "%s abort fail timeout:%d is_rx_active:%d 0x%x\n", __func__, timeout, is_rx_active, geni_status); + msm_geni_update_uart_error_code(port, + UART_ERROR_RX_ABORT_FAIL); geni_se_dump_dbg_regs(&port->serial_rsc, uport->membase, port->ipc_log_misc); } @@ -1563,9 +1626,12 @@ static int stop_rx_sequencer(struct uart_port *uport) timeout = geni_wait_for_cmd_done(uport, is_irq_masked); - if (timeout) + if (timeout) { IPC_LOG_MSG(port->ipc_log_misc, "%s: rx fsm reset failed\n", __func__); + msm_geni_update_uart_error_code(port, + UART_ERROR_RX_FSM_RESET_FAIL); + } } } /* Enable the interrupts once the cancel operation is done. */ @@ -1631,6 +1697,8 @@ static int handle_rx_hs(struct uart_port *uport, if (ret != rx_bytes) { dev_err(uport->dev, "%s: ret %d rx_bytes %d\n", __func__, ret, rx_bytes); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_RX_TTY_INSERT_FAIL); WARN_ON(1); } uport->icount.rx += ret; @@ -1916,6 +1984,8 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) "%s.Rx Errors. 0x%x parity:%d\n", __func__, dma_rx_status, uport->icount.parity); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_RX_PARITY_ERROR); drop_rx = true; } else if (dma_rx_status & UART_DMA_RX_BREAK) { uport->icount.brk++; @@ -1923,6 +1993,8 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) "%s.Rx Errors. 0x%x break:%d\n", __func__, dma_rx_status, uport->icount.brk); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_RX_BREAK_ERROR); } if (dma_rx_status & RX_EOT || @@ -1942,6 +2014,8 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.Rx Errors. 0x%x\n", __func__, dma_rx_status); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_RX_SBE_ERROR); WARN_ON(1); } @@ -2000,8 +2074,11 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport, IPC_LOG_MSG(msm_port->console_log, "%s.Illegal interrupt. sirq 0x%x mirq:0x%x\n", __func__, s_irq_status, m_irq_status); - else + else { + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_ILLEGAL_INTERRUPT); WARN_ON(1); + } goto exit_geni_serial_isr; } @@ -2018,6 +2095,8 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport, IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.sirq 0x%x buf_overrun:%d\n", __func__, s_irq_status, uport->icount.buf_overrun); + msm_geni_update_uart_error_code(msm_port, + UART_ERROR_BUFFER_OVERRUN); } dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN); @@ -2531,6 +2610,8 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, if (ret) { dev_err(uport->dev, "%s: Failed(%d) to find src clk for 0x%x\n", __func__, ret, baud); + msm_geni_update_uart_error_code(port, + UART_ERROR_SE_CLK_RATE_FIND_FAIL); goto exit_set_termios; } @@ -3252,8 +3333,11 @@ static int msm_geni_serial_probe(struct platform_device *pdev) UART_CORE2X_VOTE, (DEFAULT_SE_CLK * DEFAULT_BUS_WIDTH)); - if (ret) + if (ret) { + msm_geni_update_uart_error_code(dev_port, + UART_ERROR_SE_RESOURCES_INIT_FAIL); goto exit_geni_serial_probe; + } dev_port->serial_rsc.ctrl_dev = &pdev->dev; @@ -3274,6 +3358,8 @@ static int msm_geni_serial_probe(struct platform_device *pdev) if (IS_ERR(dev_port->serial_rsc.se_clk)) { ret = PTR_ERR(dev_port->serial_rsc.se_clk); dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret); + msm_geni_update_uart_error_code(dev_port, + UART_ERROR_CLK_GET_FAIL); goto exit_geni_serial_probe; } @@ -3281,6 +3367,8 @@ static int msm_geni_serial_probe(struct platform_device *pdev) if (IS_ERR(dev_port->serial_rsc.m_ahb_clk)) { ret = PTR_ERR(dev_port->serial_rsc.m_ahb_clk); dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret); + msm_geni_update_uart_error_code(dev_port, + UART_ERROR_CLK_GET_FAIL); goto exit_geni_serial_probe; } @@ -3288,6 +3376,8 @@ static int msm_geni_serial_probe(struct platform_device *pdev) if (IS_ERR(dev_port->serial_rsc.s_ahb_clk)) { ret = PTR_ERR(dev_port->serial_rsc.s_ahb_clk); dev_err(&pdev->dev, "Err getting S AHB clk %d\n", ret); + msm_geni_update_uart_error_code(dev_port, + UART_ERROR_CLK_GET_FAIL); goto exit_geni_serial_probe; } @@ -3427,6 +3517,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev) device_create_file(uport->dev, &dev_attr_ver_info); msm_geni_serial_debug_init(uport, is_console); dev_port->port_setup = false; + dev_port->uart_error = UART_ERROR_DEFAULT; ret = msm_geni_serial_get_ver_info(uport); if (ret) goto exit_wakeup_unregister; @@ -3524,6 +3615,8 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) ret = se_geni_resources_off(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); + msm_geni_update_uart_error_code(port, + UART_ERROR_SE_RESOURCES_OFF_FAIL); goto exit_runtime_suspend; } @@ -3560,6 +3653,8 @@ static int msm_geni_serial_runtime_resume(struct device *dev) ret = se_geni_resources_on(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); + msm_geni_update_uart_error_code(port, + UART_ERROR_SE_RESOURCES_ON_FAIL); __pm_relax(port->geni_wake); goto exit_runtime_resume; } diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h index 44ccfd8818f2..6b14abd5180c 100644 --- a/include/uapi/asm-generic/ioctls.h +++ b/include/uapi/asm-generic/ioctls.h @@ -79,7 +79,7 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ -#define TIOCFAULT 0x544C /* Uart fault */ +#define TIOCFAULT 0x54EC /* Uart fault */ #define TIOCPMGET 0x544D /* PM get */ #define TIOCPMPUT 0x544E /* PM put */ #define TIOCPMACT 0x544F /* PM is active */