From bc17be09dac840cd3d0e00c8bf70e749080908d8 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 14 Oct 2016 19:38:05 -0700 Subject: [PATCH] fg-memif: update IMA error handling and clear sequence Based on the hardware documentation, update the IMA error handling and clear sequence. In addition, check for DMA errors and clear it before SRAM transactions begin. Also, check for IMA hardware status to run the IMA clear sequence during ima_init and not just based on IMA exception status alone. This is to help with FG SRAM access to resume again properly in case of an error encountered. Change-Id: I583fa51599a1cbbd029cb45c075429730d2e071b Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/qcom-charger/fg-core.h | 2 + drivers/power/qcom-charger/fg-memif.c | 195 +++++++++++++++++----- drivers/power/qcom-charger/fg-reg.h | 22 +++ drivers/power/qcom-charger/qpnp-fg-gen3.c | 33 +++- 4 files changed, 213 insertions(+), 39 deletions(-) diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 3f8f66f1f7d8..2167eb8bae59 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -342,6 +342,8 @@ extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len); extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len); extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val); extern int fg_ima_init(struct fg_chip *chip); +extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts); +extern int fg_clear_dma_errors_if_any(struct fg_chip *chip); extern int fg_debugfs_create(struct fg_chip *chip); extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c index c271b24adfc4..88c96bc55ec4 100644 --- a/drivers/power/qcom-charger/fg-memif.c +++ b/drivers/power/qcom-charger/fg-memif.c @@ -64,44 +64,90 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst) static int fg_run_iacs_clear_sequence(struct fg_chip *chip) { - u8 tmp; - int rc; + u8 val, hw_sts, exp_sts; + int rc, tries = 250; /* * Values to write for running IACS clear sequence comes from * hardware documentation. */ - rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, - IACS_CLR_BIT); + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), + IACS_CLR_BIT | STATIC_CLK_EN_BIT, + IACS_CLR_BIT | STATIC_CLK_EN_BIT); if (rc < 0) { pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), rc); return rc; } - tmp = 0x4; - rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1); + rc = fg_config_access_mode(chip, FG_READ, false); if (rc < 0) { - pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip), - rc); + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_IMA_CTL(chip), rc); return rc; } - tmp = 0x0; - rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1); + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT); if (rc < 0) { - pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip), - rc); + pr_err("failed to set ima_req_access bit rc=%d\n", rc); return rc; } - rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1); - if (rc < 0) { - pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip), - rc); - return rc; + /* Delay for the clock to reach FG */ + usleep_range(35, 40); + + while (1) { + val = 0; + rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &val, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", + MEM_IF_ADDR_MSB(chip), rc); + return rc; + } + + val = 0; + rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &val, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", + MEM_IF_WR_DATA3(chip), rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &val, 1); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", + MEM_IF_RD_DATA3(chip), rc); + return rc; + } + + /* Delay for IMA hardware to clear */ + usleep_range(35, 40); + + rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_hw_sts rc=%d\n", rc); + return rc; + } + + if (hw_sts != 0) + continue; + + rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_exp_sts rc=%d\n", rc); + return rc; + } + + if (exp_sts == 0 || !(--tries)) + break; } + if (!tries) + pr_err("Failed to clear the error? hw_sts: %x exp_sts: %d\n", + hw_sts, exp_sts); + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0); if (rc < 0) { pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), @@ -109,14 +155,65 @@ static int fg_run_iacs_clear_sequence(struct fg_chip *chip) return rc; } + udelay(5); + + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) { + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_MEM_INTF_CFG(chip), rc); + return rc; + } + + /* Delay before next transaction is attempted */ + usleep_range(35, 40); fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n"); return rc; } -static int fg_check_for_ima_errors(struct fg_chip *chip) +int fg_clear_dma_errors_if_any(struct fg_chip *chip) +{ + int rc; + u8 dma_sts; + + rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + MEM_IF_DMA_STS(chip), rc); + return rc; + } + fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts); + + if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) { + rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), + DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT); + if (rc < 0) { + pr_err("failed to write addr=0x%04x, rc=%d\n", + MEM_IF_DMA_CTL(chip), rc); + return rc; + } + } + + return 0; +} + +int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts) { int rc = 0; u8 err_sts, exp_sts = 0, hw_sts = 0; + bool run_err_clr_seq = false; + + rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_exp_sts rc=%d\n", rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_hw_sts rc=%d\n", rc); + return rc; + } rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1); if (rc < 0) { @@ -124,22 +221,30 @@ static int fg_check_for_ima_errors(struct fg_chip *chip) return rc; } - if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) { - rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1); - if (rc < 0) { - pr_err("failed to read ima_exp_sts rc=%d\n", rc); - return rc; + fg_dbg(chip, FG_STATUS, "ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", + err_sts, exp_sts, hw_sts); + + if (check_hw_sts) { + /* + * Lower nibble should be equal to upper nibble before SRAM + * transactions begins from SW side. If they are unequal, then + * the error clear sequence should be run irrespective of IMA + * exception errors. + */ + if ((hw_sts & 0x0F) != hw_sts >> 4) { + pr_err("IMA HW not in correct state, hw_sts=%x\n", + hw_sts); + run_err_clr_seq = true; } + } - rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1); - if (rc < 0) { - pr_err("failed to read ima_hw_sts rc=%d\n", rc); - return rc; - } - - pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", - err_sts, exp_sts, hw_sts); + if (exp_sts & (IACS_ERR_BIT | XCT_TYPE_ERR_BIT | DATA_RD_ERR_BIT | + DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_STABLE_ERR_BIT)) { + pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts); + run_err_clr_seq = true; + } + if (run_err_clr_seq) { /* clear the error */ rc = fg_run_iacs_clear_sequence(chip); if (rc < 0) { @@ -156,7 +261,7 @@ static int fg_check_for_ima_errors(struct fg_chip *chip) static int fg_check_iacs_ready(struct fg_chip *chip) { - int rc = 0, timeout = 250; + int rc = 0, tries = 250; u8 ima_opr_sts = 0; /* @@ -176,17 +281,17 @@ static int fg_check_iacs_ready(struct fg_chip *chip) if (ima_opr_sts & IACS_RDY_BIT) break; - if (!(--timeout)) + if (!(--tries)) break; /* delay for iacs_ready to be asserted */ usleep_range(5000, 7000); } - if (!timeout) { + if (!tries) { pr_err("IACS_RDY not set\n"); - - rc = fg_check_for_ima_errors(chip); + /* check for error condition */ + rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { pr_err("Failed to check for ima errors rc=%d\n", rc); return rc; @@ -250,7 +355,7 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, } /* check for error condition */ - rc = fg_check_for_ima_errors(chip); + rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { pr_err("Failed to check for ima errors rc=%d\n", rc); return rc; @@ -296,7 +401,7 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address, offset = 0; /* check for error condition */ - rc = fg_check_for_ima_errors(chip); + rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { pr_err("Failed to check for ima errors rc=%d\n", rc); return rc; @@ -581,5 +686,19 @@ int fg_ima_init(struct fg_chip *chip) return rc; } + /* Clear DMA errors if any before clearing IMA errors */ + rc = fg_clear_dma_errors_if_any(chip); + if (rc < 0) { + pr_err("Error in checking DMA errors rc:%d\n", rc); + return rc; + } + + /* Clear IMA errors if any before SRAM transactions can begin */ + rc = fg_clear_ima_errors_if_any(chip, true); + if (rc < 0 && rc != -EAGAIN) { + pr_err("Error in checking IMA errors rc:%d\n", rc); + return rc; + } + return 0; } diff --git a/drivers/power/qcom-charger/fg-reg.h b/drivers/power/qcom-charger/fg-reg.h index 431e28a7eb1f..183a1819ab3c 100644 --- a/drivers/power/qcom-charger/fg-reg.h +++ b/drivers/power/qcom-charger/fg-reg.h @@ -258,6 +258,7 @@ #define ESR_REQ_CTL_EN_BIT BIT(0) /* FG_MEM_IF register and bit definitions */ +#define MEM_IF_INT_RT_STS(chip) ((chip->mem_if_base) + 0x10) #define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50) #define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51) #define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52) @@ -273,6 +274,11 @@ #define MEM_IF_WR_DATA3(chip) ((chip->mem_if_base) + 0x66) #define MEM_IF_RD_DATA0(chip) ((chip->mem_if_base) + 0x67) #define MEM_IF_RD_DATA3(chip) ((chip->mem_if_base) + 0x6A) +#define MEM_IF_DMA_STS(chip) ((chip->mem_if_base) + 0x70) +#define MEM_IF_DMA_CTL(chip) ((chip->mem_if_base) + 0x71) + +/* MEM_IF_INT_RT_STS */ +#define MEM_XCP_BIT BIT(1) /* MEM_IF_MEM_INTF_CFG */ #define MEM_ACCESS_REQ_BIT BIT(7) @@ -286,10 +292,19 @@ /* MEM_IF_IMA_CFG */ #define IACS_CLR_BIT BIT(2) #define IACS_INTR_SRC_SLCT_BIT BIT(3) +#define STATIC_CLK_EN_BIT BIT(4) /* MEM_IF_IMA_OPR_STS */ #define IACS_RDY_BIT BIT(1) +/* MEM_IF_IMA_EXP_STS */ +#define IACS_ERR_BIT BIT(0) +#define XCT_TYPE_ERR_BIT BIT(1) +#define DATA_RD_ERR_BIT BIT(3) +#define DATA_WR_ERR_BIT BIT(4) +#define ADDR_BURST_WRAP_BIT BIT(5) +#define ADDR_STABLE_ERR_BIT BIT(7) + /* MEM_IF_IMA_ERR_STS */ #define ADDR_STBL_ERR_BIT BIT(7) #define WR_ACS_ERR_BIT BIT(6) @@ -297,4 +312,11 @@ /* MEM_IF_FG_BEAT_COUNT */ #define BEAT_COUNT_MASK GENMASK(3, 0) + +/* MEM_IF_DMA_STS */ +#define DMA_WRITE_ERROR_BIT BIT(1) +#define DMA_READ_ERROR_BIT BIT(2) + +/* MEM_IF_DMA_CTL */ +#define DMA_CLEAR_LOG_BIT BIT(0) #endif diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index 00a3f3ecdc20..d9ae95c45f83 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -2267,6 +2267,37 @@ static int fg_memif_init(struct fg_chip *chip) /* INTERRUPT HANDLERS STAY HERE */ +static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + u8 status; + int rc; + + rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &status, 1); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + MEM_IF_INT_RT_STS(chip), rc); + return IRQ_HANDLED; + } + + fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status); + if (status & MEM_XCP_BIT) { + rc = fg_clear_dma_errors_if_any(chip); + if (rc < 0) { + pr_err("Error in clearing DMA error, rc=%d\n", rc); + return IRQ_HANDLED; + } + + mutex_lock(&chip->sram_rw_lock); + rc = fg_clear_ima_errors_if_any(chip, true); + if (rc < 0 && rc != -EAGAIN) + pr_err("Error in checking IMA errors rc:%d\n", rc); + mutex_unlock(&chip->sram_rw_lock); + } + + return IRQ_HANDLED; +} + static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) { struct fg_chip *chip = data; @@ -2483,7 +2514,7 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { }, [MEM_XCP_IRQ] = { .name = "mem-xcp", - .handler = fg_dummy_irq_handler, + .handler = fg_mem_xcp_irq_handler, }, [IMA_RDY_IRQ] = { .name = "ima-rdy",