18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020-2022 MaxLinear, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Intel Corporation. 58c2ecf20Sopenharmony_ci * Zhu Yixin <yzhu@maxlinear.com> 68c2ecf20Sopenharmony_ci * Rahul Tanwar <rtanwar@maxlinear.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "clk-cgu.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define GATE_HW_REG_STAT(reg) ((reg) + 0x0) 158c2ecf20Sopenharmony_ci#define GATE_HW_REG_EN(reg) ((reg) + 0x4) 168c2ecf20Sopenharmony_ci#define GATE_HW_REG_DIS(reg) ((reg) + 0x8) 178c2ecf20Sopenharmony_ci#define MAX_DDIV_REG 8 188c2ecf20Sopenharmony_ci#define MAX_DIVIDER_VAL 64 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define to_lgm_clk_mux(_hw) container_of(_hw, struct lgm_clk_mux, hw) 218c2ecf20Sopenharmony_ci#define to_lgm_clk_divider(_hw) container_of(_hw, struct lgm_clk_divider, hw) 228c2ecf20Sopenharmony_ci#define to_lgm_clk_gate(_hw) container_of(_hw, struct lgm_clk_gate, hw) 238c2ecf20Sopenharmony_ci#define to_lgm_clk_ddiv(_hw) container_of(_hw, struct lgm_clk_ddiv, hw) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct clk_hw *lgm_clk_register_fixed(struct lgm_clk_provider *ctx, 268c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (list->div_flags & CLOCK_FLAG_VAL_INIT) 308c2ecf20Sopenharmony_ci lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift, 318c2ecf20Sopenharmony_ci list->div_width, list->div_val); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return clk_hw_register_fixed_rate(NULL, list->name, 348c2ecf20Sopenharmony_ci list->parent_data[0].name, 358c2ecf20Sopenharmony_ci list->flags, list->mux_flags); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u8 lgm_clk_mux_get_parent(struct clk_hw *hw) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 418c2ecf20Sopenharmony_ci u32 val; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (mux->flags & MUX_CLK_SW) 448c2ecf20Sopenharmony_ci val = mux->reg; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci val = lgm_get_clk_val(mux->membase, mux->reg, mux->shift, 478c2ecf20Sopenharmony_ci mux->width); 488c2ecf20Sopenharmony_ci return clk_mux_val_to_index(hw, NULL, mux->flags, val); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int lgm_clk_mux_set_parent(struct clk_hw *hw, u8 index) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 548c2ecf20Sopenharmony_ci u32 val; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci val = clk_mux_index_to_val(NULL, mux->flags, index); 578c2ecf20Sopenharmony_ci if (mux->flags & MUX_CLK_SW) 588c2ecf20Sopenharmony_ci mux->reg = val; 598c2ecf20Sopenharmony_ci else 608c2ecf20Sopenharmony_ci lgm_set_clk_val(mux->membase, mux->reg, mux->shift, 618c2ecf20Sopenharmony_ci mux->width, val); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int lgm_clk_mux_determine_rate(struct clk_hw *hw, 678c2ecf20Sopenharmony_ci struct clk_rate_request *req) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return clk_mux_determine_rate_flags(hw, req, mux->flags); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic const struct clk_ops lgm_clk_mux_ops = { 758c2ecf20Sopenharmony_ci .get_parent = lgm_clk_mux_get_parent, 768c2ecf20Sopenharmony_ci .set_parent = lgm_clk_mux_set_parent, 778c2ecf20Sopenharmony_ci .determine_rate = lgm_clk_mux_determine_rate, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic struct clk_hw * 818c2ecf20Sopenharmony_cilgm_clk_register_mux(struct lgm_clk_provider *ctx, 828c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci unsigned long cflags = list->mux_flags; 858c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 868c2ecf20Sopenharmony_ci u8 shift = list->mux_shift; 878c2ecf20Sopenharmony_ci u8 width = list->mux_width; 888c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 898c2ecf20Sopenharmony_ci struct lgm_clk_mux *mux; 908c2ecf20Sopenharmony_ci u32 reg = list->mux_off; 918c2ecf20Sopenharmony_ci struct clk_hw *hw; 928c2ecf20Sopenharmony_ci int ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 958c2ecf20Sopenharmony_ci if (!mux) 968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci init.name = list->name; 998c2ecf20Sopenharmony_ci init.ops = &lgm_clk_mux_ops; 1008c2ecf20Sopenharmony_ci init.flags = list->flags; 1018c2ecf20Sopenharmony_ci init.parent_data = list->parent_data; 1028c2ecf20Sopenharmony_ci init.num_parents = list->num_parents; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci mux->membase = ctx->membase; 1058c2ecf20Sopenharmony_ci mux->reg = reg; 1068c2ecf20Sopenharmony_ci mux->shift = shift; 1078c2ecf20Sopenharmony_ci mux->width = width; 1088c2ecf20Sopenharmony_ci mux->flags = cflags; 1098c2ecf20Sopenharmony_ci mux->hw.init = &init; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci hw = &mux->hw; 1128c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, hw); 1138c2ecf20Sopenharmony_ci if (ret) 1148c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (cflags & CLOCK_FLAG_VAL_INIT) 1178c2ecf20Sopenharmony_ci lgm_set_clk_val(mux->membase, reg, shift, width, list->mux_val); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return hw; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic unsigned long 1238c2ecf20Sopenharmony_cilgm_clk_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 1268c2ecf20Sopenharmony_ci unsigned int val; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci val = lgm_get_clk_val(divider->membase, divider->reg, 1298c2ecf20Sopenharmony_ci divider->shift, divider->width); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return divider_recalc_rate(hw, parent_rate, val, divider->table, 1328c2ecf20Sopenharmony_ci divider->flags, divider->width); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic long 1368c2ecf20Sopenharmony_cilgm_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, 1378c2ecf20Sopenharmony_ci unsigned long *prate) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return divider_round_rate(hw, rate, prate, divider->table, 1428c2ecf20Sopenharmony_ci divider->width, divider->flags); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int 1468c2ecf20Sopenharmony_cilgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, 1478c2ecf20Sopenharmony_ci unsigned long prate) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 1508c2ecf20Sopenharmony_ci int value; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci value = divider_get_val(rate, prate, divider->table, 1538c2ecf20Sopenharmony_ci divider->width, divider->flags); 1548c2ecf20Sopenharmony_ci if (value < 0) 1558c2ecf20Sopenharmony_ci return value; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci lgm_set_clk_val(divider->membase, divider->reg, 1588c2ecf20Sopenharmony_ci divider->shift, divider->width, value); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int lgm_clk_divider_enable_disable(struct clk_hw *hw, int enable) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct lgm_clk_divider *div = to_lgm_clk_divider(hw); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (div->flags != DIV_CLK_NO_MASK) 1688c2ecf20Sopenharmony_ci lgm_set_clk_val(div->membase, div->reg, div->shift_gate, 1698c2ecf20Sopenharmony_ci div->width_gate, enable); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int lgm_clk_divider_enable(struct clk_hw *hw) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci return lgm_clk_divider_enable_disable(hw, 1); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void lgm_clk_divider_disable(struct clk_hw *hw) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci lgm_clk_divider_enable_disable(hw, 0); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const struct clk_ops lgm_clk_divider_ops = { 1848c2ecf20Sopenharmony_ci .recalc_rate = lgm_clk_divider_recalc_rate, 1858c2ecf20Sopenharmony_ci .round_rate = lgm_clk_divider_round_rate, 1868c2ecf20Sopenharmony_ci .set_rate = lgm_clk_divider_set_rate, 1878c2ecf20Sopenharmony_ci .enable = lgm_clk_divider_enable, 1888c2ecf20Sopenharmony_ci .disable = lgm_clk_divider_disable, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic struct clk_hw * 1928c2ecf20Sopenharmony_cilgm_clk_register_divider(struct lgm_clk_provider *ctx, 1938c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned long cflags = list->div_flags; 1968c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 1978c2ecf20Sopenharmony_ci struct lgm_clk_divider *div; 1988c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 1998c2ecf20Sopenharmony_ci u8 shift = list->div_shift; 2008c2ecf20Sopenharmony_ci u8 width = list->div_width; 2018c2ecf20Sopenharmony_ci u8 shift_gate = list->div_shift_gate; 2028c2ecf20Sopenharmony_ci u8 width_gate = list->div_width_gate; 2038c2ecf20Sopenharmony_ci u32 reg = list->div_off; 2048c2ecf20Sopenharmony_ci struct clk_hw *hw; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); 2088c2ecf20Sopenharmony_ci if (!div) 2098c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci init.name = list->name; 2128c2ecf20Sopenharmony_ci init.ops = &lgm_clk_divider_ops; 2138c2ecf20Sopenharmony_ci init.flags = list->flags; 2148c2ecf20Sopenharmony_ci init.parent_data = list->parent_data; 2158c2ecf20Sopenharmony_ci init.num_parents = 1; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci div->membase = ctx->membase; 2188c2ecf20Sopenharmony_ci div->reg = reg; 2198c2ecf20Sopenharmony_ci div->shift = shift; 2208c2ecf20Sopenharmony_ci div->width = width; 2218c2ecf20Sopenharmony_ci div->shift_gate = shift_gate; 2228c2ecf20Sopenharmony_ci div->width_gate = width_gate; 2238c2ecf20Sopenharmony_ci div->flags = cflags; 2248c2ecf20Sopenharmony_ci div->table = list->div_table; 2258c2ecf20Sopenharmony_ci div->hw.init = &init; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci hw = &div->hw; 2288c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, hw); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (cflags & CLOCK_FLAG_VAL_INIT) 2338c2ecf20Sopenharmony_ci lgm_set_clk_val(div->membase, reg, shift, width, list->div_val); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return hw; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct clk_hw * 2398c2ecf20Sopenharmony_cilgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx, 2408c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct clk_hw *hw; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci hw = clk_hw_register_fixed_factor(ctx->dev, list->name, 2458c2ecf20Sopenharmony_ci list->parent_data[0].name, list->flags, 2468c2ecf20Sopenharmony_ci list->mult, list->div); 2478c2ecf20Sopenharmony_ci if (IS_ERR(hw)) 2488c2ecf20Sopenharmony_ci return ERR_CAST(hw); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (list->div_flags & CLOCK_FLAG_VAL_INIT) 2518c2ecf20Sopenharmony_ci lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift, 2528c2ecf20Sopenharmony_ci list->div_width, list->div_val); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return hw; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int lgm_clk_gate_enable(struct clk_hw *hw) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 2608c2ecf20Sopenharmony_ci unsigned int reg; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci reg = GATE_HW_REG_EN(gate->reg); 2638c2ecf20Sopenharmony_ci lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void lgm_clk_gate_disable(struct clk_hw *hw) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 2718c2ecf20Sopenharmony_ci unsigned int reg; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci reg = GATE_HW_REG_DIS(gate->reg); 2748c2ecf20Sopenharmony_ci lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int lgm_clk_gate_is_enabled(struct clk_hw *hw) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 2808c2ecf20Sopenharmony_ci unsigned int reg, ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci reg = GATE_HW_REG_STAT(gate->reg); 2838c2ecf20Sopenharmony_ci ret = lgm_get_clk_val(gate->membase, reg, gate->shift, 1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const struct clk_ops lgm_clk_gate_ops = { 2898c2ecf20Sopenharmony_ci .enable = lgm_clk_gate_enable, 2908c2ecf20Sopenharmony_ci .disable = lgm_clk_gate_disable, 2918c2ecf20Sopenharmony_ci .is_enabled = lgm_clk_gate_is_enabled, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct clk_hw * 2958c2ecf20Sopenharmony_cilgm_clk_register_gate(struct lgm_clk_provider *ctx, 2968c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci unsigned long cflags = list->gate_flags; 2998c2ecf20Sopenharmony_ci const char *pname = list->parent_data[0].name; 3008c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 3018c2ecf20Sopenharmony_ci u8 shift = list->gate_shift; 3028c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 3038c2ecf20Sopenharmony_ci struct lgm_clk_gate *gate; 3048c2ecf20Sopenharmony_ci u32 reg = list->gate_off; 3058c2ecf20Sopenharmony_ci struct clk_hw *hw; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); 3098c2ecf20Sopenharmony_ci if (!gate) 3108c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci init.name = list->name; 3138c2ecf20Sopenharmony_ci init.ops = &lgm_clk_gate_ops; 3148c2ecf20Sopenharmony_ci init.flags = list->flags; 3158c2ecf20Sopenharmony_ci init.parent_names = pname ? &pname : NULL; 3168c2ecf20Sopenharmony_ci init.num_parents = pname ? 1 : 0; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci gate->membase = ctx->membase; 3198c2ecf20Sopenharmony_ci gate->reg = reg; 3208c2ecf20Sopenharmony_ci gate->shift = shift; 3218c2ecf20Sopenharmony_ci gate->flags = cflags; 3228c2ecf20Sopenharmony_ci gate->hw.init = &init; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci hw = &gate->hw; 3258c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, hw); 3268c2ecf20Sopenharmony_ci if (ret) 3278c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (cflags & CLOCK_FLAG_VAL_INIT) { 3308c2ecf20Sopenharmony_ci lgm_set_clk_val(gate->membase, reg, shift, 1, list->gate_val); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return hw; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ciint lgm_clk_register_branches(struct lgm_clk_provider *ctx, 3378c2ecf20Sopenharmony_ci const struct lgm_clk_branch *list, 3388c2ecf20Sopenharmony_ci unsigned int nr_clk) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct clk_hw *hw; 3418c2ecf20Sopenharmony_ci unsigned int idx; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci for (idx = 0; idx < nr_clk; idx++, list++) { 3448c2ecf20Sopenharmony_ci switch (list->type) { 3458c2ecf20Sopenharmony_ci case CLK_TYPE_FIXED: 3468c2ecf20Sopenharmony_ci hw = lgm_clk_register_fixed(ctx, list); 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci case CLK_TYPE_MUX: 3498c2ecf20Sopenharmony_ci hw = lgm_clk_register_mux(ctx, list); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case CLK_TYPE_DIVIDER: 3528c2ecf20Sopenharmony_ci hw = lgm_clk_register_divider(ctx, list); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case CLK_TYPE_FIXED_FACTOR: 3558c2ecf20Sopenharmony_ci hw = lgm_clk_register_fixed_factor(ctx, list); 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci case CLK_TYPE_GATE: 3588c2ecf20Sopenharmony_ci if (list->gate_flags & GATE_CLK_HW) { 3598c2ecf20Sopenharmony_ci hw = lgm_clk_register_gate(ctx, list); 3608c2ecf20Sopenharmony_ci } else { 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * GATE_CLKs can be controlled either from 3638c2ecf20Sopenharmony_ci * CGU clk driver i.e. this driver or directly 3648c2ecf20Sopenharmony_ci * from power management driver/daemon. It is 3658c2ecf20Sopenharmony_ci * dependent on the power policy/profile requirements 3668c2ecf20Sopenharmony_ci * of the end product. To override control of gate 3678c2ecf20Sopenharmony_ci * clks from this driver, provide NULL for this index 3688c2ecf20Sopenharmony_ci * of gate clk provider. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci hw = NULL; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci dev_err(ctx->dev, "invalid clk type\n"); 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 3808c2ecf20Sopenharmony_ci dev_err(ctx->dev, 3818c2ecf20Sopenharmony_ci "register clk: %s, type: %u failed!\n", 3828c2ecf20Sopenharmony_ci list->name, list->type); 3838c2ecf20Sopenharmony_ci return -EIO; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci ctx->clk_data.hws[list->id] = hw; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic unsigned long 3928c2ecf20Sopenharmony_cilgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 3958c2ecf20Sopenharmony_ci unsigned int div0, div1, exdiv; 3968c2ecf20Sopenharmony_ci u64 prate; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci div0 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 3998c2ecf20Sopenharmony_ci ddiv->shift0, ddiv->width0) + 1; 4008c2ecf20Sopenharmony_ci div1 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 4018c2ecf20Sopenharmony_ci ddiv->shift1, ddiv->width1) + 1; 4028c2ecf20Sopenharmony_ci exdiv = lgm_get_clk_val(ddiv->membase, ddiv->reg, 4038c2ecf20Sopenharmony_ci ddiv->shift2, ddiv->width2); 4048c2ecf20Sopenharmony_ci prate = (u64)parent_rate; 4058c2ecf20Sopenharmony_ci do_div(prate, div0); 4068c2ecf20Sopenharmony_ci do_div(prate, div1); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (exdiv) { 4098c2ecf20Sopenharmony_ci do_div(prate, ddiv->div); 4108c2ecf20Sopenharmony_ci prate *= ddiv->mult; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return prate; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int lgm_clk_ddiv_enable(struct clk_hw *hw) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 4218c2ecf20Sopenharmony_ci ddiv->width_gate, 1); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void lgm_clk_ddiv_disable(struct clk_hw *hw) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 4308c2ecf20Sopenharmony_ci ddiv->width_gate, 0); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int 4348c2ecf20Sopenharmony_cilgm_clk_get_ddiv_val(u32 div, u32 *ddiv1, u32 *ddiv2) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci u32 idx, temp; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci *ddiv1 = 1; 4398c2ecf20Sopenharmony_ci *ddiv2 = 1; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (div > MAX_DIVIDER_VAL) 4428c2ecf20Sopenharmony_ci div = MAX_DIVIDER_VAL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (div > 1) { 4458c2ecf20Sopenharmony_ci for (idx = 2; idx <= MAX_DDIV_REG; idx++) { 4468c2ecf20Sopenharmony_ci temp = DIV_ROUND_UP_ULL((u64)div, idx); 4478c2ecf20Sopenharmony_ci if (div % idx == 0 && temp <= MAX_DDIV_REG) 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (idx > MAX_DDIV_REG) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci *ddiv1 = temp; 4558c2ecf20Sopenharmony_ci *ddiv2 = idx; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int 4628c2ecf20Sopenharmony_cilgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, 4638c2ecf20Sopenharmony_ci unsigned long prate) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 4668c2ecf20Sopenharmony_ci u32 div, ddiv1, ddiv2; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 4718c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 4728c2ecf20Sopenharmony_ci div = div * 2; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (div <= 0) 4768c2ecf20Sopenharmony_ci return -EINVAL; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2)) 4798c2ecf20Sopenharmony_ci return -EINVAL; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0, 4828c2ecf20Sopenharmony_ci ddiv1 - 1); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift1, ddiv->width1, 4858c2ecf20Sopenharmony_ci ddiv2 - 1); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic long 4918c2ecf20Sopenharmony_cilgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate, 4928c2ecf20Sopenharmony_ci unsigned long *prate) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 4958c2ecf20Sopenharmony_ci u32 div, ddiv1, ddiv2; 4968c2ecf20Sopenharmony_ci u64 rate64; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST_ULL((u64)*prate, rate); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* if predivide bit is enabled, modify div by factor of 2.5 */ 5018c2ecf20Sopenharmony_ci if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 5028c2ecf20Sopenharmony_ci div = div * 2; 5038c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (div <= 0) 5078c2ecf20Sopenharmony_ci return *prate; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2) != 0) 5108c2ecf20Sopenharmony_ci if (lgm_clk_get_ddiv_val(div + 1, &ddiv1, &ddiv2) != 0) 5118c2ecf20Sopenharmony_ci return -EINVAL; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci rate64 = *prate; 5148c2ecf20Sopenharmony_ci do_div(rate64, ddiv1); 5158c2ecf20Sopenharmony_ci do_div(rate64, ddiv2); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* if predivide bit is enabled, modify rounded rate by factor of 2.5 */ 5188c2ecf20Sopenharmony_ci if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 5198c2ecf20Sopenharmony_ci rate64 = rate64 * 2; 5208c2ecf20Sopenharmony_ci rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return rate64; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic const struct clk_ops lgm_clk_ddiv_ops = { 5278c2ecf20Sopenharmony_ci .recalc_rate = lgm_clk_ddiv_recalc_rate, 5288c2ecf20Sopenharmony_ci .enable = lgm_clk_ddiv_enable, 5298c2ecf20Sopenharmony_ci .disable = lgm_clk_ddiv_disable, 5308c2ecf20Sopenharmony_ci .set_rate = lgm_clk_ddiv_set_rate, 5318c2ecf20Sopenharmony_ci .round_rate = lgm_clk_ddiv_round_rate, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciint lgm_clk_register_ddiv(struct lgm_clk_provider *ctx, 5358c2ecf20Sopenharmony_ci const struct lgm_clk_ddiv_data *list, 5368c2ecf20Sopenharmony_ci unsigned int nr_clk) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 5398c2ecf20Sopenharmony_ci struct clk_hw *hw; 5408c2ecf20Sopenharmony_ci unsigned int idx; 5418c2ecf20Sopenharmony_ci int ret; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci for (idx = 0; idx < nr_clk; idx++, list++) { 5448c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 5458c2ecf20Sopenharmony_ci struct lgm_clk_ddiv *ddiv; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci ddiv = devm_kzalloc(dev, sizeof(*ddiv), GFP_KERNEL); 5488c2ecf20Sopenharmony_ci if (!ddiv) 5498c2ecf20Sopenharmony_ci return -ENOMEM; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci init.name = list->name; 5528c2ecf20Sopenharmony_ci init.ops = &lgm_clk_ddiv_ops; 5538c2ecf20Sopenharmony_ci init.flags = list->flags; 5548c2ecf20Sopenharmony_ci init.parent_data = list->parent_data; 5558c2ecf20Sopenharmony_ci init.num_parents = 1; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ddiv->membase = ctx->membase; 5588c2ecf20Sopenharmony_ci ddiv->reg = list->reg; 5598c2ecf20Sopenharmony_ci ddiv->shift0 = list->shift0; 5608c2ecf20Sopenharmony_ci ddiv->width0 = list->width0; 5618c2ecf20Sopenharmony_ci ddiv->shift1 = list->shift1; 5628c2ecf20Sopenharmony_ci ddiv->width1 = list->width1; 5638c2ecf20Sopenharmony_ci ddiv->shift_gate = list->shift_gate; 5648c2ecf20Sopenharmony_ci ddiv->width_gate = list->width_gate; 5658c2ecf20Sopenharmony_ci ddiv->shift2 = list->ex_shift; 5668c2ecf20Sopenharmony_ci ddiv->width2 = list->ex_width; 5678c2ecf20Sopenharmony_ci ddiv->flags = list->div_flags; 5688c2ecf20Sopenharmony_ci ddiv->mult = 2; 5698c2ecf20Sopenharmony_ci ddiv->div = 5; 5708c2ecf20Sopenharmony_ci ddiv->hw.init = &init; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci hw = &ddiv->hw; 5738c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, hw); 5748c2ecf20Sopenharmony_ci if (ret) { 5758c2ecf20Sopenharmony_ci dev_err(dev, "register clk: %s failed!\n", list->name); 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci ctx->clk_data.hws[list->id] = hw; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 583