[Feature] add GA346 baseline version
Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/spmi/mtk-pmif.c b/src/kernel/linux/v4.19/drivers/spmi/mtk-pmif.c
new file mode 100644
index 0000000..b254a88
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/spmi/mtk-pmif.c
@@ -0,0 +1,2007 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+/* #define DEBUG */
+
+#include <dt-bindings/spmi/spmi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
+#include <linux/pmif.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sched/clock.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <mt-plat/aee.h>
+
+#include "spmi_sw.h"
+
+/*
+ * marco
+ */
+#define PMIF_TIMEOUT 1
+#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 mt6880_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_SAMPL_CTRL] = 0x000c,
+ [SPMI_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 mt6880_spmi_regs[] = {
+ [SPMI_OP_ST_CTRL] = 0x0000,
+ [SPMI_GRP_ID_EN] = 0x0004,
+ [SPMI_OP_ST_STA] = 0x0008,
+ [SPMI_SAMPL_CTRL] = 0x000C,
+ [SPMI_REQ_EN] = 0x0010,
+ [SPMI_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,
+};
+
+static const u32 mt6853_spmi_regs[] = {
+ [SPMI_OP_ST_CTRL] = 0x0000,
+ [SPMI_GRP_ID_EN] = 0x0004,
+ [SPMI_OP_ST_STA] = 0x0008,
+ [SPMI_SAMPL_CTRL] = 0x000C,
+ [SPMI_REQ_EN] = 0x0010,
+ [SPMI_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,
+};
+
+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,
+};
+
+enum topckgen_regs {
+ CLK_CFG_UPDATE,
+ CLK_CFG_UPDATE1,
+ CLK_CFG_UPDATE2,
+ CLK_CFG_3_CLR,
+ CLK_CFG_6,
+ CLK_CFG_6_SET,
+ CLK_CFG_6_CLR,
+ CLK_CFG_8_CLR,
+ CLK_CFG_15,
+ CLK_CFG_15_CLR,
+ CLK_CFG_16,
+ CLK_CFG_16_CLR,
+};
+
+static const u32 mt6xxx_topckgen_regs[] = {
+ [CLK_CFG_UPDATE] = 0x0004,
+ [CLK_CFG_UPDATE1] = 0x0008,
+ [CLK_CFG_UPDATE2] = 0x000c,
+ [CLK_CFG_3_CLR] = 0x0048,
+ [CLK_CFG_6] = 0x0070,
+ [CLK_CFG_6_SET] = 0x0074,
+ [CLK_CFG_6_CLR] = 0x0078,
+ [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/MT6880 series */
+ IRQ_PMIF_ACC_VIO_V2 = 19,
+ IRQ_PMIC_ACC_VIO_V2 = 20,
+ IRQ_HW_MONITOR_V2 = 10,
+ IRQ_WDT_V2 = 11,
+};
+
+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_enable_clk_set_v2(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 void pmif_spmi_enable_m_v2(struct pmif *arb);
+static void pmif_spmi_enable_p_v2(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_v2(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_config_master_m_v2(struct pmif *arb,
+ unsigned int mstid, bool en);
+static int mtk_spmi_config_master_p_v2(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_cali_rd_clock_polarity_m_p_v2(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 mt6885_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 mt6873_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_v2,
+};
+
+static struct pmif mt6853_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 mt6853_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 struct pmif mt6880_pmif_m_arb = {
+ .base = 0x0,
+ .regs = mt6880_regs,
+ .spmimst_base = 0x0,
+ .spmimst_regs = mt6880_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_v2,
+ .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_v2,
+ .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_v2,
+ .spmi_config_master = mtk_spmi_config_master_m_v2,
+};
+
+static struct pmif mt6880_pmif_p_arb = {
+ .base = 0x0,
+ .regs = mt6880_regs,
+ .spmimst_base = 0x0,
+ .spmimst_regs = mt6880_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_v2,
+ .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_v2,
+ .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_v2,
+ .spmi_config_master = mtk_spmi_config_master_p_v2,
+};
+
+static const struct of_device_id pmif_match_table[] = {
+ {
+ .compatible = "mediatek,mt6885-pmif",
+ .data = &mt6885_pmif_arb,
+ }, {
+ .compatible = "mediatek,mt6873-pmif",
+ .data = &mt6873_pmif_arb,
+ }, {
+ .compatible = "mediatek,mt6853-pmif-m",
+ .data = &mt6853_pmif_m_arb,
+ }, {
+ .compatible = "mediatek,mt6853-pmif-p",
+ .data = &mt6853_pmif_p_arb,
+ }, {
+ .compatible = "mediatek,mt6880-pmif-m",
+ .data = &mt6880_pmif_m_arb,
+ }, {
+ .compatible = "mediatek,mt6880-pmif-p",
+ .data = &mt6880_pmif_p_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;
+
+ start_time_ns = sched_clock();
+ timeout_ns = 10000 * 1000; /* 10000us */
+
+ do {
+ if (pmif_timeout_ns(ctrl, start_time_ns, timeout_ns)) {
+ if (fp(arb)) {
+ return 0;
+ } else if (fp(arb) == 0) {
+ dev_notice(&ctrl->dev, "[PMIF] FSM Timeout\n");
+ 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,
+ arb->infra_base + arb->infra_regs[INFRA_GLOBALCON_RST2_SET]);
+ writel(0x1,
+ 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_enable_clk_set_v2(struct pmif *arb)
+{
+ /* sys_ck cg enable, turn off clock */
+ writel(0x0000000f,
+ arb->infra_base + arb->infra_regs[MODULE_SW_CG_0_SET]);
+
+#if !defined(CONFIG_FPGA_EARLY_PORTING)
+ writel((0x1 << 31) | (0x1 << 28) | (0x7 << 24),
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_3_CLR]);
+ writel(0x1 << 15,
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE]);
+#endif
+
+ /* 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);
+#if defined(CONFIG_FPGA_EARLY_PORTING)
+ pmif_writel(arb, pmif_readl(arb, PMIF_SPI_MODE_CTRL) & (~(0x1 << 13)),
+ PMIF_SPI_MODE_CTRL);
+#endif
+}
+
+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 void pmif_spmi_enable_m_v2(struct pmif *arb)
+{
+ pmif_writel(arb, 0x1FFF, PMIF_INF_EN);
+ pmif_writel(arb, 0x1FFF, PMIF_ARB_EN);
+ pmif_writel(arb, 0x3, PMIF_TIMER_CTRL);
+ pmif_writel(arb, 0x1, PMIF_INIT_DONE);
+}
+
+static void pmif_spmi_enable_p_v2(struct pmif *arb)
+{
+ pmif_writel(arb, 0x1FFF, PMIF_INF_EN);
+ pmif_writel(arb, 0x1FFF, 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)
+{
+ return pmif_readl(arb, PMIF_INIT_DONE) & 0x1;
+}
+
+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_REQ_EN);
+
+ return 0;
+}
+
+static int mtk_spmi_config_master_v2(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 << 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 SPMI */
+ mtk_spmi_writel(arb, en, SPMI_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_RCS_CTRL);
+ /* Enable SPMI */
+ mtk_spmi_writel(arb, en, SPMI_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_RCS_CTRL);
+ /* Enable SPMI */
+ mtk_spmi_writel(arb, en, SPMI_REQ_EN);
+
+ return 0;
+}
+
+static int mtk_spmi_config_master_m_v2(struct pmif *arb,
+ unsigned int mstid, bool en)
+{
+ /* Software reset */
+ writel(0x85 << 24 | 0x1 << 4,
+ arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]);
+
+#if !defined(CONFIG_FPGA_EARLY_PORTING)
+ writel((0x7 << 16) | (0x1 << 20) | (0x1 << 23),
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6_CLR]);
+ writel(0x3 << 16,
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6_SET]);
+ writel(0x1 << 26,
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE]);
+#endif
+
+ /* Software reset */
+ writel(0x85 << 24,
+ arb->toprgu_base + arb->toprgu_regs[WDT_SWSYSRST2]);
+
+ /* Enable master rcs */
+ mtk_spmi_writel(arb, 0x14 | arb->mstid, SPMI_RCS_CTRL);
+ /* Enable SPMI */
+ mtk_spmi_writel(arb, en, SPMI_REQ_EN);
+
+ return 0;
+}
+
+static int mtk_spmi_config_master_p_v2(struct pmif *arb,
+ unsigned int mstid, bool en)
+{
+ /* no need to reset SPMI master again */
+
+#if !defined(CONFIG_FPGA_EARLY_PORTING)
+ writel((0xF << 24) | (0x1 << 28) | (0x1 << 31),
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6_CLR]);
+ writel(0x7 << 24,
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6_SET]);
+ writel(0x1 << 27,
+ arb->topckgen_base + arb->topckgen_regs[CLK_CFG_UPDATE]);
+#endif
+ /* Enable SPMI */
+ mtk_spmi_writel(arb, en, SPMI_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_SAMPL_CTRL);
+
+ 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_SAMPL_CTRL);
+
+ return 0;
+}
+
+static int mtk_spmi_cali_rd_clock_polarity_m_p_v2(struct pmif *arb)
+{
+#if defined(CONFIG_MTK_FPGA) || defined(CONFIG_FPGA_EARLY_PORTING)
+ unsigned int dly = 1, 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_SAMPL_CTRL);
+
+ 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:
+ 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:
+ pmif_hw_monitor_irq_handler(irq, data);
+ break;
+ case IRQ_WDT:
+ pmif_wdt_irq_handler(irq, data);
+ break;
+ case IRQ_PMIF_ACC_VIO_V2:
+ pmif_pmif_acc_vio_irq_handler(irq, data);
+ break;
+ case IRQ_PMIC_ACC_VIO_V2:
+ 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_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 void rcs_irq_lock(struct irq_data *data)
+{
+ struct pmif *arb = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&arb->rcs_irqlock);
+}
+
+static void rcs_irq_sync_unlock(struct irq_data *data)
+{
+ struct pmif *arb = irq_data_get_irq_chip_data(data);
+
+ mutex_unlock(&arb->rcs_irqlock);
+}
+
+static void rcs_irq_enable(struct irq_data *data)
+{
+ unsigned int hwirq = irqd_to_hwirq(data);
+ struct pmif *arb = irq_data_get_irq_chip_data(data);
+
+ arb->rcs_enable_hwirq[hwirq] = true;
+}
+
+static void rcs_irq_disable(struct irq_data *data)
+{
+ unsigned int hwirq = irqd_to_hwirq(data);
+ struct pmif *arb = irq_data_get_irq_chip_data(data);
+
+ arb->rcs_enable_hwirq[hwirq] = false;
+}
+
+static const struct irq_chip rcs_irq_chip = {
+ .name = "rcs_irq",
+ .irq_bus_lock = rcs_irq_lock,
+ .irq_bus_sync_unlock = rcs_irq_sync_unlock,
+ .irq_enable = rcs_irq_enable,
+ .irq_disable = rcs_irq_disable,
+};
+
+static int rcs_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct pmif *arb = d->host_data;
+
+ irq_set_chip_data(virq, arb);
+ irq_set_chip(virq, &arb->irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_parent(virq, arb->rcs_irq);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops rcs_irq_domain_ops = {
+ .map = rcs_irq_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static irqreturn_t rcs_irq_handler(int irq, void *data)
+{
+ struct pmif *arb = data;
+ unsigned int slv_irq_sta;
+ int i;
+
+ for (i = 0; i < SPMI_MAX_SLAVE_ID; i++) {
+ slv_irq_sta = mtk_spmi_readl(arb, SPMI_SLV_3_0_EINT + (i / 4));
+ slv_irq_sta >>= (((i % 4) * 8) & 0xFF);
+ if (arb->rcs_enable_hwirq[i] && slv_irq_sta) {
+ dev_info(&arb->spmic->dev,
+ "hwirq=%d, sta=0x%x\n", i, slv_irq_sta);
+ handle_nested_irq(irq_find_mapping(arb->domain, i));
+ }
+ /* need to clear using 0xFF to avoid new irq happen
+ * after read SPMI_SLV_3_0_EINT + (i/4) value then use
+ * this value to clean
+ */
+ mtk_spmi_writel(arb, (0xFF << ((i % 4) * 8)),
+ SPMI_SLV_3_0_EINT + (i / 4));
+ }
+ return IRQ_HANDLED;
+}
+
+static int rcs_irq_register(struct platform_device *pdev,
+ struct pmif *arb, int irq)
+{
+ int i, ret = 0;
+
+ mutex_init(&arb->rcs_irqlock);
+ arb->rcs_enable_hwirq = devm_kcalloc(&pdev->dev, SPMI_MAX_SLAVE_ID,
+ sizeof(*arb->rcs_enable_hwirq),
+ GFP_KERNEL);
+ if (!arb->rcs_enable_hwirq)
+ return -ENOMEM;
+
+ arb->irq_chip = rcs_irq_chip;
+ arb->domain = irq_domain_add_linear(pdev->dev.of_node,
+ SPMI_MAX_SLAVE_ID,
+ &rcs_irq_domain_ops, arb);
+ if (!arb->domain) {
+ dev_notice(&pdev->dev, "Failed to create IRQ domain\n");
+ return -ENODEV;
+ }
+ /* clear all slave irq status */
+ for (i = 0; i < SPMI_MAX_SLAVE_ID; i++) {
+ mtk_spmi_writel(arb, (0xFF << ((i % 4) * 8)),
+ SPMI_SLV_3_0_EINT + (i / 4));
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ rcs_irq_handler, IRQF_ONESHOT,
+ rcs_irq_chip.name, arb);
+ if (ret < 0) {
+ dev_notice(&pdev->dev, "Failed to request IRQ=%d, ret = %d\n",
+ irq, ret);
+ return ret;
+ }
+ enable_irq_wake(irq);
+
+ return ret;
+}
+
+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_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_clk = 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);
+ arb->spmic = 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);
+ 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);
+ 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);
+ 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);
+ }
+ /* now enable pmif/spmimst clock */
+ pmif_clk26m =
+ readl(arb->infra_base + arb->infra_regs[PMICW_CLOCK_CTRL]);
+
+ if ((pmif_clk26m & 0x1) == 0x1) {
+ 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);
+ }
+ dev_info(&pdev->dev, "[PMIF]:enable clk26m.\n");
+ err = clk_prepare_enable(arb->pmif_clk26m);
+ if (err)
+ return err;
+ } else {
+ arb->pmif_clk_mux_parent = devm_clk_get(&pdev->dev,
+ "pmif_clk_osc_d10");
+ dev_info(&pdev->dev, "[PMIF]:enable ulposc1 osc d10.\n");
+ if (IS_ERR(arb->pmif_clk_mux_parent)) {
+ dev_notice(&pdev->dev,
+ "[PMIF]:failed to get clock: %ld\n",
+ PTR_ERR(arb->pmif_clk_mux_parent));
+ return PTR_ERR(arb->pmif_clk_mux_parent);
+ }
+ err = clk_prepare_enable(arb->pmif_clk_mux);
+ if (err)
+ return err;
+ err = clk_set_parent(arb->pmif_clk_mux,
+ arb->pmif_clk_mux_parent);
+ 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);
+ }
+ if (of_device_is_compatible(ctrl->dev.parent->of_node,
+ "mediatek,mt6885-pmif")) {
+ spmimst_clk =
+ readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_16]);
+ spmimst_clk = 0x7;
+ } else if (of_device_is_compatible(ctrl->dev.parent->of_node,
+ "mediatek,mt6880-pmif-m")) {
+ spmimst_clk =
+ readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6]);
+ spmimst_clk = (spmimst_clk >> 16) & 0x7;
+ } else if (of_device_is_compatible(ctrl->dev.parent->of_node,
+ "mediatek,mt6880-pmif-p")) {
+ spmimst_clk =
+ readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_6]);
+ spmimst_clk = (spmimst_clk >> 24) & 0xF;
+ } else {
+ spmimst_clk =
+ readl(arb->topckgen_base + arb->topckgen_regs[CLK_CFG_15]);
+ spmimst_clk = (spmimst_clk >> 0x8) & 0x7;
+ }
+ switch (spmimst_clk) {
+ case 0:
+ arb->spmimst_clk_mux_parent =
+ devm_clk_get(&pdev->dev, "spmimst_clk26m");
+ dev_info(&pdev->dev, "[SPMIMST]:enable clk26m.\n");
+ break;
+ case 3:
+ arb->spmimst_clk_mux_parent =
+ devm_clk_get(&pdev->dev, "spmimst_clk_osc_d10");
+ dev_info(&pdev->dev, "[SPMIMST]:enable ulposc1 osc d10.\n");
+ break;
+ case 7:
+ arb->spmimst_clk_mux_parent =
+ devm_clk_get(&pdev->dev, "spmimst_clk_mainpll_d7_d8");
+ dev_info(&pdev->dev, "[SPMIMST]:enable mainpll d7 d8.\n");
+ break;
+ }
+ if (IS_ERR(arb->spmimst_clk_mux_parent)) {
+ dev_notice(&pdev->dev,
+ "[SPMIMST]:failed to get clock: %ld\n",
+ PTR_ERR(arb->spmimst_clk_mux_parent));
+ return PTR_ERR(arb->spmimst_clk_mux_parent);
+ }
+ err = clk_prepare_enable(arb->spmimst_clk_mux);
+ if (err)
+ return err;
+ err = clk_set_parent(arb->spmimst_clk_mux, arb->spmimst_clk_mux_parent);
+ 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);
+ /* 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)) {
+ /* pmif already done, call spmi master driver init */
+ err = mtk_spmimst_init(pdev, arb);
+ if (err)
+ goto err_put_ctrl;
+ }
+ if (arb->pmifid != PMIF_PMIFID_SPMI2) {
+ /* enable debugger for SPMI-M */
+ 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);
+
+ arb->rcs_irq = platform_get_irq_byname(pdev, "rcs_irq");
+ if (arb->rcs_irq < 0) {
+ dev_notice(&pdev->dev,
+ "Failed to get rcs_irq, ret = %d\n", arb->rcs_irq);
+ } else {
+ err = rcs_irq_register(pdev, arb, arb->rcs_irq);
+ if (err)
+ goto err_domain_remove;
+ }
+
+ platform_set_drvdata(pdev, ctrl);
+
+ err = spmi_controller_add(ctrl);
+ if (err)
+ goto err_domain_remove;
+
+ return 0;
+
+err_domain_remove:
+ if (arb->domain)
+ irq_domain_remove(arb->domain);
+#if !defined(CONFIG_FPGA_EARLY_PORTING)
+ clk_disable_unprepare(arb->spmimst_clk_mux);
+ clk_disable_unprepare(arb->pmif_tmr_ck);
+ clk_disable_unprepare(arb->pmif_sys_ck);
+ 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);
+ struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+
+ if (arb->domain)
+ irq_domain_remove(arb->domain);
+ 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 <argus.lin@mediatek.com>");
+MODULE_ALIAS("platform:pmif");