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

Quelle  clk-sun4i-tcon-ch1.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 2015 Maxime Ripard
 *
 * Maxime Ripard <maxime.ripard@free-electrons.com>
 */


#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>

#define TCON_CH1_SCLK2_PARENTS  4

#define TCON_CH1_SCLK2_GATE_BIT  BIT(31)
#define TCON_CH1_SCLK2_MUX_MASK  3
#define TCON_CH1_SCLK2_MUX_SHIFT 24
#define TCON_CH1_SCLK2_DIV_MASK  0xf
#define TCON_CH1_SCLK2_DIV_SHIFT 0

#define TCON_CH1_SCLK1_GATE_BIT  BIT(15)
#define TCON_CH1_SCLK1_HALF_BIT  BIT(11)

struct tcon_ch1_clk {
 struct clk_hw hw;
 spinlock_t lock;
 void __iomem *reg;
};

#define hw_to_tclk(hw) container_of(hw, struct tcon_ch1_clk, hw)

static void tcon_ch1_disable(struct clk_hw *hw)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 unsigned long flags;
 u32 reg;

 spin_lock_irqsave(&tclk->lock, flags);
 reg = readl(tclk->reg);
 reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
 writel(reg, tclk->reg);
 spin_unlock_irqrestore(&tclk->lock, flags);
}

static int tcon_ch1_enable(struct clk_hw *hw)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 unsigned long flags;
 u32 reg;

 spin_lock_irqsave(&tclk->lock, flags);
 reg = readl(tclk->reg);
 reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT;
 writel(reg, tclk->reg);
 spin_unlock_irqrestore(&tclk->lock, flags);

 return 0;
}

static int tcon_ch1_is_enabled(struct clk_hw *hw)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 u32 reg;

 reg = readl(tclk->reg);
 return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
}

static u8 tcon_ch1_get_parent(struct clk_hw *hw)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 u32 reg;

 reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
 reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;

 return reg;
}

static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 unsigned long flags;
 u32 reg;

 spin_lock_irqsave(&tclk->lock, flags);
 reg = readl(tclk->reg);
 reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT);
 reg |= index << TCON_CH1_SCLK2_MUX_SHIFT;
 writel(reg, tclk->reg);
 spin_unlock_irqrestore(&tclk->lock, flags);

 return 0;
};

static unsigned long tcon_ch1_calc_divider(unsigned long rate,
        unsigned long parent_rate,
        u8 *div,
        bool *half)
{
 unsigned long best_rate = 0;
 u8 best_m = 0, m;
 bool is_double;

 for (m = 1; m < 16; m++) {
  u8 d;

  for (d = 1; d < 3; d++) {
   unsigned long tmp_rate;

   tmp_rate = parent_rate / m / d;

   if (tmp_rate > rate)
    continue;

   if (!best_rate ||
       (rate - tmp_rate) < (rate - best_rate)) {
    best_rate = tmp_rate;
    best_m = m;
    is_double = d;
   }
  }
 }

 if (div && half) {
  *div = best_m;
  *half = is_double;
 }

 return best_rate;
}

static int tcon_ch1_determine_rate(struct clk_hw *hw,
       struct clk_rate_request *req)
{
 long best_rate = -EINVAL;
 int i;

 for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  unsigned long parent_rate;
  unsigned long tmp_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 = tcon_ch1_calc_divider(req->rate, parent_rate,
       NULL, NULL);

  if (best_rate < 0 ||
      (req->rate - tmp_rate) < (req->rate - best_rate)) {
   best_rate = tmp_rate;
   req->best_parent_rate = parent_rate;
   req->best_parent_hw = parent;
  }
 }

 if (best_rate < 0)
  return best_rate;

 req->rate = best_rate;
 return 0;
}

static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw,
       unsigned long parent_rate)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 u32 reg;

 reg = readl(tclk->reg);

 parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1;

 if (reg & TCON_CH1_SCLK1_HALF_BIT)
  parent_rate /= 2;

 return parent_rate;
}

static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate,
        unsigned long parent_rate)
{
 struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
 unsigned long flags;
 bool half;
 u8 div_m;
 u32 reg;

 tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half);

 spin_lock_irqsave(&tclk->lock, flags);
 reg = readl(tclk->reg);
 reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT);
 reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK;

 if (half)
  reg |= TCON_CH1_SCLK1_HALF_BIT;

 writel(reg, tclk->reg);
 spin_unlock_irqrestore(&tclk->lock, flags);

 return 0;
}

static const struct clk_ops tcon_ch1_ops = {
 .disable = tcon_ch1_disable,
 .enable  = tcon_ch1_enable,
 .is_enabled = tcon_ch1_is_enabled,

 .get_parent = tcon_ch1_get_parent,
 .set_parent = tcon_ch1_set_parent,

 .determine_rate = tcon_ch1_determine_rate,
 .recalc_rate = tcon_ch1_recalc_rate,
 .set_rate = tcon_ch1_set_rate,
};

static void __init tcon_ch1_setup(struct device_node *node)
{
 const char *parents[TCON_CH1_SCLK2_PARENTS];
 const char *clk_name = node->name;
 struct clk_init_data init;
 struct tcon_ch1_clk *tclk;
 struct resource res;
 struct clk *clk;
 void __iomem *reg;
 int ret;

 of_property_read_string(node, "clock-output-names", &clk_name);

 reg = of_io_request_and_map(node, 0, of_node_full_name(node));
 if (IS_ERR(reg)) {
  pr_err("%s: Could not map the clock registers\n", clk_name);
  return;
 }

 ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS);
 if (ret != TCON_CH1_SCLK2_PARENTS) {
  pr_err("%s Could not retrieve the parents\n", clk_name);
  goto err_unmap;
 }

 tclk = kzalloc(sizeof(*tclk), GFP_KERNEL);
 if (!tclk)
  goto err_unmap;

 init.name = clk_name;
 init.ops = &tcon_ch1_ops;
 init.parent_names = parents;
 init.num_parents = TCON_CH1_SCLK2_PARENTS;
 init.flags = CLK_SET_RATE_PARENT;

 tclk->reg = reg;
 tclk->hw.init = &init;
 spin_lock_init(&tclk->lock);

 clk = clk_register(NULL, &tclk->hw);
 if (IS_ERR(clk)) {
  pr_err("%s: Couldn't register the clock\n", clk_name);
  goto err_free_data;
 }

 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
 if (ret) {
  pr_err("%s: Couldn't register our clock provider\n", clk_name);
  goto err_unregister_clk;
 }

 return;

err_unregister_clk:
 clk_unregister(clk);
err_free_data:
 kfree(tclk);
err_unmap:
 iounmap(reg);
 of_address_to_resource(node, 0, &res);
 release_mem_region(res.start, resource_size(&res));
}

CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",
        tcon_ch1_setup);

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

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© 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.