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 <akhilrajeev@nvidia.com>
Reviewed-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Reviewed-by: Rahul Bedarkar <rabedarkar@nvidia.com>
Reviewed-by: Abhilash G <abhilashg@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Abhilash G <abhilashg@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Akhil R
2022-01-24 11:12:49 +00:00
committed by Noah Wager
parent 2cceb6e9ed
commit 955a3023d6
+79 -21
View File
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/tegra-epl.h>
#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)