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

Quelle  pll.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
//
// Spreadtrum pll clock driver
//
// Copyright (C) 2015~2017 Spreadtrum, Inc.
// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

#include "pll.h"

#define CLK_PLL_1M 1000000
#define CLK_PLL_10M (CLK_PLL_1M * 10)

#define pindex(pll, member)  \
 (pll->factors[member].shift / (8 * sizeof(pll->regs_num)))

#define pshift(pll, member)  \
 (pll->factors[member].shift % (8 * sizeof(pll->regs_num)))

#define pwidth(pll, member)  \
 pll->factors[member].width

#define pmask(pll, member)     \
 ((pwidth(pll, member)) ?    \
 GENMASK(pwidth(pll, member) + pshift(pll, member) - 1, \
 pshift(pll, member)) : 0)

#define pinternal(pll, cfg, member) \
 (cfg[pindex(pll, member)] & pmask(pll, member))

#define pinternal_val(pll, cfg, member) \
 (pinternal(pll, cfg, member) >> pshift(pll, member))

static inline unsigned int
sprd_pll_read(const struct sprd_pll *pll, u8 index)
{
 const struct sprd_clk_common *common = &pll->common;
 unsigned int val = 0;

 if (WARN_ON(index >= pll->regs_num))
  return 0;

 regmap_read(common->regmap, common->reg + index * 4, &val);

 return val;
}

static inline void
sprd_pll_write(const struct sprd_pll *pll, u8 index,
      u32 msk, u32 val)
{
 const struct sprd_clk_common *common = &pll->common;
 unsigned int offset, reg;
 int ret = 0;

 if (WARN_ON(index >= pll->regs_num))
  return;

 offset = common->reg + index * 4;
 ret = regmap_read(common->regmap, offset, ®);
 if (!ret)
  regmap_write(common->regmap, offset, (reg & ~msk) | val);
}

static unsigned long pll_get_refin(const struct sprd_pll *pll)
{
 u32 shift, mask, index, refin_id = 3;
 const unsigned long refin[4] = { 2, 4, 13, 26 };

 if (pwidth(pll, PLL_REFIN)) {
  index = pindex(pll, PLL_REFIN);
  shift = pshift(pll, PLL_REFIN);
  mask = pmask(pll, PLL_REFIN);
  refin_id = (sprd_pll_read(pll, index) & mask) >> shift;
  if (refin_id > 3)
   refin_id = 3;
 }

 return refin[refin_id];
}

static u32 pll_get_ibias(u64 rate, const u64 *table)
{
 u32 i, num = table[0];

 /* table[0] indicates the number of items in this table */
 for (i = 0; i < num; i++)
  if (rate <= table[i + 1])
   break;

 return i == num ? num - 1 : i;
}

static unsigned long _sprd_pll_recalc_rate(const struct sprd_pll *pll,
        unsigned long parent_rate)
{
 u32 *cfg;
 u32 i, mask, regs_num = pll->regs_num;
 unsigned long rate, nint, kint = 0;
 u64 refin;
 u16 k1, k2;

 cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL);
 if (!cfg)
  return parent_rate;

 for (i = 0; i < regs_num; i++)
  cfg[i] = sprd_pll_read(pll, i);

 refin = pll_get_refin(pll);

 if (pinternal(pll, cfg, PLL_PREDIV))
  refin = refin * 2;

 if (pwidth(pll, PLL_POSTDIV) &&
     ((pll->fflag == 1 && pinternal(pll, cfg, PLL_POSTDIV)) ||
      (!pll->fflag && !pinternal(pll, cfg, PLL_POSTDIV))))
  refin = refin / 2;

 if (!pinternal(pll, cfg, PLL_DIV_S)) {
  rate = refin * pinternal_val(pll, cfg, PLL_N) * CLK_PLL_10M;
 } else {
  nint = pinternal_val(pll, cfg, PLL_NINT);
  if (pinternal(pll, cfg, PLL_SDM_EN))
   kint = pinternal_val(pll, cfg, PLL_KINT);

  mask = pmask(pll, PLL_KINT);

  k1 = pll->k1;
  k2 = pll->k2;
  rate = DIV_ROUND_CLOSEST_ULL(refin * kint * k1,
      ((mask >> __ffs(mask)) + 1)) *
      k2 + refin * nint * CLK_PLL_1M;
 }

 kfree(cfg);
 return rate;
}

#define SPRD_PLL_WRITE_CHECK(pll, i, mask, val)  \
 (((sprd_pll_read(pll, i) & mask) == val) ? 0 : (-EFAULT))

static int _sprd_pll_set_rate(const struct sprd_pll *pll,
         unsigned long rate,
         unsigned long parent_rate)
{
 struct reg_cfg *cfg;
 int ret = 0;
 u32 mask, shift, width, ibias_val, index;
 u32 regs_num = pll->regs_num, i = 0;
 unsigned long kint, nint;
 u64 tmp, refin, fvco = rate;

 cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL);
 if (!cfg)
  return -ENOMEM;

 refin = pll_get_refin(pll);

 mask = pmask(pll, PLL_PREDIV);
 index = pindex(pll, PLL_PREDIV);
 width = pwidth(pll, PLL_PREDIV);
 if (width && (sprd_pll_read(pll, index) & mask))
  refin = refin * 2;

 mask = pmask(pll, PLL_POSTDIV);
 index = pindex(pll, PLL_POSTDIV);
 width = pwidth(pll, PLL_POSTDIV);
 cfg[index].msk = mask;
 if (width && ((pll->fflag == 1 && fvco <= pll->fvco) ||
        (pll->fflag == 0 && fvco > pll->fvco)))
  cfg[index].val |= mask;

 if (width && fvco <= pll->fvco)
  fvco = fvco * 2;

 mask = pmask(pll, PLL_DIV_S);
 index = pindex(pll, PLL_DIV_S);
 cfg[index].val |= mask;
 cfg[index].msk |= mask;

 mask = pmask(pll, PLL_SDM_EN);
 index = pindex(pll, PLL_SDM_EN);
 cfg[index].val |= mask;
 cfg[index].msk |= mask;

 nint = do_div(fvco, refin * CLK_PLL_1M);
 mask = pmask(pll, PLL_NINT);
 index = pindex(pll, PLL_NINT);
 shift = pshift(pll, PLL_NINT);
 cfg[index].val |= (nint << shift) & mask;
 cfg[index].msk |= mask;

 mask = pmask(pll, PLL_KINT);
 index = pindex(pll, PLL_KINT);
 width = pwidth(pll, PLL_KINT);
 shift = pshift(pll, PLL_KINT);
 tmp = fvco - refin * nint * CLK_PLL_1M;
 tmp = do_div(tmp, 10000) * ((mask >> shift) + 1);
 kint = DIV_ROUND_CLOSEST_ULL(tmp, refin * 100);
 cfg[index].val |= (kint << shift) & mask;
 cfg[index].msk |= mask;

 ibias_val = pll_get_ibias(fvco, pll->itable);

 mask = pmask(pll, PLL_IBIAS);
 index = pindex(pll, PLL_IBIAS);
 shift = pshift(pll, PLL_IBIAS);
 cfg[index].val |= ibias_val << shift & mask;
 cfg[index].msk |= mask;

 for (i = 0; i < regs_num; i++) {
  if (cfg[i].msk) {
   sprd_pll_write(pll, i, cfg[i].msk, cfg[i].val);
   ret |= SPRD_PLL_WRITE_CHECK(pll, i, cfg[i].msk,
         cfg[i].val);
  }
 }

 if (!ret)
  udelay(pll->udelay);

 kfree(cfg);
 return ret;
}

static unsigned long sprd_pll_recalc_rate(struct clk_hw *hw,
       unsigned long parent_rate)
{
 struct sprd_pll *pll = hw_to_sprd_pll(hw);

 return _sprd_pll_recalc_rate(pll, parent_rate);
}

static int sprd_pll_set_rate(struct clk_hw *hw,
        unsigned long rate,
        unsigned long parent_rate)
{
 struct sprd_pll *pll = hw_to_sprd_pll(hw);

 return _sprd_pll_set_rate(pll, rate, parent_rate);
}

static int sprd_pll_clk_prepare(struct clk_hw *hw)
{
 struct sprd_pll *pll = hw_to_sprd_pll(hw);

 udelay(pll->udelay);

 return 0;
}

static long sprd_pll_round_rate(struct clk_hw *hw, unsigned long rate,
    unsigned long *prate)
{
 return rate;
}

const struct clk_ops sprd_pll_ops = {
 .prepare = sprd_pll_clk_prepare,
 .recalc_rate = sprd_pll_recalc_rate,
 .round_rate = sprd_pll_round_rate,
 .set_rate = sprd_pll_set_rate,
};
EXPORT_SYMBOL_GPL(sprd_pll_ops);

Messung V0.5
C=94 H=84 G=88

¤ 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.