1*036935a8SXiaoDong Huang // SPDX-License-Identifier: BSD-3-Clause 2*036935a8SXiaoDong Huang /* 3*036935a8SXiaoDong Huang * Copyright (c) 2025, Rockchip Electronics Co., Ltd. 4*036935a8SXiaoDong Huang */ 5*036935a8SXiaoDong Huang 6*036935a8SXiaoDong Huang #include <assert.h> 7*036935a8SXiaoDong Huang #include <errno.h> 8*036935a8SXiaoDong Huang 9*036935a8SXiaoDong Huang #include <drivers/scmi.h> 10*036935a8SXiaoDong Huang #include <lib/mmio.h> 11*036935a8SXiaoDong Huang #include <platform_def.h> 12*036935a8SXiaoDong Huang 13*036935a8SXiaoDong Huang #include <plat_private.h> 14*036935a8SXiaoDong Huang #include <scmi_clock.h> 15*036935a8SXiaoDong Huang 16*036935a8SXiaoDong Huang #define MUX_ADDR_INFO 0 17*036935a8SXiaoDong Huang #define MUX_SHIFT_INFO 1 18*036935a8SXiaoDong Huang #define MUX_WIDTH_INFO 2 19*036935a8SXiaoDong Huang #define DIV_ADDR_INFO 3 20*036935a8SXiaoDong Huang #define DIV_SHIFT_INFO 4 21*036935a8SXiaoDong Huang #define DIV_WIDTH_INFO 5 22*036935a8SXiaoDong Huang #define GATE_ADDR_INFO 6 23*036935a8SXiaoDong Huang #define GATE_SHIFT_INFO 7 24*036935a8SXiaoDong Huang 25*036935a8SXiaoDong Huang #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 26*036935a8SXiaoDong Huang 27*036935a8SXiaoDong Huang #define abs(x) ({ \ 28*036935a8SXiaoDong Huang long ret; \ 29*036935a8SXiaoDong Huang if (sizeof(x) == sizeof(long)) { \ 30*036935a8SXiaoDong Huang long __x = (x); \ 31*036935a8SXiaoDong Huang ret = (__x < 0) ? -__x : __x; \ 32*036935a8SXiaoDong Huang } else { \ 33*036935a8SXiaoDong Huang int __x = (x); \ 34*036935a8SXiaoDong Huang ret = (__x < 0) ? -__x : __x; \ 35*036935a8SXiaoDong Huang } \ 36*036935a8SXiaoDong Huang ret; \ 37*036935a8SXiaoDong Huang }) 38*036935a8SXiaoDong Huang 39*036935a8SXiaoDong Huang static unsigned long clk_scmi_common_get_parent_rate(rk_scmi_clock_t *clock, 40*036935a8SXiaoDong Huang int id) 41*036935a8SXiaoDong Huang { 42*036935a8SXiaoDong Huang rk_scmi_clock_t *p_clock; 43*036935a8SXiaoDong Huang 44*036935a8SXiaoDong Huang if (clock->is_dynamic_prate != 0) { 45*036935a8SXiaoDong Huang p_clock = rockchip_scmi_get_clock(0, clock->parent_table[id]); 46*036935a8SXiaoDong Huang if (p_clock == NULL) 47*036935a8SXiaoDong Huang return 0; 48*036935a8SXiaoDong Huang if ((p_clock->clk_ops != NULL) && (p_clock->clk_ops->get_rate != NULL)) 49*036935a8SXiaoDong Huang return p_clock->clk_ops->get_rate(p_clock); 50*036935a8SXiaoDong Huang else 51*036935a8SXiaoDong Huang return 0; 52*036935a8SXiaoDong Huang } else { 53*036935a8SXiaoDong Huang return clock->parent_table[id]; 54*036935a8SXiaoDong Huang } 55*036935a8SXiaoDong Huang } 56*036935a8SXiaoDong Huang 57*036935a8SXiaoDong Huang unsigned long clk_scmi_common_get_rate(rk_scmi_clock_t *clock) 58*036935a8SXiaoDong Huang { 59*036935a8SXiaoDong Huang unsigned long parent_rate, sel, div; 60*036935a8SXiaoDong Huang 61*036935a8SXiaoDong Huang sel = mmio_read_32(clock->info[MUX_ADDR_INFO]) >> 62*036935a8SXiaoDong Huang clock->info[MUX_SHIFT_INFO]; 63*036935a8SXiaoDong Huang sel = sel & (BIT(clock->info[MUX_WIDTH_INFO]) - 1); 64*036935a8SXiaoDong Huang div = mmio_read_32(clock->info[DIV_ADDR_INFO]) >> 65*036935a8SXiaoDong Huang clock->info[DIV_SHIFT_INFO]; 66*036935a8SXiaoDong Huang div = div & (BIT(clock->info[DIV_WIDTH_INFO]) - 1); 67*036935a8SXiaoDong Huang parent_rate = clk_scmi_common_get_parent_rate(clock, sel); 68*036935a8SXiaoDong Huang 69*036935a8SXiaoDong Huang return parent_rate / (div + 1); 70*036935a8SXiaoDong Huang } 71*036935a8SXiaoDong Huang 72*036935a8SXiaoDong Huang int clk_scmi_common_set_rate(rk_scmi_clock_t *clock, unsigned long rate) 73*036935a8SXiaoDong Huang { 74*036935a8SXiaoDong Huang unsigned long parent_rate, now, best_rate = 0; 75*036935a8SXiaoDong Huang int i = 0, sel_mask, div_mask, best_sel = 0, best_div = 0, div; 76*036935a8SXiaoDong Huang 77*036935a8SXiaoDong Huang if ((rate == 0) || 78*036935a8SXiaoDong Huang (clock->info[MUX_WIDTH_INFO] == 0 && clock->info[DIV_WIDTH_INFO] == 0)) 79*036935a8SXiaoDong Huang return SCMI_INVALID_PARAMETERS; 80*036935a8SXiaoDong Huang 81*036935a8SXiaoDong Huang sel_mask = BIT(clock->info[MUX_WIDTH_INFO]) - 1; 82*036935a8SXiaoDong Huang div_mask = BIT(clock->info[DIV_WIDTH_INFO]) - 1; 83*036935a8SXiaoDong Huang if (clock->info[MUX_WIDTH_INFO] == 0) { 84*036935a8SXiaoDong Huang parent_rate = clk_scmi_common_get_parent_rate(clock, 0); 85*036935a8SXiaoDong Huang div = DIV_ROUND_UP(parent_rate, rate); 86*036935a8SXiaoDong Huang if (div > div_mask + 1) 87*036935a8SXiaoDong Huang div = div_mask + 1; 88*036935a8SXiaoDong Huang mmio_write_32(clock->info[DIV_ADDR_INFO], 89*036935a8SXiaoDong Huang BITS_WITH_WMASK(div - 1, div_mask, 90*036935a8SXiaoDong Huang clock->info[DIV_SHIFT_INFO])); 91*036935a8SXiaoDong Huang } else if (clock->info[DIV_WIDTH_INFO] == 0) { 92*036935a8SXiaoDong Huang for (i = 0; i <= sel_mask; i++) { 93*036935a8SXiaoDong Huang parent_rate = clk_scmi_common_get_parent_rate(clock, i); 94*036935a8SXiaoDong Huang now = parent_rate; 95*036935a8SXiaoDong Huang if (abs(rate - now) < abs(rate - best_rate)) { 96*036935a8SXiaoDong Huang best_rate = now; 97*036935a8SXiaoDong Huang best_sel = i; 98*036935a8SXiaoDong Huang } 99*036935a8SXiaoDong Huang } 100*036935a8SXiaoDong Huang if (best_rate == 0) 101*036935a8SXiaoDong Huang best_sel = 0; 102*036935a8SXiaoDong Huang mmio_write_32(clock->info[MUX_ADDR_INFO], 103*036935a8SXiaoDong Huang BITS_WITH_WMASK(best_sel, sel_mask, 104*036935a8SXiaoDong Huang clock->info[MUX_SHIFT_INFO])); 105*036935a8SXiaoDong Huang } else { 106*036935a8SXiaoDong Huang for (i = 0; i <= sel_mask; i++) { 107*036935a8SXiaoDong Huang parent_rate = clk_scmi_common_get_parent_rate(clock, i); 108*036935a8SXiaoDong Huang div = DIV_ROUND_UP(parent_rate, rate); 109*036935a8SXiaoDong Huang if (div > div_mask + 1) 110*036935a8SXiaoDong Huang div = div_mask + 1; 111*036935a8SXiaoDong Huang now = parent_rate / div; 112*036935a8SXiaoDong Huang if (abs(rate - now) < abs(rate - best_rate)) { 113*036935a8SXiaoDong Huang best_rate = now; 114*036935a8SXiaoDong Huang best_div = div; 115*036935a8SXiaoDong Huang best_sel = i; 116*036935a8SXiaoDong Huang } 117*036935a8SXiaoDong Huang } 118*036935a8SXiaoDong Huang if (best_rate == 0) { 119*036935a8SXiaoDong Huang best_div = div_mask + 1; 120*036935a8SXiaoDong Huang best_sel = 0; 121*036935a8SXiaoDong Huang } 122*036935a8SXiaoDong Huang 123*036935a8SXiaoDong Huang mmio_write_32(clock->info[DIV_ADDR_INFO], 124*036935a8SXiaoDong Huang BITS_WITH_WMASK(div_mask, div_mask, 125*036935a8SXiaoDong Huang clock->info[DIV_SHIFT_INFO])); 126*036935a8SXiaoDong Huang mmio_write_32(clock->info[MUX_ADDR_INFO], 127*036935a8SXiaoDong Huang BITS_WITH_WMASK(best_sel, sel_mask, 128*036935a8SXiaoDong Huang clock->info[MUX_SHIFT_INFO])); 129*036935a8SXiaoDong Huang mmio_write_32(clock->info[DIV_ADDR_INFO], 130*036935a8SXiaoDong Huang BITS_WITH_WMASK(best_div - 1, div_mask, 131*036935a8SXiaoDong Huang clock->info[DIV_SHIFT_INFO])); 132*036935a8SXiaoDong Huang } 133*036935a8SXiaoDong Huang return 0; 134*036935a8SXiaoDong Huang } 135*036935a8SXiaoDong Huang 136*036935a8SXiaoDong Huang int clk_scmi_common_set_status(rk_scmi_clock_t *clock, bool status) 137*036935a8SXiaoDong Huang { 138*036935a8SXiaoDong Huang mmio_write_32(clock->info[GATE_ADDR_INFO], 139*036935a8SXiaoDong Huang BITS_WITH_WMASK(!status, 0x1U, 140*036935a8SXiaoDong Huang clock->info[GATE_SHIFT_INFO])); 141*036935a8SXiaoDong Huang 142*036935a8SXiaoDong Huang return 0; 143*036935a8SXiaoDong Huang } 144