Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/clk/sophgo/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 10 kB image not shown  

Quelle  clk-cv18xx-pll.c   Sprache: C

 
// 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,
};

Messung V0.5
C=95 H=93 G=93

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.