/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2021 MediaTek Inc. */ /* #define DEBUG */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * marco */ #define PMIF_TIMEOUT 0 #define PMIF_BRINGUP 0 /* macro for PMIF SWINF FSM */ #define PMIF_SWINF_FSM_IDLE 0x00 #define PMIF_SWINF_FSM_REQ 0x02 #define PMIF_SWINF_FSM_WFDLE 0x04 #define PMIF_SWINF_FSM_WFVLDCLR 0x06 #define PMIF_SWINF_INIT_DONE 0x01 #define PMIF_SWINF_SYS_IDLE 0x00 #define PMIF_SWINF_SYS_BUSY 0x01 #define PMIF_GET_SWINF_FSM(x) ((x>>1) & 0x00000007) #define PMIF_CMD_REG_0 0 #define PMIF_CMD_REG 1 #define PMIF_CMD_EXT_REG 2 #define PMIF_CMD_EXT_REG_LONG 3 /* 0: SPI, 1: SPMI */ #define PMIF_PMIFID_SPI 0 #define PMIF_PMIFID_SPMI0 0 #define PMIF_PMIFID_SPMI1 1 #define PMIF_PMIFID_SPMI2 2 #define PMIF_PMIFID_SPMI3 3 enum pmif_regs { PMIF_INIT_DONE, PMIF_INF_EN, PMIF_ARB_EN, PMIF_CMDISSUE_EN, PMIF_TIMER_CTRL, PMIF_SPI_MODE_CTRL, PMIF_IRQ_EVENT_EN_0, PMIF_IRQ_FLAG_0, PMIF_IRQ_CLR_0, PMIF_IRQ_EVENT_EN_1, PMIF_IRQ_FLAG_1, PMIF_IRQ_CLR_1, PMIF_IRQ_EVENT_EN_2, PMIF_IRQ_FLAG_2, PMIF_IRQ_CLR_2, PMIF_IRQ_EVENT_EN_3, PMIF_IRQ_FLAG_3, PMIF_IRQ_CLR_3, PMIF_IRQ_EVENT_EN_4, PMIF_IRQ_FLAG_4, PMIF_IRQ_CLR_4, PMIF_WDT_EVENT_EN_0, PMIF_WDT_FLAG_0, PMIF_WDT_EVENT_EN_1, PMIF_WDT_FLAG_1, PMIF_SWINF_0_STA, PMIF_SWINF_0_WDATA_31_0, PMIF_SWINF_0_RDATA_31_0, PMIF_SWINF_0_ACC, PMIF_SWINF_0_VLD_CLR, PMIF_SWINF_1_STA, PMIF_SWINF_1_WDATA_31_0, PMIF_SWINF_1_RDATA_31_0, PMIF_SWINF_1_ACC, PMIF_SWINF_1_VLD_CLR, PMIF_SWINF_2_STA, PMIF_SWINF_2_WDATA_31_0, PMIF_SWINF_2_RDATA_31_0, PMIF_SWINF_2_ACC, PMIF_SWINF_2_VLD_CLR, PMIF_SWINF_3_STA, PMIF_SWINF_3_WDATA_31_0, PMIF_SWINF_3_RDATA_31_0, PMIF_SWINF_3_ACC, PMIF_SWINF_3_VLD_CLR, }; static const u32 mt6xxx_regs[] = { [PMIF_INIT_DONE] = 0x0000, [PMIF_INF_EN] = 0x0024, [PMIF_ARB_EN] = 0x0150, [PMIF_CMDISSUE_EN] = 0x03B4, [PMIF_TIMER_CTRL] = 0x03E0, [PMIF_SPI_MODE_CTRL] = 0x0400, [PMIF_IRQ_EVENT_EN_0] = 0x0418, [PMIF_IRQ_FLAG_0] = 0x0420, [PMIF_IRQ_CLR_0] = 0x0424, [PMIF_IRQ_EVENT_EN_1] = 0x0428, [PMIF_IRQ_FLAG_1] = 0x0430, [PMIF_IRQ_CLR_1] = 0x0434, [PMIF_IRQ_EVENT_EN_2] = 0x0438, [PMIF_IRQ_FLAG_2] = 0x0440, [PMIF_IRQ_CLR_2] = 0x0444, [PMIF_IRQ_EVENT_EN_3] = 0x0448, [PMIF_IRQ_FLAG_3] = 0x0450, [PMIF_IRQ_CLR_3] = 0x0454, [PMIF_IRQ_EVENT_EN_4] = 0x0458, [PMIF_IRQ_FLAG_4] = 0x0460, [PMIF_IRQ_CLR_4] = 0x0464, [PMIF_WDT_EVENT_EN_0] = 0x046C, [PMIF_WDT_FLAG_0] = 0x0470, [PMIF_WDT_EVENT_EN_1] = 0x0474, [PMIF_WDT_FLAG_1] = 0x0478, [PMIF_SWINF_0_ACC] = 0x0C00, [PMIF_SWINF_0_WDATA_31_0] = 0x0C04, [PMIF_SWINF_0_RDATA_31_0] = 0x0C14, [PMIF_SWINF_0_VLD_CLR] = 0x0C24, [PMIF_SWINF_0_STA] = 0x0C28, [PMIF_SWINF_1_ACC] = 0x0C40, [PMIF_SWINF_1_WDATA_31_0] = 0x0C44, [PMIF_SWINF_1_RDATA_31_0] = 0x0C54, [PMIF_SWINF_1_VLD_CLR] = 0x0C64, [PMIF_SWINF_1_STA] = 0x0C68, [PMIF_SWINF_2_ACC] = 0x0C80, [PMIF_SWINF_2_WDATA_31_0] = 0x0C84, [PMIF_SWINF_2_RDATA_31_0] = 0x0C94, [PMIF_SWINF_2_VLD_CLR] = 0x0CA4, [PMIF_SWINF_2_STA] = 0x0CA8, [PMIF_SWINF_3_ACC] = 0x0CC0, [PMIF_SWINF_3_WDATA_31_0] = 0x0CC4, [PMIF_SWINF_3_RDATA_31_0] = 0x0CD4, [PMIF_SWINF_3_VLD_CLR] = 0x0CE4, [PMIF_SWINF_3_STA] = 0x0CE8, }; static const u32 mt6833_regs[] = { [PMIF_INIT_DONE] = 0x0000, [PMIF_INF_EN] = 0x0024, [PMIF_ARB_EN] = 0x0150, [PMIF_CMDISSUE_EN] = 0x03B8, [PMIF_TIMER_CTRL] = 0x03E4, [PMIF_SPI_MODE_CTRL] = 0x0408, [PMIF_IRQ_EVENT_EN_0] = 0x0420, [PMIF_IRQ_FLAG_0] = 0x0428, [PMIF_IRQ_CLR_0] = 0x042C, [PMIF_IRQ_EVENT_EN_1] = 0x0430, [PMIF_IRQ_FLAG_1] = 0x0438, [PMIF_IRQ_CLR_1] = 0x043C, [PMIF_IRQ_EVENT_EN_2] = 0x0440, [PMIF_IRQ_FLAG_2] = 0x0448, [PMIF_IRQ_CLR_2] = 0x044C, [PMIF_IRQ_EVENT_EN_3] = 0x0450, [PMIF_IRQ_FLAG_3] = 0x0458, [PMIF_IRQ_CLR_3] = 0x045C, [PMIF_IRQ_EVENT_EN_4] = 0x0460, [PMIF_IRQ_FLAG_4] = 0x0468, [PMIF_IRQ_CLR_4] = 0x046C, [PMIF_WDT_EVENT_EN_0] = 0x0474, [PMIF_WDT_FLAG_0] = 0x0478, [PMIF_WDT_EVENT_EN_1] = 0x047C, [PMIF_WDT_FLAG_1] = 0x0480, [PMIF_SWINF_0_ACC] = 0x0800, [PMIF_SWINF_0_WDATA_31_0] = 0x0804, [PMIF_SWINF_0_RDATA_31_0] = 0x0814, [PMIF_SWINF_0_VLD_CLR] = 0x0824, [PMIF_SWINF_0_STA] = 0x0828, [PMIF_SWINF_1_ACC] = 0x0840, [PMIF_SWINF_1_WDATA_31_0] = 0x0844, [PMIF_SWINF_1_RDATA_31_0] = 0x0854, [PMIF_SWINF_1_VLD_CLR] = 0x0864, [PMIF_SWINF_1_STA] = 0x0868, [PMIF_SWINF_2_ACC] = 0x0880, [PMIF_SWINF_2_WDATA_31_0] = 0x0884, [PMIF_SWINF_2_RDATA_31_0] = 0x0894, [PMIF_SWINF_2_VLD_CLR] = 0x08A4, [PMIF_SWINF_2_STA] = 0x08A8, [PMIF_SWINF_3_ACC] = 0x08C0, [PMIF_SWINF_3_WDATA_31_0] = 0x08C4, [PMIF_SWINF_3_RDATA_31_0] = 0x08D4, [PMIF_SWINF_3_VLD_CLR] = 0x08E4, [PMIF_SWINF_3_STA] = 0x08E8, }; static const u32 mt6853_regs[] = { [PMIF_INIT_DONE] = 0x0000, [PMIF_INF_EN] = 0x0024, [PMIF_ARB_EN] = 0x0150, [PMIF_CMDISSUE_EN] = 0x03B8, [PMIF_TIMER_CTRL] = 0x03E4, [PMIF_SPI_MODE_CTRL] = 0x0408, [PMIF_IRQ_EVENT_EN_0] = 0x0420, [PMIF_IRQ_FLAG_0] = 0x0428, [PMIF_IRQ_CLR_0] = 0x042C, [PMIF_IRQ_EVENT_EN_1] = 0x0430, [PMIF_IRQ_FLAG_1] = 0x0438, [PMIF_IRQ_CLR_1] = 0x043C, [PMIF_IRQ_EVENT_EN_2] = 0x0440, [PMIF_IRQ_FLAG_2] = 0x0448, [PMIF_IRQ_CLR_2] = 0x044C, [PMIF_IRQ_EVENT_EN_3] = 0x0450, [PMIF_IRQ_FLAG_3] = 0x0458, [PMIF_IRQ_CLR_3] = 0x045C, [PMIF_IRQ_EVENT_EN_4] = 0x0460, [PMIF_IRQ_FLAG_4] = 0x0468, [PMIF_IRQ_CLR_4] = 0x046C, [PMIF_WDT_EVENT_EN_0] = 0x0474, [PMIF_WDT_FLAG_0] = 0x0478, [PMIF_WDT_EVENT_EN_1] = 0x047C, [PMIF_WDT_FLAG_1] = 0x0480, [PMIF_SWINF_0_ACC] = 0x0C00, [PMIF_SWINF_0_WDATA_31_0] = 0x0C04, [PMIF_SWINF_0_RDATA_31_0] = 0x0C14, [PMIF_SWINF_0_VLD_CLR] = 0x0C24, [PMIF_SWINF_0_STA] = 0x0C28, [PMIF_SWINF_1_ACC] = 0x0C40, [PMIF_SWINF_1_WDATA_31_0] = 0x0C44, [PMIF_SWINF_1_RDATA_31_0] = 0x0C54, [PMIF_SWINF_1_VLD_CLR] = 0x0C64, [PMIF_SWINF_1_STA] = 0x0C68, [PMIF_SWINF_2_ACC] = 0x0C80, [PMIF_SWINF_2_WDATA_31_0] = 0x0C84, [PMIF_SWINF_2_RDATA_31_0] = 0x0C94, [PMIF_SWINF_2_VLD_CLR] = 0x0CA4, [PMIF_SWINF_2_STA] = 0x0CA8, [PMIF_SWINF_3_ACC] = 0x0CC0, [PMIF_SWINF_3_WDATA_31_0] = 0x0CC4, [PMIF_SWINF_3_RDATA_31_0] = 0x0CD4, [PMIF_SWINF_3_VLD_CLR] = 0x0CE4, [PMIF_SWINF_3_STA] = 0x0CE8, }; static const u32 mt6xxx_spmi_regs[] = { [SPMI_OP_ST_CTRL] = 0x0000, [SPMI_GRP_ID_EN] = 0x0004, [SPMI_OP_ST_STA] = 0x0008, [SPMI_MST_SAMPL] = 0x000c, [SPMI_MST_REQ_EN] = 0x0010, [SPMI_REC_CTRL] = 0x0040, [SPMI_REC0] = 0x0044, [SPMI_REC1] = 0x0048, [SPMI_REC2] = 0x004c, [SPMI_REC3] = 0x0050, [SPMI_REC4] = 0x0054, [SPMI_MST_DBG] = 0x00fc, }; static const u32 mt6853_spmi_regs[] = { [SPMI_OP_ST_CTRL] = 0x0000, [SPMI_GRP_ID_EN] = 0x0004, [SPMI_OP_ST_STA] = 0x0008, [SPMI_MST_SAMPL] = 0x000C, [SPMI_MST_REQ_EN] = 0x0010, [SPMI_MST_RCS_CTRL] = 0x0014, [SPMI_SLV_3_0_EINT] = 0x0020, [SPMI_SLV_7_4_EINT] = 0x0024, [SPMI_SLV_B_8_EINT] = 0x0028, [SPMI_SLV_F_C_EINT] = 0x002C, [SPMI_REC_CTRL] = 0x0040, [SPMI_REC0] = 0x0044, [SPMI_REC1] = 0x0048, [SPMI_REC2] = 0x004C, [SPMI_REC3] = 0x0050, [SPMI_REC4] = 0x0054, [SPMI_REC_CMD_DEC] = 0x005C, [SPMI_DEC_DBG] = 0x00F8, [SPMI_MST_DBG] = 0x00FC, }; enum { SPMI_OP_ST_BUSY = 1, SPMI_OP_ST_ACK = 0, SPMI_OP_ST_NACK = 1 }; enum { SPMI_RCS_SR_BIT, SPMI_RCS_A_BIT }; enum { SPMI_RCS_MST_W = 1, SPMI_RCS_SLV_W = 3 }; enum infra_regs { MODULE_SW_CG_0_SET, MODULE_SW_CG_0_CLR, MODULE_SW_CG_2_SET, MODULE_SW_CG_2_CLR, PMICW_CLOCK_CTRL, INFRA_GLOBALCON_RST2_SET, INFRA_GLOBALCON_RST2_CLR, INFRA_GLOBALCON_RST2, }; static const u32 mt6xxx_infra_regs[] = { [MODULE_SW_CG_0_SET] = 0x0080, [MODULE_SW_CG_0_CLR] = 0x0084, [MODULE_SW_CG_2_SET] = 0x00a4, [MODULE_SW_CG_2_CLR] = 0x00a8, [PMICW_CLOCK_CTRL] = 0x0108, [INFRA_GLOBALCON_RST2_SET] = 0x0140, [INFRA_GLOBALCON_RST2_CLR] = 0x0144, [INFRA_GLOBALCON_RST2] = 0x0148, }; enum topckgen_regs { CLK_CFG_UPDATE1, CLK_CFG_UPDATE2, CLK_CFG_8_CLR, CLK_CFG_8, CLK_CFG_15, CLK_CFG_16, CLK_CFG_15_CLR, CLK_CFG_16_CLR, }; static const u32 mt6xxx_topckgen_regs[] = { [CLK_CFG_UPDATE1] = 0x0008, [CLK_CFG_UPDATE2] = 0x000c, [CLK_CFG_8] = 0x0090, [CLK_CFG_8_CLR] = 0x0098, [CLK_CFG_15] = 0x0100, [CLK_CFG_15_CLR] = 0x0108, [CLK_CFG_16] = 0x0110, [CLK_CFG_16_CLR] = 0x0118, }; enum toprgu_regs { WDT_SWSYSRST2, }; static const u32 mt6xxx_toprgu_regs[] = { [WDT_SWSYSRST2] = 0x0090, }; enum { /* MT6885/MT6873 series */ IRQ_PMIC_CMD_ERR_PARITY_ERR = 17, IRQ_PMIF_ACC_VIO = 20, IRQ_PMIC_ACC_VIO = 21, IRQ_LAT_LIMIT_REACHED = 6, IRQ_HW_MONITOR = 7, IRQ_WDT = 8, /* MT6853 series */ IRQ_PMIF_ACC_VIO_V2 = 31, IRQ_PMIC_ACC_VIO_V2 = 0, IRQ_HW_MONITOR_V2 = 18, IRQ_WDT_V2 = 19, IRQ_ALL_PMIC_MPU_VIO_V2 = 20, /* MT6833/MT6877 series */ IRQ_HW_MONITOR_V3 = 12, IRQ_WDT_V3 = 13, IRQ_ALL_PMIC_MPU_VIO_V3 = 14, }; struct pmif_irq_desc { const char *name; irq_handler_t irq_handler; int irq; }; #define PMIF_IRQDESC(name) { #name, pmif_##name##_irq_handler, -1} /* * pmif internal API declaration */ int __attribute__((weak)) spmi_pmif_create_attr(struct device_driver *driver); int __attribute__((weak)) spmi_pmif_dbg_init(struct spmi_controller *ctrl); void __attribute__((weak)) spmi_dump_spmimst_record_reg(struct pmif *arb); static int pmif_timeout_ns(struct spmi_controller *ctrl, unsigned long long start_time_ns, unsigned long long timeout_time_ns); static void pmif_enable_soft_reset(struct pmif *arb); static void pmif_enable_soft_reset_p_v1(struct pmif *arb); static void pmif_spmi_enable_clk_set(struct pmif *arb); static void pmif_spmi_force_normal_mode(struct pmif *arb); static void pmif_spmi_enable_swinf(struct pmif *arb, unsigned int chan_no, unsigned int swinf_no); static void pmif_spmi_enable_cmdIssue(struct pmif *arb, bool en); static void pmif_spmi_enable(struct pmif *arb); static void pmif_spmi_enable_m_v1(struct pmif *arb); static void pmif_spmi_enable_p_v1(struct pmif *arb); static int is_pmif_spmi_init_done(struct pmif *arb); static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 addr, u8 *buf, size_t len); static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 addr, const u8 *buf, size_t len); static int mtk_spmi_config_master(struct pmif *arb, unsigned int mstid, bool en); static int mtk_spmi_config_master_m_v1(struct pmif *arb, unsigned int mstid, bool en); static int mtk_spmi_config_master_p_v1(struct pmif *arb, unsigned int mstid, bool en); static int mtk_spmi_cali_rd_clock_polarity(struct pmif *arb); static int mtk_spmi_cali_rd_clock_polarity_m_p_v1(struct pmif *arb); static int mtk_spmi_ctrl_op_st(struct spmi_controller *ctrl, u8 opc, u8 sid); static int mtk_spmi_enable_group_id(struct pmif *arb, u8 grpid); static struct pmif mt6xxx_pmif_arb[] = { { .base = 0x0, .regs = mt6xxx_regs, .spmimst_base = 0x0, .spmimst_regs = mt6xxx_spmi_regs, .infra_base = 0x0, .infra_regs = mt6xxx_infra_regs, .topckgen_base = 0x0, .topckgen_regs = mt6xxx_topckgen_regs, .toprgu_base = 0x0, .toprgu_regs = mt6xxx_toprgu_regs, .swinf_ch_start = 0, .ap_swinf_no = 0, .write = 0x0, .mstid = SPMI_MASTER_0, .pmifid = PMIF_PMIFID_SPMI0, .spmic = 0x0, .read_cmd = pmif_spmi_read_cmd, .write_cmd = pmif_spmi_write_cmd, .pmif_enable_clk_set = pmif_spmi_enable_clk_set, .pmif_force_normal_mode = pmif_spmi_force_normal_mode, .pmif_enable_swinf = pmif_spmi_enable_swinf, .pmif_enable_cmdIssue = pmif_spmi_enable_cmdIssue, .pmif_enable = pmif_spmi_enable, .is_pmif_init_done = is_pmif_spmi_init_done, .pmif_enable_reset = pmif_enable_soft_reset, .pmif_cali_clock = mtk_spmi_cali_rd_clock_polarity, .spmi_config_master = mtk_spmi_config_master, }, }; static struct pmif mt6xxx_pmif_m_arb[] = { { .base = 0x0, .regs = mt6853_regs, .spmimst_base = 0x0, .spmimst_regs = mt6853_spmi_regs, .infra_base = 0x0, .infra_regs = mt6xxx_infra_regs, .topckgen_base = 0x0, .topckgen_regs = mt6xxx_topckgen_regs, .toprgu_base = 0x0, .toprgu_regs = mt6xxx_toprgu_regs, .swinf_ch_start = 0, .ap_swinf_no = 0, .write = 0x0, .mstid = SPMI_MASTER_1, .pmifid = PMIF_PMIFID_SPMI1, .spmic = 0x0, .read_cmd = pmif_spmi_read_cmd, .write_cmd = pmif_spmi_write_cmd, .pmif_enable_clk_set = pmif_spmi_enable_clk_set, .pmif_force_normal_mode = pmif_spmi_force_normal_mode, .pmif_enable_swinf = pmif_spmi_enable_swinf, .pmif_enable_cmdIssue = pmif_spmi_enable_cmdIssue, .pmif_enable = pmif_spmi_enable_m_v1, .is_pmif_init_done = is_pmif_spmi_init_done, .pmif_enable_reset = pmif_enable_soft_reset, .pmif_cali_clock = mtk_spmi_cali_rd_clock_polarity_m_p_v1, .spmi_config_master = mtk_spmi_config_master_m_v1, }, }; static struct pmif mt6xxx_pmif_m_arb_v2[] = { { .base = 0x0, .regs = mt6833_regs, .spmimst_base = 0x0, .spmimst_regs = mt6853_spmi_regs, .infra_base = 0x0, .infra_regs = mt6xxx_infra_regs, .topckgen_base = 0x0, .topckgen_regs = mt6xxx_topckgen_regs, .toprgu_base = 0x0, .toprgu_regs = mt6xxx_toprgu_regs, .swinf_ch_start = 0, .ap_swinf_no = 0, .write = 0x0, .mstid = SPMI_MASTER_1, .pmifid = PMIF_PMIFID_SPMI1, .spmic = 0x0, .read_cmd = pmif_spmi_read_cmd, .write_cmd = pmif_spmi_write_cmd, .pmif_enable_clk_set = pmif_spmi_enable_clk_set, .pmif_force_normal_mode = pmif_spmi_force_normal_mode, .pmif_enable_swinf = pmif_spmi_enable_swinf, .pmif_enable_cmdIssue = pmif_spmi_enable_cmdIssue, .pmif_enable = pmif_spmi_enable_m_v1, .is_pmif_init_done = is_pmif_spmi_init_done, .pmif_enable_reset = pmif_enable_soft_reset, .pmif_cali_clock = mtk_spmi_cali_rd_clock_polarity_m_p_v1, .spmi_config_master = mtk_spmi_config_master_m_v1, }, }; static struct pmif mt6xxx_pmif_p_arb[] = { { .base = 0x0, .regs = mt6853_regs, .spmimst_base = 0x0, .spmimst_regs = mt6853_spmi_regs, .infra_base = 0x0, .infra_regs = mt6xxx_infra_regs, .topckgen_base = 0x0, .topckgen_regs = mt6xxx_topckgen_regs, .toprgu_base = 0x0, .toprgu_regs = mt6xxx_toprgu_regs, .swinf_ch_start = 0, .ap_swinf_no = 0, .write = 0x0, .mstid = SPMI_MASTER_1, .pmifid = PMIF_PMIFID_SPMI2, .spmic = 0x0, .read_cmd = pmif_spmi_read_cmd, .write_cmd = pmif_spmi_write_cmd, .pmif_enable_clk_set = pmif_spmi_enable_clk_set, .pmif_force_normal_mode = pmif_spmi_force_normal_mode, .pmif_enable_swinf = pmif_spmi_enable_swinf, .pmif_enable_cmdIssue = pmif_spmi_enable_cmdIssue, .pmif_enable = pmif_spmi_enable_p_v1, .is_pmif_init_done = is_pmif_spmi_init_done, .pmif_enable_reset = pmif_enable_soft_reset_p_v1, .pmif_cali_clock = mtk_spmi_cali_rd_clock_polarity_m_p_v1, .spmi_config_master = mtk_spmi_config_master_p_v1, }, }; static const struct of_device_id pmif_match_table[] = { { .compatible = "mediatek,mt6833-pmif-m", .data = &mt6xxx_pmif_m_arb_v2, }, { .compatible = "mediatek,mt6853-pmif-m", .data = &mt6xxx_pmif_m_arb, }, { .compatible = "mediatek,mt6853-pmif-p", .data = &mt6xxx_pmif_p_arb, }, { .compatible = "mediatek,mt6877-pmif-m", .data = &mt6xxx_pmif_m_arb_v2, }, { .compatible = "mediatek,mt6885-pmif", .data = &mt6xxx_pmif_arb, }, { .compatible = "mediatek,pmif", .data = &mt6xxx_pmif_arb, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, pmif_match_table); static struct platform_driver pmif_driver; /* * pmif timeout */ #if PMIF_TIMEOUT static int pmif_timeout_ns(struct spmi_controller *ctrl, unsigned long long start_time_ns, unsigned long long timeout_time_ns) { unsigned long long cur_time = 0; unsigned long long elapse_time = 0; /* get current tick */ cur_time = sched_clock(); /* ns */ /* avoid timer over flow exiting in FPGA env */ if (cur_time < start_time_ns) start_time_ns = cur_time; elapse_time = cur_time - start_time_ns; /* check if timeout */ if (timeout_time_ns <= elapse_time) { dev_notice(&ctrl->dev, "[PMIF] Timeout start time: %lld\n", start_time_ns); dev_notice(&ctrl->dev, "[PMIF] Timeout cur time: %lld\n", cur_time); dev_notice(&ctrl->dev, "[PMIF] Timeout elapse time: %lld\n", elapse_time); dev_notice(&ctrl->dev, "[PMIF] Timeout set timeout: %lld\n", timeout_time_ns); return 1; } return 0; } #else static int pmif_timeout_ns(struct spmi_controller *ctrl, unsigned long long start_time_ns, unsigned long long timeout_time_ns) { return 0; } #endif /* * pmif define for FSM */ static inline bool pmif_is_fsm_idle(struct pmif *arb) { u32 offset = 0, reg_rdata = 0; offset = arb->regs[PMIF_SWINF_0_STA] + (0x40 * arb->ap_swinf_no); reg_rdata = readl(arb->base + offset); return PMIF_GET_SWINF_FSM(reg_rdata) == PMIF_SWINF_FSM_IDLE; } static inline bool pmif_is_fsm_vldclr(struct pmif *arb) { u32 offset = 0, reg_rdata = 0; offset = arb->regs[PMIF_SWINF_0_STA] + (0x40 * arb->ap_swinf_no); reg_rdata = readl(arb->base + offset); return PMIF_GET_SWINF_FSM(reg_rdata) == PMIF_SWINF_FSM_WFVLDCLR; } static inline void pmif_leave_fsm_vldclr(struct pmif *arb) { u32 offset = 0; offset = arb->regs[PMIF_SWINF_0_VLD_CLR] + (0x40 * arb->ap_swinf_no); if (pmif_is_fsm_vldclr(arb)) writel(0x1, arb->base + offset); } static int pmif_wait_for_state(struct spmi_controller *ctrl, bool (*fp)(struct pmif *)) { struct pmif *arb = spmi_controller_get_drvdata(ctrl); unsigned long long start_time_ns = 0, timeout_ns = 0; u32 offset = 0, offset1 = 0, offset2 = 0; start_time_ns = sched_clock(); timeout_ns = 10000 * 1000; /* 10000us */ offset = arb->toprgu_regs[WDT_SWSYSRST2]; offset1 = arb->infra_regs[INFRA_GLOBALCON_RST2]; offset2 = arb->topckgen_regs[CLK_CFG_8]; do { if (pmif_timeout_ns(ctrl, start_time_ns, timeout_ns)) { dev_notice(&ctrl->dev, "[PMIF] WDT_RST2:0x%x INF_RST2:0x%x CLK:0x%x\n", readl(arb->toprgu_base + offset), readl(arb->infra_base + offset1), readl(arb->topckgen_base + offset2)); if (fp(arb)) { return 0; } else if (fp(arb) == 0) { dev_notice(&ctrl->dev, "[PMIF] FSM Timeout\n"); spmi_dump_pmif_swinf_reg(); spmi_dump_pmif_all_reg(); spmi_dump_spmimst_all_reg(); return -ETIMEDOUT; } } if (fp(arb)) return 0; } while (1); } /* * Function : pmif_readl() * Description : mtk pmif controller read api * Parameter : * Return : */ static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg) { return readl(arb->base + arb->regs[reg]); } /* * Function : pmif_writel() * Description : mtk pmif controller write api * Parameter : * Return : */ static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg) { writel(val, arb->base + arb->regs[reg]); } /* * Function : mtk_spmi_readl() * Description : mtk spmi controller read api * Parameter : * Return : */ u32 mtk_spmi_readl(struct pmif *arb, enum spmi_regs reg) { return readl(arb->spmimst_base + arb->spmimst_regs[reg]); } /* * Function : mtk_spmi_writel() * Description : mtk spmi controller write api * Parameter : * Return : */ void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg) { writel(val, arb->spmimst_base + arb->spmimst_regs[reg]); } static void pmif_enable_soft_reset(struct pmif *arb) { writel(0x1 << 14, arb->infra_base + arb->infra_regs[INFRA_GLOBALCON_RST2_SET]); writel(0x1 << 14, arb->infra_base + arb->infra_regs[INFRA_GLOBALCON_RST2_CLR]); } static void pmif_enable_soft_reset_p_v1(struct pmif *arb) { writel(0x1 << 15, arb->infra_base + arb->infra_regs[INFRA_GLOBALCON_RST2_SET]); writel(0x1 << 15, arb->infra_base + arb->infra_regs[INFRA_GLOBALCON_RST2_CLR]); } static void pmif_spmi_enable_clk_set(struct pmif *arb) { /* sys_ck cg enable, turn off clock */ writel(0x0000000f, arb->infra_base + arb->infra_regs[MODULE_SW_CG_0_SET]); writel((0x1 << 15) | (0x1 << 12) | (0x7 << 8), arb->topckgen_base + arb->topckgen_regs[CLK_CFG_8_CLR]); writel(0x1 << 2, arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE1]); /* toggle SPMI sw reset */ arb->pmif_enable_reset(arb); /* sys_ck cg enable, turn on clock */ writel(0x0000000f, arb->infra_base + arb->infra_regs[MODULE_SW_CG_0_CLR]); } static void pmif_spmi_force_normal_mode(struct pmif *arb) { /* Force SPMI in normal mode. */ pmif_writel(arb, pmif_readl(arb, PMIF_SPI_MODE_CTRL) & (~(0x3 << 9)), PMIF_SPI_MODE_CTRL); pmif_writel(arb, pmif_readl(arb, PMIF_SPI_MODE_CTRL) | (0x1 << 9), PMIF_SPI_MODE_CTRL); } static void pmif_spmi_enable_swinf(struct pmif *arb, unsigned int chan_no, unsigned int swinf_no) { /* Enable swinf */ pmif_writel(arb, 0x1 << (chan_no + swinf_no), PMIF_INF_EN); /* Enable arbitration */ pmif_writel(arb, 0x1 << (chan_no + swinf_no), PMIF_ARB_EN); } static void pmif_spmi_enable_cmdIssue(struct pmif *arb, bool en) { /* Enable cmdIssue */ pmif_writel(arb, en, PMIF_CMDISSUE_EN); } static void pmif_spmi_enable(struct pmif *arb) { pmif_writel(arb, 0x2F7, PMIF_INF_EN); pmif_writel(arb, 0x2F7, PMIF_ARB_EN); pmif_writel(arb, 0x3, PMIF_TIMER_CTRL); pmif_writel(arb, 0x1, PMIF_INIT_DONE); } static void pmif_spmi_enable_m_v1(struct pmif *arb) { pmif_writel(arb, 0x2F6, PMIF_INF_EN); pmif_writel(arb, 0x2F6, PMIF_ARB_EN); pmif_writel(arb, 0x3, PMIF_TIMER_CTRL); pmif_writel(arb, 0x1, PMIF_INIT_DONE); } static void pmif_spmi_enable_p_v1(struct pmif *arb) { pmif_writel(arb, 0xF1, PMIF_INF_EN); pmif_writel(arb, 0xF1, PMIF_ARB_EN); pmif_writel(arb, 0x3, PMIF_TIMER_CTRL); pmif_writel(arb, 0x1, PMIF_INIT_DONE); } static int mtk_spmi_ctrl_op_st(struct spmi_controller *ctrl, u8 opc, u8 sid) { struct pmif *arb = spmi_controller_get_drvdata(ctrl); u32 rdata = 0x0; u8 cmd = 0; /* Check the opcode */ if (opc == SPMI_CMD_RESET) cmd = 0; else if (opc == SPMI_CMD_SLEEP) cmd = 1; else if (opc == SPMI_CMD_SHUTDOWN) cmd = 2; else if (opc == SPMI_CMD_WAKEUP) cmd = 3; mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL); rdata = mtk_spmi_readl(arb, SPMI_OP_ST_CTRL); pr_notice("[SPMIMST]:pmif_ctrl_op_st 0x%x\r\n", rdata); do { rdata = mtk_spmi_readl(arb, SPMI_OP_ST_STA); pr_notice("[SPMIMST]:pmif_ctrl_op_st 0x%x\r\n", rdata); if (((rdata >> 0x1) & SPMI_OP_ST_NACK) == SPMI_OP_ST_NACK) { spmi_dump_spmimst_record_reg(arb); break; } } while ((rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY); return 0; } /* Non-data command */ static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) { return mtk_spmi_ctrl_op_st(ctrl, opc, sid); } static int mtk_spmi_enable_group_id(struct pmif *arb, u8 grpid) { mtk_spmi_writel(arb, 0x1 << grpid, SPMI_GRP_ID_EN); return 0; } static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 addr, u8 *buf, size_t len) { struct pmif *arb = spmi_controller_get_drvdata(ctrl); int ret = 0, write = 0x0; u32 offset = 0, data = 0; u8 bc = len - 1; unsigned long flags; /* Check for argument validation. */ if ((arb->ap_swinf_no & ~(0x3)) != 0x0) return -EINVAL; if ((arb->pmifid & ~(0x3)) != 0x0) return -EINVAL; if ((sid & ~(0xf)) != 0x0) return -EINVAL; /* Check the opcode */ if (opc >= 0x60 && opc <= 0x7F) opc = PMIF_CMD_REG; else if (opc >= 0x20 && opc <= 0x2F) opc = PMIF_CMD_EXT_REG_LONG; /* wk1 opc = PMIF_CMD_EXT_REG; */ else if (opc >= 0x38 && opc <= 0x3F) opc = PMIF_CMD_EXT_REG_LONG; else return -EINVAL; raw_spin_lock_irqsave(&arb->lock, flags); /* Wait for Software Interface FSM state to be IDLE. */ ret = pmif_wait_for_state(ctrl, pmif_is_fsm_idle); if (ret) { pmif_leave_fsm_vldclr(arb); raw_spin_unlock_irqrestore(&arb->lock, flags); return ret; } /* Send the command. */ offset = arb->regs[PMIF_SWINF_0_ACC] + (0x40 * arb->ap_swinf_no); writel((opc << 30) | (write << 29) | (sid << 24) | (bc << 16) | addr, arb->base + offset); /* Wait for Software Interface FSM state to be WFVLDCLR, * * read the data and clear the valid flag. */ if (write == 0) { ret = pmif_wait_for_state(ctrl, pmif_is_fsm_vldclr); if (ret) { raw_spin_unlock_irqrestore(&arb->lock, flags); return ret; } offset = arb->regs[PMIF_SWINF_0_RDATA_31_0] + (0x40 * arb->ap_swinf_no); data = readl(arb->base + offset); memcpy(buf, &data, (bc & 3) + 1); offset = arb->regs[PMIF_SWINF_0_VLD_CLR] + (0x40 * arb->ap_swinf_no); writel(0x1, arb->base + offset); } raw_spin_unlock_irqrestore(&arb->lock, flags); return 0x0; } static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 addr, const u8 *buf, size_t len) { struct pmif *arb = spmi_controller_get_drvdata(ctrl); int ret = 0, write = 0x1; u32 offset = 0, data = 0; u8 bc = len - 1; unsigned long flags = 0; /* Check for argument validation. */ if ((arb->ap_swinf_no & ~(0x3)) != 0x0) return -EINVAL; if ((arb->pmifid & ~(0x3)) != 0x0) return -EINVAL; if ((sid & ~(0xf)) != 0x0) return -EINVAL; /* Check the opcode */ if (opc >= 0x40 && opc <= 0x5F) opc = PMIF_CMD_REG; else if (opc <= 0x0F) opc = PMIF_CMD_EXT_REG_LONG; /* wk1 opc = PMIF_CMD_EXT_REG; */ else if (opc >= 0x30 && opc <= 0x37) opc = PMIF_CMD_EXT_REG_LONG; else if (opc >= 0x80) opc = PMIF_CMD_REG_0; else return -EINVAL; raw_spin_lock_irqsave(&arb->lock, flags); /* Wait for Software Interface FSM state to be IDLE. */ ret = pmif_wait_for_state(ctrl, pmif_is_fsm_idle); if (ret) { pmif_leave_fsm_vldclr(arb); raw_spin_unlock_irqrestore(&arb->lock, flags); return ret; } /* Set the write data. */ offset = arb->regs[PMIF_SWINF_0_WDATA_31_0] + (0x40 * arb->ap_swinf_no); if (write == 1) { memcpy(&data, buf, (bc & 3) + 1); writel(data, arb->base + offset); } /* Send the command. */ offset = arb->regs[PMIF_SWINF_0_ACC] + (0x40 * arb->ap_swinf_no); writel((opc << 30) | (write << 29) | (sid << 24) | (bc << 16) | addr, arb->base + offset); raw_spin_unlock_irqrestore(&arb->lock, flags); return 0x0; } int is_pmif_spmi_init_done(struct pmif *arb) { int ret = 0; ret = pmif_readl(arb, PMIF_INIT_DONE); if ((ret & 0x1) == 1) return 0; return -1; } static int mtk_spmi_config_master(struct pmif *arb, unsigned int mstid, bool en) { /* Software reset */ writel(0x85 << 24 | 0x1 << 4, arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]); writel(0x7 | (0x1 << 4) | (0x1 << 7), arb->topckgen_base + arb->topckgen_regs[CLK_CFG_16_CLR]); writel(0x1 << 2, arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE2]); /* Software reset */ writel(0x85 << 24, arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]); /* Enable SPMI */ mtk_spmi_writel(arb, en, SPMI_MST_REQ_EN); return 0; } static int mtk_spmi_config_master_m_v1(struct pmif *arb, unsigned int mstid, bool en) { /* Software reset */ writel(0x85 << 24 | 0x3 << 3, arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]); writel((0x7 << 8) | (0x1 << 12) | (0x1 << 15), arb->topckgen_base + arb->topckgen_regs[CLK_CFG_15_CLR]); writel(0x1 << 30, arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE2]); /* Software reset */ writel(0x85 << 24, arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]); /* Enable master rcs */ mtk_spmi_writel(arb, 0x14 | arb->mstid, SPMI_MST_RCS_CTRL); /* Enable SPMI */ mtk_spmi_writel(arb, en, SPMI_MST_REQ_EN); return 0; } static int mtk_spmi_config_master_p_v1(struct pmif *arb, unsigned int mstid, bool en) { /* Enable master rcs */ mtk_spmi_writel(arb, 0x14 | arb->mstid, SPMI_MST_RCS_CTRL); /* Enable SPMI */ mtk_spmi_writel(arb, en, SPMI_MST_REQ_EN); return 0; } static int mtk_spmi_cali_rd_clock_polarity(struct pmif *arb) { #if defined(CONFIG_MTK_FPGA) || defined(CONFIG_FPGA_EARLY_PORTING) unsigned int dly = 0, pol = 0; #else unsigned int dly = 1, pol = 1; #endif /* Indicate sampling clock polarity, 1: Positive 0: Negative */ mtk_spmi_writel(arb, (dly << 0x1) | pol, SPMI_MST_SAMPL); return 0; } static int mtk_spmi_cali_rd_clock_polarity_m_p_v1(struct pmif *arb) { #if defined(CONFIG_MTK_FPGA) || defined(CONFIG_FPGA_EARLY_PORTING) unsigned int dly = 0, pol = 0; #else unsigned int dly = 0, pol = 1; #endif /* Indicate sampling clock polarity, 1: Positive 0: Negative */ mtk_spmi_writel(arb, ((dly+1) << 0x4) | (dly << 0x1) | pol, SPMI_MST_SAMPL); return 0; } /* * PMIF Exception IRQ Handler */ static void pmif_cmd_err_parity_err_irq_handler(int irq, void *data) { spmi_dump_spmimst_all_reg(); spmi_dump_pmif_record_reg(); aee_kernel_warning("PMIF:parity error", "PMIF"); } static void pmif_pmif_acc_vio_irq_handler(int irq, void *data) { spmi_dump_pmif_acc_vio_reg(); aee_kernel_warning("PMIF:pmif_acc_vio", "PMIF"); } static void pmif_pmic_acc_vio_irq_handler(int irq, void *data) { spmi_dump_pmic_acc_vio_reg(); aee_kernel_warning("PMIF:pmic_acc_vio", "PMIF"); } static void pmif_lat_limit_reached_irq_handler(int irq, void *data) { spmi_dump_pmif_busy_reg(); spmi_dump_pmif_record_reg(); } static void pmif_hw_monitor_irq_handler(int irq, void *data) { spmi_dump_pmif_record_reg(); aee_kernel_warning("PMIF:pmif_hw_monitor_match", "PMIF"); } static void pmif_wdt_irq_handler(int irq, void *data) { spmi_dump_pmif_busy_reg(); spmi_dump_pmif_record_reg(); spmi_dump_wdt_reg(); } static irqreturn_t pmif_event_0_irq_handler(int irq, void *data) { struct pmif *arb = data; int irq_f = 0, idx = 0; __pm_stay_awake(arb->pmifThread_lock); mutex_lock(&arb->pmif_mutex); irq_f = pmif_readl(arb, PMIF_IRQ_FLAG_0); if (irq_f == 0) { mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_NONE; } for (idx = 0; idx < 31; idx++) { if ((irq_f & (0x1 << idx)) != 0) { switch (idx) { default: pr_notice("%s IRQ[%d] triggered\n", __func__, idx); spmi_dump_pmif_record_reg(); break; } pmif_writel(arb, irq_f, PMIF_IRQ_CLR_0); break; } } mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_HANDLED; } static irqreturn_t pmif_event_1_irq_handler(int irq, void *data) { struct pmif *arb = data; int irq_f = 0, idx = 0; __pm_stay_awake(arb->pmifThread_lock); mutex_lock(&arb->pmif_mutex); irq_f = pmif_readl(arb, PMIF_IRQ_FLAG_1); if (irq_f == 0) { mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_NONE; } for (idx = 0; idx < 31; idx++) { if ((irq_f & (0x1 << idx)) != 0) { switch (idx) { default: pr_notice("%s IRQ[%d] triggered\n", __func__, idx); spmi_dump_pmif_record_reg(); break; } pmif_writel(arb, irq_f, PMIF_IRQ_CLR_1); break; } } mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_HANDLED; } static irqreturn_t pmif_event_2_irq_handler(int irq, void *data) { struct pmif *arb = data; int irq_f = 0, idx = 0; __pm_stay_awake(arb->pmifThread_lock); mutex_lock(&arb->pmif_mutex); irq_f = pmif_readl(arb, PMIF_IRQ_FLAG_2); if (irq_f == 0) { mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_NONE; } for (idx = 0; idx < 31; idx++) { if ((irq_f & (0x1 << idx)) != 0) { switch (idx) { case IRQ_PMIC_CMD_ERR_PARITY_ERR: pmif_cmd_err_parity_err_irq_handler(irq, data); break; case IRQ_PMIF_ACC_VIO: case IRQ_PMIF_ACC_VIO_V2: pmif_pmif_acc_vio_irq_handler(irq, data); break; case IRQ_PMIC_ACC_VIO: pmif_pmic_acc_vio_irq_handler(irq, data); break; default: pr_notice("%s IRQ[%d] triggered\n", __func__, idx); spmi_dump_pmif_record_reg(); break; } pmif_writel(arb, irq_f, PMIF_IRQ_CLR_2); break; } } mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_HANDLED; } static irqreturn_t pmif_event_3_irq_handler(int irq, void *data) { struct pmif *arb = data; int irq_f = 0, idx = 0; __pm_stay_awake(arb->pmifThread_lock); mutex_lock(&arb->pmif_mutex); irq_f = pmif_readl(arb, PMIF_IRQ_FLAG_3); if (irq_f == 0) { mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_NONE; } for (idx = 0; idx < 31; idx++) { if ((irq_f & (0x1 << idx)) != 0) { switch (idx) { case IRQ_LAT_LIMIT_REACHED: pmif_lat_limit_reached_irq_handler(irq, data); break; case IRQ_HW_MONITOR: case IRQ_HW_MONITOR_V2: case IRQ_HW_MONITOR_V3: pmif_hw_monitor_irq_handler(irq, data); break; case IRQ_WDT: case IRQ_WDT_V2: case IRQ_WDT_V3: pmif_wdt_irq_handler(irq, data); break; case IRQ_PMIC_ACC_VIO_V2: pmif_pmic_acc_vio_irq_handler(irq, data); break; case IRQ_ALL_PMIC_MPU_VIO_V2: case IRQ_ALL_PMIC_MPU_VIO_V3: pmif_pmif_acc_vio_irq_handler(irq, data); break; default: pr_notice("%s IRQ[%d] triggered\n", __func__, idx); spmi_dump_pmif_record_reg(); break; } pmif_writel(arb, irq_f, PMIF_IRQ_CLR_3); break; } } mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_HANDLED; } static irqreturn_t pmif_event_4_irq_handler(int irq, void *data) { struct pmif *arb = data; int irq_f = 0, idx = 0; __pm_stay_awake(arb->pmifThread_lock); mutex_lock(&arb->pmif_mutex); irq_f = pmif_readl(arb, PMIF_IRQ_FLAG_4); if (irq_f == 0) { mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_NONE; } for (idx = 0; idx < 31; idx++) { if ((irq_f & (0x1 << idx)) != 0) { switch (idx) { default: pr_notice("%s IRQ[%d] triggered\n", __func__, idx); spmi_dump_pmif_record_reg(); break; } pmif_writel(arb, irq_f, PMIF_IRQ_CLR_4); break; } } mutex_unlock(&arb->pmif_mutex); __pm_relax(arb->pmifThread_lock); return IRQ_HANDLED; } static struct pmif_irq_desc pmif_event_irq[] = { PMIF_IRQDESC(event_0), PMIF_IRQDESC(event_1), PMIF_IRQDESC(event_2), PMIF_IRQDESC(event_3), PMIF_IRQDESC(event_4), }; static void pmif_irq_register(struct platform_device *pdev, struct pmif *arb, int irq) { int i = 0, ret = 0; u32 irq_event_en[5] = {0}; for (i = 0; i < ARRAY_SIZE(pmif_event_irq); i++) { if (!pmif_event_irq[i].name) continue; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, pmif_event_irq[i].irq_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_SHARED, pmif_event_irq[i].name, arb); if (ret < 0) { dev_notice(&pdev->dev, "request %s irq fail\n", pmif_event_irq[i].name); continue; } pmif_event_irq[i].irq = irq; } ret = of_property_read_u32_array(pdev->dev.of_node, "irq_event_en", irq_event_en, ARRAY_SIZE(irq_event_en)); pmif_writel(arb, irq_event_en[0] | pmif_readl(arb, PMIF_IRQ_EVENT_EN_0), PMIF_IRQ_EVENT_EN_0); pmif_writel(arb, irq_event_en[1] | pmif_readl(arb, PMIF_IRQ_EVENT_EN_1), PMIF_IRQ_EVENT_EN_1); pmif_writel(arb, irq_event_en[2] | pmif_readl(arb, PMIF_IRQ_EVENT_EN_2), PMIF_IRQ_EVENT_EN_2); pmif_writel(arb, irq_event_en[3] | pmif_readl(arb, PMIF_IRQ_EVENT_EN_3), PMIF_IRQ_EVENT_EN_3); pmif_writel(arb, irq_event_en[4] | pmif_readl(arb, PMIF_IRQ_EVENT_EN_4), PMIF_IRQ_EVENT_EN_4); } static int mtk_spmimst_init(struct platform_device *pdev, struct pmif *arb) { struct resource *res = NULL; int err = 0; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spmimst"); arb->spmimst_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(arb->spmimst_base)) { err = PTR_ERR(arb->spmimst_base); return err; } err = of_property_read_u32(pdev->dev.of_node, "grpid", &arb->grpid); if (err) { dev_notice(&pdev->dev, "[SPMIMST]:grpid unspecified.\n"); return -EINVAL; } /* set group id */ mtk_spmi_enable_group_id(arb, arb->grpid); /* if spmimst not enabled, enable it */ if ((mtk_spmi_readl(arb, SPMI_MST_REQ_EN) & 0x1) != 0x1) { dev_info(&pdev->dev, "[SPMIMST]:enable spmimst.\n"); arb->spmi_config_master(arb, arb->mstid, true); arb->pmif_cali_clock(arb); } pr_notice("[SPMIMST]:%s done\n", __func__); return 0; } static int pmif_probe(struct platform_device *pdev) { struct device_node *node = NULL; const struct of_device_id *of_id = of_match_device(pmif_match_table, &pdev->dev); struct spmi_controller *ctrl = NULL; struct pmif *arb = NULL; struct resource *res = NULL; u32 swinf_ch_start = 0, ap_swinf_no = 0; #if !defined(CONFIG_FPGA_EARLY_PORTING) int pmif_clk26m = 0, spmimst_clk26m = 0; #endif int err = 0; #if PMIF_BRINGUP dev_notice(&pdev->dev, "[PMIF]bringup do nothing\n"); return 0; #endif if (!of_id) { dev_notice(&pdev->dev, "[PMIF]:Error: No device match found\n"); return -ENODEV; } ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*arb)); if (!ctrl) return -ENOMEM; /* re-assign of_id->data */ spmi_controller_set_drvdata(ctrl, (void *)of_id->data); arb = spmi_controller_get_drvdata(ctrl); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmif"); arb->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(arb->base)) { err = PTR_ERR(arb->base); goto err_put_ctrl; } /* pmif is not initialized, just init once */ node = of_find_compatible_node(NULL, NULL, "mediatek,infracfg_ao"); arb->infra_base = of_iomap(node, 0); dev_info(&pdev->dev, "[PMIF]:mtk-pmif arb infra ao base:0x%x\n", arb->infra_base); if (IS_ERR(arb->infra_base)) { err = PTR_ERR(arb->infra_base); goto err_put_ctrl; } node = of_find_compatible_node(NULL, NULL, "mediatek,topckgen"); arb->topckgen_base = of_iomap(node, 0); dev_info(&pdev->dev, "[PMIF]:mtk-pmif arb topckgen base:0x%x\n", arb->topckgen_base); if (IS_ERR(arb->topckgen_base)) { err = PTR_ERR(arb->topckgen_base); goto err_put_ctrl; } node = of_find_compatible_node(NULL, NULL, "mediatek,toprgu"); arb->toprgu_base = of_iomap(node, 0); dev_info(&pdev->dev, "[PMIF]:mtk-pmif arb toprgu base:0x%x\n", arb->toprgu_base); if (IS_ERR(arb->toprgu_base)) { err = PTR_ERR(arb->toprgu_base); goto err_put_ctrl; } #if !defined(CONFIG_FPGA_EARLY_PORTING) /* get pmif infracfg_ao clock */ arb->pmif_sys_ck = devm_clk_get(&pdev->dev, "pmif_sys_ck"); if (IS_ERR(arb->pmif_sys_ck)) { dev_notice(&pdev->dev, "[PMIF]:failed to get ap clock: %ld\n", PTR_ERR(arb->pmif_sys_ck)); return PTR_ERR(arb->pmif_sys_ck); } arb->pmif_tmr_ck = devm_clk_get(&pdev->dev, "pmif_tmr_ck"); if (IS_ERR(arb->pmif_tmr_ck)) { dev_notice(&pdev->dev, "[PMIF]:failed to get tmr clock: %ld\n", PTR_ERR(arb->pmif_tmr_ck)); return PTR_ERR(arb->pmif_tmr_ck); } /* get pmif topckgen clock */ arb->pmif_clk_mux = devm_clk_get(&pdev->dev, "pmif_clk_mux"); if (IS_ERR(arb->pmif_clk_mux)) { dev_notice(&pdev->dev, "[PMIF]:failed to get clock: %ld\n", PTR_ERR(arb->pmif_clk_mux)); return PTR_ERR(arb->pmif_clk_mux); } arb->pmif_clk_osc_d10 = devm_clk_get(&pdev->dev, "pmif_clk_osc_d10"); if (IS_ERR(arb->pmif_clk_osc_d10)) { dev_notice(&pdev->dev, "[PMIF]:failed to get clock: %ld\n", PTR_ERR(arb->pmif_clk_osc_d10)); return PTR_ERR(arb->pmif_clk_osc_d10); } arb->pmif_clk26m = devm_clk_get(&pdev->dev, "pmif_clk26m"); if (IS_ERR(arb->pmif_clk26m)) { dev_notice(&pdev->dev, "[PMIF]:failed to get clock: %ld\n", PTR_ERR(arb->pmif_clk26m)); return PTR_ERR(arb->pmif_clk26m); } /* now enable pmif/spmimst clock */ pmif_clk26m = readl(arb->infra_base + arb->infra_regs[PMICW_CLOCK_CTRL]); if ((pmif_clk26m & 0x1) == 0x1) { dev_info(&pdev->dev, "[PMIF]:enable clk26m.\n"); err = clk_prepare_enable(arb->pmif_clk26m); if (err) return err; } else { dev_info(&pdev->dev, "[PMIF]:enable ulposc1 osc d10.\n"); err = clk_prepare_enable(arb->pmif_clk_mux); if (err) return err; err = clk_set_parent(arb->pmif_clk_mux, arb->pmif_clk_osc_d10); if (err) return err; } err = clk_prepare_enable(arb->pmif_sys_ck); if (err) return err; err = clk_prepare_enable(arb->pmif_tmr_ck); if (err) return err; /* get spmimst topckgen clock */ arb->spmimst_clk_mux = devm_clk_get(&pdev->dev, "spmimst_clk_mux"); if (IS_ERR(arb->spmimst_clk_mux)) { dev_notice(&pdev->dev, "[SPMIMST]:failed to get clock: %ld\n", PTR_ERR(arb->spmimst_clk_mux)); return PTR_ERR(arb->spmimst_clk_mux); } arb->spmimst_clk26m = devm_clk_get(&pdev->dev, "spmimst_clk26m"); if (IS_ERR(arb->spmimst_clk26m)) { dev_notice(&pdev->dev, "[SPMIMST]:failed to get clock: %ld\n", PTR_ERR(arb->spmimst_clk26m)); return PTR_ERR(arb->spmimst_clk26m); } arb->spmimst_clk_osc_d10 = devm_clk_get(&pdev->dev, "spmimst_clk_osc_d10"); if (IS_ERR(arb->spmimst_clk_osc_d10)) { dev_notice(&pdev->dev, "[SPMIMST]:failed to get clock: %ld\n", PTR_ERR(arb->spmimst_clk_osc_d10)); return PTR_ERR(arb->spmimst_clk_osc_d10); } err = clk_prepare_enable(arb->spmimst_clk_mux); if (err) return err; if (of_device_is_compatible(ctrl->dev.parent->of_node, "mediatek,mt6885-pmif")) { spmimst_clk26m = readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_16]); } else { spmimst_clk26m = readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_15]); spmimst_clk26m = (spmimst_clk26m >> 0x8) & 0x7; } if ((spmimst_clk26m & 0x7) == 0) { dev_info(&pdev->dev, "[SPMIMST]:enable clk26m.\n"); err = clk_set_parent(arb->spmimst_clk_mux, arb->spmimst_clk26m); if (err) return err; } else if ((spmimst_clk26m & 0x7) == 0x3) { dev_info(&pdev->dev, "[SPMIMST]:enable ulposc1 osc d10.\n"); err = clk_set_parent(arb->spmimst_clk_mux, arb->spmimst_clk_osc_d10); if (err) return err; } #else dev_notice(&pdev->dev, "[PMIF]:no need to get clock at fpga\n"); #endif /* #if defined(CONFIG_MTK_FPGA) || defined(CONFIG_FPGA_EARLY_PORTING) */ err = of_property_read_u32(pdev->dev.of_node, "swinf_ch_start", &swinf_ch_start); if (err) { dev_notice(&pdev->dev, "[PMIF]:swinf_ch_start unspecified.\n"); goto err_put_ctrl; } arb->swinf_ch_start = swinf_ch_start; err = of_property_read_u32(pdev->dev.of_node, "ap_swinf_no", &ap_swinf_no); if (err) { dev_notice(&pdev->dev, "[PMIF]:ap_swinf_no unspecified.\n"); goto err_put_ctrl; } arb->ap_swinf_no = ap_swinf_no; if (arb->is_pmif_init_done(arb) != 0) { /* pmif initialize start */ arb->pmif_enable_clk_set(arb); arb->pmif_force_normal_mode(arb); /* Enable SWINF and arbitration for AP. */ arb->pmif_enable_swinf(arb, arb->swinf_ch_start, arb->ap_swinf_no); arb->pmif_enable_cmdIssue(arb, true); arb->pmif_enable(arb); arb->is_pmif_init_done(arb); /* pmif initialize end */ } raw_spin_lock_init(&arb->lock); arb->pmifThread_lock = wakeup_source_register(NULL, "pmif wakelock"); mutex_init(&arb->pmif_mutex); ctrl->cmd = pmif_arb_cmd; ctrl->read_cmd = pmif_spmi_read_cmd; ctrl->write_cmd = pmif_spmi_write_cmd; if (arb->is_pmif_init_done(arb) == 0) { /* pmif already done, call spmi master driver init */ err = mtk_spmimst_init(pdev, arb); if (err) goto err_put_ctrl; } /* enable debugger */ spmi_pmif_dbg_init(ctrl); spmi_pmif_create_attr(&pmif_driver.driver); arb->irq = platform_get_irq_byname(pdev, "pmif_irq"); if (arb->irq < 0) { err = arb->irq; goto err_put_ctrl; } pmif_irq_register(pdev, arb, arb->irq); platform_set_drvdata(pdev, ctrl); err = spmi_controller_add(ctrl); if (err) goto err_domain_remove; return 0; err_domain_remove: #if !defined(CONFIG_FPGA_EARLY_PORTING) clk_disable_unprepare(arb->spmimst_clk_mux); if ((pmif_clk26m & 0x1) == 0x1) clk_disable_unprepare(arb->pmif_clk26m); else clk_disable_unprepare(arb->pmif_clk_mux); #endif err_put_ctrl: spmi_controller_put(ctrl); return err; } static int pmif_remove(struct platform_device *pdev) { struct spmi_controller *ctrl = platform_get_drvdata(pdev); spmi_controller_remove(ctrl); spmi_controller_put(ctrl); return 0; } static struct platform_driver pmif_driver = { .probe = pmif_probe, .remove = pmif_remove, .driver = { .name = "pmif", .of_match_table = of_match_ptr(pmif_match_table), }, }; static int __init mtk_pmif_init(void) { int ret = 0; ret = platform_driver_register(&pmif_driver); if (ret) return -ENODEV; return 0; } postcore_initcall(mtk_pmif_init); /* Module information */ MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PMIF module"); MODULE_AUTHOR("Argus Lin "); MODULE_ALIAS("platform:pmif");