ホームページ > システムチュートリアル > Linux > 1 つの記事で理解 | Linux クロック サブシステム

1 つの記事で理解 | Linux クロック サブシステム

WBOY
リリース: 2024-02-12 09:42:02
転載
1281 人が閲覧しました

クロック クロックは SoC 内のパルスであり、各コンポーネントが独自のペースで動作するように制御します。たとえば、CPU 周波数設定、シリアル ポートのボー レート設定、I2S サンプリング レート設定、I2C レート設定などです。これらの異なるクロック設定は 1 つまたは複数のクロック ソースから取得され、最終的にクロック ツリーを形成する必要があります。このクロック ツリーは、cat /sys/kernel/debug/clk/clk_summary コマンドを通じて表示できます。

CCF フレームワークは、カーネルでクロックを管理するために使用されます。下図に示すように、右側がクロックプロバイダー、つまり Clock Provider、中央が CCF、左側がデバイスドライバーのクロックコンシューマー、つまり Clock Consumer です。

一文搞懂 | Linux 时钟子系统

クロックプロバイダー

  • ルート ノードは通常、オシレーター (アクティブ オシレーター) またはクリスタル (パッシブ オシレーター) です。
  • 中間ノードには、PLL (位相ロック ループ、周波数を上げるために使用)、Divider (分周器、周波数を下げるために使用)、Mux (複数のクロック パスから 1 つを選択)、Gate (ON/OFF の制御に使用) など、さまざまな種類があります。オフ)。
  • リーフ ノードは、クロックを入力として使用する特定の機能を備えた HW ブロックです。

クロックの特性に応じて、クロック フレームワークはクロックを 6 つのカテゴリ (固定レート、ゲート、デバイダー、マルチプレクサ、固定ファクター、コンポジット) に分類します。

#########データ構造###### 一文搞懂 | Linux 时钟子系统上記の 6 つのカテゴリは本質的にクロック デバイスです。カーネルはこれらのクロック HW ブロックの特性を抽出し、struct clk_hw を使用してそれらを表現します。詳細は次のとおりです:

リーリー 固定周波数バイブレータの固定レートを例にとると、そのデータ構造は次のとおりです。 リーリー これはおそらく他の特定のクロック デバイスにも当てはまるため、ここでは詳しく説明しません。

これらのデータ構造間の関係を説明する図は次のとおりです:

登録方法

データ構造を理解したところで、各種時計デバイスの登録方法を見ていきましょう。 一文搞懂 | Linux 时钟子系统

1. 固定レートクロック

このタイプのクロックは周波数が固定されており、オン/オフの切り替え、周波数の調整、親の選択はできません。最も単純なタイプのクロックです。 DTS 構成を通じて直接サポートできます。次のように、インターフェイスを介して固定レート クロックを直接登録することもできます。 リーリー

2. ゲートクロック

このタイプの時計はオンとオフのみ可能であり (.enable/.disable コールバックが提供されます)、次のインターフェイスを使用して登録できます:

リーリー

3.分周器クロック

このタイプのクロックは、分周値を設定できます (したがって、.recalc_rate/.set_rate/.round_rate コールバックが提供されます)。これは、次の 2 つのインターフェイスを通じて登録できます。

リーリー

4.マルチプレクサクロック

このタイプのクロックは、.get_parent/.set_parent/.recalc_rate コールバックを実装し、次の 2 つのインターフェイスを通じて登録できるため、複数の親を選択できます。 リーリー

5. 固定要素クロック

このタイプのクロックには固定係数 (つまり、乗算器と除算器) があります。クロックの周波数は、親クロックの周波数を mul で乗算し、div で除算したものです。主に、固定周波数の一部のクロックに使用されます。除算係数。親クロックの周波数を変更できるため、固定係数クロックの周波数も変更できるため、.recalc_rate/.set_rate/.round_rate などのコールバックも提供されています。次のインターフェイスを通じて登録できます:

リーリー

6.複合クロック

名前が示すように、マルチプレクサ、ディバイダ、ゲート、その他のクロックを組み合わせたもので、次のインターフェイスを通じて登録できます。 リーリー

これらの登録された関数は、最終的に関数

clk_register

を通じて Common Clock Framework に登録され、struct clk ポインターを返します。次のように:###

次に、返された struct clk ポインターを配列に保存し、of_clk_add_provider

インターフェイスを呼び出して共通クロック フレームワークに通知します。

Clock Consumer

获取 clock

即通过 clock 名称获取 struct clk 指针的过程,由 clk_get、devm_clk_get、clk_get_sys、of_clk_get、of_clk_get_by_name、of_clk_get_from_provider 等接口负责实现,这里以 clk_get 为例,分析其实现过程:

struct clk *clk_get(struct device *dev, const char *con_id)
{
 const char *dev_id = dev ? dev_name(dev) : NULL;
 struct clk *clk;

 if (dev) {
  //通过扫描所有“clock-names”中的值,和传入的name比较,如果相同,获得它的index(即“clock-names”中的

第几个),调用of_clk_get,取得clock指针。
  clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
  if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
   return clk;
 }

 return clk_get_sys(dev_id, con_id);
}
struct clk *of_clk_get(struct device_node *np, int index)
{
        struct of_phandle_args clkspec;
        struct clk *clk;
        int rc;
 
        if (index return ERR_PTR(-EINVAL);
 
        rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
                                        &clkspec);
        if (rc)
                return ERR_PTR(rc);
       //获取clock指针
        clk = of_clk_get_from_provider(&clkspec);
        of_node_put(clkspec.np);
        return clk;
}
ログイン後にコピー

of_clk_get_from_provider 通过便利 of_clk_providers 链表,并调用每一个 provider 的 get 回调函数,获取 clock 指针。如下:

struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
        struct of_clk_provider *provider;
        struct clk *clk = ERR_PTR(-ENOENT);
 
        /* Check if we have such a provider in our array */
        mutex_lock(&of_clk_lock);
        list_for_each_entry(provider, &of_clk_providers, link) {
                if (provider->node == clkspec->np)
                        clk = provider->get(clkspec, provider->data);
                if (!IS_ERR(clk))
                        break;
        }
        mutex_unlock(&of_clk_lock);
 
        return clk;
}
ログイン後にコピー

至此,ConsumerProvider 里讲的 of_clk_add_provider 对应起来了。

操作 clock

//启动clock前的准备工作/停止clock后的善后工作。可能会睡眠。
int clk_prepare(struct clk *clk)
void clk_unprepare(struct clk *clk)
 
//启动/停止clock。不会睡眠。
static inline int clk_enable(struct clk *clk)
static inline void clk_disable(struct clk *clk)

//clock频率的获取和设置
static inline unsigned long clk_get_rate(struct 
clk *clk)
static inline int clk_set_rate(struct clk *clk, unsigned long rate)
static inline long clk_round_rate(struct clk *clk, unsigned long rate)

//获取/选择clock的parent clock
static inline int clk_set_parent(struct clk *clk, struct clk *parent)
static inline struct clk *clk_get_parent(struct clk *clk)
 
//将clk_prepare和clk_enable组合起来,一起调用。将clk_disable和clk_unprepare组合起来,一起调用
static inline int clk_prepare_enable(struct clk *clk)
static inline void clk_disable_unprepare(struct clk *clk)
ログイン後にコピー

总结

一文搞懂 | Linux 时钟子系统

以上が1 つの記事で理解 | Linux クロック サブシステムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:lxlinux.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート