クロック クロックは SoC 内のパルスであり、各コンポーネントが独自のペースで動作するように制御します。たとえば、CPU 周波数設定、シリアル ポートのボー レート設定、I2S サンプリング レート設定、I2C レート設定などです。これらの異なるクロック設定は 1 つまたは複数のクロック ソースから取得され、最終的にクロック ツリーを形成する必要があります。このクロック ツリーは、cat /sys/kernel/debug/clk/clk_summary コマンドを通じて表示できます。
CCF フレームワークは、カーネルでクロックを管理するために使用されます。下図に示すように、右側がクロックプロバイダー、つまり Clock Provider、中央が CCF、左側がデバイスドライバーのクロックコンシューマー、つまり Clock Consumer です。
クロックの特性に応じて、クロック フレームワークはクロックを 6 つのカテゴリ (固定レート、ゲート、デバイダー、マルチプレクサ、固定ファクター、コンポジット) に分類します。
#########データ構造###### 上記の 6 つのカテゴリは本質的にクロック デバイスです。カーネルはこれらのクロック HW ブロックの特性を抽出し、struct clk_hw を使用してそれらを表現します。詳細は次のとおりです:これらのデータ構造間の関係を説明する図は次のとおりです:
登録方法
データ構造を理解したところで、各種時計デバイスの登録方法を見ていきましょう。
このタイプのクロックは周波数が固定されており、オン/オフの切り替え、周波数の調整、親の選択はできません。最も単純なタイプのクロックです。 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 名称获取 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; }
至此,Consumer 与 Provider 里讲的 of_clk_add_provider 对应起来了。
//启动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)
以上が1 つの記事で理解 | Linux クロック サブシステムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。