// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ #include #include "rkpm_helpers.h" #include "rockchip_hptimer.h" /* hp timer regs */ #define TIMER_HP_REVISION 0x0 #define TIMER_HP_CTRL 0x4 #define TIMER_HP_INT_EN 0x8 #define TIMER_HP_T24_GCD 0xc #define TIMER_HP_T32_GCD 0x10 #define TIMER_HP_LOAD_COUNT0 0x14 #define TIMER_HP_LOAD_COUNT1 0x18 #define TIMER_HP_T24_DELAT_COUNT0 0x1c #define TIMER_HP_T24_DELAT_COUNT1 0x20 #define TIMER_HP_CURR_32K_VALUE0 0x24 #define TIMER_HP_CURR_32K_VALUE1 0x28 #define TIMER_HP_CURR_TIMER_VALUE0 0x2c #define TIMER_HP_CURR_TIMER_VALUE1 0x30 #define TIMER_HP_T24_32BEGIN0 0x34 #define TIMER_HP_T24_32BEGIN1 0x38 #define TIMER_HP_T32_24END0 0x3c #define TIMER_HP_T32_24END1 0x40 #define TIMER_HP_BEGIN_END_VALID 0x44 #define TIMER_HP_SYNC_REQ 0x48 #define TIMER_HP_INTR_STATUS 0x4c /* hptimer ctlr */ enum rk_hptimer_ctlr_reg { RK_HPTIMER_CTRL_EN = 0, RK_HPTIMER_CTRL_MODE = 1, RK_HPTIMER_CTRL_CNT_MODE = 3, }; /* hptimer int */ enum rk_hptimer_int_id_t { RK_HPTIMER_INT_REACH = 0, RK_HPTIMER_INT_ADJ_DONE = 1, RK_HPTIMER_INT_SYNC = 2, }; #define T24M_GCD 0xb71b #define T32K_GCD 0x40 #define HPTIMER_WAIT_MAX_US 1000000 static void rk_hptimer_clear_int_st(void __iomem *base, enum rk_hptimer_int_id_t id) { writel_relaxed(BIT(id), base + TIMER_HP_INTR_STATUS); } static int rk_hptimer_wait_int_st(void __iomem *base, enum rk_hptimer_int_id_t id, u64 wait_us) { while (!(readl_relaxed(base + TIMER_HP_INTR_STATUS) & BIT(id)) && --wait_us > 0) rkpm_raw_udelay(1); dsb(); if (wait_us == 0) { rkpm_printstr("can't wait hptimer int:"); rkpm_printdec(id); rkpm_printch('-'); rkpm_printhex(readl_relaxed(base + TIMER_HP_INTR_STATUS)); rkpm_printch('\n'); return -1; } else { return 0; } } static int rk_hptimer_wait_begin_end_valid(void __iomem *base, u64 wait_us) { while ((readl_relaxed(base + TIMER_HP_BEGIN_END_VALID) & 0x3) != 0x3 && --wait_us > 0) rkpm_raw_udelay(1); dsb(); if (wait_us == 0) { rkpm_printstr("can't wait hptimer begin_end valid:"); rkpm_printhex(readl_relaxed(base + TIMER_HP_BEGIN_END_VALID)); rkpm_printch('\n'); return -1; } else { return 0; } } static u64 rk_hptimer_get_soft_adjust_delt_cnt(void __iomem *base, u32 hf, u32 lf) { u64 begin, end, delt; u32 tmp; if (rk_hptimer_wait_begin_end_valid(base, HPTIMER_WAIT_MAX_US)) return 0; /* (T32_24END - T24_32BEGIN + 2) * (T24 - T32) / T32 + 2.5 * T24/T32 + 2 */ begin = (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN0) | (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN1) << 32; end = (u64)readl_relaxed(base + TIMER_HP_T32_24END0) | (u64)readl_relaxed(base + TIMER_HP_T32_24END1) << 32; delt = (end - begin + 2) * (hf - lf); delt = div_u64(delt, lf); tmp = (2 * hf + hf / 2) / lf; delt = delt + tmp + 2; writel_relaxed(0x3, base + TIMER_HP_BEGIN_END_VALID); return delt; } static void rk_hptimer_soft_adjust_req(void __iomem *base, u64 delt) { if (delt == 0) return; writel_relaxed(delt & 0xffffffff, base + TIMER_HP_T24_DELAT_COUNT0); writel_relaxed((delt >> 32) & 0xffffffff, base + TIMER_HP_T24_DELAT_COUNT1); dsb(); writel_relaxed(0x1, base + TIMER_HP_SYNC_REQ); dsb(); } int rk_hptimer_is_enabled(void __iomem *base) { return !!(readl_relaxed(base + TIMER_HP_CTRL) & BIT(RK_HPTIMER_CTRL_EN)); } int rk_hptimer_get_mode(void __iomem *base) { return (readl_relaxed(base + TIMER_HP_CTRL) >> RK_HPTIMER_CTRL_MODE) & 0x3; } u64 rk_hptimer_get_count(void __iomem *base) { u64 cnt; cnt = (u64)readl_relaxed(base + TIMER_HP_CURR_TIMER_VALUE0) | (u64)readl_relaxed(base + TIMER_HP_CURR_TIMER_VALUE1) << 32; return cnt; } int rk_hptimer_wait_mode(void __iomem *base, enum rk_hptimer_mode_t mode) { if (mode == RK_HPTIMER_NORM_MODE) return 0; if (mode == RK_HPTIMER_HARD_ADJUST_MODE) { /* wait adjust done if hard_adjust_mode */ if (rk_hptimer_wait_int_st(base, RK_HPTIMER_INT_ADJ_DONE, HPTIMER_WAIT_MAX_US)) return -1; rk_hptimer_clear_int_st(base, RK_HPTIMER_INT_ADJ_DONE); } else if (mode == RK_HPTIMER_SOFT_ADJUST_MODE) { /* wait 32k sync done */ if (rk_hptimer_wait_int_st(base, RK_HPTIMER_INT_SYNC, HPTIMER_WAIT_MAX_US)) return -1; rk_hptimer_clear_int_st(base, RK_HPTIMER_INT_SYNC); } return 0; } void rk_hptimer_do_soft_adjust(void __iomem *base, u32 hf, u32 lf) { u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base, hf, lf); rk_hptimer_soft_adjust_req(base, delt); rk_hptimer_wait_mode(base, RK_HPTIMER_SOFT_ADJUST_MODE); } void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base, u32 hf, u32 lf) { u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base, hf, lf); rk_hptimer_soft_adjust_req(base, delt); } void rk_hptimer_mode_init(void __iomem *base, enum rk_hptimer_mode_t mode) { u64 old_cnt = rk_hptimer_get_count(base); u32 val; writel_relaxed(0x0, base + TIMER_HP_CTRL); writel_relaxed(0x0, base + TIMER_HP_INT_EN); writel_relaxed(0x7, base + TIMER_HP_INTR_STATUS); writel_relaxed(0x3, base + TIMER_HP_BEGIN_END_VALID); writel_relaxed(0xffffffff, base + TIMER_HP_LOAD_COUNT0); writel_relaxed(0xffffffff, base + TIMER_HP_LOAD_COUNT1); /* config T24/T32 GCD if hard_adjust_mode */ if (mode == RK_HPTIMER_HARD_ADJUST_MODE) { writel_relaxed(T24M_GCD, base + TIMER_HP_T24_GCD); writel_relaxed(T32K_GCD, base + TIMER_HP_T32_GCD); } dsb(); if (mode != RK_HPTIMER_NORM_MODE) { writel_relaxed(0x7, base + TIMER_HP_INT_EN); writel_relaxed(mode << RK_HPTIMER_CTRL_MODE, base + TIMER_HP_CTRL); dsb(); } val = readl_relaxed(base + TIMER_HP_CTRL); writel_relaxed(val | BIT(RK_HPTIMER_CTRL_EN), base + TIMER_HP_CTRL); dsb(); /* compensate old_cnt to hptimer if soft_adjust_mode */ if (mode == RK_HPTIMER_SOFT_ADJUST_MODE) rk_hptimer_soft_adjust_req(base, old_cnt); if (rk_hptimer_wait_mode(base, mode)) pr_err("%s: can't wait hptimer mode:%d\n", __func__, mode); }