| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
- */
- #include <linux/clk-provider.h>
- #include <linux/io.h>
- #include <linux/limits.h>
- #include <linux/spinlock.h>
- #include "clk-cv18xx-pll.h"
- static inline struct cv1800_clk_pll *hw_to_cv1800_clk_pll(struct clk_hw *hw)
- {
- struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
- return container_of(common, struct cv1800_clk_pll, common);
- }
- static unsigned long ipll_calc_rate(unsigned long parent_rate,
- unsigned long pre_div_sel,
- unsigned long div_sel,
- unsigned long post_div_sel)
- {
- uint64_t rate = parent_rate;
- rate *= div_sel;
- do_div(rate, pre_div_sel * post_div_sel);
- return rate;
- }
- static unsigned long ipll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- u32 value;
- value = readl(pll->common.base + pll->pll_reg);
- return ipll_calc_rate(parent_rate,
- PLL_GET_PRE_DIV_SEL(value),
- PLL_GET_DIV_SEL(value),
- PLL_GET_POST_DIV_SEL(value));
- }
- static int ipll_find_rate(const struct cv1800_clk_pll_limit *limit,
- unsigned long prate, unsigned long *rate,
- u32 *value)
- {
- unsigned long best_rate = 0;
- unsigned long trate = *rate;
- unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
- unsigned long pre, div, post;
- u32 detected = *value;
- unsigned long tmp;
- for_each_pll_limit_range(pre, &limit->pre_div) {
- for_each_pll_limit_range(div, &limit->div) {
- for_each_pll_limit_range(post, &limit->post_div) {
- tmp = ipll_calc_rate(prate, pre, div, post);
- if (tmp > trate)
- continue;
- if ((trate - tmp) < (trate - best_rate)) {
- best_rate = tmp;
- pre_div_sel = pre;
- div_sel = div;
- post_div_sel = post;
- }
- }
- }
- }
- if (best_rate) {
- detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
- detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
- detected = PLL_SET_DIV_SEL(detected, div_sel);
- *value = detected;
- *rate = best_rate;
- return 0;
- }
- return -EINVAL;
- }
- static int ipll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
- {
- u32 val;
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- return ipll_find_rate(pll->pll_limit, req->best_parent_rate,
- &req->rate, &val);
- }
- static void pll_get_mode_ctrl(unsigned long div_sel,
- bool (*mode_ctrl_check)(unsigned long,
- unsigned long,
- unsigned long),
- const struct cv1800_clk_pll_limit *limit,
- u32 *value)
- {
- unsigned long ictrl = 0, mode = 0;
- u32 detected = *value;
- for_each_pll_limit_range(mode, &limit->mode) {
- for_each_pll_limit_range(ictrl, &limit->ictrl) {
- if (mode_ctrl_check(div_sel, ictrl, mode)) {
- detected = PLL_SET_SEL_MODE(detected, mode);
- detected = PLL_SET_ICTRL(detected, ictrl);
- *value = detected;
- return;
- }
- }
- }
- }
- static bool ipll_check_mode_ctrl_restrict(unsigned long div_sel,
- unsigned long ictrl,
- unsigned long mode)
- {
- unsigned long left_rest = 20 * div_sel;
- unsigned long right_rest = 35 * div_sel;
- unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
- return test > left_rest && test <= right_rest;
- }
- static int ipll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
- {
- u32 regval, detected = 0;
- unsigned long flags;
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- ipll_find_rate(pll->pll_limit, parent_rate, &rate, &detected);
- pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
- ipll_check_mode_ctrl_restrict,
- pll->pll_limit, &detected);
- spin_lock_irqsave(pll->common.lock, flags);
- regval = readl(pll->common.base + pll->pll_reg);
- regval = PLL_COPY_REG(regval, detected);
- writel(regval, pll->common.base + pll->pll_reg);
- spin_unlock_irqrestore(pll->common.lock, flags);
- cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
- BIT(pll->pll_status.shift));
- return 0;
- }
- static int pll_enable(struct clk_hw *hw)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- return cv1800_clk_clearbit(&pll->common, &pll->pll_pwd);
- }
- static void pll_disable(struct clk_hw *hw)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- cv1800_clk_setbit(&pll->common, &pll->pll_pwd);
- }
- static int pll_is_enable(struct clk_hw *hw)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- return cv1800_clk_checkbit(&pll->common, &pll->pll_pwd) == 0;
- }
- const struct clk_ops cv1800_clk_ipll_ops = {
- .disable = pll_disable,
- .enable = pll_enable,
- .is_enabled = pll_is_enable,
- .recalc_rate = ipll_recalc_rate,
- .determine_rate = ipll_determine_rate,
- .set_rate = ipll_set_rate,
- };
- #define PLL_SYN_FACTOR_DOT_POS 26
- #define PLL_SYN_FACTOR_MINIMUM ((4 << PLL_SYN_FACTOR_DOT_POS) + 1)
- static bool fpll_is_factional_mode(struct cv1800_clk_pll *pll)
- {
- return cv1800_clk_checkbit(&pll->common, &pll->pll_syn->en);
- }
- static unsigned long fpll_calc_rate(unsigned long parent_rate,
- unsigned long pre_div_sel,
- unsigned long div_sel,
- unsigned long post_div_sel,
- unsigned long ssc_syn_set,
- bool is_full_parent)
- {
- u64 dividend = parent_rate * div_sel;
- u64 factor = ssc_syn_set * pre_div_sel * post_div_sel;
- unsigned long rate;
- dividend <<= PLL_SYN_FACTOR_DOT_POS - 1;
- rate = div64_u64_rem(dividend, factor, ÷nd);
- if (is_full_parent) {
- dividend <<= 1;
- rate <<= 1;
- }
- rate += DIV64_U64_ROUND_CLOSEST(dividend, factor);
- return rate;
- }
- static unsigned long fpll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- u32 value;
- bool clk_full;
- u32 syn_set;
- if (!fpll_is_factional_mode(pll))
- return ipll_recalc_rate(hw, parent_rate);
- syn_set = readl(pll->common.base + pll->pll_syn->set);
- if (syn_set == 0)
- return 0;
- clk_full = cv1800_clk_checkbit(&pll->common,
- &pll->pll_syn->clk_half);
- value = readl(pll->common.base + pll->pll_reg);
- return fpll_calc_rate(parent_rate,
- PLL_GET_PRE_DIV_SEL(value),
- PLL_GET_DIV_SEL(value),
- PLL_GET_POST_DIV_SEL(value),
- syn_set, clk_full);
- }
- static unsigned long fpll_find_synthesizer(unsigned long parent,
- unsigned long rate,
- unsigned long pre_div,
- unsigned long div,
- unsigned long post_div,
- bool is_full_parent,
- u32 *ssc_syn_set)
- {
- u32 test_max = U32_MAX, test_min = PLL_SYN_FACTOR_MINIMUM;
- unsigned long trate;
- while (test_min < test_max) {
- u32 tssc = (test_max + test_min) / 2;
- trate = fpll_calc_rate(parent, pre_div, div, post_div,
- tssc, is_full_parent);
- if (trate == rate) {
- test_min = tssc;
- break;
- }
- if (trate > rate)
- test_min = tssc + 1;
- else
- test_max = tssc - 1;
- }
- if (trate != 0)
- *ssc_syn_set = test_min;
- return trate;
- }
- static int fpll_find_rate(struct cv1800_clk_pll *pll,
- const struct cv1800_clk_pll_limit *limit,
- unsigned long prate,
- unsigned long *rate,
- u32 *value, u32 *ssc_syn_set)
- {
- unsigned long best_rate = 0;
- unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
- unsigned long pre, div, post;
- unsigned long trate = *rate;
- u32 detected = *value;
- unsigned long tmp;
- bool clk_full = cv1800_clk_checkbit(&pll->common,
- &pll->pll_syn->clk_half);
- for_each_pll_limit_range(pre, &limit->pre_div) {
- for_each_pll_limit_range(post, &limit->post_div) {
- for_each_pll_limit_range(div, &limit->div) {
- tmp = fpll_find_synthesizer(prate, trate,
- pre, div, post,
- clk_full,
- ssc_syn_set);
- if ((trate - tmp) < (trate - best_rate)) {
- best_rate = tmp;
- pre_div_sel = pre;
- div_sel = div;
- post_div_sel = post;
- }
- }
- }
- }
- if (best_rate) {
- detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
- detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
- detected = PLL_SET_DIV_SEL(detected, div_sel);
- *value = detected;
- *rate = best_rate;
- return 0;
- }
- return -EINVAL;
- }
- static int fpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- u32 val, ssc_syn_set;
- if (!fpll_is_factional_mode(pll))
- return ipll_determine_rate(hw, req);
- fpll_find_rate(pll, &pll->pll_limit[2], req->best_parent_rate,
- &req->rate, &val, &ssc_syn_set);
- return 0;
- }
- static bool fpll_check_mode_ctrl_restrict(unsigned long div_sel,
- unsigned long ictrl,
- unsigned long mode)
- {
- unsigned long left_rest = 10 * div_sel;
- unsigned long right_rest = 24 * div_sel;
- unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
- return test > left_rest && test <= right_rest;
- }
- static int fpll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
- {
- u32 regval;
- u32 detected = 0, detected_ssc = 0;
- unsigned long flags;
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- if (!fpll_is_factional_mode(pll))
- return ipll_set_rate(hw, rate, parent_rate);
- fpll_find_rate(pll, &pll->pll_limit[2], parent_rate,
- &rate, &detected, &detected_ssc);
- pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
- fpll_check_mode_ctrl_restrict,
- pll->pll_limit, &detected);
- spin_lock_irqsave(pll->common.lock, flags);
- writel(detected_ssc, pll->common.base + pll->pll_syn->set);
- regval = readl(pll->common.base + pll->pll_reg);
- regval = PLL_COPY_REG(regval, detected);
- writel(regval, pll->common.base + pll->pll_reg);
- spin_unlock_irqrestore(pll->common.lock, flags);
- cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
- BIT(pll->pll_status.shift));
- return 0;
- }
- static u8 fpll_get_parent(struct clk_hw *hw)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- if (fpll_is_factional_mode(pll))
- return 1;
- return 0;
- }
- static int fpll_set_parent(struct clk_hw *hw, u8 index)
- {
- struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
- if (index)
- cv1800_clk_setbit(&pll->common, &pll->pll_syn->en);
- else
- cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en);
- return 0;
- }
- const struct clk_ops cv1800_clk_fpll_ops = {
- .disable = pll_disable,
- .enable = pll_enable,
- .is_enabled = pll_is_enable,
- .recalc_rate = fpll_recalc_rate,
- .determine_rate = fpll_determine_rate,
- .set_rate = fpll_set_rate,
- .set_parent = fpll_set_parent,
- .get_parent = fpll_get_parent,
- };
|