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 21 kB image not shown  

Quelle  clk-cv18xx-ip.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/gcd.h>
#include <linux/spinlock.h>

#include "clk-cv18xx-ip.h"

/* GATE */
static inline struct cv1800_clk_gate *hw_to_cv1800_clk_gate(struct clk_hw *hw)
{
 struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);

 return container_of(common, struct cv1800_clk_gate, common);
}

static int gate_enable(struct clk_hw *hw)
{
 struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);

 return cv1800_clk_setbit(&gate->common, &gate->gate);
}

static void gate_disable(struct clk_hw *hw)
{
 struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);

 cv1800_clk_clearbit(&gate->common, &gate->gate);
}

static int gate_is_enabled(struct clk_hw *hw)
{
 struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);

 return cv1800_clk_checkbit(&gate->common, &gate->gate);
}

static unsigned long gate_recalc_rate(struct clk_hw *hw,
          unsigned long parent_rate)
{
 return parent_rate;
}

static long gate_round_rate(struct clk_hw *hw, unsigned long rate,
       unsigned long *parent_rate)
{
 return *parent_rate;
}

static int gate_set_rate(struct clk_hw *hw, unsigned long rate,
    unsigned long parent_rate)
{
 return 0;
}

const struct clk_ops cv1800_clk_gate_ops = {
 .disable = gate_disable,
 .enable = gate_enable,
 .is_enabled = gate_is_enabled,

 .recalc_rate = gate_recalc_rate,
 .round_rate = gate_round_rate,
 .set_rate = gate_set_rate,
};

/* DIV */
#define _DIV_EN_CLK_DIV_FACTOR_FIELD  BIT(3)

#define DIV_GET_EN_CLK_DIV_FACTOR(_reg) \
 FIELD_GET(_DIV_EN_CLK_DIV_FACTOR_FIELD, _reg)

#define DIV_SET_EN_DIV_FACTOR(_reg) \
 _CV1800_SET_FIELD(_reg, 1, _DIV_EN_CLK_DIV_FACTOR_FIELD)

static inline struct cv1800_clk_div *hw_to_cv1800_clk_div(struct clk_hw *hw)
{
 struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);

 return container_of(common, struct cv1800_clk_div, common);
}

static int div_enable(struct clk_hw *hw)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);

 return cv1800_clk_setbit(&div->common, &div->gate);
}

static void div_disable(struct clk_hw *hw)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);

 cv1800_clk_clearbit(&div->common, &div->gate);
}

static int div_is_enabled(struct clk_hw *hw)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);

 return cv1800_clk_checkbit(&div->common, &div->gate);
}

static int div_helper_set_rate(struct cv1800_clk_common *common,
          struct cv1800_clk_regfield *div,
          unsigned long val)
{
 unsigned long flags;
 u32 reg;

 if (div->width == 0)
  return 0;

 spin_lock_irqsave(common->lock, flags);

 reg = readl(common->base + div->reg);
 reg = cv1800_clk_regfield_set(reg, val, div);
 if (div->initval > 0)
  reg = DIV_SET_EN_DIV_FACTOR(reg);

 writel(reg, common->base + div->reg);

 spin_unlock_irqrestore(common->lock, flags);

 return 0;
}

static u32 div_helper_get_clockdiv(struct cv1800_clk_common *common,
       struct cv1800_clk_regfield *div)
{
 u32 clockdiv = 1;
 u32 reg;

 if (!div || div->initval < 0 || (div->width == 0 && div->initval <= 0))
  return 1;

 if (div->width == 0 && div->initval > 0)
  return div->initval;

 reg = readl(common->base + div->reg);

 if (div->initval == 0 || DIV_GET_EN_CLK_DIV_FACTOR(reg))
  clockdiv = cv1800_clk_regfield_get(reg, div);
 else if (div->initval > 0)
  clockdiv = div->initval;

 return clockdiv;
}

static u32 div_helper_round_rate(struct cv1800_clk_regfield *div,
     struct clk_hw *hw, struct clk_hw *parent,
     unsigned long rate, unsigned long *prate)
{
 if (div->width == 0) {
  if (div->initval <= 0)
   return DIV_ROUND_UP_ULL(*prate, 1);
  else
   return DIV_ROUND_UP_ULL(*prate, div->initval);
 }

 return divider_round_rate_parent(hw, parent, rate, prate, NULL,
      div->width, div->flags);
}

static long div_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
      unsigned long rate, int id, void *data)
{
 struct cv1800_clk_div *div = data;

 return div_helper_round_rate(&div->div, &div->common.hw, parent,
         rate, parent_rate);
}

static bool div_is_better_rate(struct cv1800_clk_common *common,
          unsigned long target, unsigned long now,
          unsigned long best)
{
 if (common->features & CLK_DIVIDER_ROUND_CLOSEST)
  return abs_diff(target, now) < abs_diff(target, best);

 return now <= target && now > best;
}

static int mux_helper_determine_rate(struct cv1800_clk_common *common,
         struct clk_rate_request *req,
         long (*round)(struct clk_hw *,
         unsigned long *,
         unsigned long,
         int,
         void *),
         void *data)
{
 unsigned long best_parent_rate = 0, best_rate = 0;
 struct clk_hw *best_parent, *hw = &common->hw;
 unsigned int i;

 if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
  unsigned long adj_parent_rate;

  best_parent = clk_hw_get_parent(hw);
  best_parent_rate = clk_hw_get_rate(best_parent);

  best_rate = round(best_parent, &adj_parent_rate,
      req->rate, -1, data);

  goto find;
 }

 for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  unsigned long tmp_rate, parent_rate;
  struct clk_hw *parent;

  parent = clk_hw_get_parent_by_index(hw, i);
  if (!parent)
   continue;

  parent_rate = clk_hw_get_rate(parent);

  tmp_rate = round(parent, &parent_rate, req->rate, i, data);

  if (tmp_rate == req->rate) {
   best_parent = parent;
   best_parent_rate = parent_rate;
   best_rate = tmp_rate;
   goto find;
  }

  if (div_is_better_rate(common, req->rate,
           tmp_rate, best_rate)) {
   best_parent = parent;
   best_parent_rate = parent_rate;
   best_rate = tmp_rate;
  }
 }

 if (best_rate == 0)
  return -EINVAL;

find:
 req->best_parent_hw = best_parent;
 req->best_parent_rate = best_parent_rate;
 req->rate = best_rate;
 return 0;
}

static int div_determine_rate(struct clk_hw *hw,
         struct clk_rate_request *req)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);

 return mux_helper_determine_rate(&div->common, req,
      div_round_rate, div);
}

static unsigned long div_recalc_rate(struct clk_hw *hw,
         unsigned long parent_rate)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
 unsigned long val;

 val = div_helper_get_clockdiv(&div->common, &div->div);
 if (val == 0)
  return 0;

 return divider_recalc_rate(hw, parent_rate, val, NULL,
       div->div.flags, div->div.width);
}

static int div_set_rate(struct clk_hw *hw, unsigned long rate,
    unsigned long parent_rate)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
 unsigned long val;

 val = divider_get_val(rate, parent_rate, NULL,
         div->div.width, div->div.flags);

 return div_helper_set_rate(&div->common, &div->div, val);
}

const struct clk_ops cv1800_clk_div_ops = {
 .disable = div_disable,
 .enable = div_enable,
 .is_enabled = div_is_enabled,

 .determine_rate = div_determine_rate,
 .recalc_rate = div_recalc_rate,
 .set_rate = div_set_rate,
};

static inline struct cv1800_clk_bypass_div *
hw_to_cv1800_clk_bypass_div(struct clk_hw *hw)
{
 struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);

 return container_of(div, struct cv1800_clk_bypass_div, div);
}

static long bypass_div_round_rate(struct clk_hw *parent,
      unsigned long *parent_rate,
      unsigned long rate, int id, void *data)
{
 struct cv1800_clk_bypass_div *div = data;

 if (id == -1) {
  if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
   return *parent_rate;
  else
   return div_round_rate(parent, parent_rate, rate,
           -1, &div->div);
 }

 if (id == 0)
  return *parent_rate;

 return div_round_rate(parent, parent_rate, rate, id - 1, &div->div);
}

static int bypass_div_determine_rate(struct clk_hw *hw,
         struct clk_rate_request *req)
{
 struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);

 return mux_helper_determine_rate(&div->div.common, req,
      bypass_div_round_rate, div);
}

static unsigned long bypass_div_recalc_rate(struct clk_hw *hw,
         unsigned long parent_rate)
{
 struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);

 if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
  return parent_rate;

 return div_recalc_rate(hw, parent_rate);
}

static int bypass_div_set_rate(struct clk_hw *hw, unsigned long rate,
          unsigned long parent_rate)
{
 struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);

 if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
  return 0;

 return div_set_rate(hw, rate, parent_rate);
}

static u8 bypass_div_get_parent(struct clk_hw *hw)
{
 struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);

 if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
  return 0;

 return 1;
}

static int bypass_div_set_parent(struct clk_hw *hw, u8 index)
{
 struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);

 if (index)
  return cv1800_clk_clearbit(&div->div.common, &div->bypass);

 return cv1800_clk_setbit(&div->div.common, &div->bypass);
}

const struct clk_ops cv1800_clk_bypass_div_ops = {
 .disable = div_disable,
 .enable = div_enable,
 .is_enabled = div_is_enabled,

 .determine_rate = bypass_div_determine_rate,
 .recalc_rate = bypass_div_recalc_rate,
 .set_rate = bypass_div_set_rate,

 .set_parent = bypass_div_set_parent,
 .get_parent = bypass_div_get_parent,
};

/* MUX */
static inline struct cv1800_clk_mux *hw_to_cv1800_clk_mux(struct clk_hw *hw)
{
 struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);

 return container_of(common, struct cv1800_clk_mux, common);
}

static int mux_enable(struct clk_hw *hw)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);

 return cv1800_clk_setbit(&mux->common, &mux->gate);
}

static void mux_disable(struct clk_hw *hw)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);

 cv1800_clk_clearbit(&mux->common, &mux->gate);
}

static int mux_is_enabled(struct clk_hw *hw)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);

 return cv1800_clk_checkbit(&mux->common, &mux->gate);
}

static long mux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
      unsigned long rate, int id, void *data)
{
 struct cv1800_clk_mux *mux = data;

 return div_helper_round_rate(&mux->div, &mux->common.hw, parent,
         rate, parent_rate);
}

static int mux_determine_rate(struct clk_hw *hw,
         struct clk_rate_request *req)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);

 return mux_helper_determine_rate(&mux->common, req,
      mux_round_rate, mux);
}

static unsigned long mux_recalc_rate(struct clk_hw *hw,
         unsigned long parent_rate)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
 unsigned long val;

 val = div_helper_get_clockdiv(&mux->common, &mux->div);
 if (val == 0)
  return 0;

 return divider_recalc_rate(hw, parent_rate, val, NULL,
       mux->div.flags, mux->div.width);
}

static int mux_set_rate(struct clk_hw *hw, unsigned long rate,
   unsigned long parent_rate)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
 unsigned long val;

 val = divider_get_val(rate, parent_rate, NULL,
         mux->div.width, mux->div.flags);

 return div_helper_set_rate(&mux->common, &mux->div, val);
}

static u8 mux_get_parent(struct clk_hw *hw)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
 u32 reg = readl(mux->common.base + mux->mux.reg);

 return cv1800_clk_regfield_get(reg, &mux->mux);
}

static int _mux_set_parent(struct cv1800_clk_mux *mux, u8 index)
{
 u32 reg;

 reg = readl(mux->common.base + mux->mux.reg);
 reg = cv1800_clk_regfield_set(reg, index, &mux->mux);
 writel(reg, mux->common.base + mux->mux.reg);

 return 0;
}

static int mux_set_parent(struct clk_hw *hw, u8 index)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
 unsigned long flags;

 spin_lock_irqsave(mux->common.lock, flags);

 _mux_set_parent(mux, index);

 spin_unlock_irqrestore(mux->common.lock, flags);

 return 0;
}

const struct clk_ops cv1800_clk_mux_ops = {
 .disable = mux_disable,
 .enable = mux_enable,
 .is_enabled = mux_is_enabled,

 .determine_rate = mux_determine_rate,
 .recalc_rate = mux_recalc_rate,
 .set_rate = mux_set_rate,

 .set_parent = mux_set_parent,
 .get_parent = mux_get_parent,
};

static inline struct cv1800_clk_bypass_mux *
hw_to_cv1800_clk_bypass_mux(struct clk_hw *hw)
{
 struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);

 return container_of(mux, struct cv1800_clk_bypass_mux, mux);
}

static long bypass_mux_round_rate(struct clk_hw *parent,
      unsigned long *parent_rate,
      unsigned long rate, int id, void *data)
{
 struct cv1800_clk_bypass_mux *mux = data;

 if (id == -1) {
  if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
   return *parent_rate;
  else
   return mux_round_rate(parent, parent_rate, rate,
           -1, &mux->mux);
 }

 if (id == 0)
  return *parent_rate;

 return mux_round_rate(parent, parent_rate, rate, id - 1, &mux->mux);
}

static int bypass_mux_determine_rate(struct clk_hw *hw,
         struct clk_rate_request *req)
{
 struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);

 return mux_helper_determine_rate(&mux->mux.common, req,
      bypass_mux_round_rate, mux);
}

static unsigned long bypass_mux_recalc_rate(struct clk_hw *hw,
         unsigned long parent_rate)
{
 struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);

 if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
  return parent_rate;

 return mux_recalc_rate(hw, parent_rate);
}

static int bypass_mux_set_rate(struct clk_hw *hw, unsigned long rate,
          unsigned long parent_rate)
{
 struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);

 if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
  return 0;

 return mux_set_rate(hw, rate, parent_rate);
}

static u8 bypass_mux_get_parent(struct clk_hw *hw)
{
 struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);

 if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
  return 0;

 return mux_get_parent(hw) + 1;
}

static int bypass_mux_set_parent(struct clk_hw *hw, u8 index)
{
 struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);

 if (index == 0)
  return cv1800_clk_setbit(&mux->mux.common, &mux->bypass);

 return cv1800_clk_clearbit(&mux->mux.common, &mux->bypass);
}

const struct clk_ops cv1800_clk_bypass_mux_ops = {
 .disable = mux_disable,
 .enable = mux_enable,
 .is_enabled = mux_is_enabled,

 .determine_rate = bypass_mux_determine_rate,
 .recalc_rate = bypass_mux_recalc_rate,
 .set_rate = bypass_mux_set_rate,

 .set_parent = bypass_mux_set_parent,
 .get_parent = bypass_mux_get_parent,
};

/* MMUX */
static inline struct cv1800_clk_mmux *hw_to_cv1800_clk_mmux(struct clk_hw *hw)
{
 struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);

 return container_of(common, struct cv1800_clk_mmux, common);
}

static u8 mmux_get_parent_id(struct cv1800_clk_mmux *mmux)
{
 struct clk_hw *hw = &mmux->common.hw;
 struct clk_hw *parent = clk_hw_get_parent(hw);
 unsigned int i;

 for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  if (parent == clk_hw_get_parent_by_index(hw, i))
   return i;
 }

 BUG();
}

static int mmux_enable(struct clk_hw *hw)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);

 return cv1800_clk_setbit(&mmux->common, &mmux->gate);
}

static void mmux_disable(struct clk_hw *hw)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);

 cv1800_clk_clearbit(&mmux->common, &mmux->gate);
}

static int mmux_is_enabled(struct clk_hw *hw)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);

 return cv1800_clk_checkbit(&mmux->common, &mmux->gate);
}

static long mmux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
       unsigned long rate, int id, void *data)
{
 struct cv1800_clk_mmux *mmux = data;
 s8 div_id;

 if (id == -1) {
  if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
   return *parent_rate;

  id = mmux_get_parent_id(mmux);
 }

 div_id = mmux->parent2sel[id];

 if (div_id < 0)
  return *parent_rate;

 return div_helper_round_rate(&mmux->div[div_id],
         &mmux->common.hw, parent,
         rate, parent_rate);
}

static int mmux_determine_rate(struct clk_hw *hw,
          struct clk_rate_request *req)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);

 return mux_helper_determine_rate(&mmux->common, req,
      mmux_round_rate, mmux);
}

static unsigned long mmux_recalc_rate(struct clk_hw *hw,
          unsigned long parent_rate)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
 unsigned long val;
 struct cv1800_clk_regfield *div;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
  return parent_rate;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
  div = &mmux->div[0];
 else
  div = &mmux->div[1];

 val = div_helper_get_clockdiv(&mmux->common, div);
 if (val == 0)
  return 0;

 return divider_recalc_rate(hw, parent_rate, val, NULL,
       div->flags, div->width);
}

static int mmux_set_rate(struct clk_hw *hw, unsigned long rate,
    unsigned long parent_rate)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
 struct cv1800_clk_regfield *div;
 unsigned long val;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
  return parent_rate;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
  div = &mmux->div[0];
 else
  div = &mmux->div[1];

 val = divider_get_val(rate, parent_rate, NULL,
         div->width, div->flags);

 return div_helper_set_rate(&mmux->common, div, val);
}

static u8 mmux_get_parent(struct clk_hw *hw)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
 struct cv1800_clk_regfield *mux;
 u32 reg;
 s8 clk_sel;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
  return 0;

 if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
  clk_sel = 0;
 else
  clk_sel = 1;
 mux = &mmux->mux[clk_sel];

 reg = readl(mmux->common.base + mux->reg);

 return mmux->sel2parent[clk_sel][cv1800_clk_regfield_get(reg, mux)];
}

static int mmux_set_parent(struct clk_hw *hw, u8 index)
{
 struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
 struct cv1800_clk_regfield *mux;
 unsigned long flags;
 u32 reg;
 s8 clk_sel = mmux->parent2sel[index];

 if (index == 0 || clk_sel == -1) {
  cv1800_clk_setbit(&mmux->common, &mmux->bypass);
  goto release;
 }

 cv1800_clk_clearbit(&mmux->common, &mmux->bypass);

 if (clk_sel)
  cv1800_clk_clearbit(&mmux->common, &mmux->clk_sel);
 else
  cv1800_clk_setbit(&mmux->common, &mmux->clk_sel);

 spin_lock_irqsave(mmux->common.lock, flags);

 mux = &mmux->mux[clk_sel];
 reg = readl(mmux->common.base + mux->reg);
 reg = cv1800_clk_regfield_set(reg, index, mux);

 writel(reg, mmux->common.base + mux->reg);

 spin_unlock_irqrestore(mmux->common.lock, flags);

release:
 return 0;
}

const struct clk_ops cv1800_clk_mmux_ops = {
 .disable = mmux_disable,
 .enable = mmux_enable,
 .is_enabled = mmux_is_enabled,

 .determine_rate = mmux_determine_rate,
 .recalc_rate = mmux_recalc_rate,
 .set_rate = mmux_set_rate,

 .set_parent = mmux_set_parent,
 .get_parent = mmux_get_parent,
};

/* AUDIO CLK */
static inline struct cv1800_clk_audio *
hw_to_cv1800_clk_audio(struct clk_hw *hw)
{
 struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);

 return container_of(common, struct cv1800_clk_audio, common);
}

static int aclk_enable(struct clk_hw *hw)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);

 cv1800_clk_setbit(&aclk->common, &aclk->src_en);
 return cv1800_clk_setbit(&aclk->common, &aclk->output_en);
}

static void aclk_disable(struct clk_hw *hw)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);

 cv1800_clk_clearbit(&aclk->common, &aclk->output_en);
 cv1800_clk_clearbit(&aclk->common, &aclk->src_en);
}

static int aclk_is_enabled(struct clk_hw *hw)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);

 return cv1800_clk_checkbit(&aclk->common, &aclk->output_en);
}

static int aclk_determine_rate(struct clk_hw *hw,
          struct clk_rate_request *req)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);

 req->rate = aclk->target_rate;

 return 0;
}

static unsigned long aclk_recalc_rate(struct clk_hw *hw,
          unsigned long parent_rate)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
 u64 rate = parent_rate;
 u64 factor = 2;
 u32 regval;

 if (!cv1800_clk_checkbit(&aclk->common, &aclk->div_en))
  return 0;

 regval = readl(aclk->common.base + aclk->m.reg);
 factor *= cv1800_clk_regfield_get(regval, &aclk->m);

 regval = readl(aclk->common.base + aclk->n.reg);
 rate *= cv1800_clk_regfield_get(regval, &aclk->n);

 return DIV64_U64_ROUND_UP(rate, factor);
}

static void aclk_determine_mn(unsigned long parent_rate, unsigned long rate,
         u32 *m, u32 *n)
{
 u32 tm = parent_rate / 2;
 u32 tn = rate;
 u32 tcommon = gcd(tm, tn);
 *m = tm / tcommon;
 *n = tn / tcommon;
}

static int aclk_set_rate(struct clk_hw *hw, unsigned long rate,
    unsigned long parent_rate)
{
 struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
 unsigned long flags;
 u32 m, n;

 aclk_determine_mn(parent_rate, rate,
     &m, &n);

 spin_lock_irqsave(aclk->common.lock, flags);

 writel(m, aclk->common.base + aclk->m.reg);
 writel(n, aclk->common.base + aclk->n.reg);

 cv1800_clk_setbit(&aclk->common, &aclk->div_en);
 cv1800_clk_setbit(&aclk->common, &aclk->div_up);

 spin_unlock_irqrestore(aclk->common.lock, flags);

 return 0;
}

const struct clk_ops cv1800_clk_audio_ops = {
 .disable = aclk_disable,
 .enable = aclk_enable,
 .is_enabled = aclk_is_enabled,

 .determine_rate = aclk_determine_rate,
 .recalc_rate = aclk_recalc_rate,
 .set_rate = aclk_set_rate,
};

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

¤ Dauer der Verarbeitung: 0.7 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.