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
clk_scmi_common_get_parent_rate(rk_scmi_clock_t * clock,int id)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
clk_scmi_common_get_rate(rk_scmi_clock_t * clock)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
clk_scmi_common_set_rate(rk_scmi_clock_t * clock,unsigned long rate)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
clk_scmi_common_set_status(rk_scmi_clock_t * clock,bool status)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