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",