From 955a3023d67d8adc95f06b31fb829edce07d8778 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Mon, 24 Jan 2022 11:12:49 +0000 Subject: [PATCH] NVIDIA: SAUCE: i2c: tegra: Report critical errors to FSI BugLink: https://bugs.launchpad.net/bugs/2072591 Report critical errors to FSI via epd mailbox mechanism. Capture the TSC timestamp to support error reporting. http://nvbugs/200776298 Signed-off-by: Akhil R Reviewed-by: Petlozu Pravareshwar Reviewed-by: Rahul Bedarkar Reviewed-by: Abhilash G Reviewed-by: Laxman Dewangan Tested-by: Abhilash G Signed-off-by: Laxman Dewangan Acked-by: Jacob Martin Acked-by: Noah Wager Signed-off-by: Noah Wager --- drivers/i2c/busses/i2c-tegra.c | 100 ++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 91be04b534fe..1dc5ce22c042 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -27,6 +27,7 @@ #include #include #include +#include #define BYTES_PER_FIFO_WORD 4 @@ -278,7 +279,9 @@ struct tegra_i2c_dev { struct completion msg_complete; size_t msg_buf_remaining; unsigned int msg_len; - int msg_err; + u32 msg_err; + u32 msg_err_time; + u16 epl_reporter_id; u8 *msg_buf; struct completion dma_complete; @@ -864,6 +867,26 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) return 0; } +static void tegra_i2c_clear_error(struct tegra_i2c_dev *i2c_dev) +{ + i2c_dev->msg_err = I2C_ERR_NONE; + i2c_dev->msg_err_time = 0; +} + +static void tegra_i2c_mark_error(struct tegra_i2c_dev *i2c_dev, u32 error) +{ + u64 ltime; + + /* Log time on the first error */ + if (i2c_dev->epl_reporter_id && !i2c_dev->msg_err_time) { + /* Get TDC counter value */ + asm volatile("mrs %0, cntvct_el0" : "=r" (ltime)); + i2c_dev->msg_err_time = (u32)ltime; + } + + i2c_dev->msg_err |= error; +} + static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) { const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; @@ -877,16 +900,16 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), i2c_readl(i2c_dev, I2C_STATUS), i2c_readl(i2c_dev, I2C_CNFG)); - i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; + tegra_i2c_mark_error(i2c_dev, I2C_ERR_UNKNOWN_INTERRUPT); goto err; } if (status & status_err) { tegra_i2c_disable_packet_mode(i2c_dev); if (status & I2C_INT_NO_ACK) - i2c_dev->msg_err |= I2C_ERR_NO_ACK; + tegra_i2c_mark_error(i2c_dev, I2C_ERR_NO_ACK); if (status & I2C_INT_ARBITRATION_LOST) - i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST; + tegra_i2c_mark_error(i2c_dev, I2C_ERR_ARBITRATION_LOST); goto err; } @@ -905,7 +928,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) * with no XFER_COMPLETE interrupt but hardware * asks to transfer more. */ - i2c_dev->msg_err |= I2C_ERR_RX_BUFFER_OVERFLOW; + tegra_i2c_mark_error(i2c_dev, I2C_ERR_RX_BUFFER_OVERFLOW); goto err; } } @@ -939,7 +962,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) * fully sent. */ if (WARN_ON_ONCE(i2c_dev->msg_buf_remaining)) { - i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; + tegra_i2c_mark_error(i2c_dev, I2C_ERR_UNKNOWN_INTERRUPT); goto err; } complete(&i2c_dev->msg_complete); @@ -1184,30 +1207,60 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev, i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); } +static void tegra_i2c_report_error_to_fsi(struct tegra_i2c_dev *i2c_dev) +{ + struct epl_error_report_frame err_report; + + if (!i2c_dev->epl_reporter_id) + return; + + err_report.error_code = i2c_dev->msg_err; + err_report.error_attribute = 0; + err_report.timestamp = i2c_dev->msg_err_time; + err_report.reporter_id = i2c_dev->epl_reporter_id; + + dev_dbg(i2c_dev->dev, "err: %#x time: %u id: %#x", + err_report.error_code, + err_report.timestamp, + err_report.reporter_id); + + epl_report_error(err_report); + +} + static int tegra_i2c_error_recover(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *msg) { + int ret = 0; + if (i2c_dev->msg_err == I2C_ERR_NONE) return 0; tegra_i2c_init(i2c_dev); - /* start recovery upon arbitration loss in single master mode */ - if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { - if (!i2c_dev->multimaster_mode) - return i2c_recover_bus(&i2c_dev->adapter); - - return -EAGAIN; + /* start recovery upon arbitration loss in single master mode. + * Return the appropriate error otherwise + */ + switch (i2c_dev->msg_err) { + case I2C_ERR_ARBITRATION_LOST: + if (i2c_dev->multimaster_mode) { + ret = -EAGAIN; + } else { + ret = i2c_recover_bus(&i2c_dev->adapter); + if (ret && ret != -EAGAIN) + tegra_i2c_report_error_to_fsi(i2c_dev); + } + break; + case I2C_ERR_NO_ACK: + if (!(msg->flags & I2C_M_IGNORE_NAK)) + ret = -EREMOTEIO; + break; + default: + ret = -EIO; + tegra_i2c_report_error_to_fsi(i2c_dev); } - if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { - if (msg->flags & I2C_M_IGNORE_NAK) - return 0; - - return -EREMOTEIO; - } - - return -EIO; + return ret; } static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, @@ -1226,9 +1279,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->msg_buf = msg->buf; i2c_dev->msg_len = msg->len; - i2c_dev->msg_err = I2C_ERR_NONE; i2c_dev->msg_read = !!(msg->flags & I2C_M_RD); reinit_completion(&i2c_dev->msg_complete); + tegra_i2c_clear_error(i2c_dev); /* * For SMBUS block read command, read only 1 byte in the first transfer. @@ -1648,6 +1701,8 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) { struct device_node *np = i2c_dev->dev->of_node; bool multi_mode; + int err; + u32 prop; i2c_parse_fw_timings(i2c_dev->dev, &i2c_dev->timings, true); @@ -1661,6 +1716,9 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) if (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && of_device_is_compatible(np, "nvidia,tegra210-i2c-vi")) i2c_dev->is_vi = true; + + err = of_property_read_u32(np, "nvidia,epl-reporter-id", &prop); + i2c_dev->epl_reporter_id = (err) ? 0 : prop; } static int tegra_i2c_init_reset(struct tegra_i2c_dev *i2c_dev)