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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user