162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/bitops.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/bug.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/clk-provider.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/rational.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/math64.h> 1662306a36Sopenharmony_ci#include <linux/minmax.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/div64.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "clk-rcg.h" 2262306a36Sopenharmony_ci#include "common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define CMD_REG 0x0 2562306a36Sopenharmony_ci#define CMD_UPDATE BIT(0) 2662306a36Sopenharmony_ci#define CMD_ROOT_EN BIT(1) 2762306a36Sopenharmony_ci#define CMD_DIRTY_CFG BIT(4) 2862306a36Sopenharmony_ci#define CMD_DIRTY_N BIT(5) 2962306a36Sopenharmony_ci#define CMD_DIRTY_M BIT(6) 3062306a36Sopenharmony_ci#define CMD_DIRTY_D BIT(7) 3162306a36Sopenharmony_ci#define CMD_ROOT_OFF BIT(31) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CFG_REG 0x4 3462306a36Sopenharmony_ci#define CFG_SRC_DIV_SHIFT 0 3562306a36Sopenharmony_ci#define CFG_SRC_SEL_SHIFT 8 3662306a36Sopenharmony_ci#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT) 3762306a36Sopenharmony_ci#define CFG_MODE_SHIFT 12 3862306a36Sopenharmony_ci#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT) 3962306a36Sopenharmony_ci#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT) 4062306a36Sopenharmony_ci#define CFG_HW_CLK_CTRL_MASK BIT(20) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define M_REG 0x8 4362306a36Sopenharmony_ci#define N_REG 0xc 4462306a36Sopenharmony_ci#define D_REG 0x10 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define RCG_CFG_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + CFG_REG) 4762306a36Sopenharmony_ci#define RCG_M_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + M_REG) 4862306a36Sopenharmony_ci#define RCG_N_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + N_REG) 4962306a36Sopenharmony_ci#define RCG_D_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + D_REG) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Dynamic Frequency Scaling */ 5262306a36Sopenharmony_ci#define MAX_PERF_LEVEL 8 5362306a36Sopenharmony_ci#define SE_CMD_DFSR_OFFSET 0x14 5462306a36Sopenharmony_ci#define SE_CMD_DFS_EN BIT(0) 5562306a36Sopenharmony_ci#define SE_PERF_DFSR(level) (0x1c + 0x4 * (level)) 5662306a36Sopenharmony_ci#define SE_PERF_M_DFSR(level) (0x5c + 0x4 * (level)) 5762306a36Sopenharmony_ci#define SE_PERF_N_DFSR(level) (0x9c + 0x4 * (level)) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cienum freq_policy { 6062306a36Sopenharmony_ci FLOOR, 6162306a36Sopenharmony_ci CEIL, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int clk_rcg2_is_enabled(struct clk_hw *hw) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 6762306a36Sopenharmony_ci u32 cmd; 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); 7162306a36Sopenharmony_ci if (ret) 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return (cmd & CMD_ROOT_OFF) == 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic u8 __clk_rcg2_get_parent(struct clk_hw *hw, u32 cfg) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 8062306a36Sopenharmony_ci int num_parents = clk_hw_get_num_parents(hw); 8162306a36Sopenharmony_ci int i; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci cfg &= CFG_SRC_SEL_MASK; 8462306a36Sopenharmony_ci cfg >>= CFG_SRC_SEL_SHIFT; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci for (i = 0; i < num_parents; i++) 8762306a36Sopenharmony_ci if (cfg == rcg->parent_map[i].cfg) 8862306a36Sopenharmony_ci return i; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci pr_debug("%s: Clock %s has invalid parent, using default.\n", 9162306a36Sopenharmony_ci __func__, clk_hw_get_name(hw)); 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic u8 clk_rcg2_get_parent(struct clk_hw *hw) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 9862306a36Sopenharmony_ci u32 cfg; 9962306a36Sopenharmony_ci int ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); 10262306a36Sopenharmony_ci if (ret) { 10362306a36Sopenharmony_ci pr_debug("%s: Unable to read CFG register for %s\n", 10462306a36Sopenharmony_ci __func__, clk_hw_get_name(hw)); 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return __clk_rcg2_get_parent(hw, cfg); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int update_config(struct clk_rcg2 *rcg) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci int count, ret; 11462306a36Sopenharmony_ci u32 cmd; 11562306a36Sopenharmony_ci struct clk_hw *hw = &rcg->clkr.hw; 11662306a36Sopenharmony_ci const char *name = clk_hw_get_name(hw); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, 11962306a36Sopenharmony_ci CMD_UPDATE, CMD_UPDATE); 12062306a36Sopenharmony_ci if (ret) 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Wait for update to take effect */ 12462306a36Sopenharmony_ci for (count = 500; count > 0; count--) { 12562306a36Sopenharmony_ci ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); 12662306a36Sopenharmony_ci if (ret) 12762306a36Sopenharmony_ci return ret; 12862306a36Sopenharmony_ci if (!(cmd & CMD_UPDATE)) 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci udelay(1); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci WARN(1, "%s: rcg didn't update its configuration.", name); 13462306a36Sopenharmony_ci return -EBUSY; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), 14462306a36Sopenharmony_ci CFG_SRC_SEL_MASK, cfg); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return update_config(rcg); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Calculate m/n:d rate 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * parent_rate m 15562306a36Sopenharmony_ci * rate = ----------- x --- 15662306a36Sopenharmony_ci * hid_div n 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic unsigned long 15962306a36Sopenharmony_cicalc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci if (hid_div) 16262306a36Sopenharmony_ci rate = mult_frac(rate, 2, hid_div + 1); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (mode) 16562306a36Sopenharmony_ci rate = mult_frac(rate, m, n); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return rate; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned long 17162306a36Sopenharmony_ci__clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, u32 cfg) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 17462306a36Sopenharmony_ci u32 hid_div, m = 0, n = 0, mode = 0, mask; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (rcg->mnd_width) { 17762306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 17862306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); 17962306a36Sopenharmony_ci m &= mask; 18062306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &n); 18162306a36Sopenharmony_ci n = ~n; 18262306a36Sopenharmony_ci n &= mask; 18362306a36Sopenharmony_ci n += m; 18462306a36Sopenharmony_ci mode = cfg & CFG_MODE_MASK; 18562306a36Sopenharmony_ci mode >>= CFG_MODE_SHIFT; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mask = BIT(rcg->hid_width) - 1; 18962306a36Sopenharmony_ci hid_div = cfg >> CFG_SRC_DIV_SHIFT; 19062306a36Sopenharmony_ci hid_div &= mask; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return calc_rate(parent_rate, m, n, mode, hid_div); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic unsigned long 19662306a36Sopenharmony_ciclk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 19962306a36Sopenharmony_ci u32 cfg; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return __clk_rcg2_recalc_rate(hw, parent_rate, cfg); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, 20762306a36Sopenharmony_ci struct clk_rate_request *req, 20862306a36Sopenharmony_ci enum freq_policy policy) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci unsigned long clk_flags, rate = req->rate; 21162306a36Sopenharmony_ci struct clk_hw *p; 21262306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 21362306a36Sopenharmony_ci int index; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci switch (policy) { 21662306a36Sopenharmony_ci case FLOOR: 21762306a36Sopenharmony_ci f = qcom_find_freq_floor(f, rate); 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci case CEIL: 22062306a36Sopenharmony_ci f = qcom_find_freq(f, rate); 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci default: 22362306a36Sopenharmony_ci return -EINVAL; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!f) 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci index = qcom_find_src_index(hw, rcg->parent_map, f->src); 23062306a36Sopenharmony_ci if (index < 0) 23162306a36Sopenharmony_ci return index; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci clk_flags = clk_hw_get_flags(hw); 23462306a36Sopenharmony_ci p = clk_hw_get_parent_by_index(hw, index); 23562306a36Sopenharmony_ci if (!p) 23662306a36Sopenharmony_ci return -EINVAL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (clk_flags & CLK_SET_RATE_PARENT) { 23962306a36Sopenharmony_ci rate = f->freq; 24062306a36Sopenharmony_ci if (f->pre_div) { 24162306a36Sopenharmony_ci if (!rate) 24262306a36Sopenharmony_ci rate = req->rate; 24362306a36Sopenharmony_ci rate /= 2; 24462306a36Sopenharmony_ci rate *= f->pre_div + 1; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (f->n) { 24862306a36Sopenharmony_ci u64 tmp = rate; 24962306a36Sopenharmony_ci tmp = tmp * f->n; 25062306a36Sopenharmony_ci do_div(tmp, f->m); 25162306a36Sopenharmony_ci rate = tmp; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci rate = clk_hw_get_rate(p); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci req->best_parent_hw = p; 25762306a36Sopenharmony_ci req->best_parent_rate = rate; 25862306a36Sopenharmony_ci req->rate = f->freq; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int clk_rcg2_determine_rate(struct clk_hw *hw, 26462306a36Sopenharmony_ci struct clk_rate_request *req) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int clk_rcg2_determine_floor_rate(struct clk_hw *hw, 27262306a36Sopenharmony_ci struct clk_rate_request *req) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f, 28062306a36Sopenharmony_ci u32 *_cfg) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci u32 cfg, mask, d_val, not2d_val, n_minus_m; 28362306a36Sopenharmony_ci struct clk_hw *hw = &rcg->clkr.hw; 28462306a36Sopenharmony_ci int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (index < 0) 28762306a36Sopenharmony_ci return index; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (rcg->mnd_width && f->n) { 29062306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 29162306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, 29262306a36Sopenharmony_ci RCG_M_OFFSET(rcg), mask, f->m); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, 29762306a36Sopenharmony_ci RCG_N_OFFSET(rcg), mask, ~(f->n - f->m)); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Calculate 2d value */ 30262306a36Sopenharmony_ci d_val = f->n; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci n_minus_m = f->n - f->m; 30562306a36Sopenharmony_ci n_minus_m *= 2; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci d_val = clamp_t(u32, d_val, f->m, n_minus_m); 30862306a36Sopenharmony_ci not2d_val = ~d_val & mask; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, 31162306a36Sopenharmony_ci RCG_D_OFFSET(rcg), mask, not2d_val); 31262306a36Sopenharmony_ci if (ret) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mask = BIT(rcg->hid_width) - 1; 31762306a36Sopenharmony_ci mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK; 31862306a36Sopenharmony_ci cfg = f->pre_div << CFG_SRC_DIV_SHIFT; 31962306a36Sopenharmony_ci cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; 32062306a36Sopenharmony_ci if (rcg->mnd_width && f->n && (f->m != f->n)) 32162306a36Sopenharmony_ci cfg |= CFG_MODE_DUAL_EDGE; 32262306a36Sopenharmony_ci if (rcg->hw_clk_ctrl) 32362306a36Sopenharmony_ci cfg |= CFG_HW_CLK_CTRL_MASK; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci *_cfg &= ~mask; 32662306a36Sopenharmony_ci *_cfg |= cfg; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci u32 cfg; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = __clk_rcg2_configure(rcg, f, &cfg); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg); 34562306a36Sopenharmony_ci if (ret) 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return update_config(rcg); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, 35262306a36Sopenharmony_ci enum freq_policy policy) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 35562306a36Sopenharmony_ci const struct freq_tbl *f; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (policy) { 35862306a36Sopenharmony_ci case FLOOR: 35962306a36Sopenharmony_ci f = qcom_find_freq_floor(rcg->freq_tbl, rate); 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci case CEIL: 36262306a36Sopenharmony_ci f = qcom_find_freq(rcg->freq_tbl, rate); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci default: 36562306a36Sopenharmony_ci return -EINVAL; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!f) 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return clk_rcg2_configure(rcg, f); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, 37562306a36Sopenharmony_ci unsigned long parent_rate) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return __clk_rcg2_set_rate(hw, rate, CEIL); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate, 38162306a36Sopenharmony_ci unsigned long parent_rate) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci return __clk_rcg2_set_rate(hw, rate, FLOOR); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int clk_rcg2_set_rate_and_parent(struct clk_hw *hw, 38762306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci return __clk_rcg2_set_rate(hw, rate, CEIL); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw, 39362306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci return __clk_rcg2_set_rate(hw, rate, FLOOR); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 40162306a36Sopenharmony_ci u32 notn_m, n, m, d, not2d, mask; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!rcg->mnd_width) { 40462306a36Sopenharmony_ci /* 50 % duty-cycle for Non-MND RCGs */ 40562306a36Sopenharmony_ci duty->num = 1; 40662306a36Sopenharmony_ci duty->den = 2; 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_D_OFFSET(rcg), ¬2d); 41162306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); 41262306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), ¬n_m); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!not2d && !m && !notn_m) { 41562306a36Sopenharmony_ci /* 50 % duty-cycle always */ 41662306a36Sopenharmony_ci duty->num = 1; 41762306a36Sopenharmony_ci duty->den = 2; 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci d = ~(not2d) & mask; 42462306a36Sopenharmony_ci d = DIV_ROUND_CLOSEST(d, 2); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci n = (~(notn_m) + m) & mask; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci duty->num = d; 42962306a36Sopenharmony_ci duty->den = n; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 43762306a36Sopenharmony_ci u32 notn_m, n, m, d, not2d, mask, duty_per, cfg; 43862306a36Sopenharmony_ci int ret; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Duty-cycle cannot be modified for non-MND RCGs */ 44162306a36Sopenharmony_ci if (!rcg->mnd_width) 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), ¬n_m); 44762306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); 44862306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Duty-cycle cannot be modified if MND divider is in bypass mode. */ 45162306a36Sopenharmony_ci if (!(cfg & CFG_MODE_MASK)) 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci n = (~(notn_m) + m) & mask; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci duty_per = (duty->num * 100) / duty->den; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Calculate 2d value */ 45962306a36Sopenharmony_ci d = DIV_ROUND_CLOSEST(n * duty_per * 2, 100); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* 46262306a36Sopenharmony_ci * Check bit widths of 2d. If D is too big reduce duty cycle. 46362306a36Sopenharmony_ci * Also make sure it is never zero. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci d = clamp_val(d, 1, mask); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if ((d / 2) > (n - m)) 46862306a36Sopenharmony_ci d = (n - m) * 2; 46962306a36Sopenharmony_ci else if ((d / 2) < (m / 2)) 47062306a36Sopenharmony_ci d = m; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci not2d = ~d & mask; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, RCG_D_OFFSET(rcg), mask, 47562306a36Sopenharmony_ci not2d); 47662306a36Sopenharmony_ci if (ret) 47762306a36Sopenharmony_ci return ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return update_config(rcg); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciconst struct clk_ops clk_rcg2_ops = { 48362306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 48462306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 48562306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 48662306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 48762306a36Sopenharmony_ci .determine_rate = clk_rcg2_determine_rate, 48862306a36Sopenharmony_ci .set_rate = clk_rcg2_set_rate, 48962306a36Sopenharmony_ci .set_rate_and_parent = clk_rcg2_set_rate_and_parent, 49062306a36Sopenharmony_ci .get_duty_cycle = clk_rcg2_get_duty_cycle, 49162306a36Sopenharmony_ci .set_duty_cycle = clk_rcg2_set_duty_cycle, 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_rcg2_ops); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciconst struct clk_ops clk_rcg2_floor_ops = { 49662306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 49762306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 49862306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 49962306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 50062306a36Sopenharmony_ci .determine_rate = clk_rcg2_determine_floor_rate, 50162306a36Sopenharmony_ci .set_rate = clk_rcg2_set_floor_rate, 50262306a36Sopenharmony_ci .set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent, 50362306a36Sopenharmony_ci .get_duty_cycle = clk_rcg2_get_duty_cycle, 50462306a36Sopenharmony_ci .set_duty_cycle = clk_rcg2_set_duty_cycle, 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_rcg2_floor_ops); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ciconst struct clk_ops clk_rcg2_mux_closest_ops = { 50962306a36Sopenharmony_ci .determine_rate = __clk_mux_determine_rate_closest, 51062306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 51162306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_rcg2_mux_closest_ops); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistruct frac_entry { 51662306a36Sopenharmony_ci int num; 51762306a36Sopenharmony_ci int den; 51862306a36Sopenharmony_ci}; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic const struct frac_entry frac_table_675m[] = { /* link rate of 270M */ 52162306a36Sopenharmony_ci { 52, 295 }, /* 119 M */ 52262306a36Sopenharmony_ci { 11, 57 }, /* 130.25 M */ 52362306a36Sopenharmony_ci { 63, 307 }, /* 138.50 M */ 52462306a36Sopenharmony_ci { 11, 50 }, /* 148.50 M */ 52562306a36Sopenharmony_ci { 47, 206 }, /* 154 M */ 52662306a36Sopenharmony_ci { 31, 100 }, /* 205.25 M */ 52762306a36Sopenharmony_ci { 107, 269 }, /* 268.50 M */ 52862306a36Sopenharmony_ci { }, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic struct frac_entry frac_table_810m[] = { /* Link rate of 162M */ 53262306a36Sopenharmony_ci { 31, 211 }, /* 119 M */ 53362306a36Sopenharmony_ci { 32, 199 }, /* 130.25 M */ 53462306a36Sopenharmony_ci { 63, 307 }, /* 138.50 M */ 53562306a36Sopenharmony_ci { 11, 60 }, /* 148.50 M */ 53662306a36Sopenharmony_ci { 50, 263 }, /* 154 M */ 53762306a36Sopenharmony_ci { 31, 120 }, /* 205.25 M */ 53862306a36Sopenharmony_ci { 119, 359 }, /* 268.50 M */ 53962306a36Sopenharmony_ci { }, 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate, 54362306a36Sopenharmony_ci unsigned long parent_rate) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 54662306a36Sopenharmony_ci struct freq_tbl f = *rcg->freq_tbl; 54762306a36Sopenharmony_ci const struct frac_entry *frac; 54862306a36Sopenharmony_ci int delta = 100000; 54962306a36Sopenharmony_ci s64 src_rate = parent_rate; 55062306a36Sopenharmony_ci s64 request; 55162306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 55262306a36Sopenharmony_ci u32 hid_div; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (src_rate == 810000000) 55562306a36Sopenharmony_ci frac = frac_table_810m; 55662306a36Sopenharmony_ci else 55762306a36Sopenharmony_ci frac = frac_table_675m; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci for (; frac->num; frac++) { 56062306a36Sopenharmony_ci request = rate; 56162306a36Sopenharmony_ci request *= frac->den; 56262306a36Sopenharmony_ci request = div_s64(request, frac->num); 56362306a36Sopenharmony_ci if ((src_rate < (request - delta)) || 56462306a36Sopenharmony_ci (src_rate > (request + delta))) 56562306a36Sopenharmony_ci continue; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, 56862306a36Sopenharmony_ci &hid_div); 56962306a36Sopenharmony_ci f.pre_div = hid_div; 57062306a36Sopenharmony_ci f.pre_div >>= CFG_SRC_DIV_SHIFT; 57162306a36Sopenharmony_ci f.pre_div &= mask; 57262306a36Sopenharmony_ci f.m = frac->num; 57362306a36Sopenharmony_ci f.n = frac->den; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return clk_rcg2_configure(rcg, &f); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw, 58262306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci /* Parent index is set statically in frequency table */ 58562306a36Sopenharmony_ci return clk_edp_pixel_set_rate(hw, rate, parent_rate); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int clk_edp_pixel_determine_rate(struct clk_hw *hw, 58962306a36Sopenharmony_ci struct clk_rate_request *req) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 59262306a36Sopenharmony_ci const struct freq_tbl *f = rcg->freq_tbl; 59362306a36Sopenharmony_ci const struct frac_entry *frac; 59462306a36Sopenharmony_ci int delta = 100000; 59562306a36Sopenharmony_ci s64 request; 59662306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 59762306a36Sopenharmony_ci u32 hid_div; 59862306a36Sopenharmony_ci int index = qcom_find_src_index(hw, rcg->parent_map, f->src); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Force the correct parent */ 60162306a36Sopenharmony_ci req->best_parent_hw = clk_hw_get_parent_by_index(hw, index); 60262306a36Sopenharmony_ci req->best_parent_rate = clk_hw_get_rate(req->best_parent_hw); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (req->best_parent_rate == 810000000) 60562306a36Sopenharmony_ci frac = frac_table_810m; 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci frac = frac_table_675m; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci for (; frac->num; frac++) { 61062306a36Sopenharmony_ci request = req->rate; 61162306a36Sopenharmony_ci request *= frac->den; 61262306a36Sopenharmony_ci request = div_s64(request, frac->num); 61362306a36Sopenharmony_ci if ((req->best_parent_rate < (request - delta)) || 61462306a36Sopenharmony_ci (req->best_parent_rate > (request + delta))) 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, 61862306a36Sopenharmony_ci &hid_div); 61962306a36Sopenharmony_ci hid_div >>= CFG_SRC_DIV_SHIFT; 62062306a36Sopenharmony_ci hid_div &= mask; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci req->rate = calc_rate(req->best_parent_rate, 62362306a36Sopenharmony_ci frac->num, frac->den, 62462306a36Sopenharmony_ci !!frac->den, hid_div); 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciconst struct clk_ops clk_edp_pixel_ops = { 63262306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 63362306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 63462306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 63562306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 63662306a36Sopenharmony_ci .set_rate = clk_edp_pixel_set_rate, 63762306a36Sopenharmony_ci .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent, 63862306a36Sopenharmony_ci .determine_rate = clk_edp_pixel_determine_rate, 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_edp_pixel_ops); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int clk_byte_determine_rate(struct clk_hw *hw, 64362306a36Sopenharmony_ci struct clk_rate_request *req) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 64662306a36Sopenharmony_ci const struct freq_tbl *f = rcg->freq_tbl; 64762306a36Sopenharmony_ci int index = qcom_find_src_index(hw, rcg->parent_map, f->src); 64862306a36Sopenharmony_ci unsigned long parent_rate, div; 64962306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 65062306a36Sopenharmony_ci struct clk_hw *p; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (req->rate == 0) 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci req->best_parent_hw = p = clk_hw_get_parent_by_index(hw, index); 65662306a36Sopenharmony_ci req->best_parent_rate = parent_rate = clk_hw_round_rate(p, req->rate); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci div = DIV_ROUND_UP((2 * parent_rate), req->rate) - 1; 65962306a36Sopenharmony_ci div = min_t(u32, div, mask); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci req->rate = calc_rate(parent_rate, 0, 0, 0, div); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate, 66762306a36Sopenharmony_ci unsigned long parent_rate) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 67062306a36Sopenharmony_ci struct freq_tbl f = *rcg->freq_tbl; 67162306a36Sopenharmony_ci unsigned long div; 67262306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; 67562306a36Sopenharmony_ci div = min_t(u32, div, mask); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci f.pre_div = div; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return clk_rcg2_configure(rcg, &f); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int clk_byte_set_rate_and_parent(struct clk_hw *hw, 68362306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci /* Parent index is set statically in frequency table */ 68662306a36Sopenharmony_ci return clk_byte_set_rate(hw, rate, parent_rate); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciconst struct clk_ops clk_byte_ops = { 69062306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 69162306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 69262306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 69362306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 69462306a36Sopenharmony_ci .set_rate = clk_byte_set_rate, 69562306a36Sopenharmony_ci .set_rate_and_parent = clk_byte_set_rate_and_parent, 69662306a36Sopenharmony_ci .determine_rate = clk_byte_determine_rate, 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_byte_ops); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int clk_byte2_determine_rate(struct clk_hw *hw, 70162306a36Sopenharmony_ci struct clk_rate_request *req) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 70462306a36Sopenharmony_ci unsigned long parent_rate, div; 70562306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 70662306a36Sopenharmony_ci struct clk_hw *p; 70762306a36Sopenharmony_ci unsigned long rate = req->rate; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (rate == 0) 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci p = req->best_parent_hw; 71362306a36Sopenharmony_ci req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; 71662306a36Sopenharmony_ci div = min_t(u32, div, mask); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci req->rate = calc_rate(parent_rate, 0, 0, 0, div); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate, 72462306a36Sopenharmony_ci unsigned long parent_rate) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 72762306a36Sopenharmony_ci struct freq_tbl f = { 0 }; 72862306a36Sopenharmony_ci unsigned long div; 72962306a36Sopenharmony_ci int i, num_parents = clk_hw_get_num_parents(hw); 73062306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 73162306a36Sopenharmony_ci u32 cfg; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; 73462306a36Sopenharmony_ci div = min_t(u32, div, mask); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci f.pre_div = div; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); 73962306a36Sopenharmony_ci cfg &= CFG_SRC_SEL_MASK; 74062306a36Sopenharmony_ci cfg >>= CFG_SRC_SEL_SHIFT; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci for (i = 0; i < num_parents; i++) { 74362306a36Sopenharmony_ci if (cfg == rcg->parent_map[i].cfg) { 74462306a36Sopenharmony_ci f.src = rcg->parent_map[i].src; 74562306a36Sopenharmony_ci return clk_rcg2_configure(rcg, &f); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic int clk_byte2_set_rate_and_parent(struct clk_hw *hw, 75362306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci /* Read the hardware to determine parent during set_rate */ 75662306a36Sopenharmony_ci return clk_byte2_set_rate(hw, rate, parent_rate); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ciconst struct clk_ops clk_byte2_ops = { 76062306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 76162306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 76262306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 76362306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 76462306a36Sopenharmony_ci .set_rate = clk_byte2_set_rate, 76562306a36Sopenharmony_ci .set_rate_and_parent = clk_byte2_set_rate_and_parent, 76662306a36Sopenharmony_ci .determine_rate = clk_byte2_determine_rate, 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_byte2_ops); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic const struct frac_entry frac_table_pixel[] = { 77162306a36Sopenharmony_ci { 3, 8 }, 77262306a36Sopenharmony_ci { 2, 9 }, 77362306a36Sopenharmony_ci { 4, 9 }, 77462306a36Sopenharmony_ci { 1, 1 }, 77562306a36Sopenharmony_ci { 2, 3 }, 77662306a36Sopenharmony_ci { } 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int clk_pixel_determine_rate(struct clk_hw *hw, 78062306a36Sopenharmony_ci struct clk_rate_request *req) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci unsigned long request, src_rate; 78362306a36Sopenharmony_ci int delta = 100000; 78462306a36Sopenharmony_ci const struct frac_entry *frac = frac_table_pixel; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci for (; frac->num; frac++) { 78762306a36Sopenharmony_ci request = (req->rate * frac->den) / frac->num; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci src_rate = clk_hw_round_rate(req->best_parent_hw, request); 79062306a36Sopenharmony_ci if ((src_rate < (request - delta)) || 79162306a36Sopenharmony_ci (src_rate > (request + delta))) 79262306a36Sopenharmony_ci continue; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci req->best_parent_rate = src_rate; 79562306a36Sopenharmony_ci req->rate = (src_rate * frac->num) / frac->den; 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, 80362306a36Sopenharmony_ci unsigned long parent_rate) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 80662306a36Sopenharmony_ci struct freq_tbl f = { 0 }; 80762306a36Sopenharmony_ci const struct frac_entry *frac = frac_table_pixel; 80862306a36Sopenharmony_ci unsigned long request; 80962306a36Sopenharmony_ci int delta = 100000; 81062306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 81162306a36Sopenharmony_ci u32 hid_div, cfg; 81262306a36Sopenharmony_ci int i, num_parents = clk_hw_get_num_parents(hw); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); 81562306a36Sopenharmony_ci cfg &= CFG_SRC_SEL_MASK; 81662306a36Sopenharmony_ci cfg >>= CFG_SRC_SEL_SHIFT; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci for (i = 0; i < num_parents; i++) 81962306a36Sopenharmony_ci if (cfg == rcg->parent_map[i].cfg) { 82062306a36Sopenharmony_ci f.src = rcg->parent_map[i].src; 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci for (; frac->num; frac++) { 82562306a36Sopenharmony_ci request = (rate * frac->den) / frac->num; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if ((parent_rate < (request - delta)) || 82862306a36Sopenharmony_ci (parent_rate > (request + delta))) 82962306a36Sopenharmony_ci continue; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, 83262306a36Sopenharmony_ci &hid_div); 83362306a36Sopenharmony_ci f.pre_div = hid_div; 83462306a36Sopenharmony_ci f.pre_div >>= CFG_SRC_DIV_SHIFT; 83562306a36Sopenharmony_ci f.pre_div &= mask; 83662306a36Sopenharmony_ci f.m = frac->num; 83762306a36Sopenharmony_ci f.n = frac->den; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return clk_rcg2_configure(rcg, &f); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci return -EINVAL; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 84562306a36Sopenharmony_ci unsigned long parent_rate, u8 index) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci return clk_pixel_set_rate(hw, rate, parent_rate); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciconst struct clk_ops clk_pixel_ops = { 85162306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 85262306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 85362306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 85462306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 85562306a36Sopenharmony_ci .set_rate = clk_pixel_set_rate, 85662306a36Sopenharmony_ci .set_rate_and_parent = clk_pixel_set_rate_and_parent, 85762306a36Sopenharmony_ci .determine_rate = clk_pixel_determine_rate, 85862306a36Sopenharmony_ci}; 85962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_pixel_ops); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic int clk_gfx3d_determine_rate(struct clk_hw *hw, 86262306a36Sopenharmony_ci struct clk_rate_request *req) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct clk_rate_request parent_req = { .min_rate = 0, .max_rate = ULONG_MAX }; 86562306a36Sopenharmony_ci struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw); 86662306a36Sopenharmony_ci struct clk_hw *xo, *p0, *p1, *p2; 86762306a36Sopenharmony_ci unsigned long p0_rate; 86862306a36Sopenharmony_ci u8 mux_div = cgfx->div; 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci p0 = cgfx->hws[0]; 87262306a36Sopenharmony_ci p1 = cgfx->hws[1]; 87362306a36Sopenharmony_ci p2 = cgfx->hws[2]; 87462306a36Sopenharmony_ci /* 87562306a36Sopenharmony_ci * This function does ping-pong the RCG between PLLs: if we don't 87662306a36Sopenharmony_ci * have at least one fixed PLL and two variable ones, 87762306a36Sopenharmony_ci * then it's not going to work correctly. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_ci if (WARN_ON(!p0 || !p1 || !p2)) 88062306a36Sopenharmony_ci return -EINVAL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci xo = clk_hw_get_parent_by_index(hw, 0); 88362306a36Sopenharmony_ci if (req->rate == clk_hw_get_rate(xo)) { 88462306a36Sopenharmony_ci req->best_parent_hw = xo; 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (mux_div == 0) 88962306a36Sopenharmony_ci mux_div = 1; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci parent_req.rate = req->rate * mux_div; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* This has to be a fixed rate PLL */ 89462306a36Sopenharmony_ci p0_rate = clk_hw_get_rate(p0); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (parent_req.rate == p0_rate) { 89762306a36Sopenharmony_ci req->rate = req->best_parent_rate = p0_rate; 89862306a36Sopenharmony_ci req->best_parent_hw = p0; 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (req->best_parent_hw == p0) { 90362306a36Sopenharmony_ci /* Are we going back to a previously used rate? */ 90462306a36Sopenharmony_ci if (clk_hw_get_rate(p2) == parent_req.rate) 90562306a36Sopenharmony_ci req->best_parent_hw = p2; 90662306a36Sopenharmony_ci else 90762306a36Sopenharmony_ci req->best_parent_hw = p1; 90862306a36Sopenharmony_ci } else if (req->best_parent_hw == p2) { 90962306a36Sopenharmony_ci req->best_parent_hw = p1; 91062306a36Sopenharmony_ci } else { 91162306a36Sopenharmony_ci req->best_parent_hw = p2; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci clk_hw_get_rate_range(req->best_parent_hw, 91562306a36Sopenharmony_ci &parent_req.min_rate, &parent_req.max_rate); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (req->min_rate > parent_req.min_rate) 91862306a36Sopenharmony_ci parent_req.min_rate = req->min_rate; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (req->max_rate < parent_req.max_rate) 92162306a36Sopenharmony_ci parent_req.max_rate = req->max_rate; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ret = __clk_determine_rate(req->best_parent_hw, &parent_req); 92462306a36Sopenharmony_ci if (ret) 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci req->rate = req->best_parent_rate = parent_req.rate; 92862306a36Sopenharmony_ci req->rate /= mux_div; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 93462306a36Sopenharmony_ci unsigned long parent_rate, u8 index) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw); 93762306a36Sopenharmony_ci struct clk_rcg2 *rcg = &cgfx->rcg; 93862306a36Sopenharmony_ci u32 cfg; 93962306a36Sopenharmony_ci int ret; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; 94262306a36Sopenharmony_ci /* On some targets, the GFX3D RCG may need to divide PLL frequency */ 94362306a36Sopenharmony_ci if (cgfx->div > 1) 94462306a36Sopenharmony_ci cfg |= ((2 * cgfx->div) - 1) << CFG_SRC_DIV_SHIFT; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); 94762306a36Sopenharmony_ci if (ret) 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return update_config(rcg); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate, 95462306a36Sopenharmony_ci unsigned long parent_rate) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * We should never get here; clk_gfx3d_determine_rate() should always 95862306a36Sopenharmony_ci * make us use a different parent than what we're currently using, so 95962306a36Sopenharmony_ci * clk_gfx3d_set_rate_and_parent() should always be called. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci return 0; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciconst struct clk_ops clk_gfx3d_ops = { 96562306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 96662306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 96762306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 96862306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 96962306a36Sopenharmony_ci .set_rate = clk_gfx3d_set_rate, 97062306a36Sopenharmony_ci .set_rate_and_parent = clk_gfx3d_set_rate_and_parent, 97162306a36Sopenharmony_ci .determine_rate = clk_gfx3d_determine_rate, 97262306a36Sopenharmony_ci}; 97362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_gfx3d_ops); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int clk_rcg2_set_force_enable(struct clk_hw *hw) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 97862306a36Sopenharmony_ci const char *name = clk_hw_get_name(hw); 97962306a36Sopenharmony_ci int ret, count; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, 98262306a36Sopenharmony_ci CMD_ROOT_EN, CMD_ROOT_EN); 98362306a36Sopenharmony_ci if (ret) 98462306a36Sopenharmony_ci return ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* wait for RCG to turn ON */ 98762306a36Sopenharmony_ci for (count = 500; count > 0; count--) { 98862306a36Sopenharmony_ci if (clk_rcg2_is_enabled(hw)) 98962306a36Sopenharmony_ci return 0; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci udelay(1); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci pr_err("%s: RCG did not turn on\n", name); 99562306a36Sopenharmony_ci return -ETIMEDOUT; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int clk_rcg2_clear_force_enable(struct clk_hw *hw) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, 100362306a36Sopenharmony_ci CMD_ROOT_EN, 0); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int 100762306a36Sopenharmony_ciclk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 101062306a36Sopenharmony_ci int ret; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci ret = clk_rcg2_set_force_enable(hw); 101362306a36Sopenharmony_ci if (ret) 101462306a36Sopenharmony_ci return ret; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci ret = clk_rcg2_configure(rcg, f); 101762306a36Sopenharmony_ci if (ret) 101862306a36Sopenharmony_ci return ret; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return clk_rcg2_clear_force_enable(hw); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate, 102462306a36Sopenharmony_ci unsigned long parent_rate) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 102762306a36Sopenharmony_ci const struct freq_tbl *f; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci f = qcom_find_freq(rcg->freq_tbl, rate); 103062306a36Sopenharmony_ci if (!f) 103162306a36Sopenharmony_ci return -EINVAL; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* 103462306a36Sopenharmony_ci * In case clock is disabled, update the M, N and D registers, cache 103562306a36Sopenharmony_ci * the CFG value in parked_cfg and don't hit the update bit of CMD 103662306a36Sopenharmony_ci * register. 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_ci if (!clk_hw_is_enabled(hw)) 103962306a36Sopenharmony_ci return __clk_rcg2_configure(rcg, f, &rcg->parked_cfg); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return clk_rcg2_shared_force_enable_clear(hw, f); 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int clk_rcg2_shared_set_rate_and_parent(struct clk_hw *hw, 104562306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci return clk_rcg2_shared_set_rate(hw, rate, parent_rate); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic int clk_rcg2_shared_enable(struct clk_hw *hw) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 105362306a36Sopenharmony_ci int ret; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* 105662306a36Sopenharmony_ci * Set the update bit because required configuration has already 105762306a36Sopenharmony_ci * been written in clk_rcg2_shared_set_rate() 105862306a36Sopenharmony_ci */ 105962306a36Sopenharmony_ci ret = clk_rcg2_set_force_enable(hw); 106062306a36Sopenharmony_ci if (ret) 106162306a36Sopenharmony_ci return ret; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* Write back the stored configuration corresponding to current rate */ 106462306a36Sopenharmony_ci ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->parked_cfg); 106562306a36Sopenharmony_ci if (ret) 106662306a36Sopenharmony_ci return ret; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci ret = update_config(rcg); 106962306a36Sopenharmony_ci if (ret) 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return clk_rcg2_clear_force_enable(hw); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void clk_rcg2_shared_disable(struct clk_hw *hw) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* 108062306a36Sopenharmony_ci * Store current configuration as switching to safe source would clear 108162306a36Sopenharmony_ci * the SRC and DIV of CFG register 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &rcg->parked_cfg); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* 108662306a36Sopenharmony_ci * Park the RCG at a safe configuration - sourced off of safe source. 108762306a36Sopenharmony_ci * Force enable and disable the RCG while configuring it to safeguard 108862306a36Sopenharmony_ci * against any update signal coming from the downstream clock. 108962306a36Sopenharmony_ci * The current parent is still prepared and enabled at this point, and 109062306a36Sopenharmony_ci * the safe source is always on while application processor subsystem 109162306a36Sopenharmony_ci * is online. Therefore, the RCG can safely switch its parent. 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_ci clk_rcg2_set_force_enable(hw); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, 109662306a36Sopenharmony_ci rcg->safe_src_index << CFG_SRC_SEL_SHIFT); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci update_config(rcg); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci clk_rcg2_clear_force_enable(hw); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic u8 clk_rcg2_shared_get_parent(struct clk_hw *hw) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* If the shared rcg is parked use the cached cfg instead */ 110862306a36Sopenharmony_ci if (!clk_hw_is_enabled(hw)) 110962306a36Sopenharmony_ci return __clk_rcg2_get_parent(hw, rcg->parked_cfg); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci return clk_rcg2_get_parent(hw); 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic int clk_rcg2_shared_set_parent(struct clk_hw *hw, u8 index) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* If the shared rcg is parked only update the cached cfg */ 111962306a36Sopenharmony_ci if (!clk_hw_is_enabled(hw)) { 112062306a36Sopenharmony_ci rcg->parked_cfg &= ~CFG_SRC_SEL_MASK; 112162306a36Sopenharmony_ci rcg->parked_cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return 0; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return clk_rcg2_set_parent(hw, index); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic unsigned long 113062306a36Sopenharmony_ciclk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* If the shared rcg is parked use the cached cfg instead */ 113562306a36Sopenharmony_ci if (!clk_hw_is_enabled(hw)) 113662306a36Sopenharmony_ci return __clk_rcg2_recalc_rate(hw, parent_rate, rcg->parked_cfg); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return clk_rcg2_recalc_rate(hw, parent_rate); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ciconst struct clk_ops clk_rcg2_shared_ops = { 114262306a36Sopenharmony_ci .enable = clk_rcg2_shared_enable, 114362306a36Sopenharmony_ci .disable = clk_rcg2_shared_disable, 114462306a36Sopenharmony_ci .get_parent = clk_rcg2_shared_get_parent, 114562306a36Sopenharmony_ci .set_parent = clk_rcg2_shared_set_parent, 114662306a36Sopenharmony_ci .recalc_rate = clk_rcg2_shared_recalc_rate, 114762306a36Sopenharmony_ci .determine_rate = clk_rcg2_determine_rate, 114862306a36Sopenharmony_ci .set_rate = clk_rcg2_shared_set_rate, 114962306a36Sopenharmony_ci .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent, 115062306a36Sopenharmony_ci}; 115162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci/* Common APIs to be used for DFS based RCGR */ 115462306a36Sopenharmony_cistatic void clk_rcg2_dfs_populate_freq(struct clk_hw *hw, unsigned int l, 115562306a36Sopenharmony_ci struct freq_tbl *f) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 115862306a36Sopenharmony_ci struct clk_hw *p; 115962306a36Sopenharmony_ci unsigned long prate = 0; 116062306a36Sopenharmony_ci u32 val, mask, cfg, mode, src; 116162306a36Sopenharmony_ci int i, num_parents; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(l), &cfg); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci mask = BIT(rcg->hid_width) - 1; 116662306a36Sopenharmony_ci f->pre_div = 1; 116762306a36Sopenharmony_ci if (cfg & mask) 116862306a36Sopenharmony_ci f->pre_div = cfg & mask; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci src = cfg & CFG_SRC_SEL_MASK; 117162306a36Sopenharmony_ci src >>= CFG_SRC_SEL_SHIFT; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci num_parents = clk_hw_get_num_parents(hw); 117462306a36Sopenharmony_ci for (i = 0; i < num_parents; i++) { 117562306a36Sopenharmony_ci if (src == rcg->parent_map[i].cfg) { 117662306a36Sopenharmony_ci f->src = rcg->parent_map[i].src; 117762306a36Sopenharmony_ci p = clk_hw_get_parent_by_index(&rcg->clkr.hw, i); 117862306a36Sopenharmony_ci prate = clk_hw_get_rate(p); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci mode = cfg & CFG_MODE_MASK; 118362306a36Sopenharmony_ci mode >>= CFG_MODE_SHIFT; 118462306a36Sopenharmony_ci if (mode) { 118562306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 118662306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_M_DFSR(l), 118762306a36Sopenharmony_ci &val); 118862306a36Sopenharmony_ci val &= mask; 118962306a36Sopenharmony_ci f->m = val; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_N_DFSR(l), 119262306a36Sopenharmony_ci &val); 119362306a36Sopenharmony_ci val = ~val; 119462306a36Sopenharmony_ci val &= mask; 119562306a36Sopenharmony_ci val += f->m; 119662306a36Sopenharmony_ci f->n = val; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci f->freq = calc_rate(prate, f->m, f->n, mode, f->pre_div); 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 *rcg) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct freq_tbl *freq_tbl; 120562306a36Sopenharmony_ci int i; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Allocate space for 1 extra since table is NULL terminated */ 120862306a36Sopenharmony_ci freq_tbl = kcalloc(MAX_PERF_LEVEL + 1, sizeof(*freq_tbl), GFP_KERNEL); 120962306a36Sopenharmony_ci if (!freq_tbl) 121062306a36Sopenharmony_ci return -ENOMEM; 121162306a36Sopenharmony_ci rcg->freq_tbl = freq_tbl; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci for (i = 0; i < MAX_PERF_LEVEL; i++) 121462306a36Sopenharmony_ci clk_rcg2_dfs_populate_freq(&rcg->clkr.hw, i, freq_tbl + i); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic int clk_rcg2_dfs_determine_rate(struct clk_hw *hw, 122062306a36Sopenharmony_ci struct clk_rate_request *req) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 122362306a36Sopenharmony_ci int ret; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (!rcg->freq_tbl) { 122662306a36Sopenharmony_ci ret = clk_rcg2_dfs_populate_freq_table(rcg); 122762306a36Sopenharmony_ci if (ret) { 122862306a36Sopenharmony_ci pr_err("Failed to update DFS tables for %s\n", 122962306a36Sopenharmony_ci clk_hw_get_name(hw)); 123062306a36Sopenharmony_ci return ret; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci return clk_rcg2_determine_rate(hw, req); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic unsigned long 123862306a36Sopenharmony_ciclk_rcg2_dfs_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 124162306a36Sopenharmony_ci u32 level, mask, cfg, m = 0, n = 0, mode, pre_div; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, 124462306a36Sopenharmony_ci rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &level); 124562306a36Sopenharmony_ci level &= GENMASK(4, 1); 124662306a36Sopenharmony_ci level >>= 1; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (rcg->freq_tbl) 124962306a36Sopenharmony_ci return rcg->freq_tbl[level].freq; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* 125262306a36Sopenharmony_ci * Assume that parent_rate is actually the parent because 125362306a36Sopenharmony_ci * we can't do any better at figuring it out when the table 125462306a36Sopenharmony_ci * hasn't been populated yet. We only populate the table 125562306a36Sopenharmony_ci * in determine_rate because we can't guarantee the parents 125662306a36Sopenharmony_ci * will be registered with the framework until then. 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(level), 125962306a36Sopenharmony_ci &cfg); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci mask = BIT(rcg->hid_width) - 1; 126262306a36Sopenharmony_ci pre_div = 1; 126362306a36Sopenharmony_ci if (cfg & mask) 126462306a36Sopenharmony_ci pre_div = cfg & mask; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci mode = cfg & CFG_MODE_MASK; 126762306a36Sopenharmony_ci mode >>= CFG_MODE_SHIFT; 126862306a36Sopenharmony_ci if (mode) { 126962306a36Sopenharmony_ci mask = BIT(rcg->mnd_width) - 1; 127062306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, 127162306a36Sopenharmony_ci rcg->cmd_rcgr + SE_PERF_M_DFSR(level), &m); 127262306a36Sopenharmony_ci m &= mask; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, 127562306a36Sopenharmony_ci rcg->cmd_rcgr + SE_PERF_N_DFSR(level), &n); 127662306a36Sopenharmony_ci n = ~n; 127762306a36Sopenharmony_ci n &= mask; 127862306a36Sopenharmony_ci n += m; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci return calc_rate(parent_rate, m, n, mode, pre_div); 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic const struct clk_ops clk_rcg2_dfs_ops = { 128562306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 128662306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 128762306a36Sopenharmony_ci .determine_rate = clk_rcg2_dfs_determine_rate, 128862306a36Sopenharmony_ci .recalc_rate = clk_rcg2_dfs_recalc_rate, 128962306a36Sopenharmony_ci}; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic int clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data *data, 129262306a36Sopenharmony_ci struct regmap *regmap) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci struct clk_rcg2 *rcg = data->rcg; 129562306a36Sopenharmony_ci struct clk_init_data *init = data->init; 129662306a36Sopenharmony_ci u32 val; 129762306a36Sopenharmony_ci int ret; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci ret = regmap_read(regmap, rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &val); 130062306a36Sopenharmony_ci if (ret) 130162306a36Sopenharmony_ci return -EINVAL; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (!(val & SE_CMD_DFS_EN)) 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* 130762306a36Sopenharmony_ci * Rate changes with consumer writing a register in 130862306a36Sopenharmony_ci * their own I/O region 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci init->flags |= CLK_GET_RATE_NOCACHE; 131162306a36Sopenharmony_ci init->ops = &clk_rcg2_dfs_ops; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci rcg->freq_tbl = NULL; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci return 0; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ciint qcom_cc_register_rcg_dfs(struct regmap *regmap, 131962306a36Sopenharmony_ci const struct clk_rcg_dfs_data *rcgs, size_t len) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci int i, ret; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci for (i = 0; i < len; i++) { 132462306a36Sopenharmony_ci ret = clk_rcg2_enable_dfs(&rcgs[i], regmap); 132562306a36Sopenharmony_ci if (ret) 132662306a36Sopenharmony_ci return ret; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_cc_register_rcg_dfs); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic int clk_rcg2_dp_set_rate(struct clk_hw *hw, unsigned long rate, 133462306a36Sopenharmony_ci unsigned long parent_rate) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 133762306a36Sopenharmony_ci struct freq_tbl f = { 0 }; 133862306a36Sopenharmony_ci u32 mask = BIT(rcg->hid_width) - 1; 133962306a36Sopenharmony_ci u32 hid_div, cfg; 134062306a36Sopenharmony_ci int i, num_parents = clk_hw_get_num_parents(hw); 134162306a36Sopenharmony_ci unsigned long num, den; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci rational_best_approximation(parent_rate, rate, 134462306a36Sopenharmony_ci GENMASK(rcg->mnd_width - 1, 0), 134562306a36Sopenharmony_ci GENMASK(rcg->mnd_width - 1, 0), &den, &num); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (!num || !den) 134862306a36Sopenharmony_ci return -EINVAL; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); 135162306a36Sopenharmony_ci hid_div = cfg; 135262306a36Sopenharmony_ci cfg &= CFG_SRC_SEL_MASK; 135362306a36Sopenharmony_ci cfg >>= CFG_SRC_SEL_SHIFT; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci for (i = 0; i < num_parents; i++) { 135662306a36Sopenharmony_ci if (cfg == rcg->parent_map[i].cfg) { 135762306a36Sopenharmony_ci f.src = rcg->parent_map[i].src; 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci f.pre_div = hid_div; 136362306a36Sopenharmony_ci f.pre_div >>= CFG_SRC_DIV_SHIFT; 136462306a36Sopenharmony_ci f.pre_div &= mask; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (num != den) { 136762306a36Sopenharmony_ci f.m = num; 136862306a36Sopenharmony_ci f.n = den; 136962306a36Sopenharmony_ci } else { 137062306a36Sopenharmony_ci f.m = 0; 137162306a36Sopenharmony_ci f.n = 0; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci return clk_rcg2_configure(rcg, &f); 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic int clk_rcg2_dp_set_rate_and_parent(struct clk_hw *hw, 137862306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, u8 index) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci return clk_rcg2_dp_set_rate(hw, rate, parent_rate); 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic int clk_rcg2_dp_determine_rate(struct clk_hw *hw, 138462306a36Sopenharmony_ci struct clk_rate_request *req) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct clk_rcg2 *rcg = to_clk_rcg2(hw); 138762306a36Sopenharmony_ci unsigned long num, den; 138862306a36Sopenharmony_ci u64 tmp; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* Parent rate is a fixed phy link rate */ 139162306a36Sopenharmony_ci rational_best_approximation(req->best_parent_rate, req->rate, 139262306a36Sopenharmony_ci GENMASK(rcg->mnd_width - 1, 0), 139362306a36Sopenharmony_ci GENMASK(rcg->mnd_width - 1, 0), &den, &num); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (!num || !den) 139662306a36Sopenharmony_ci return -EINVAL; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci tmp = req->best_parent_rate * num; 139962306a36Sopenharmony_ci do_div(tmp, den); 140062306a36Sopenharmony_ci req->rate = tmp; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci return 0; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ciconst struct clk_ops clk_dp_ops = { 140662306a36Sopenharmony_ci .is_enabled = clk_rcg2_is_enabled, 140762306a36Sopenharmony_ci .get_parent = clk_rcg2_get_parent, 140862306a36Sopenharmony_ci .set_parent = clk_rcg2_set_parent, 140962306a36Sopenharmony_ci .recalc_rate = clk_rcg2_recalc_rate, 141062306a36Sopenharmony_ci .set_rate = clk_rcg2_dp_set_rate, 141162306a36Sopenharmony_ci .set_rate_and_parent = clk_rcg2_dp_set_rate_and_parent, 141262306a36Sopenharmony_ci .determine_rate = clk_rcg2_dp_determine_rate, 141362306a36Sopenharmony_ci}; 141462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_dp_ops); 1415