| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 MediaTek Inc. |
| * Author: Argus Lin <argus.lin@mediatek.com> |
| */ |
| /* #define DEBUG */ |
| |
| #include <linux/clk.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/regmap.h> |
| #include <linux/reset.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/kernel.h> |
| #include <linux/mfd/mt6315/registers.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/of_address.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/sched/clock.h> |
| #include <linux/kthread.h> |
| #include <linux/mutex.h> |
| #include <linux/pm_wakeup.h> |
| #include <linux/spmi.h> |
| #include <linux/pmif.h> |
| #include <spmi_sw.h> |
| #include <dt-bindings/spmi/spmi.h> |
| #include <mt-plat/aee.h> |
| |
| /* |
| * 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, |
| }; |
| 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,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: |
| pmif_hw_monitor_irq_handler(irq, data); |
| break; |
| case IRQ_WDT: |
| case IRQ_WDT_V2: |
| 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: |
| 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 <argus.lin@mediatek.com>"); |
| MODULE_ALIAS("platform:pmif"); |