From 23461a38516ce0dd88062903a98e4a46a36c58f4 Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Wed, 6 Jun 2018 20:56:53 +0800 Subject: [PATCH] CEC: add timeout and recovery in CEC frame sending To avoid hang on CEC frame sending, introduce timeout and recovery. Bug 2151251 Change-Id: Ia7c87ee874734c3ed976f3a9142ecc7e2509ec43 Reviewed-on: https://git-master.nvidia.com/r/1741606 (cherry picked from commit e0010f8fb4443cc146a84d76b8666ee831010f56) Signed-off-by: Chun Xu Reviewed-on: https://git-master.nvidia.com/r/1764220 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 42 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 904f48be..eaf68014 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -50,6 +50,12 @@ #define LOGICAL_ADDRESS_BROADCAST 0xF #define TEXT_VIEW_ON 0x0D #define ACTIVE_SOURCE 0x82 +/* + * 400 ms is the time it takes for one 16 byte message to be + * transferred and 5 is the maximum number of retries. Add + * another 100 ms as a margin. + */ +#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) static bool previous_reboot_reason_is_recovery, text_view_on_sent; static u8 text_view_on_command[] = { @@ -110,6 +116,16 @@ static inline void tegra_cec_native_tx(const struct tegra_cec *cec, u32 block) cec->cec_base + TEGRA_CEC_INT_STAT); } +static inline void tegra_cec_error_recovery(struct tegra_cec *cec) +{ + u32 hw_ctrl; + + hw_ctrl = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); + writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); +} + static int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) { @@ -125,8 +141,10 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) * subsequent transmission. */ ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, - msecs_to_jiffies(1000)); - if (ret <= 0) + msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); + if (ret == 0) + return -ETIME; + else if (ret < 0) return ret; mode = TEGRA_CEC_LADDR_MODE(buf[0]) << TEGRA_CEC_TX_REG_ADDR_MODE_SHIFT; @@ -147,9 +165,15 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) cec->cec_base + TEGRA_CEC_INT_MASK); ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, - msecs_to_jiffies(1000)); - if (ret > 0) + msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); + if (ret > 0) { ret = cec->tx_error; + } else if (ret == 0) { + dev_err(cec->dev, "timeout in %s:%d.", __func__, __LINE__); + tegra_cec_error_recovery(cec); + cec->tx_wake = 1; + ret = -ETIME; + } return ret; } @@ -214,16 +238,6 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer, return count; } -static inline void tegra_cec_error_recovery(struct tegra_cec *cec) -{ - u32 hw_ctrl; - - hw_ctrl = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); - writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); -} - static irqreturn_t tegra_cec_irq_handler(int irq, void *data) { struct device *dev = data;