| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright 2025 Linaro Ltd. |
| * |
| * GS101 PMU (Power Management Unit) support |
| */ |
| |
| #include <linux/arm-smccc.h> |
| #include <linux/array_size.h> |
| #include <linux/soc/samsung/exynos-pmu.h> |
| #include <linux/soc/samsung/exynos-regs-pmu.h> |
| #include <linux/regmap.h> |
| |
| #include "exynos-pmu.h" |
| |
| #define PMUALIVE_MASK GENMASK(13, 0) |
| #define TENSOR_SET_BITS (BIT(15) | BIT(14)) |
| #define TENSOR_CLR_BITS BIT(15) |
| #define TENSOR_SMC_PMU_SEC_REG 0x82000504 |
| #define TENSOR_PMUREG_READ 0 |
| #define TENSOR_PMUREG_WRITE 1 |
| #define TENSOR_PMUREG_RMW 2 |
| |
| static const struct regmap_range gs101_pmu_registers[] = { |
| regmap_reg_range(GS101_OM_STAT, GS101_SYSTEM_INFO), |
| regmap_reg_range(GS101_IDLE_IP(0), GS101_IDLE_IP_MASK(3)), |
| regmap_reg_range(GS101_DATARAM_STATE_SLC_CH(0), |
| GS101_PPMPURAM_INFORM_SCL_CH(3)), |
| regmap_reg_range(GS101_INFORM0, GS101_SYSIP_DAT(0)), |
| /* skip SYSIP_DAT1 SYSIP_DAT2 */ |
| regmap_reg_range(GS101_SYSIP_DAT(3), GS101_PWR_HOLD_SW_TRIP), |
| regmap_reg_range(GS101_GSA_INFORM(0), GS101_GSA_INFORM(1)), |
| regmap_reg_range(GS101_INFORM4, GS101_IROM_INFORM), |
| regmap_reg_range(GS101_IROM_CPU_INFORM(0), GS101_IROM_CPU_INFORM(7)), |
| regmap_reg_range(GS101_PMU_SPARE(0), GS101_PMU_SPARE(3)), |
| /* skip most IROM_xxx registers */ |
| regmap_reg_range(GS101_DREX_CALIBRATION(0), GS101_DREX_CALIBRATION(7)), |
| |
| #define CLUSTER_CPU_RANGE(cl, cpu) \ |
| regmap_reg_range(GS101_CLUSTER_CPU_CONFIGURATION(cl, cpu), \ |
| GS101_CLUSTER_CPU_OPTION(cl, cpu)), \ |
| regmap_reg_range(GS101_CLUSTER_CPU_OUT(cl, cpu), \ |
| GS101_CLUSTER_CPU_IN(cl, cpu)), \ |
| regmap_reg_range(GS101_CLUSTER_CPU_INT_IN(cl, cpu), \ |
| GS101_CLUSTER_CPU_INT_DIR(cl, cpu)) |
| |
| /* cluster 0..2 and cpu 0..4 or 0..1 */ |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 1), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 2), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 3), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER1_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER1_OFFSET, 1), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER2_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER2_OFFSET, 1), |
| #undef CLUSTER_CPU_RANGE |
| |
| #define CLUSTER_NONCPU_RANGE(cl) \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_CONFIGURATION(cl), \ |
| GS101_CLUSTER_NONCPU_OPTION(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_OUT(cl), \ |
| GS101_CLUSTER_NONCPU_IN(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_INT_IN(cl), \ |
| GS101_CLUSTER_NONCPU_INT_DIR(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_DUALRAIL_CTRL_OUT(cl), \ |
| GS101_CLUSTER_NONCPU_DUALRAIL_POS_OUT(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_DUALRAIL_CTRL_IN(cl), \ |
| GS101_CLUSTER_NONCPU_DUALRAIL_CTRL_IN(cl)) |
| |
| CLUSTER_NONCPU_RANGE(0), |
| regmap_reg_range(GS101_CLUSTER0_NONCPU_DSU_PCH, |
| GS101_CLUSTER0_NONCPU_DSU_PCH), |
| CLUSTER_NONCPU_RANGE(1), |
| CLUSTER_NONCPU_RANGE(2), |
| #undef CLUSTER_NONCPU_RANGE |
| |
| #define SUBBLK_RANGE(blk) \ |
| regmap_reg_range(GS101_SUBBLK_CONFIGURATION(blk), \ |
| GS101_SUBBLK_CTRL(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_OUT(blk), GS101_SUBBLK_IN(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_INT_IN(blk), \ |
| GS101_SUBBLK_INT_DIR(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_MEMORY_OUT(blk), \ |
| GS101_SUBBLK_MEMORY_IN(blk)) |
| |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_ALIVE), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_AOC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_APM), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CMU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CORE), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EH), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G3D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EMBEDDED_CPUCL0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EMBEDDED_G3D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DPU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DISP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G2D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MFC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CSIS), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PDP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DNS), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G3AA), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_IPP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_ITP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MCSC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_GDC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_TNR), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BO), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_TPU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF3), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MISC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PERIC0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PERIC1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_S2D), |
| #undef SUBBLK_RANGE |
| |
| #define SUBBLK_CPU_RANGE(blk) \ |
| regmap_reg_range(GS101_SUBBLK_CPU_CONFIGURATION(blk), \ |
| GS101_SUBBLK_CPU_OPTION(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_CPU_OUT(blk), \ |
| GS101_SUBBLK_CPU_IN(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_CPU_INT_IN(blk), \ |
| GS101_SUBBLK_CPU_INT_DIR(blk)) |
| |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_APM), |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_DBGCORE), |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_SSS), |
| #undef SUBBLK_CPU_RANGE |
| |
| regmap_reg_range(GS101_MIF_CONFIGURATION, GS101_MIF_CTRL), |
| regmap_reg_range(GS101_MIF_OUT, GS101_MIF_IN), |
| regmap_reg_range(GS101_MIF_INT_IN, GS101_MIF_INT_DIR), |
| regmap_reg_range(GS101_TOP_CONFIGURATION, GS101_TOP_OPTION), |
| regmap_reg_range(GS101_TOP_OUT, GS101_TOP_IN), |
| regmap_reg_range(GS101_TOP_INT_IN, GS101_WAKEUP2_STAT), |
| regmap_reg_range(GS101_WAKEUP2_INT_IN, GS101_WAKEUP2_INT_DIR), |
| regmap_reg_range(GS101_SYSTEM_CONFIGURATION, GS101_USER_DEFINED_OUT), |
| regmap_reg_range(GS101_SYSTEM_OUT, GS101_SYSTEM_IN), |
| regmap_reg_range(GS101_SYSTEM_INT_IN, GS101_EINT_WAKEUP_MASK3), |
| regmap_reg_range(GS101_USER_DEFINED_INT_IN, GS101_SCAN2DRAM_INT_DIR), |
| /* skip HCU_START */ |
| regmap_reg_range(GS101_CUSTOM_OUT, GS101_CUSTOM_IN), |
| regmap_reg_range(GS101_CUSTOM_INT_IN, GS101_CUSTOM_INT_DIR), |
| regmap_reg_range(GS101_ACK_LAST_CPU, GS101_HCU_R(3)), |
| regmap_reg_range(GS101_HCU_SP, GS101_HCU_PC), |
| /* skip PMU_RAM_CTRL */ |
| regmap_reg_range(GS101_APM_HCU_CTRL, GS101_APM_HCU_CTRL), |
| regmap_reg_range(GS101_APM_NMI_ENABLE, GS101_RST_STAT_PMU), |
| regmap_reg_range(GS101_HPM_INT_IN, GS101_BOOT_STAT), |
| regmap_reg_range(GS101_PMLINK_OUT, GS101_PMLINK_AOC_CTRL), |
| regmap_reg_range(GS101_TCXO_BUF_CTRL, GS101_ADD_CTRL), |
| regmap_reg_range(GS101_HCU_TIMEOUT_RESET, GS101_HCU_TIMEOUT_SCAN2DRAM), |
| regmap_reg_range(GS101_TIMER(0), GS101_TIMER(3)), |
| regmap_reg_range(GS101_PPC_MIF(0), GS101_PPC_EH), |
| /* PPC_OFFSET, skip PPC_CPUCL1_0 PPC_CPUCL1_1 */ |
| regmap_reg_range(GS101_EXT_REGULATOR_MIF_DURATION, GS101_TCXO_DURATION), |
| regmap_reg_range(GS101_BURNIN_CTRL, GS101_TMU_SUB_TRIP), |
| regmap_reg_range(GS101_MEMORY_CEN, GS101_MEMORY_SMX_FEEDBACK), |
| regmap_reg_range(GS101_SLC_PCH_CHANNEL, GS101_SLC_PCH_CB), |
| regmap_reg_range(GS101_FORCE_NOMC, GS101_FORCE_NOMC), |
| regmap_reg_range(GS101_FORCE_BOOST, GS101_PMLINK_SLC_BUSY), |
| regmap_reg_range(GS101_BOOTSYNC_OUT, GS101_CTRL_SECJTAG_ALIVE), |
| regmap_reg_range(GS101_CTRL_DIV_PLL_ALV_DIVLOW, GS101_CTRL_CLKDIV__CLKRTC), |
| regmap_reg_range(GS101_CTRL_SOC32K, GS101_CTRL_SBU_SW_EN), |
| regmap_reg_range(GS101_PAD_CTRL_CLKOUT0, GS101_PAD_CTRL_WRESETO_n), |
| regmap_reg_range(GS101_PHY_CTRL_USB20, GS101_PHY_CTRL_UFS), |
| }; |
| |
| static const struct regmap_range gs101_pmu_ro_registers[] = { |
| regmap_reg_range(GS101_OM_STAT, GS101_VERSION), |
| regmap_reg_range(GS101_OTP_STATUS, GS101_OTP_STATUS), |
| |
| regmap_reg_range(GS101_DATARAM_STATE_SLC_CH(0), |
| GS101_PPMPURAM_STATE_SLC_CH(0)), |
| regmap_reg_range(GS101_DATARAM_STATE_SLC_CH(1), |
| GS101_PPMPURAM_STATE_SLC_CH(1)), |
| regmap_reg_range(GS101_DATARAM_STATE_SLC_CH(2), |
| GS101_PPMPURAM_STATE_SLC_CH(2)), |
| regmap_reg_range(GS101_DATARAM_STATE_SLC_CH(3), |
| GS101_PPMPURAM_STATE_SLC_CH(3)), |
| |
| #define CLUSTER_CPU_RANGE(cl, cpu) \ |
| regmap_reg_range(GS101_CLUSTER_CPU_IN(cl, cpu), \ |
| GS101_CLUSTER_CPU_IN(cl, cpu)), \ |
| regmap_reg_range(GS101_CLUSTER_CPU_INT_IN(cl, cpu), \ |
| GS101_CLUSTER_CPU_INT_IN(cl, cpu)) |
| |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 1), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 2), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER0_OFFSET, 3), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER1_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER1_OFFSET, 1), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER2_OFFSET, 0), |
| CLUSTER_CPU_RANGE(GS101_CLUSTER2_OFFSET, 1), |
| #undef CLUSTER_CPU_RANGE |
| |
| #define CLUSTER_NONCPU_RANGE(cl) \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_IN(cl), \ |
| GS101_CLUSTER_NONCPU_IN(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_INT_IN(cl), \ |
| GS101_CLUSTER_NONCPU_INT_IN(cl)), \ |
| regmap_reg_range(GS101_CLUSTER_NONCPU_DUALRAIL_CTRL_IN(cl), \ |
| GS101_CLUSTER_NONCPU_DUALRAIL_CTRL_IN(cl)) |
| |
| CLUSTER_NONCPU_RANGE(0), |
| CLUSTER_NONCPU_RANGE(1), |
| CLUSTER_NONCPU_RANGE(2), |
| regmap_reg_range(GS101_CLUSTER_NONCPU_INT_EN(2), |
| GS101_CLUSTER_NONCPU_INT_DIR(2)), |
| #undef CLUSTER_NONCPU_RANGE |
| |
| #define SUBBLK_RANGE(blk) \ |
| regmap_reg_range(GS101_SUBBLK_IN(blk), GS101_SUBBLK_IN(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_INT_IN(blk), \ |
| GS101_SUBBLK_INT_IN(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_MEMORY_IN(blk), \ |
| GS101_SUBBLK_MEMORY_IN(blk)) |
| |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_ALIVE), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_AOC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_APM), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CMU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BUS2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CORE), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EH), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CPUCL2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G3D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EMBEDDED_CPUCL0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_EMBEDDED_G3D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_HSI2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DPU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DISP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G2D), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MFC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_CSIS), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PDP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_DNS), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_G3AA), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_IPP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_ITP), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MCSC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_GDC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_TNR), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_BO), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_TPU), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF2), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MIF3), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_MISC), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PERIC0), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_PERIC1), |
| SUBBLK_RANGE(GS101_SUBBBLK_OFFSET_S2D), |
| #undef SUBBLK_RANGE |
| |
| #define SUBBLK_CPU_RANGE(blk) \ |
| regmap_reg_range(GS101_SUBBLK_CPU_IN(blk), \ |
| GS101_SUBBLK_CPU_IN(blk)), \ |
| regmap_reg_range(GS101_SUBBLK_CPU_INT_IN(blk), \ |
| GS101_SUBBLK_CPU_INT_IN(blk)) |
| |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_APM), |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_DBGCORE), |
| SUBBLK_CPU_RANGE(GS101_SUBBBLK_CPU_OFFSET_SSS), |
| #undef SUBBLK_CPU_RANGE |
| |
| regmap_reg_range(GS101_MIF_CONFIGURATION, GS101_MIF_CONFIGURATION), |
| regmap_reg_range(GS101_MIF_IN, GS101_MIF_IN), |
| regmap_reg_range(GS101_MIF_INT_IN, GS101_MIF_INT_IN), |
| regmap_reg_range(GS101_TOP_IN, GS101_TOP_IN), |
| regmap_reg_range(GS101_TOP_INT_IN, GS101_TOP_INT_IN), |
| regmap_reg_range(GS101_WAKEUP2_INT_IN, GS101_WAKEUP2_INT_IN), |
| regmap_reg_range(GS101_SYSTEM_IN, GS101_SYSTEM_IN), |
| regmap_reg_range(GS101_SYSTEM_INT_IN, GS101_SYSTEM_INT_IN), |
| regmap_reg_range(GS101_EINT_INT_IN, GS101_EINT_INT_IN), |
| regmap_reg_range(GS101_EINT2_INT_IN, GS101_EINT2_INT_IN), |
| regmap_reg_range(GS101_EINT3_INT_IN, GS101_EINT3_INT_IN), |
| regmap_reg_range(GS101_USER_DEFINED_INT_IN, GS101_USER_DEFINED_INT_IN), |
| regmap_reg_range(GS101_SCAN2DRAM_INT_IN, GS101_SCAN2DRAM_INT_IN), |
| regmap_reg_range(GS101_CUSTOM_IN, GS101_CUSTOM_IN), |
| regmap_reg_range(GS101_CUSTOM_INT_IN, GS101_CUSTOM_INT_IN), |
| regmap_reg_range(GS101_HCU_R(0), GS101_HCU_R(3)), |
| regmap_reg_range(GS101_HCU_SP, GS101_HCU_PC), |
| regmap_reg_range(GS101_NMI_SRC_IN, GS101_NMI_SRC_IN), |
| regmap_reg_range(GS101_HPM_INT_IN, GS101_HPM_INT_IN), |
| regmap_reg_range(GS101_MEMORY_PGEN_FEEDBACK, GS101_MEMORY_PGEN_FEEDBACK), |
| regmap_reg_range(GS101_MEMORY_SMX_FEEDBACK, GS101_MEMORY_SMX_FEEDBACK), |
| regmap_reg_range(GS101_PMLINK_SLC_ACK, GS101_PMLINK_SLC_BUSY), |
| regmap_reg_range(GS101_BOOTSYNC_IN, GS101_BOOTSYNC_IN), |
| regmap_reg_range(GS101_SCAN_READY_IN, GS101_SCAN_READY_IN), |
| regmap_reg_range(GS101_CTRL_PLL_ALV_LOCK, GS101_CTRL_PLL_ALV_LOCK), |
| }; |
| |
| static const struct regmap_access_table gs101_pmu_rd_table = { |
| .yes_ranges = gs101_pmu_registers, |
| .n_yes_ranges = ARRAY_SIZE(gs101_pmu_registers), |
| }; |
| |
| static const struct regmap_access_table gs101_pmu_wr_table = { |
| .yes_ranges = gs101_pmu_registers, |
| .n_yes_ranges = ARRAY_SIZE(gs101_pmu_registers), |
| .no_ranges = gs101_pmu_ro_registers, |
| .n_no_ranges = ARRAY_SIZE(gs101_pmu_ro_registers), |
| }; |
| |
| const struct exynos_pmu_data gs101_pmu_data = { |
| .pmu_secure = true, |
| .pmu_cpuhp = true, |
| .rd_table = &gs101_pmu_rd_table, |
| .wr_table = &gs101_pmu_wr_table, |
| }; |
| |
| /* |
| * Tensor SoCs are configured so that PMU_ALIVE registers can only be written |
| * from EL3, but are still read accessible. As Linux needs to write some of |
| * these registers, the following functions are provided and exposed via |
| * regmap. |
| * |
| * Note: This SMC interface is known to be implemented on gs101 and derivative |
| * SoCs. |
| */ |
| |
| /* Write to a protected PMU register. */ |
| int tensor_sec_reg_write(void *context, unsigned int reg, unsigned int val) |
| { |
| struct arm_smccc_res res; |
| unsigned long pmu_base = (unsigned long)context; |
| |
| arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, |
| TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res); |
| |
| /* returns -EINVAL if access isn't allowed or 0 */ |
| if (res.a0) |
| pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); |
| |
| return (int)res.a0; |
| } |
| |
| /* Read/Modify/Write a protected PMU register. */ |
| static int tensor_sec_reg_rmw(void *context, unsigned int reg, |
| unsigned int mask, unsigned int val) |
| { |
| struct arm_smccc_res res; |
| unsigned long pmu_base = (unsigned long)context; |
| |
| arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, |
| TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res); |
| |
| /* returns -EINVAL if access isn't allowed or 0 */ |
| if (res.a0) |
| pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); |
| |
| return (int)res.a0; |
| } |
| |
| /* |
| * Read a protected PMU register. All PMU registers can be read by Linux. |
| * Note: The SMC read register is not used, as only registers that can be |
| * written are readable via SMC. |
| */ |
| int tensor_sec_reg_read(void *context, unsigned int reg, unsigned int *val) |
| { |
| *val = pmu_raw_readl(reg); |
| return 0; |
| } |
| |
| /* |
| * For SoCs that have set/clear bit hardware this function can be used when |
| * the PMU register will be accessed by multiple masters. |
| * |
| * For example, to set bits 13:8 in PMU reg offset 0x3e80 |
| * tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00); |
| * |
| * Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80 |
| * tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00); |
| */ |
| static int tensor_set_bits_atomic(void *context, unsigned int offset, u32 val, |
| u32 mask) |
| { |
| int ret; |
| unsigned int i; |
| |
| for (i = 0; i < 32; i++) { |
| if (!(mask & BIT(i))) |
| continue; |
| |
| offset &= ~TENSOR_SET_BITS; |
| |
| if (val & BIT(i)) |
| offset |= TENSOR_SET_BITS; |
| else |
| offset |= TENSOR_CLR_BITS; |
| |
| ret = tensor_sec_reg_write(context, offset, i); |
| if (ret) |
| return ret; |
| } |
| return 0; |
| } |
| |
| static bool tensor_is_atomic(unsigned int reg) |
| { |
| /* |
| * Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF) |
| * as the target registers can be accessed by multiple masters. SFRs |
| * that don't support atomic are added to the switch statement below. |
| */ |
| if (reg > PMUALIVE_MASK) |
| return false; |
| |
| switch (reg) { |
| case GS101_SYSIP_DAT(0): |
| case GS101_SYSTEM_CONFIGURATION: |
| return false; |
| default: |
| return true; |
| } |
| } |
| |
| int tensor_sec_update_bits(void *context, unsigned int reg, unsigned int mask, |
| unsigned int val) |
| { |
| if (!tensor_is_atomic(reg)) |
| return tensor_sec_reg_rmw(context, reg, mask, val); |
| |
| return tensor_set_bits_atomic(context, reg, val, mask); |
| } |