// SPDX-License-Identifier: GPL-2.0-only /* * MT6357 regulator driver * * Copyright (c) 2026 BayLibre, SAS. * Author: Julien Masson */ #include #include #include #include #include #include #include #include #include #include static const struct pmic_child_info mt6357_pmic_children_info[] = { { .prefix = "buck", .driver = MT6357_REGULATOR_DRIVER }, { .prefix = "ldo", .driver = MT6357_REGULATOR_DRIVER }, { } }; static const struct pmic_child_info mt6359_pmic_children_info[] = { { .prefix = "buck", .driver = MT6359_REGULATOR_DRIVER }, { .prefix = "ldo", .driver = MT6359_REGULATOR_DRIVER }, { } }; /* macro for wrapper status */ #define PWRAP_GET_WACS_RDATA GENMASK(15, 0) #define PWRAP_GET_WACS_FSM GENMASK(18, 16) #define PWRAP_GET_WACS_ARB_FSM GENMASK(3, 1) #define PWRAP_STATE_SYNC_IDLE0 BIT(20) #define PWRAP_STATE_INIT_DONE0 BIT(21) #define PWRAP_STATE_INIT_DONE1 BIT(15) /* macro for WACS FSM */ #define PWRAP_WACS_FSM_IDLE 0x00 #define PWRAP_WACS_FSM_WFVLDCLR 0x06 /* macro for device wrapper default value */ #define PWRAP_DEW_READ_TEST_VAL 0x5aa5 /* macro for manual command */ #define PWRAP_MAN_CMD_SPI_WRITE BIT(13) #define PWRAP_MAN_CMD_OP_CSH (0x0 << 8) #define PWRAP_MAN_CMD_OP_CSL (0x1 << 8) #define PWRAP_MAN_CMD_OP_OUTS (0x8 << 8) /* macro for Watch Dog Timer Source */ #define PWRAP_WDT_SRC_MASK_ALL GENMASK(31, 0) /* Group of bits used for shown slave capability */ #define PWRAP_SLV_CAP_SPI BIT(0) #define PWRAP_SLV_CAP_DUALIO BIT(1) #define HAS_CAP(_c, _x_val) (((_c) & (_x_val)) == (_x_val)) /* Group of bits used for shown pwrap capability */ #define PWRAP_CAP_INT1_EN BIT(3) #define PWRAP_CAP_WDT_SRC1 BIT(4) #define PWRAP_CAP_ARB BIT(5) /* defines for slave device wrapper registers */ enum dew_regs { PWRAP_DEW_BASE, PWRAP_DEW_DIO_EN, PWRAP_DEW_READ_TEST, PWRAP_DEW_WRITE_TEST, PWRAP_DEW_CRC_EN, PWRAP_DEW_CRC_VAL, PWRAP_DEW_MON_GRP_SEL, PWRAP_DEW_CIPHER_KEY_SEL, PWRAP_DEW_CIPHER_IV_SEL, PWRAP_DEW_CIPHER_RDY, PWRAP_DEW_CIPHER_MODE, PWRAP_DEW_CIPHER_SWRST, /* MT6323 only regs */ PWRAP_DEW_CIPHER_EN, PWRAP_DEW_RDDMY_NO, /* MT6358 only regs */ PWRAP_SMT_CON1, PWRAP_DRV_CON1, PWRAP_FILTER_CON0, PWRAP_GPIO_PULLEN0_CLR, PWRAP_RG_SPI_CON0, PWRAP_RG_SPI_RECORD0, PWRAP_RG_SPI_CON2, PWRAP_RG_SPI_CON3, PWRAP_RG_SPI_CON4, PWRAP_RG_SPI_CON5, PWRAP_RG_SPI_CON6, PWRAP_RG_SPI_CON7, PWRAP_RG_SPI_CON8, PWRAP_RG_SPI_CON13, PWRAP_SPISLV_KEY, /* MT6359 only regs */ PWRAP_DEW_CRC_SWRST, PWRAP_DEW_RG_EN_RECORD, PWRAP_DEW_RECORD_CMD0, PWRAP_DEW_RECORD_CMD1, PWRAP_DEW_RECORD_CMD2, PWRAP_DEW_RECORD_CMD3, PWRAP_DEW_RECORD_CMD4, PWRAP_DEW_RECORD_CMD5, PWRAP_DEW_RECORD_WDATA0, PWRAP_DEW_RECORD_WDATA1, PWRAP_DEW_RECORD_WDATA2, PWRAP_DEW_RECORD_WDATA3, PWRAP_DEW_RECORD_WDATA4, PWRAP_DEW_RECORD_WDATA5, PWRAP_DEW_RG_ADDR_TARGET, PWRAP_DEW_RG_ADDR_MASK, PWRAP_DEW_RG_WDATA_TARGET, PWRAP_DEW_RG_WDATA_MASK, PWRAP_DEW_RG_SPI_RECORD_CLR, PWRAP_DEW_RG_CMD_ALERT_CLR, }; static const u32 mt6357_regs[] = { [PWRAP_DEW_DIO_EN] = 0x040A, [PWRAP_DEW_READ_TEST] = 0x040C, [PWRAP_DEW_WRITE_TEST] = 0x040E, [PWRAP_DEW_CRC_EN] = 0x0412, [PWRAP_DEW_CRC_VAL] = 0x0414, [PWRAP_DEW_CIPHER_KEY_SEL] = 0x0418, [PWRAP_DEW_CIPHER_IV_SEL] = 0x041A, [PWRAP_DEW_CIPHER_RDY] = 0x041E, [PWRAP_DEW_CIPHER_MODE] = 0x0420, [PWRAP_DEW_CIPHER_SWRST] = 0x0422, [PWRAP_DEW_CIPHER_EN] = 0x041C, [PWRAP_DEW_RDDMY_NO] = 0x0424, }; static const u32 mt6359_regs[] = { [PWRAP_DEW_RG_EN_RECORD] = 0x040a, [PWRAP_DEW_DIO_EN] = 0x040c, [PWRAP_DEW_READ_TEST] = 0x040e, [PWRAP_DEW_WRITE_TEST] = 0x0410, [PWRAP_DEW_CRC_SWRST] = 0x0412, [PWRAP_DEW_CRC_EN] = 0x0414, [PWRAP_DEW_CRC_VAL] = 0x0416, [PWRAP_DEW_CIPHER_KEY_SEL] = 0x0418, [PWRAP_DEW_CIPHER_IV_SEL] = 0x041a, [PWRAP_DEW_CIPHER_EN] = 0x041c, [PWRAP_DEW_CIPHER_RDY] = 0x041e, [PWRAP_DEW_CIPHER_MODE] = 0x0420, [PWRAP_DEW_CIPHER_SWRST] = 0x0422, [PWRAP_DEW_RDDMY_NO] = 0x0424, [PWRAP_DEW_RECORD_CMD0] = 0x0428, [PWRAP_DEW_RECORD_CMD1] = 0x042a, [PWRAP_DEW_RECORD_CMD2] = 0x042c, [PWRAP_DEW_RECORD_CMD3] = 0x042e, [PWRAP_DEW_RECORD_CMD4] = 0x0430, [PWRAP_DEW_RECORD_CMD5] = 0x0432, [PWRAP_DEW_RECORD_WDATA0] = 0x0434, [PWRAP_DEW_RECORD_WDATA1] = 0x0436, [PWRAP_DEW_RECORD_WDATA2] = 0x0438, [PWRAP_DEW_RECORD_WDATA3] = 0x043a, [PWRAP_DEW_RECORD_WDATA4] = 0x043c, [PWRAP_DEW_RECORD_WDATA5] = 0x043e, [PWRAP_DEW_RG_ADDR_TARGET] = 0x0440, [PWRAP_DEW_RG_ADDR_MASK] = 0x0442, [PWRAP_DEW_RG_WDATA_TARGET] = 0x0444, [PWRAP_DEW_RG_WDATA_MASK] = 0x0446, [PWRAP_DEW_RG_SPI_RECORD_CLR] = 0x0448, [PWRAP_DEW_RG_CMD_ALERT_CLR] = 0x0448, [PWRAP_SPISLV_KEY] = 0x044a, }; enum pwrap_regs { PWRAP_MUX_SEL, PWRAP_WRAP_EN, PWRAP_DIO_EN, PWRAP_SIDLY, PWRAP_CSHEXT_WRITE, PWRAP_CSHEXT_READ, PWRAP_CSLEXT_START, PWRAP_CSLEXT_END, PWRAP_STAUPD_PRD, PWRAP_STAUPD_GRPEN, PWRAP_STAUPD_MAN_TRIG, PWRAP_STAUPD_STA, PWRAP_WRAP_STA, PWRAP_HARB_INIT, PWRAP_HARB_HPRIO, PWRAP_HIPRIO_ARB_EN, PWRAP_HARB_STA0, PWRAP_HARB_STA1, PWRAP_MAN_EN, PWRAP_MAN_CMD, PWRAP_MAN_RDATA, PWRAP_MAN_VLDCLR, PWRAP_WACS0_EN, PWRAP_INIT_DONE0, PWRAP_WACS0_CMD, PWRAP_WACS0_RDATA, PWRAP_WACS0_VLDCLR, PWRAP_WACS1_EN, PWRAP_INIT_DONE1, PWRAP_WACS1_CMD, PWRAP_WACS1_RDATA, PWRAP_WACS1_VLDCLR, PWRAP_WACS2_EN, PWRAP_INIT_DONE2, PWRAP_WACS2_CMD, PWRAP_WACS2_RDATA, PWRAP_WACS2_VLDCLR, PWRAP_INT_EN, PWRAP_INT_FLG_RAW, PWRAP_INT_FLG, PWRAP_INT_CLR, PWRAP_SIG_ADR, PWRAP_SIG_MODE, PWRAP_SIG_VALUE, PWRAP_SIG_ERRVAL, PWRAP_CRC_EN, PWRAP_TIMER_EN, PWRAP_TIMER_STA, PWRAP_WDT_UNIT, PWRAP_WDT_SRC_EN, PWRAP_WDT_FLG, PWRAP_DEBUG_INT_SEL, PWRAP_CIPHER_KEY_SEL, PWRAP_CIPHER_IV_SEL, PWRAP_CIPHER_RDY, PWRAP_CIPHER_MODE, PWRAP_CIPHER_SWRST, PWRAP_DCM_EN, PWRAP_DCM_DBC_PRD, PWRAP_EINT_STA0_ADR, PWRAP_EINT_STA1_ADR, PWRAP_SWINF_2_WDATA_31_0, PWRAP_SWINF_2_RDATA_31_0, /* MT8390 only regs */ PWRAP_STAUPD_CTRL, /* MT8365 only regs */ PWRAP_INT1_EN, PWRAP_INT1_FLG, PWRAP_INT1_CLR, PWRAP_WDT_SRC_EN_1, }; static int mt8188_regs[] = { [PWRAP_INIT_DONE2] = 0x0, [PWRAP_STAUPD_CTRL] = 0x4C, [PWRAP_TIMER_EN] = 0x3E4, [PWRAP_INT_EN] = 0x420, [PWRAP_INT_FLG] = 0x428, [PWRAP_INT_CLR] = 0x42C, [PWRAP_INT1_EN] = 0x450, [PWRAP_INT1_FLG] = 0x458, [PWRAP_INT1_CLR] = 0x45C, [PWRAP_WACS2_CMD] = 0x880, [PWRAP_SWINF_2_WDATA_31_0] = 0x884, [PWRAP_SWINF_2_RDATA_31_0] = 0x894, [PWRAP_WACS2_VLDCLR] = 0x8A4, [PWRAP_WACS2_RDATA] = 0x8A8, }; static int mt8189_regs[] = { [PWRAP_INIT_DONE2] = 0x0, [PWRAP_TIMER_EN] = 0x3E4, [PWRAP_INT_EN] = 0x450, [PWRAP_WACS2_CMD] = 0x880, [PWRAP_SWINF_2_WDATA_31_0] = 0x884, [PWRAP_SWINF_2_RDATA_31_0] = 0x894, [PWRAP_WACS2_VLDCLR] = 0x8A4, [PWRAP_WACS2_RDATA] = 0x8A8, }; static int mt8365_regs[] = { [PWRAP_MUX_SEL] = 0x0, [PWRAP_WRAP_EN] = 0x4, [PWRAP_DIO_EN] = 0x8, [PWRAP_CSHEXT_WRITE] = 0x24, [PWRAP_CSHEXT_READ] = 0x28, [PWRAP_STAUPD_PRD] = 0x3c, [PWRAP_STAUPD_GRPEN] = 0x40, [PWRAP_STAUPD_MAN_TRIG] = 0x58, [PWRAP_STAUPD_STA] = 0x5c, [PWRAP_WRAP_STA] = 0x60, [PWRAP_HARB_INIT] = 0x64, [PWRAP_HARB_HPRIO] = 0x68, [PWRAP_HIPRIO_ARB_EN] = 0x6c, [PWRAP_HARB_STA0] = 0x70, [PWRAP_HARB_STA1] = 0x74, [PWRAP_MAN_EN] = 0x7c, [PWRAP_MAN_CMD] = 0x80, [PWRAP_MAN_RDATA] = 0x84, [PWRAP_MAN_VLDCLR] = 0x88, [PWRAP_WACS0_EN] = 0x8c, [PWRAP_INIT_DONE0] = 0x90, [PWRAP_WACS0_CMD] = 0xc00, [PWRAP_WACS0_RDATA] = 0xc04, [PWRAP_WACS0_VLDCLR] = 0xc08, [PWRAP_WACS1_EN] = 0x94, [PWRAP_INIT_DONE1] = 0x98, [PWRAP_WACS2_EN] = 0x9c, [PWRAP_INIT_DONE2] = 0xa0, [PWRAP_WACS2_CMD] = 0xc20, [PWRAP_WACS2_RDATA] = 0xc24, [PWRAP_WACS2_VLDCLR] = 0xc28, [PWRAP_INT_EN] = 0xb4, [PWRAP_INT_FLG_RAW] = 0xb8, [PWRAP_INT_FLG] = 0xbc, [PWRAP_INT_CLR] = 0xc0, [PWRAP_SIG_ADR] = 0xd4, [PWRAP_SIG_MODE] = 0xd8, [PWRAP_SIG_VALUE] = 0xdc, [PWRAP_SIG_ERRVAL] = 0xe0, [PWRAP_CRC_EN] = 0xe4, [PWRAP_TIMER_EN] = 0xe8, [PWRAP_TIMER_STA] = 0xec, [PWRAP_WDT_UNIT] = 0xf0, [PWRAP_WDT_SRC_EN] = 0xf4, [PWRAP_WDT_FLG] = 0xfc, [PWRAP_DEBUG_INT_SEL] = 0x104, [PWRAP_CIPHER_KEY_SEL] = 0x1c4, [PWRAP_CIPHER_IV_SEL] = 0x1c8, [PWRAP_CIPHER_RDY] = 0x1d0, [PWRAP_CIPHER_MODE] = 0x1d4, [PWRAP_CIPHER_SWRST] = 0x1d8, [PWRAP_DCM_EN] = 0x1dc, [PWRAP_DCM_DBC_PRD] = 0x1e0, [PWRAP_EINT_STA0_ADR] = 0x44, [PWRAP_EINT_STA1_ADR] = 0x48, [PWRAP_INT1_EN] = 0xc4, [PWRAP_INT1_FLG] = 0xcc, [PWRAP_INT1_CLR] = 0xd0, [PWRAP_WDT_SRC_EN_1] = 0xf8, }; enum pwrap_type { PWRAP_MT8188, PWRAP_MT8189, PWRAP_MT8365, }; struct pwrap_slv_type { const u32 *dew_regs; u32 caps; }; struct pmic_wrapper { struct udevice *dev; void __iomem *base; const struct pmic_wrapper_type *master; const struct pwrap_slv_type *slave; struct clk *clk_spi; struct clk *clk_wrap; struct clk *clk_wrap_sys; struct clk *clk_wrap_tmr; }; struct pmic_wrapper_type { int *regs; enum pwrap_type type; u32 arb_en_all; u32 int_en_all; u32 int1_en_all; u32 spi_w; u32 wdt_src; /* Flags indicating the capability for the target pwrap */ u32 caps; }; static u32 pwrap_readl(struct pmic_wrapper *wrp, enum pwrap_regs reg) { return readl(wrp->base + wrp->master->regs[reg]); } static void pwrap_writel(struct pmic_wrapper *wrp, u32 val, enum pwrap_regs reg) { writel(val, wrp->base + wrp->master->regs[reg]); } static u32 pwrap_get_fsm_state(struct pmic_wrapper *wrp) { u32 val = pwrap_readl(wrp, PWRAP_WACS2_RDATA); if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) return FIELD_GET(PWRAP_GET_WACS_ARB_FSM, val); return FIELD_GET(PWRAP_GET_WACS_FSM, val); } static bool pwrap_is_fsm_idle(struct pmic_wrapper *wrp) { return pwrap_get_fsm_state(wrp) == PWRAP_WACS_FSM_IDLE; } static bool pwrap_is_fsm_vldclr(struct pmic_wrapper *wrp) { return pwrap_get_fsm_state(wrp) == PWRAP_WACS_FSM_WFVLDCLR; } /* * Timeout issue sometimes caused by the last read command * failed because pmic wrap could not got the FSM_VLDCLR * in time after finishing WACS2_CMD. It made state machine * still on FSM_VLDCLR and timeout next time. * Check the status of FSM and clear the vldclr to recovery the * error. */ static inline void pwrap_leave_fsm_vldclr(struct pmic_wrapper *wrp) { if (pwrap_is_fsm_vldclr(wrp)) pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR); } static bool pwrap_is_sync_idle(struct pmic_wrapper *wrp) { return FIELD_GET(PWRAP_STATE_SYNC_IDLE0, pwrap_readl(wrp, PWRAP_WACS2_RDATA)); } static bool pwrap_is_fsm_idle_and_sync_idle(struct pmic_wrapper *wrp) { u32 val = pwrap_readl(wrp, PWRAP_WACS2_RDATA); return FIELD_GET(PWRAP_GET_WACS_FSM, val) == PWRAP_WACS_FSM_IDLE && FIELD_GET(PWRAP_STATE_SYNC_IDLE0, val); } static int pwrap_wait_for_state(struct pmic_wrapper *wrp, bool (*fp)(struct pmic_wrapper *)) { unsigned long timeout; timeout = timer_get_us() + 10000; do { if (time_after(timer_get_us(), timeout)) return fp(wrp) ? 0 : -ETIMEDOUT; if (fp(wrp)) return 0; } while (1); } /* pwrap_read16 in linux kernel */ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { int ret; u32 val; ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; } if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) val = adr; else val = (adr >> 1) << 16; pwrap_writel(wrp, val, PWRAP_WACS2_CMD); ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr); if (ret) return ret; if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) val = pwrap_readl(wrp, PWRAP_SWINF_2_RDATA_31_0); else val = pwrap_readl(wrp, PWRAP_WACS2_RDATA); *rdata = FIELD_GET(PWRAP_GET_WACS_RDATA, val); pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR); return 0; } /* pwrap_write16 in linux kernel */ static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata) { int ret; ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; } if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) { pwrap_writel(wrp, wdata, PWRAP_SWINF_2_WDATA_31_0); pwrap_writel(wrp, BIT(29) | adr, PWRAP_WACS2_CMD); } else { pwrap_writel(wrp, BIT(31) | ((adr >> 1) << 16) | wdata, PWRAP_WACS2_CMD); } return 0; } static int pwrap_reset_spislave(struct pmic_wrapper *wrp) { int ret, i; pwrap_writel(wrp, 0, PWRAP_HIPRIO_ARB_EN); pwrap_writel(wrp, 0, PWRAP_WRAP_EN); pwrap_writel(wrp, 1, PWRAP_MUX_SEL); pwrap_writel(wrp, 1, PWRAP_MAN_EN); pwrap_writel(wrp, 0, PWRAP_DIO_EN); pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_CSL, PWRAP_MAN_CMD); pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS, PWRAP_MAN_CMD); pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_CSH, PWRAP_MAN_CMD); for (i = 0; i < 4; i++) pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS, PWRAP_MAN_CMD); ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle); if (ret) { dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); return ret; } pwrap_writel(wrp, 0, PWRAP_MAN_EN); pwrap_writel(wrp, 0, PWRAP_MUX_SEL); return 0; } /* * pwrap_init_sidly - configure serial input delay * * This configures the serial input delay. We can configure 0, 2, 4 or 6ns * delay. Do a read test with all possible values and chose the best delay. */ static int pwrap_init_sidly(struct pmic_wrapper *wrp) { u32 rdata; u32 i; u32 pass = 0; signed char dly[16] = { -1, 0, 1, 0, 2, -1, 1, 1, 3, -1, -1, -1, 3, -1, 2, 1 }; for (i = 0; i < 4; i++) { pwrap_writel(wrp, i, PWRAP_SIDLY); pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata); if (rdata == PWRAP_DEW_READ_TEST_VAL) { dev_dbg(wrp->dev, "[Read Test] pass, SIDLY=%x\n", i); pass |= 1 << i; } } if (dly[pass] < 0) { dev_err(wrp->dev, "sidly pass range 0x%x not continuous\n", pass); return -EIO; } pwrap_writel(wrp, dly[pass], PWRAP_SIDLY); return 0; } static int pwrap_init_dual_io(struct pmic_wrapper *wrp) { int ret; u32 rdata; /* Enable dual IO mode */ pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1); /* Check IDLE & INIT_DONE in advance */ ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle); if (ret) { dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); return ret; } pwrap_writel(wrp, 1, PWRAP_DIO_EN); /* Read Test */ pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata); if (rdata != PWRAP_DEW_READ_TEST_VAL) { dev_err(wrp->dev, "Read failed on DIO mode: 0x%04x!=0x%04x\n", PWRAP_DEW_READ_TEST_VAL, rdata); return -EFAULT; } return 0; } static int pwrap_init(struct pmic_wrapper *wrp) { int ret; /* Reset SPI slave */ if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) { ret = pwrap_reset_spislave(wrp); if (ret) return ret; } pwrap_writel(wrp, 1, PWRAP_WRAP_EN); pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN); pwrap_writel(wrp, 1, PWRAP_WACS2_EN); /* Setup serial input delay */ if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) { ret = pwrap_init_sidly(wrp); if (ret) return ret; } /* Enable dual I/O mode */ if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_DUALIO)) { ret = pwrap_init_dual_io(wrp); if (ret) return ret; } pwrap_writel(wrp, 0x1, PWRAP_WACS0_EN); pwrap_writel(wrp, 0x1, PWRAP_WACS1_EN); pwrap_writel(wrp, 0x1, PWRAP_WACS2_EN); pwrap_writel(wrp, 0x5, PWRAP_STAUPD_PRD); pwrap_writel(wrp, 0xff, PWRAP_STAUPD_GRPEN); /* Setup the init done registers */ pwrap_writel(wrp, 1, PWRAP_INIT_DONE2); pwrap_writel(wrp, 1, PWRAP_INIT_DONE0); pwrap_writel(wrp, 1, PWRAP_INIT_DONE1); return 0; } static const struct pwrap_slv_type pmic_mt6357 = { .dew_regs = mt6357_regs, .caps = 0, }; static const struct pwrap_slv_type pmic_mt6359 = { .dew_regs = mt6359_regs, .caps = PWRAP_SLV_CAP_DUALIO, }; static const struct udevice_id mtk_pmic_ids[] = { { .compatible = "mediatek,mt6357", .data = (ulong)&pmic_mt6357 }, { .compatible = "mediatek,mt6359", .data = (ulong)&pmic_mt6359 }, { } }; static int mtk_pwrap_find_slave(const struct pwrap_slv_type **slave, ofnode pmic_node) { const struct udevice_id *of_match = mtk_pmic_ids; const char *pmic_name; pmic_name = ofnode_get_property(pmic_node, "compatible", NULL); if (!pmic_name) { log_err("%s: missing compatible property\n", __func__); return -EINVAL; } while (of_match->compatible) { if (!strcmp(of_match->compatible, pmic_name)) { *slave = (struct pwrap_slv_type *)of_match->data; return 0; } of_match++; } return -ENOENT; } static int mtk_pwrap_probe(struct udevice *dev) { struct pmic_wrapper *wrp = dev_get_priv(dev); ofnode pmic_node; u32 mask_done; int ret; wrp->dev = dev; wrp->base = dev_remap_addr(dev); if (IS_ERR(wrp->base)) return PTR_ERR(wrp->base); wrp->master = (void *)dev_get_driver_data(dev); pmic_node = dev_read_first_subnode(dev); if (!ofnode_valid(pmic_node)) { dev_err(dev, "pmic subnode not found\n"); return -ENXIO; } ret = mtk_pwrap_find_slave(&wrp->slave, pmic_node); if (ret) { dev_err(dev, "pmic slave not found\n"); return -EINVAL; } wrp->clk_spi = devm_clk_get(dev, "spi"); if (IS_ERR(wrp->clk_spi)) return PTR_ERR(wrp->clk_spi); wrp->clk_wrap = devm_clk_get(dev, "wrap"); if (IS_ERR(wrp->clk_wrap)) return PTR_ERR(wrp->clk_wrap); wrp->clk_wrap_sys = devm_clk_get_optional(dev, "wrap_sys"); wrp->clk_wrap_tmr = devm_clk_get_optional(dev, "wrap_tmr"); ret = clk_enable(wrp->clk_spi); if (ret) return ret; ret = clk_enable(wrp->clk_wrap); if (ret) return ret; ret = clk_enable(wrp->clk_wrap_sys); if (ret) return ret; ret = clk_enable(wrp->clk_wrap_tmr); if (ret) return ret; /* * The PMIC could already be initialized by the bootloader. * Skip initialization here in this case. */ if (!pwrap_readl(wrp, PWRAP_INIT_DONE2)) { ret = pwrap_init(wrp); if (ret) { dev_err(dev, "init failed with %d\n", ret); return ret; } } if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) mask_done = PWRAP_STATE_INIT_DONE1; else mask_done = PWRAP_STATE_INIT_DONE0; if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & mask_done)) { dev_dbg(dev, "initialization isn't finished\n"); return -ENODEV; } /* * Since STAUPD was not used on mt8173 platform, * so STAUPD of WDT_SRC which should be turned off */ pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN); if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC1)) pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN_1); if (HAS_CAP(wrp->master->caps, PWRAP_CAP_ARB)) pwrap_writel(wrp, 0x3, PWRAP_TIMER_EN); else pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN); pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN); /* * We add INT1 interrupt to handle starvation and request exception * If we support it, we should enable it here. */ if (HAS_CAP(wrp->master->caps, PWRAP_CAP_INT1_EN)) pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN); return 0; } static int mtk_pwrap_bind(struct udevice *dev) { ofnode pmic_node, regulators_node; int children; const struct pmic_child_info *pmic_children_info; struct pmic_wrapper_type *pw_type = (void *)dev_get_driver_data(dev); pmic_node = dev_read_first_subnode(dev); if (!ofnode_valid(pmic_node)) { dev_err(dev, "pmic subnode not found\n"); return -ENXIO; } switch (pw_type->type) { case PWRAP_MT8365: pmic_children_info = mt6357_pmic_children_info; break; case PWRAP_MT8188: case PWRAP_MT8189: pmic_children_info = mt6359_pmic_children_info; break; default: dev_err(dev, "pwrap type %d not supported\n", pw_type->type); return -ENXIO; } regulators_node = ofnode_find_subnode(pmic_node, "regulators"); if (ofnode_valid(regulators_node)) { children = pmic_bind_children(dev, regulators_node, pmic_children_info); if (!children) dev_dbg(dev, "no children found\n"); } else { dev_dbg(dev, "regulators subnode not found\n"); } return 0; } static int mtk_pwrap_reg_count(struct udevice *dev) { return 0x8000; } static int mtk_pwrap_read(struct udevice *dev, uint reg, uint8_t *buf, int len) { struct pmic_wrapper *wrp = dev_get_priv(dev); if ((len * sizeof(uint8_t)) > sizeof(u32)) return -EINVAL; return pwrap_read(wrp, reg, (u32 *)buf); } static int mtk_pwrap_write(struct udevice *dev, uint reg, const uint8_t *buf, int len) { struct pmic_wrapper *wrp = dev_get_priv(dev); if ((len * sizeof(uint8_t)) > sizeof(u32)) return -EINVAL; return pwrap_write(wrp, reg, *(u32 *)buf); } static struct dm_pmic_ops mtk_pwrap_ops = { .reg_count = mtk_pwrap_reg_count, .read = mtk_pwrap_read, .write = mtk_pwrap_write, }; static struct pmic_wrapper_type pwrap_mt8188 = { .regs = mt8188_regs, .type = PWRAP_MT8188, .arb_en_all = 0x777f, .int_en_all = 0x180000, .int1_en_all = 0x0, .spi_w = PWRAP_MAN_CMD_SPI_WRITE, .wdt_src = PWRAP_WDT_SRC_MASK_ALL, .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_ARB, }; static struct pmic_wrapper_type pwrap_mt8189 = { .regs = mt8189_regs, .type = PWRAP_MT8189, .arb_en_all = 0x777f, .int_en_all = 0x180000, .spi_w = PWRAP_MAN_CMD_SPI_WRITE, .wdt_src = PWRAP_WDT_SRC_MASK_ALL, .caps = PWRAP_CAP_ARB, }; static const struct pmic_wrapper_type pwrap_mt8365 = { .regs = mt8365_regs, .type = PWRAP_MT8365, .arb_en_all = 0x3ffff, .int_en_all = 0x7f1fffff, .int1_en_all = 0x0, .spi_w = PWRAP_MAN_CMD_SPI_WRITE, .wdt_src = PWRAP_WDT_SRC_MASK_ALL, .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_WDT_SRC1, }; static const struct udevice_id mtk_pwrap_ids[] = { { .compatible = "mediatek,mt8188-pwrap", .data = (ulong)&pwrap_mt8188 }, { .compatible = "mediatek,mt8189-pwrap", .data = (ulong)&pwrap_mt8189 }, { .compatible = "mediatek,mt8365-pwrap", .data = (ulong)&pwrap_mt8365 }, { } }; U_BOOT_DRIVER(mtk_pwrap) = { .name = "mtk_pwrap", .id = UCLASS_PMIC, .of_match = mtk_pwrap_ids, .bind = mtk_pwrap_bind, .probe = mtk_pwrap_probe, .ops = &mtk_pwrap_ops, .priv_auto = sizeof(struct pmic_wrapper), };