Rumah  >  Artikel  >  Tutorial sistem  >  Fahami dalam satu artikel |. subsistem jam Linux

Fahami dalam satu artikel |. subsistem jam Linux

WBOY
WBOYke hadapan
2024-02-12 09:42:021214semak imbas

Jam Jam ialah nadi dalam SoC, yang mengawal setiap komponen untuk berjalan mengikut rentaknya sendiri. Contohnya, tetapan frekuensi CPU, tetapan kadar baud port bersiri, tetapan kadar pensampelan I2S, tetapan kadar I2C, dsb. Tetapan jam yang berbeza ini perlu datang daripada satu atau beberapa sumber jam, dan akhirnya membentuk pokok jam. Anda boleh melihat pokok jam ini melalui perintah cat /sys/kernel/debug/clk/clk_summary.

Rangka kerja CCF digunakan dalam kernel untuk mengurus jam. Seperti yang ditunjukkan dalam rajah di bawah, sebelah kanan ialah penyedia jam, iaitu, Pembekal Jam;

一文搞懂 | Linux 时钟子系统

Pembekal Jam

  • Nod akar biasanya merupakan Pengayun (pengayun aktif) atau Kristal (pengayun pasif).
  • Terdapat banyak jenis nod perantaraan, termasuk PLL (gelung berkunci fasa, digunakan untuk meningkatkan kekerapan), Pembahagi (pembahagi frekuensi, digunakan untuk mengurangkan kekerapan), Mux (pilih satu daripada berbilang laluan jam), Gate (digunakan untuk mengawal ON/ MATI).
  • Nod daun ialah blok HW dengan fungsi tertentu yang menggunakan jam sebagai input.

Mengikut ciri-ciri jam, rangka kerja jam membahagikan jam kepada enam kategori: kadar tetap, get, deviser, mux, fixed factor, dan komposit.

一文搞懂 | Linux 时钟子系统

Struktur data

Enam kategori di atas pada dasarnya adalah peranti jam Kernel mengekstrak ciri blok HW jam ini dan menggunakan struct clk_hw untuk mewakilinya, seperti berikut:

struct clk_hw {
  //指向CCF模块中对应 clock device 实例
 struct clk_core *core;
  //clk是访问clk_core的实例。每当consumer通过clk_get对CCF中的clock device(也就是clk_core)发起访

问的时候都需要获取一个句柄,也就是clk
 struct clk *clk;
  //clock provider driver初始化时的数据,数据被用来初始化clk_hw对应的clk_core数据结构。
 const struct clk_init_data *init;
};

struct clk_init_data {
  //该clock设备的名字
 const char  *name;
  //clock provider driver进行具体的 HW 操作
 const struct clk_ops *ops;
  //描述该clk_hw的拓扑结构
 const char  * const *parent_names;
 const struct clk_parent_data *parent_data;
 const struct clk_hw  **parent_hws;
 u8   num_parents;
 unsigned long  flags;
};

Ambil penggetar kadar tetap sebagai contoh struktur datanya ialah:

struct clk_fixed_rate {
  //下面是fixed rate这种clock device特有的成员
  struct        clk_hw hw;
  //基类
  unsigned long    fixed_rate;
  unsigned long    fixed_accuracy;
  u8        flags;
};

Ini mungkin berlaku untuk peranti jam khusus lain, jadi saya tidak akan menerangkan butiran di sini.

Berikut ialah gambar yang menerangkan hubungan antara struktur data ini:

一文搞懂 | Linux 时钟子系统

Kaedah pendaftaran

Sekarang kita memahami struktur data, mari lihat kaedah pendaftaran setiap jenis peranti jam.

1. jam kadar tetap

Jam jenis ini mempunyai frekuensi tetap Ia tidak boleh dihidupkan atau dimatikan, frekuensi tidak boleh dilaraskan, dan ia adalah jenis jam yang paling mudah. Ia boleh disokong terus melalui konfigurasi DTS. Anda juga boleh mendaftarkan jam kadar tetap terus melalui antara muka, seperti berikut:

CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);

struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                unsigned long fixed_rate);

2. jam pintu

Jam jenis ini hanya boleh dihidupkan dan dimatikan (.dayakan/.dayakan panggilan balik akan diberikan), dan anda boleh menggunakan antara muka berikut untuk mendaftar:

struct clk *clk_register_gate(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                void __iomem *reg, u8 bit_idx,
                u8 clk_gate_flags, spinlock_t *lock);

3. jam pembahagi

Jam jenis ini boleh menetapkan nilai pembahagian frekuensi (oleh itu menyediakan panggilan balik .recalc_rate/.set_rate/.round_rate) dan boleh didaftarkan melalui dua antara muka berikut:

struct clk *clk_register_divider(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
                u8 clk_divider_flags, spinlock_t *lock);
                
struct clk *clk_register_divider_table(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
                u8 clk_divider_flags, const struct clk_div_table *table,
                spinlock_t *lock);

4. jam mux

Jam jenis ini boleh memilih berbilang ibu bapa kerana ia akan melaksanakan panggilan balik .get_parent/.set_parent/.recalc_rate dan boleh didaftarkan melalui dua antara muka berikut:

struct clk *clk_register_mux(struct device *dev, const char *name,
                const char **parent_names, u8 num_parents, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
                u8 clk_mux_flags, spinlock_t *lock);
                
struct clk *clk_register_mux_table(struct device *dev, const char *name,
                const char **parent_names, u8 num_parents, unsigned long flags,
                void __iomem *reg, u8 shift, u32 mask,
                u8 clk_mux_flags, u32 *table, spinlock_t *lock);

5. jam faktor tetap

Jam jenis ini mempunyai faktor tetap (iaitu pengganda dan pembahagi Kekerapan jam ialah kekerapan jam induk, didarab dengan mul, dan dibahagikan dengan div Ia kebanyakannya digunakan untuk beberapa jam dengan pekali pembahagian frekuensi tetap . Memandangkan kekerapan jam induk boleh ditukar, kekerapan jam faktor tetap juga boleh ditukar, jadi panggilan balik seperti .recalc_rate/.set_rate/.round_rate turut disediakan. Anda boleh mendaftar melalui antara muka berikut:

struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                unsigned int mult, unsigned int div);

6. jam komposit

Seperti namanya, ia adalah gabungan jam seperti mux, divider, gate, dll., yang boleh didaftarkan melalui antara muka berikut:

struct clk *clk_register_composite(struct device *dev, const char *name,
                const char **parent_names, int num_parents,
                struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
                struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
                struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
                unsigned long flags);

Fungsi pendaftaran ini akhirnya akan didaftarkan dalam Rangka Kerja Jam Biasa melalui fungsi clk_register, mengembalikan penunjuk clk struct. Seperti yang ditunjukkan di bawah:

一文搞懂 | Linux 时钟子系统

Kemudian simpan penunjuk clk struct yang dikembalikan dalam tatasusunan dan panggil antara muka of_clk_add_provider untuk memaklumkan Rangka Kerja Jam Biasa.

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 时钟子系统

Atas ialah kandungan terperinci Fahami dalam satu artikel |. subsistem jam Linux. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam