Common Clock Framework -1- (초기화)

 

Common Clock Framework -1- (초기화)

컴퓨터 하드웨어에는 많은 클럭 장치를 통해 클럭이 공급되고 있다. 시스템 내부에 cpu core에 들어가는 클럭부터 timer, i2c, uart 등 수 ~ 수십 종류의 클럭이 사용된다.

각각의 ARM SoC들은 개개의 클럭 디바이스 드라이버를 통해 클럭 설정을 하는데 하드웨어도 천차만별이고 구현된 드라이버도 기존 코드를 그대로 copy & paste를 통해 사용되고 심지어 clk 구조체 까지도 바꿔서 사용해오고 있다. 이를 해결해 보고자 common clock framework을 준비하였다.

 

drivers/clk/clk.c를 제외하고 다음과 같이 많은 시스템에서 clk 구조체를 변경하여 사용했음을 알 수 있다.

./include/linux/sh_clk.h:37:struct clk {
./drivers/clk/clk.c:80:struct clk {
./arch/c6x/include/asm/clock.h:82:struct clk {
./arch/mips/include/asm/clock.h:20:struct clk {
./arch/mips/include/asm/mach-ar7/ar7.h:147:struct clk {
./arch/mips/ralink/clk.c:19:struct clk {
./arch/mips/jz4740/clock.h:45:struct clk {
./arch/mips/ath79/clock.c:31:struct clk {
./arch/mips/lantiq/clk.h:55:struct clk {
./arch/mips/bcm63xx/clk.c:19:struct clk {
./arch/blackfin/include/asm/clocks.h:61:struct clk {
./arch/blackfin/mach-common/clock.h:14:struct clk {
./arch/arm/mach-lpc32xx/clock.h:22:struct clk {
./arch/arm/mach-w90x900/clock.h:18:struct clk {
./arch/arm/mach-sa1100/clock.c:26:struct clk {
./arch/arm/mach-davinci/clock.h:87:struct clk {
./arch/arm/mach-mmp/clock.h:18:struct clk {
./arch/arm/mach-versatile/include/mach/clkdev.h:6:struct clk {
./arch/arm/mach-pxa/clock.h:11:struct clk {
./arch/arm/mach-omap1/clock.h:141:struct clk {
./arch/arm/mach-ep93xx/clock.c:30:struct clk {
./arch/m68k/include/asm/mcfclk.h:16:struct clk {
./arch/avr32/mach-at32ap/clock.h:20:struct clk {
./arch/unicore32/kernel/clock.c:30:struct clk {

 

drivers/clk 디렉토리를 살펴보면 수 백 개의 클럭 관련 디바이스 파일이 존재한다.

common clock framework에서 사용하는 구조체는 다음과 같다.

  • clk
  • clk_core
  • clk_hw
  • clk_ops
  • clk_init_data

 

Basic clock implementations common

가장 많이 사용할 수 있는 공통 기본 클럭 구현이 다음과  같이 준비되어 있다. 공통 기본 클럭 구현만으로 구성 할 수 없는 시스템은 더 확장하여 구성할 수도 있다.

  • Fixed rate clock 구현
    • 고정된 rate와 정밀도(accuracy)로 동작하고 gate는 제어할 수 없다.
    • compatible
      • rate를 s/w로 조절하는 컨트롤 기능이 없는 경우 이 common 드라이버를 사용한다.
      • fixed-clock
  • Gated clock 구현
    • gate를 제어 할 수 있다.
    • rate는 직접 지정할 수 없고 고정(fixed)된 부모 clock으로부터 rate는 상속받아야 한다.
    • compatible
      • 실제 gate를 제어해야하기 때문에 각 클럭 컨틀롤러 제조사의 레지스터를 컨트롤하는 각자의 디바이스 드라이버 필요하다.
      • 예) ti,gate-clock”
  • divider clock 구현
    • 분배기로 rate가 설정되고 gate는 제어할 수 없다.
    • rate = 고정된 부모 clock의 rate / 분배값(div)
    • compatible
      • 실제 분배율을 제어해야 하기 때문에 각 클럭 컨트롤러 제조사의 레지스터를 컨트롤하는 각사의 디바이스 드라이버를 사용한다.
      • 예) “ti,divider-clock”
  • Simple multiplexer clock 구현
    • 간단한 멀티플렉서로 동작하여 dynamic하게 부모 클럭을 선택할 수 있고 gate는 제어할 수 없다.
    • compatible
      • 실제 입력 소스를 선택해야 하기 때문에 각 클럭 컨트롤러 제조사의 레지스터를 컨트롤하는 각사의 디바이스 드라이버를 사용한다.
      • 예) “ti,mux-clock”
  • Fixed multiplier and divider clock 구현
    • 배율기 및 분배기로 동작하여 rate를 설정하고 gate는 제어할 수 없다.
    • rate  = 고정된 부모 clock의 rate / 분배값(div) * 배율값(mult)
    • compatible
      • 배율을 s/w로 조절하는 컨트롤 기능이 없는 경우 이 common 드라이버를 사용한다.
      • fixed-factor-clock
  • Fractional divider clock 구현
    • 분수 분배기로 rate를 설정하고 gate는 제어할 수 없다.
    • rate = 고정된 부모 clock의 rate * 분수(fractional)
    • compatible
      • 실제 분수 분배율을 제어해야 하기 때문에 각 클럭 컨트롤러 제조사의 레지스터를 컨트롤하는 각사의 디바이스 드라이버를 사용한다.
  • Composite clock 구현
    • fixed rate clock, mux(multiplexer) clock 및 gate clock까지 동시에 2 개 이상 복합 구성하여 동작한다.
    • compatible
      • 복합 동작에 따른 조작을 제어해야 하기 때문에 각 클럭 컨트롤러 제조사의 레지스터를 컨트롤하는 각사의 디바이스 드라이버를 사용한다.
      • 예)  “ti,composite-clock”
  • Gpio gated clock 구현
    • gpio를 사용하여 gate를 제어한다.
    • rate는 직접 지정할 수 없고 고정(fixed)된 부모로부터 rate는 상속받아야 한다.
    •  compatible
      • gate의 조작을 common gpio 드라이버를 통해서 하므로 이 common 드라이버를 사용한다.
      • 실제 하드웨어 조작을 하지만 common gpio 디바이스 드라이버를 통해 컨트롤 하므로 별도의 클럭 디바이스 드라이버를 사용하지 않아도 된다.
      • gpio-gate-clock

 

다음 그림은 구현된 공통 클럭 디바이스 드라이버들의 사용예를 각각 보여준다.

  • 8개의 common 클럭 디바이스 드라이버가 준비되어 있다.
    • 그 중 3개의 클럭 디바이스 드라이버는 device tree로 설정 가능하다. (커널 v4.10 기준)
    • 나머지 5개는 third party 클럭 디바이스 드라이버를 통해 설정 가능하다 (ti, samsung, etc…)
  • 클럭 하드웨어 도면은 아래 사이트를 참고한다.

 

다음 그림은 common 클럭 타입별 커스텀 드라이버가 제어를 해야할 레지스터 부분을 보여준다.

 

다음 그림은 common 클럭 타입별 사용되는 ops 콜백 함수를 보여준다.

 

Device Tree 지원

현재에는 클럭 디바이스 드라이버도 머신 형태의 각 개별 드라이버를 사용하지 않고 점차 Device Tree를 지원하는 형태로 바뀌어 가고 있다. 즉 런타임에 Device Tree Blob 내용을 파싱하여 시스템에 맞게 클럭을 설정하는 형태이므로 모든 설정방법이 Device Tree Script에 표현되어 있어야 한다.

다음의 추가 구조체를 사용한다.

  • clock_provider
  • of_clk_provider

 

 

DEBUGFS 관리

Device Tree로 부팅한 rpi2에서 등록된 7개의 클럭을 다음의 디렉토리를 통해 살펴볼 수 있다.

# cd /sys/kernel/debug/clk
# ls -l
total 0
drwxr-xr-x 2 root root 0 Jan  1  1970 apb_pclk
-r--r--r-- 1 root root 0 Jan  1  1970 clk_dump
-r--r--r-- 1 root root 0 Jan  1  1970 clk_orphan_dump
-r--r--r-- 1 root root 0 Jan  1  1970 clk_orphan_summary
-r--r--r-- 1 root root 0 Jan  1  1970 clk_summary
drwxr-xr-x 2 root root 0 Jan  1  1970 clock
drwxr-xr-x 2 root root 0 Jan  1  1970 core
drwxr-xr-x 2 root root 0 Jan  1  1970 mmc
drwxr-xr-x 2 root root 0 Jan  1  1970 osc
drwxr-xr-x 2 root root 0 Jan  1  1970 pwm
drwxr-xr-x 2 root root 0 Jan  1  1970 uart0_pclk

# cat clk_summary
   clock                         enable_cnt  prepare_cnt        rate   accuracy   phase
----------------------------------------------------------------------------------------
 osc                                      0            0    19200000          0 0
 pwm                                      0            0   100000000          0 0
 apb_pclk                                 1            1   126000000          0 0
 uart0_pclk                               1            1    48000000          0 0
 mmc                                      0            0   250000000          0 0
 core                                     0            0   400000000          0 0
    clock                                 0            0   800000000          0 0

 

Device Tree로 Clk 초기화

of_clk_init()

drivers/clk/clk.c

/**     
 * of_clk_init() - Scan and init clock providers from the DT
 * @matches: array of compatible values and init functions for providers.
 *
 * This function scans the device tree for matching clock providers
 * and calls their initialization functions. It also does it by trying
 * to follow the dependencies.
 */
void __init of_clk_init(const struct of_device_id *matches)
{
        const struct of_device_id *match;
        struct device_node *np;
        struct clock_provider *clk_provider, *next;
        bool is_init_done;
        bool force = false;

        if (!matches)
                matches = &__clk_of_table;

        /* First prepare the list of the clocks providers */
        for_each_matching_node_and_match(np, matches, &match) {
                struct clock_provider *parent =
                        kzalloc(sizeof(struct clock_provider),  GFP_KERNEL);

                parent->clk_init_cb = match->data; 
                parent->np = np;
                list_add_tail(&parent->node, &clk_provider_list);
        }

        while (!list_empty(&clk_provider_list)) {
                is_init_done = false;
                list_for_each_entry_safe(clk_provider, next,
                                        &clk_provider_list, node) {
                        if (force || parent_ready(clk_provider->np)) {

                                clk_provider->clk_init_cb(clk_provider->np);
                                of_clk_set_defaults(clk_provider->np, true);

                                list_del(&clk_provider->node);
                                kfree(clk_provider);
                                is_init_done = true;
                        }
                }

                /*
                 * We didn't manage to initialize any of the
                 * remaining providers during the last loop, so now we
                 * initialize all the remaining ones unconditionally
                 * in case the clock parent was not mandatory
                 */
                if (!is_init_done)
                        force = true;
        }
}

Device Tree에서 clock provider를 스캔하고 초기화한다.

  • 코드 라인 17~18에서 matches 값으로 null이 지정된 경우 컴파일 타임에 CLK_OF_DECLARE() 매크로로 만들어진 __clk_of_table 섹션에 있는 모든 클럭 엔트리를 대상으로 한다.
  • 코드 라인 21~28에서 Device Tree에서 matches와 엔트리와 비교하여 일치하는 항목들에 대해 루프를 돌며 clock_provider를 할당받아 구성하고 clk_provider_list에 추가한다.
    • clk_init_cb에는 클럭 초기화 함수가 대입된다.
    • np에는 device_node가 대입된다.
  • 코드 라인 30~31에서 clk_provider_list의 엔트리가 비워질때 까지 루프를 돈다. is_init_done을 false로 하여 다음 루프에서 초기화함수를 한 번도 호출하지 않은 경우 다음에 루프를 돌면 강제로 호출하게 만든다.
  • 코드 라인 32~33에서 clk_provide_list의 엔트리 수 만큼 루프를 돈다.
  • 코드 라인 34~42에서 이전 루프에서 한 번도 초기화 함수를 호출하지 않은 경우와 부모 클럭이 없거나 모두 초기화된 경우 해당 클럭의 초기화 함수를 호출하고 이 클럭을 default로 설정한다. 그런 후 clk_provider_list에서 제거하고 루프내에서 한 번이라도 초기화되었음을 알리도록 is_int_done을 true로 설정한다.
  • 코드 라인 51~52에서 이 전 루프를 돌 때 한 번도 초기화된 적 없으면 force를 true로 하여 다시 한 번 루프를 돌 때 남은 나머지 클럭을 무조건 초기화 처리하도록 한다.

 

다음 그림에서와 같이 rpi2는 clock@0 ~ clock@6 까지 총 7개의 clock을 사용하며 이에 대한 각각의 초기화 함수가 호출되는 것을 보여준다.

  • clock@0~4, 6의 6개의 클럭은 “fixed-clock” 디바이스 드라이버에서 구현된 fixed rate clock 타입으로 of_fixed_clk_setup() 함수를 호출한다.
  • clock@5를 사용하는 1 개의 uart 클럭은 “fixed-factor-clock” 디바이스 드라이버에서 구현된 Fixed multiplier and divider clock 타입으로 of_fixed_factor_clk_setup() 함수를 호출한다.
    • 이 클럭의 부모 클럭인 core clock에서 사용하는 rate 값인 250Mhz를 2배 곱하여(multiplex) 500Mhz로 uart clock의 rate로 동작시킨다.

 

다음 그림은 A~I 까지의 클럭 디바이스들에 대해 부모 클럭 디바이스부터 초기화되는 과정을 보여준다.

 

parent_ready()

drivers/clk/clk.c

/*
 * This function looks for a parent clock. If there is one, then it
 * checks that the provider for this parent clock was initialized, in
 * this case the parent clock will be ready.
 */
static int parent_ready(struct device_node *np)
{
        int i = 0;

        while (true) {
                struct clk *clk = of_clk_get(np, i);

                /* this parent is ready we can check the next one */
                if (!IS_ERR(clk)) {
                        clk_put(clk);
                        i++;
                        continue;
                }

                /* at least one parent is not ready, we exit now */
                if (PTR_ERR(clk) == -EPROBE_DEFER)
                        return 0;

                /*
                 * Here we make assumption that the device tree is
                 * written correctly. So an error means that there is
                 * no more parent. As we didn't exit yet, then the
                 * previous parent are ready. If there is no clock
                 * parent, no need to wait for them, then we can
                 * consider their absence as being ready
                 */           
                return 1;
        }
}

요청한 클럭 노드의 부모 클럭 노드들 모두가 초기화 되었는지 여부를 알아온다. 1=부모 클럭 노드가 없거나 모두 초기화 된 경우, 0=부모 클럭 노드들 중 하나라도 초기화 되지 않았을 경우

  • 코드 라인 10~11에서 클럭의 부모 노드가 여러 개일 수 있으므로 루프를 반복하고 지정한 인덱스의 부모 클럭을 알아온다.
  • 코드 라인 14~18에서 지정된 인덱스의 부모 클럭이 이미 초기화된 경우 인덱스를 증가시키고 skip 한다.
  • 코드 라인 21~22에서 부모 클럭 노드가 아직 초기화되지 않은 경우 0을 반환한다.
  • 코드 라인 32에서 부모가 없는 경우 1을 반환한다.

 

clk 검색 -1-

clk_core_lookup()

drivers/clk/clk.c

static struct clk_core *clk_core_lookup(const char *name)
{
        struct clk_core *root_clk;
        struct clk_core *ret;

        if (!name)
                return NULL;    

        /* search the 'proper' clk tree first */
        hlist_for_each_entry(root_clk, &clk_root_list, child_node) {
                ret = __clk_lookup_subtree(name, root_clk);
                if (ret)
                        return ret;
        }

        /* if not found, then search the orphan tree */
        hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) {
                ret = __clk_lookup_subtree(name, root_clk);
                if (ret)
                        return ret;
        }

        return NULL;
}

루트 클럭 리스트와 고아 클럭 리스트에 등록된 모든 하위 클럭들을 포함하여 요청한 이름의 클럭(clk_core)을 검색한다. 검색되지 않는 경우 null을 반환한다.

  • 코드 라인 10에서 clk_root_list에 등록된 루트 클럭들에 대해 루프를 돈다.
  • 코드 라인 11~13에서 루트 클럭을 포함해서 하위 트리에서 요청한 이름의 클럭(clk_core)을 찾아 반환한다.
  • 코드 라인 17~21에서 clk_orphan_list에 등록된 고아 클럭들에 대해 루프를 돌며 고아 클럭을 포함하여 하위 트리에서 요청한 이름의 클럭(clk_core)을 찾아 반환한다.

 

다음 그림은 “F”라는 이름의 클럭을 검색시 child 클럭을 검색하는 순서를 보여준다.

__clk_lookup_subtree()

drivers/clk/clk.c

static struct clk_core *__clk_lookup_subtree(const char *name,
                                             struct clk_core *clk)
{
        struct clk_core *child;
        struct clk_core *ret;

        if (!strcmp(clk->name, name))
                return clk;

        hlist_for_each_entry(child, &clk->children, child_node) {
                ret = __clk_lookup_subtree(name, child);
                if (ret)
                        return ret;
        }

        return NULL;
}

현재 클럭 및 그 자식 클럭에서 요청한 이름의 클럭(clk_core)을 검색한다. 검색되지 않는 경우 null을 반환한다.

  • 코드 라인 7~8에서 요청한 이름의 클럭인 경우 그 클럭을 반환한다.
  • 코드 라인 10에서 자식 클럭이 있는 경우 그 수 만큼 루프를 돈다.
  • 코드 라인 11에서 자식 클럭과 그 이하 서브 트리를 재귀 검색한다.
  • 코드 라인 12~13에서 클럭이 검색된 경우 반환한다.

 

clk 검색 -2- (부모 인덱스 사용)

 

static struct clk_core *clk_core_get_parent_by_index(struct clk_core *clk,
                                                         u8 index)
{
        if (!clk || index >= clk->num_parents)
                return NULL;
        else if (!clk->parents)
                return clk_core_lookup(clk->parent_names[index]);
        else if (!clk->parents[index])
                return clk->parents[index] =
                        clk_core_lookup(clk->parent_names[index]);
        else
                return clk->parents[index];
}

부모 인덱스 값으로 부모 클럭을 찾아 반환한다.

  • 코드 라인 4~5에서 인덱스값이 num_parents를 초과하는 경우 null을 반환한다.
  • 코드 라인 6~7에서 부모 클럭을 가리키는 포인터 어레이가 준비되지 않은 경우 그냥 부모 이름으로 검색하여 반환한다.
  • 코드 라인 8~10에서 부모 인덱스에 해당하는 부모 클럭이 설정되지 않은 경우 부모 이름으로 검색하여 설정하고 반환한다.
  • 코드 라인 11~12에서 부모 인덱스에 해당하는 부모 클럭을 반환한다.

 

Device Tree로 클럭 셋업

Fixed Rate 타입 Clk 디바이스 셋업

of_fixed_clk_setup()

/**
 * of_fixed_clk_setup() - Setup function for simple fixed rate clock
 */
void of_fixed_clk_setup(struct device_node *node)
{
        struct clk *clk;
        const char *clk_name = node->name;
        u32 rate;
        u32 accuracy = 0;

        if (of_property_read_u32(node, "clock-frequency", &rate))
                return;

        of_property_read_u32(node, "clock-accuracy", &accuracy);

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

        clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
                                                    CLK_IS_ROOT, rate,
                                                    accuracy);
        if (!IS_ERR(clk))
                of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);

Device Tree의 요청 클럭 노드 정보로 fixed rate 타입의 클럭을 설정한다.

  • 코드 라인 11~12에서 요청한 클럭 노드에서 “clock-frequency” 속성 값을 읽어 rate에 대입한다.
  • 코드 라인 14에서 “clock-accuracy” 값을 읽어 accuracy에 대입한다. 속성이 없는 경우 accuracy=0 이다.
  • 코드 라인 16에서 “clock-output-names” 속성 값(문자열)을 읽어 clk_name에 대입한다. 속성이 없는 경우 노드명을 사용한다.
  • 코드 라인 18~20에서 읽어들인 rate, accuracy 및 clk_name으로 fixed rate 타입의 루트 클럭으로 등록한다.
  • 코드 라인 21~22에서 등록이 성공된 경우 클럭 provider에 추가한다.

 

다음 그림은 rpi2의 Device Tree Script (커널 v4.10 기준)에서 7개 clock에 대한 연관도를 보여준다.

 

Fixed Factor 타입 Clk 디바이스 셋업

clk_register_fixed_factor_clk_setup()

drivers/clk/clk-fixed-factor.c

/**
 * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
 */
void __init of_fixed_factor_clk_setup(struct device_node *node)
{
        struct clk *clk;
        const char *clk_name = node->name;
        const char *parent_name;
        u32 div, mult;

        if (of_property_read_u32(node, "clock-div", &div)) {
                pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
                        __func__, node->name);
                return;
        }

        if (of_property_read_u32(node, "clock-mult", &mult)) {
                pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n",
                        __func__, node->name);
                return;
        }

        of_property_read_string(node, "clock-output-names", &clk_name);
        parent_name = of_clk_get_parent_name(node, 0);

        clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
                                        mult, div);
        if (!IS_ERR(clk))
                of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
                of_fixed_factor_clk_setup);

Device Tree의 요청 클럭 노드 정보로 fixed factor 타입의 클럭을 설정한다.

  • 코드 라인 11~15에서 요청한 클럭 노드에서 “clock-div” 속성 값을 읽어 div에 대입한다.
  • 코드 라인 17~21에서 “clock-mult” 값을 읽어 multi에 대입한다.
  • 코드 라인 23에서 “clock-output-names” 속성 값(문자열)을 읽어 clk_name에 대입한다. 속성이 없는 경우 노드명을 사용한다.
  • 코드 라인 24에서 부모 클럭 이름을 알아와서 parent_name에 대입한다.
  • 코드 라인 26에서 읽어들인 div, multi, parent_name 및 clk_name으로 fixed factor 타입의 클럭으로 등록한다.
  • 코드 라인 28~29에서 등록이 성공된 경우 클럭 provider에 추가한다.

 

타입별 클럭 등록

Fixed Rate 타입 Clk 등록

clk_register_fixed_rate()

drivers/clk/clk-fixed-rate.c

/**
 * clk_register_fixed_rate - register fixed-rate clock with the clock framework
 * @dev: device that is registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @fixed_rate: non-adjustable clock rate
 */
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                unsigned long fixed_rate)
{
        return clk_register_fixed_rate_with_accuracy(dev, name, parent_name,
                                                     flags, fixed_rate, 0);
}
EXPORT_SYMBOL_GPL(clk_register_fixed_rate);

클럭 디바이스, name, parent_name, flags, fixed_rate 정보를 인수로 accuracy가 0인 fixed rate 타입의 클럭을 등록한다.

 

clk_register_fixed_rate_with_accuracy()

drivers/clk/clk-fixed-rate.c

/**
 * clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the
 *                                         clock framework
 * @dev: device that is registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @fixed_rate: non-adjustable clock rate
 * @fixed_accuracy: non-adjustable clock rate
 */
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
                const char *name, const char *parent_name, unsigned long flags,
                unsigned long fixed_rate, unsigned long fixed_accuracy)
{
        struct clk_fixed_rate *fixed;
        struct clk *clk;
        struct clk_init_data init;

        /* allocate fixed-rate clock */
        fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
        if (!fixed) {
                pr_err("%s: could not allocate fixed clk\n", __func__);
                return ERR_PTR(-ENOMEM);
        }

        init.name = name;
        init.ops = &clk_fixed_rate_ops;
        init.flags = flags | CLK_IS_BASIC;
        init.parent_names = (parent_name ? &parent_name: NULL);
        init.num_parents = (parent_name ? 1 : 0);

        /* struct clk_fixed_rate assignments */
        fixed->fixed_rate = fixed_rate;
        fixed->fixed_accuracy = fixed_accuracy;
        fixed->hw.init = &init;

        /* register the clock */
        clk = clk_register(dev, &fixed->hw);
        if (IS_ERR(clk))
                kfree(fixed);

        return clk;
}
EXPORT_SYMBOL_GPL(clk_register_fixed_rate_with_accuracy);

클럭 디바이스, name, parent_name, flags, fixed_rate, fixed_accuracy 정보를 인수로 받아 fixed rate 타입의 클럭을 등록한다.

  • 코드 라인 20~24에서 clk_fixed_rate 구조체를 할당받아온다.
  • 코드 라인 26~30에서 임시 구조체인 clk_init_data에 초기화 정보를 구성한다.
  • 코드 라인 33~35에서 clk_fixed_rate 구조체에서 fixed_rate, fixed_accuracy 및 임시 구조체 clk_init_data를 지정한다.
  • 코드 라인 38에서 클럭을 등록한다.

 

다음 그림은 fixed rate 타입의 클럭을 등록하는 모습을 보여준다.

 

Fixed Factor 타입 Clk 등록

clk_register_fixed_factor()

drivers/clk/clk-fixed-factor.c

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)
{
        struct clk_fixed_factor *fix;
        struct clk_init_data init;
        struct clk *clk;

        fix = kmalloc(sizeof(*fix), GFP_KERNEL);
        if (!fix) {
                pr_err("%s: could not allocate fixed factor clk\n", __func__);
                return ERR_PTR(-ENOMEM);
        }

        /* struct clk_fixed_factor assignments */
        fix->mult = mult;
        fix->div = div;
        fix->hw.init = &init;

        init.name = name;
        init.ops = &clk_fixed_factor_ops;
        init.flags = flags | CLK_IS_BASIC;
        init.parent_names = &parent_name;
        init.num_parents = 1;

        clk = clk_register(dev, &fix->hw);

        if (IS_ERR(clk))
                kfree(fix);

        return clk;
}
EXPORT_SYMBOL_GPL(clk_register_fixed_factor);

클럭 디바이스, name, parent_name, flags, multi, div 정보를 인수로 받아 fixed factor 타입의 클럭을 등록한다.

  • 코드 라인 9~18에서 clk_fixed_facotr 구조체를 할당받고 multi 값과 div 값 등을 구성한다.
  • 코드 라인 20~24에서 임시 구조체인 clk_init_data에 초기화 정보를 구성한다.
  • 코드 라인 26~29에서 클럭을 등록한다

 

Clk 등록

clk_register()

drivers/clk/clk.c

/**
 * clk_register - allocate a new clock, register it and return an opaque cookie
 * @dev: device that is registering this clock
 * @hw: link to hardware-specific clock data
 *
 * clk_register is the primary interface for populating the clock tree with new
 * clock nodes.  It returns a pointer to the newly allocated struct clk which
 * cannot be dereferenced by driver code but may be used in conjuction with the
 * rest of the clock API.  In the event of an error clk_register will return an
 * error code; drivers must test for an error code after calling clk_register.
 */
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
        int i, ret;
        struct clk_core *clk;

        clk = kzalloc(sizeof(*clk), GFP_KERNEL);
        if (!clk) {     
                pr_err("%s: could not allocate clk\n", __func__);
                ret = -ENOMEM;
                goto fail_out;
        }

        clk->name = kstrdup_const(hw->init->name, GFP_KERNEL);
        if (!clk->name) {
                pr_err("%s: could not allocate clk->name\n", __func__);
                ret = -ENOMEM;
                goto fail_name;
        }
        clk->ops = hw->init->ops;
        if (dev && dev->driver)
                clk->owner = dev->driver->owner;
        clk->hw = hw;
        clk->flags = hw->init->flags;
        clk->num_parents = hw->init->num_parents;
        hw->core = clk;

        /* allocate local copy in case parent_names is __initdata */
        clk->parent_names = kcalloc(clk->num_parents, sizeof(char *),
                                        GFP_KERNEL);

        if (!clk->parent_names) {
                pr_err("%s: could not allocate clk->parent_names\n", __func__);
                ret = -ENOMEM;
                goto fail_parent_names;
        }


        /* copy each string name in case parent_names is __initdata */
        for (i = 0; i < clk->num_parents; i++) {
                clk->parent_names[i] = kstrdup_const(hw->init->parent_names[i],
                                                GFP_KERNEL);
                if (!clk->parent_names[i]) {
                        pr_err("%s: could not copy parent_names\n", __func__);
                        ret = -ENOMEM;
                        goto fail_parent_names_copy;
                }
        }

        INIT_HLIST_HEAD(&clk->clks);

        hw->clk = __clk_create_clk(hw, NULL, NULL);
        if (IS_ERR(hw->clk)) {
                pr_err("%s: could not allocate per-user clk\n", __func__);
                ret = PTR_ERR(hw->clk);
                goto fail_parent_names_copy;
        }

        ret = __clk_init(dev, hw->clk);
        if (!ret)
                return hw->clk;

        __clk_free_clk(hw->clk);
        hw->clk = NULL;

fail_parent_names_copy:
        while (--i >= 0)
                kfree_const(clk->parent_names[i]);
        kfree(clk->parent_names);
fail_parent_names:
        kfree_const(clk->name);
fail_name:
        kfree(clk);
fail_out:
        return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(clk_register);

요청한 디바이스와 clk_hw 정보로 clk_core와 clk를 구성하여 등록하고 최상위 노드인 경우 초기화한다.

  • 코드 라인 17~22에서 clk_core 구조체를 할당받아 core에 대입한다.
  • 코드 라인 24~29에서 core->name에 hw->init->name의 복사본 문자열을 대입한다.
    • kstrdup_const()
      • 문자열이 rodata(읽기 전용 커널 데이터) 섹션에 위치한 경우 복제하지 않고 const 타입으로 그냥 사용한다.
  • 코드 라인 30~36에서 clk_core 구조체를 구성한다.
  • 코드 라인 39~58에서 hw->init->parent_names[]를 core->parent_names[]에 복사한다.
  • 코드 라인 60에 core_clks 리스트를 초기화한다.
  • 코드 라인 62~67에서 clk를 할당받아 구성한다.
  • 코드 라인 69~71에서 클럭에 대해 부모 관계와 rate, accuracy 등을 설정한다.성공 시 함수를 빠져나간다.
  • 코드 라인 73~85까지 실패하는 경우 생성했던 모든 객체들을 free 시키고 에러를 반환한다.

 

다음 그림은 부모 노드를 가진 클럭을 등록하는 모습을 보여준다.

 

__clk_create_clk()

drivers/clk/clk.c

struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
                             const char *con_id)
{
        struct clk *clk;

        /* This is to allow this function to be chained to others */
        if (!hw || IS_ERR(hw))
                return (struct clk *) hw;

        clk = kzalloc(sizeof(*clk), GFP_KERNEL);
        if (!clk)
                return ERR_PTR(-ENOMEM);

        clk->core = hw->core;
        clk->dev_id = dev_id;
        clk->con_id = con_id;
        clk->max_rate = ULONG_MAX;

        clk_prepare_lock();
        hlist_add_head(&clk->child_node, &hw->core->clks);
        clk_prepare_unlock();

        return clk;
}

clk를 할당하고 구성한 후 hw->core->clks 리스트에 추가하고 clk를 반환한다.

  • 코드 라인 10~17에서 clk를 할당받은 후 구성한다.
  • 코드 라인 20에서 hw->core->clks 리스트에 할당받고 구성한 clk를 등록한다.

 

다음 그림은 clk_core의 child_node에 clk가 등록된 것을 보여준다.

 

__clk_init()

drivers/clk/clk.c

/**
 * __clk_init - initialize the data structures in a struct clk
 * @dev:        device initializing this clk, placeholder for now
 * @clk:        clk being initialized
 *
 * Initializes the lists in struct clk_core, queries the hardware for the
 * parent and rate and sets them both.
 */
static int __clk_init(struct device *dev, struct clk *clk_user)
{
        int i, ret = 0;
        struct clk_core *orphan;
        struct hlist_node *tmp2;
        struct clk_core *clk;
        unsigned long rate;

        if (!clk_user)
                return -EINVAL;

        clk = clk_user->core;

        clk_prepare_lock();

        /* check to see if a clock with this name is already registered */
        if (clk_core_lookup(clk->name)) {
                pr_debug("%s: clk %s already initialized\n",
                                __func__, clk->name);
                ret = -EEXIST;
                goto out;
        }

        /* check that clk_ops are sane.  See Documentation/clk.txt */
        if (clk->ops->set_rate &&
            !((clk->ops->round_rate || clk->ops->determine_rate) &&
              clk->ops->recalc_rate)) {
                pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
                                __func__, clk->name);
                ret = -EINVAL;
                goto out;
        }

        if (clk->ops->set_parent && !clk->ops->get_parent) {
                pr_warning("%s: %s must implement .get_parent & .set_parent\n",
                                __func__, clk->name);
                ret = -EINVAL;
                goto out;
        }

        if (clk->ops->set_rate_and_parent &&
                        !(clk->ops->set_parent && clk->ops->set_rate)) {
                pr_warn("%s: %s must implement .set_parent & .set_rate\n",
                                __func__, clk->name);
                ret = -EINVAL;
                goto out;
        }

        /* throw a WARN if any entries in parent_names are NULL */
        for (i = 0; i < clk->num_parents; i++)
                WARN(!clk->parent_names[i],
                                "%s: invalid NULL in %s's .parent_names\n",
                                __func__, clk->name);

클럭에 대해 부모 관계와 rate, accuracy 등을 설정한다.

  • 코드 라인 25~30에서 이름으로 클럭을 검색하여 이미 존재하는 경우 -EEXIST 에러를 반환한다.
  • 코드 라인 33~40에서 set_rate와 recalc_rate를 위해 round_rate 또는 determine_rate가 등록이 되어 있어야하는데 만일 그렇지 않은 경우 경고 메시지를 출력하고 -EINVAL을 반환한다.
  • 코드 라인 42~47에서 set_parent를 위해 get_parent가 필요한데 없는 경우 경고 메시지를 출력하고 -EINVAL을 반환한다.
  • 코드 라인 49~55에서 num_parents 만큼 루프를 돌며 부모 클럭 명이 지정되지 않고 null로 되어 잇는 경우 경고 메시지를 출력한다.

 

        /*
         * Allocate an array of struct clk *'s to avoid unnecessary string
         * look-ups of clk's possible parents.  This can fail for clocks passed
         * in to clk_init during early boot; thus any access to clk->parents[]
         * must always check for a NULL pointer and try to populate it if
         * necessary.
         *
         * If clk->parents is not NULL we skip this entire block.  This allows
         * for clock drivers to statically initialize clk->parents.
         */
        if (clk->num_parents > 1 && !clk->parents) {
                clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *),
                                        GFP_KERNEL);
                /*
                 * clk_core_lookup returns NULL for parents that have not been
                 * clk_init'd; thus any access to clk->parents[] must check
                 * for a NULL pointer.  We can always perform lazy lookups for
                 * missing parents later on.
                 */
                if (clk->parents)
                        for (i = 0; i < clk->num_parents; i++)
                                clk->parents[i] =
                                        clk_core_lookup(clk->parent_names[i]);
        }

        clk->parent = __clk_init_parent(clk);

        /*
         * Populate clk->parent if parent has already been __clk_init'd.  If
         * parent has not yet been __clk_init'd then place clk in the orphan
         * list.  If clk has set the CLK_IS_ROOT flag then place it in the root
         * clk list.
         *
         * Every time a new clk is clk_init'd then we walk the list of orphan
         * clocks and re-parent any that are children of the clock currently
         * being clk_init'd.
         */
        if (clk->parent)
                hlist_add_head(&clk->child_node,
                                &clk->parent->children);
        else if (clk->flags & CLK_IS_ROOT)
                hlist_add_head(&clk->child_node, &clk_root_list);
        else
                hlist_add_head(&clk->child_node, &clk_orphan_list);

        /*
         * Set clk's accuracy.  The preferred method is to use
         * .recalc_accuracy. For simple clocks and lazy developers the default
         * fallback is to use the parent's accuracy.  If a clock doesn't have a
         * parent (or is orphaned) then accuracy is set to zero (perfect
         * clock).
         */
        if (clk->ops->recalc_accuracy)
                clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
                                        __clk_get_accuracy(clk->parent));
        else if (clk->parent)
                clk->accuracy = clk->parent->accuracy;
        else
                clk->accuracy = 0;
  • 코드 라인 11~24에서 부모 클럭이 2개 이상이면서 parents가 null인 경우 부모 클럭 수 만큼 포인터 배열을 할당받고 부모 클럭 이름으로 검색하여 구성한다.
  • 코드 라인 26에서 최초 연결될 부모 클럭을 대입한다.
  • 코드 라인 38~40에서 부모가 지정된 경우 부모 클럭의 children 리스트에 등록한다.
  • 코드 라인 41~42에서 ROOT 플래그가 설정된 경우 루트 클럭으로 인식하여 전역 clk_root_list에 등록한다.
  • 코드 라인 43~44에서 그외의 클럭은 고아 클럭으로 인식하여 전역 clk_orphan_list에 등록한다.
  • 코드 라인 53~55에서 ops->recalc_accuracy 콜백 함수가 지원되는 경우 부모의 정확도를 알아와서 이 콜백 함수의 결과를 가져와서 accuracy로 설정한다.
  • 코드 라인 56~59에서 부모가 있는 경우 부모의 정확도를 상속받아 설정하고 그렇지 않으면 0으로 설정한다.

 

        /*
         * Set clk's phase.
         * Since a phase is by definition relative to its parent, just
         * query the current clock phase, or just assume it's in phase.
         */
        if (clk->ops->get_phase)
                clk->phase = clk->ops->get_phase(clk->hw);
        else
                clk->phase = 0;

        /*
         * Set clk's rate.  The preferred method is to use .recalc_rate.  For
         * simple clocks and lazy developers the default fallback is to use the
         * parent's rate.  If a clock doesn't have a parent (or is orphaned)
         * then rate is set to zero.
         */
        if (clk->ops->recalc_rate)
                rate = clk->ops->recalc_rate(clk->hw,
                                clk_core_get_rate_nolock(clk->parent));
        else if (clk->parent)
                rate = clk->parent->rate;
        else
                rate = 0;
        clk->rate = clk->req_rate = rate;

        /*
         * walk the list of orphan clocks and reparent any that are children of
         * this clock
         */
        hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
                if (orphan->num_parents && orphan->ops->get_parent) {
                        i = orphan->ops->get_parent(orphan->hw);
                        if (!strcmp(clk->name, orphan->parent_names[i]))
                                clk_core_reparent(orphan, clk);
                        continue;
                }

                for (i = 0; i < orphan->num_parents; i++)
                        if (!strcmp(clk->name, orphan->parent_names[i])) {
                                clk_core_reparent(orphan, clk);
                                break;
                        }
         }

        /*
         * optional platform-specific magic
         *
         * The .init callback is not used by any of the basic clock types, but
         * exists for weird hardware that must perform initialization magic.
         * Please consider other ways of solving initialization problems before
         * using this callback, as its use is discouraged.
         */
        if (clk->ops->init)
                clk->ops->init(clk->hw);

        kref_init(&clk->ref);
out:
        clk_prepare_unlock();

        if (!ret)
                clk_debug_register(clk);

        return ret;
}
  • 코드 라인 6~9에서 ops->get_phase 콜백 함수가 지원되는 경우 호출하여 phase를 설정하고 그렇지 않은 경우 0으로 설정한다.
  • 코드 라인 17~19에서 ops->recalc_rate 콜백 함수가 지원되는 경우 부모의 클럭 rate로 이 콜백 함수를 호출하여 rate를 산출해온다.
  • 코드 라인 20~23에서 부모 클럭이 있는 경우 부모 클럭의 rate를 상속받아 설정하고 그렇지 않은 경우 0으로 설정한다.
  • 코드 라인 30에서 고아 클럭 리스트인 clk_orphan_list에 등록된 클럭들에 루프를 돈다.
  • 코드 라인 31~36에서 부모가 있는 경우 ops->get_parent 콜백 함수를 호출하여 부모 인덱스 값을 알아와서 부모를 다시 재설정하게 한다.
  • 코드 라인 38~42에서 부모 수 만큼 루프를 돌며 부모를 다시 재설정하게 한다.
  • 코드 라인 53~54에서 ops->init 콜백 함수가 지원되면 호출한다.

 

다음 그림은 clock 루트 리스트에 포함되는 클럭과 clock 고아 리스트에 포함되는 클럭의 속성 차이를 보여준다.

 

__clk_init_parent()

drivers/clk/clk.c

/*
 * .get_parent is mandatory for clocks with multiple possible parents.  It is
 * optional for single-parent clocks.  Always call .get_parent if it is
 * available and WARN if it is missing for multi-parent clocks.
 *
 * For single-parent clocks without .get_parent, first check to see if the
 * .parents array exists, and if so use it to avoid an expensive tree
 * traversal.  If .parents does not exist then walk the tree.
 */
static struct clk_core *__clk_init_parent(struct clk_core *clk)
{
        struct clk_core *ret = NULL;
        u8 index;

        /* handle the trivial cases */

        if (!clk->num_parents)
                goto out;

        if (clk->num_parents == 1) {
                if (IS_ERR_OR_NULL(clk->parent))
                        clk->parent = clk_core_lookup(clk->parent_names[0]);
                ret = clk->parent;
                goto out;
        }

        if (!clk->ops->get_parent) {
                WARN(!clk->ops->get_parent,
                        "%s: multi-parent clocks must implement .get_parent\n",
                        __func__);
                goto out;
        };

        /*
         * Do our best to cache parent clocks in clk->parents.  This prevents
         * unnecessary and expensive lookups.  We don't set clk->parent here;
         * that is done by the calling function.
         */

        index = clk->ops->get_parent(clk->hw);

        if (!clk->parents)
                clk->parents =
                        kcalloc(clk->num_parents, sizeof(struct clk *),
                                        GFP_KERNEL);

        ret = clk_core_get_parent_by_index(clk, index);

out:
        return ret;
}

현재 클럭에서 처음 설정할 부모 클럭을 알아온다.

  • 코드 라인 17~18에서 부모가 없는 경우 함수를 빠져나온다.
  • 코드 라인 20~25에서 부모가 하나 인 경우 이름으로 검색하여 반환한다.
  • 코드 라인 27~32에서 ops->get_parent 콜백 함수가 지원되지 않는 경우 경고 메시지를 출력하고 함수를 빠져나간다.
  • 코드 라인 40에서 ops->get_parent 콜백 함수를 호출하여 사용할 부모 클럭 인덱스를 알아온다.
  • 코드 라인 42~45에서 parents가 null인 경우 num_parents 수 만큼 포인터 배열을 할당한다.
  • 코드 라인 47에서 부모 인덱스로 부모 클럭을 알아와 반환한다.

 

provider에 Clk 추가

of_clk_add_provider()

drivers/clk/clk.c

/**
 * of_clk_add_provider() - Register a clock provider for a node
 * @np: Device node pointer associated with clock provider
 * @clk_src_get: callback for decoding clock
 * @data: context pointer for @clk_src_get callback.
 */
int of_clk_add_provider(struct device_node *np,
                        struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
                                                   void *data),
                        void *data)
{
        struct of_clk_provider *cp;
        int ret;

        cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL);
        if (!cp)
                return -ENOMEM;

        cp->node = of_node_get(np);
        cp->data = data;
        cp->get = clk_src_get;

        mutex_lock(&of_clk_mutex);
        list_add(&cp->link, &of_clk_providers);
        mutex_unlock(&of_clk_mutex);
        pr_debug("Added clock from %s\n", np->full_name);

        ret = of_clk_set_defaults(np, true);
        if (ret < 0)
                of_clk_del_provider(np);

        return ret;
}
EXPORT_SYMBOL_GPL(of_clk_add_provider);

클럭을 provider 리스트에 추가한다.

  • 코드 라인 15~21에서 of_clk_provider 구조체를 할당받아 구성한다.
  • 코드 라인 23~25에서 of_clk_providers에 추가한다.
  • 코드 라인 28~30에서 default 부모 클럭과 rate를 설정한다. 만일 실패하는 경우 provider 리스트에서 제거한다.

 

clk 관련 of_api 들

 

of_clk_get()

drivers/clk/clkdev.c

struct clk *of_clk_get(struct device_node *np, int index)
{
        return __of_clk_get(np, index, np->full_name, NULL);
}
EXPORT_SYMBOL(of_clk_get);

지정한 클럭 노드에서 index로 지정한 부모 노드를 알아온다.

  • clocks 속성에서 부모노드가 지정된 경우 그 부모 노드를 알아온다. 없는 경우 자신 노드를 알아온다.

 

다음 그림은 부모 인덱스 값으로 부모 클럭을 알아오는 모습을 보여준다.

 

__of_clk_get()

drivers/clk/clkdev.c

static struct clk *__of_clk_get(struct device_node *np, int index,
                               const char *dev_id, const char *con_id)
{
        struct of_phandle_args clkspec;
        struct clk *clk;
        int rc;

        if (index < 0)
                return ERR_PTR(-EINVAL);

        rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
                                        &clkspec);
        if (rc)
                return ERR_PTR(rc);

        clk = __of_clk_get_by_clkspec(&clkspec, dev_id, con_id);
        of_node_put(clkspec.np);

        return clk;
}

요청한 클럭 디바이스 노드에서 부모 index 값으로 클럭을 알아온다.

  • 코드 라인 11~14에서 요청 노드의 “clocks” 속성 값에서 index번째 phandle이 가리키는 노드에서 “#clock-cells” 속성값을 읽어 clkspec에 저장한다. 만일 가져올 수 없는 경우 에러를 반환한다.
    • rpi2 예) clk_uart1이 가리키는 부모 클럭 clk_core의 #clock-cells 속성 값 0을 알아온다.
  • 코드 라인 16~17에서 clkspec으로 clk를 반환한다.

 

of_clk_get_by_clkspec()

drivers/clk/clkdev.c

/**
 * of_clk_get_by_clkspec() - Lookup a clock form a clock provider
 * @clkspec: pointer to a clock specifier data structure
 *
 * This function looks up a struct clk from the registered list of clock
 * providers, an input is a clock specifier data structure as returned
 * from the of_parse_phandle_with_args() function call.
 */
struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec)
{
        return __of_clk_get_by_clkspec(clkspec, NULL, __func__);
}

클럭 providers 리스트에서 of_phandle_args 값으로 클럭을 검색하여 반환한다.

 

__of_clk_get_by_clkspec()

drivers/clk/clkdev.c

static struct clk *__of_clk_get_by_clkspec(struct of_phandle_args *clkspec,
                                         const char *dev_id, const char *con_id)
{
        struct clk *clk;

        if (!clkspec)
                return ERR_PTR(-EINVAL);

        of_clk_lock();
        clk = __of_clk_get_from_provider(clkspec, dev_id, con_id);
        of_clk_unlock();
        return clk;
}

클럭 providers 리스트에서 of_phandle_args 값으로 클럭을 검색하여 반환한다.

 

__of_clk_get_from_provider()

drivers/clk/clk.c

struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
                                       const char *dev_id, const char *con_id)
{
        struct of_clk_provider *provider;
        struct clk *clk = ERR_PTR(-EPROBE_DEFER);

        /* Check if we have such a provider in our array */
        list_for_each_entry(provider, &of_clk_providers, link) {
                if (provider->node == clkspec->np) 
                        clk = provider->get(clkspec, provider->data);
                if (!IS_ERR(clk)) {
                        clk = __clk_create_clk(__clk_get_hw(clk), dev_id,
                                               con_id);
 
                        if (!IS_ERR(clk) && !__clk_get(clk)) {
                                __clk_free_clk(clk);
                                clk = ERR_PTR(-ENOENT);
                        }

                        break;
                }
        }

        return clk;       
}

of_clk_providers 리스트에서 루프를 돌며 요청 device_node를 찾은 후 등록된 get 후크 함수를 호출하여 clk을 찾게되면 clk를 새로 할당받아 구성한 후 반환한다.

  • 코드 라인 8~10에서 of_clk_providers 리스트를 루프를 돌며 등록된 of_clk_provider 엔트리와 요청한 노드가 동일한 경우
  • 코드 라인 11~21에서 clk를 새로 할당하고 알아온 clk->core->hw 정보와 인수로 받은 dev_id, con_id 등으로 구성하여 반환한다.

 

of_clk_set_defaults()

drivers/clk/clk-conf.c

/**
 * of_clk_set_defaults() - parse and set assigned clocks configuration
 * @node: device node to apply clock settings for
 * @clk_supplier: true if clocks supplied by @node should also be considered
 *
 * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties
 * and sets any specified clock parents and rates. The @clk_supplier argument
 * should be set to true if @node may be also a clock supplier of any clock
 * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
 * If @clk_supplier is false the function exits returnning 0 as soon as it
 * determines the @node is also a supplier of any of the clocks.
 */
int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
{
        int rc;

        if (!node)
                return 0;

        rc = __set_clk_parents(node, clk_supplier);
        if (rc < 0)
                return rc;

        return __set_clk_rates(node, clk_supplier);
}
EXPORT_SYMBOL_GPL(of_clk_set_defaults);

요청한 클럭 디바이스의 부모 클럭을 설정하고 rate를 설정한다.

 

__set_clk_parents()

drivers/clk/clk-conf.c

static int __set_clk_parents(struct device_node *node, bool clk_supplier)
{
        struct of_phandle_args clkspec;
        int index, rc, num_parents;
        struct clk *clk, *pclk;

        num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
                                                 "#clock-cells");
        if (num_parents == -EINVAL)
                pr_err("clk: invalid value of clock-parents property at %s\n",
                       node->full_name);

        for (index = 0; index < num_parents; index++) {
                rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
                                        "#clock-cells", index, &clkspec);
                if (rc < 0) {
                        /* skip empty (null) phandles */
                        if (rc == -ENOENT)
                                continue;
                        else
                                return rc;
                }
                if (clkspec.np == node && !clk_supplier)
                        return 0;
                pclk = of_clk_get_by_clkspec(&clkspec);
                if (IS_ERR(pclk)) {
                        pr_warn("clk: couldn't get parent clock %d for %s\n",
                                index, node->full_name);
                        return PTR_ERR(pclk);
                }

                rc = of_parse_phandle_with_args(node, "assigned-clocks",
                                        "#clock-cells", index, &clkspec);
                if (rc < 0)
                        goto err;
                if (clkspec.np == node && !clk_supplier) {
                        rc = 0;
                        goto err;
                }
                clk = of_clk_get_by_clkspec(&clkspec);
                if (IS_ERR(clk)) {
                        pr_warn("clk: couldn't get parent clock %d for %s\n",
                                index, node->full_name);
                        rc = PTR_ERR(clk);
                        goto err;
                }

                rc = clk_set_parent(clk, pclk);
                if (rc < 0)
                        pr_err("clk: failed to reparent %s to %s: %d\n",
                               __clk_get_name(clk), __clk_get_name(pclk), rc);
                clk_put(clk);
                clk_put(pclk);
        }
        return 0;
err:
        clk_put(pclk);
        return rc;
}

요청한 클럭 디바이스의 부모 클럭을 설정한다.

  • 코드 라인 7~11에서 “#clock-cells” 속성값을 사용하여 “assigned-clock-parents” 속성에서 phandle의 수를 알아온다.  읽어올 수 없는 경우 에러 메시지를 출력한다.
    • 예) #clock-cells = <1>; assigned-clocks = <&cru PLL_GPLL>, <&cru PLL_CPLL>; -> 2 개
  • 코드 라인 13~15에서 부모 수만큼 루프를 돌며 “assigned-clock-parents” 속성에서 요청한 index의 phandle 값을 알아온다.
  • 코드 라인 16~22에서 알아온 phandle 값이 null이면 skip하고 에러인 경우 함수를 빠져나간다.
  • 코드 라인 23~24에서 clk_supplier 값이 0 이면서 알아온 부모 노드가 요청 노드와 동일한 경우 성공(0)리에 함수를 빠져나간다.
  • 코드 라인 25~30에서 clkspec으로 클럭을 알아온다.
  • 코드 라인 32~35에서 “assigned-clocks” 속성에서 부모 index로 clkspec 값을 알아온다.
  • 코드 라인 36~39에서 clk_supplier 값이 0 이면서 알아온 부모 노드가 요청 노드와 동일한 경우 성공(0)리에 함수를 빠져나간다.
  • 코드 라인 40~46에서 clkspec 값으로 클럭을 알아온다. 검색이 실패하는 경우 경고 메시지를 출력하고 에러를 반환한다.
  • 코드 라인 48~51에서 부모 클럭으로 pclk를 지정한다.

 

__set_clk_rates()

drivers/clk/clk-conf.c

static int __set_clk_rates(struct device_node *node, bool clk_supplier)
{
        struct of_phandle_args clkspec;
        struct property *prop;
        const __be32 *cur;
        int rc, index = 0;
        struct clk *clk;
        u32 rate;

        of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
                if (rate) {
                        rc = of_parse_phandle_with_args(node, "assigned-clocks",
                                        "#clock-cells", index, &clkspec);
                        if (rc < 0) {
                                /* skip empty (null) phandles */
                                if (rc == -ENOENT)
                                        continue;
                                else
                                        return rc;
                        }
                        if (clkspec.np == node && !clk_supplier)
                                return 0;

                        clk = of_clk_get_by_clkspec(&clkspec);
                        if (IS_ERR(clk)) {
                                pr_warn("clk: couldn't get clock %d for %s\n",
                                        index, node->full_name);
                                return PTR_ERR(clk);
                        }

                        rc = clk_set_rate(clk, rate);
                        if (rc < 0)
                                pr_err("clk: couldn't set %s clock rate: %d\n",
                                       __clk_get_name(clk), rc);
                        clk_put(clk);
                }
                index++;
        }
        return 0;
}

요청한 클럭 디바이스의 rate를 설정한다.

  • 코드 라인 10에서 요청한 노드 이하에서 “assigned-clock-rates” 속성 값을 가진 모든 노드들을 대상으로 루프를 돈다.
  • 코드 라인 11~13에서 rate 값이 0보다 큰 경우 “assigned-clocks” 속성의 부모 인덱스의 phandle 값을 파싱하여 clkspec에 대입한다.
  • 코드 라인 14~20에서 알아온 phandle 값이 null이면 skip하고 에러인 경우 함수를 빠져나간다.
  • 코드 라인 21~22에서 clk_supplier 값이 0 이면서 알아온 부모 노드가 요청 노드와 동일한 경우 성공(0)리에 함수를 빠져나간다.
  • 코드 라인 24~29에서 clkspec 값으로 클럭을 알아온다. 검색이 실패하는 경우 경고 메시지를 출력하고 에러를 반환한다.
  • 코드 라인 31~34에서 클럭의 rate를 설정한다.

 

참고

 

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.