// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2025 Linaro Ltd. * * GS101 PMU (Power Management Unit) support */ #include #include #include #include #include #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); }