Device Tree 문법

Device Tree

Device Tree란 ?

단적으로 표현하면, 일정한 형식(문법)을 갖춘 텍스트를 이용하여, hardware(SoC, Board)를 기술하는 것을 말함.
이와 대비되는 기존의 방식으로 platform device 기반의 board 기술 방식(C coding)이 있었음.

<등장 배경 및 기존 방식의 문제점>
1) SoC 혹은 board 별로 독자적인 code 구현
2) 같은 SoC에서 파생된 보드 간에 상호 연관성이 있음에도 불구하고, 이를 전혀 고려하지 않고, 별도로 구현함.
3) 따라서, 코드의 복잡도 및 코드량이 늘어는 문제 발생함.
: arch/arm/mach-{YOURBOARD}/board-*.c 파일이 매우 복잡하고 난해함.
: ARM linux 진영의 골칫거리. Linus Torvalds의 지적 !
4) 보드 구성이 바뀌더라도 kernel code를 수정하지 않고, 동작할 수 있는 방식의 필요성 인식
5) Device Tree는 기존에 다른 쪽(CPU)에서 사용하던 방식으로 ARM에도 채용하게 됨.
: 새로 나오는 보드로 개발을 진행하여, linux kernel에 자신의 코드를 반영하고자 한다면, 반드시 device tree 기반으로 작업이 이루어져야 함.

Device Tree 소스 경로

DTS의 소스(스크립트)는 커널의 arch/arm/boot/dts 또는 arch/arm64/boot/dts 경로에 있다. 소스 스크립트 파일은 아키텍쳐별로 구성되어있다.

.dtsi 파일은 SOC 레벨에서 정의한 인클루드 파일이고, .dts파일은 보드 레벨에서 정의한 스크립트 파일이다.

dts파일은 커널을 빌드할 때 Makefile 정보를 참조하여 생성된다.

커널 소스를 빌드할때는 menuconfig에서 아래 내용이 선택되었는지 확인한다.

Boot options → Flattened Device Tree support

커널이 부팅한 이후에는 /proc/device-tree 경로에서 장치 설정 정보들을 확인할 수 있다.

기본 데이타 포멧

장치 트리는 노드 및 속성의 간단한 트리 구조입니다. 속성은 키-값 쌍이며 노드에는 속성과 자식 노드가 모두 포함될 수 있습니다. 예를 들어 다음은 .dts 형식의 간단한 트리입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/dts-v1/;

/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
};
};
};

이 트리는 아무것도 설명하지 않기 때문에 사용할 수 없지만 노드와 속성의 구조를 보여줍니다.

  • 단일 루트 노드 : “/
  • 두 개의 자식 노드 : “node1“및 “node2
  • node1에 대한 두 개의 하위 항목 : “child-node1“및 “child-node2
  • 트리를 통해 흩어져있는 많은 속성.

속성은 값이 비어 있거나 임의의 바이트 스트림을 포함 할 수있는 간단한 키-값 쌍입니다. 데이터 유형은 데이터 구조로 인코딩되지 않지만 장치 트리 소스 파일로 표현할 수있는 몇 가지 기본 데이터 표현이 있습니다.

  • 텍스트 문자열 (널로 종료 됨)은 큰 따옴표로 표시됩니다.
    • string-property = "a string";
  • ‘셀’은 꺾쇠 괄호로 구분 된 32 비트 부호없는 정수입니다.
    • cell-property = <0xbeef 123 0xabcd1234>;
  • 이진 데이터는 대괄호로 구분됩니다.
    • binary-property = [0x01 0x23 0x45 0x67];
  • 다른 표현의 데이터는 쉼표를 사용하여 함께 연결할 수 있습니다.
    • mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • 쉼표는 문자열 목록을 만드는 데에도 사용됩니다.
    • string-list = "red fish", "blue fish";

기본 개념

장치 트리가 어떻게 사용되는지 이해하기 위해 간단한 기계부터 시작하여 장치 트리를 단계별로 설명하는 장치 트리를 구축합니다.

샘플 머신

“Acme”에서 제조하고 “Coyote ‘s Revenge”라는 이름의 다음과 같은 가상 머신 (ARM Versatile을 기반으로 함)을 고려하십시오.

  • One 32bit ARM CPU
  • processor local bus attached to memory mapped serial port, spi bus controller, i2c controller, interrupt controller, and external bus bridge
  • 256MB of SDRAM based at 0
  • 2 Serial ports based at 0x101F1000 and 0x101F2000
  • GPIO controller based at 0x101F3000
  • SPI controller based at 0x10170000 with following devices
    • MMC slot with SS pin attached to GPIO #1
  • External bus bridge with following devices
    • SMC SMC91111 Ethernet device attached to external bus based at 0x10100000
    • i2c controller based at 0x10160000 with following devices
      • Maxim DS1338 real time clock. Responds to slave address 1101000 (0x58)
    • 64MB of NOR flash based at 0x30000000

초기 구조

첫 번째 단계는 기계의 골격 구조를 세우는 것입니다. 이것은 유효한 장치 트리에 필요한 최소한의 구조입니다. 이 단계에서는 머신을 고유하게 식별하려고합니다.

1
2
3
4
5
/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";
};

compatible은 시스템 이름을 지정합니다. 제조업체,모델 형식의 문자열이 포함되어 있습니다.

정확한 장치를 지정하고 제조업체 이름을 포함하여 네임 스페이스 충돌을 피하는 것이 중요합니다.

운영 체제는 compatible 값을 사용하여 방법에 대한 결정을 내릴 것이므로 머신에서 실행하려면이 compatible에 올바른 데이터를 입력해야합니다.

이론적으로는 OS가 머신을 고유하게 식별하는 데 필요한 모든 데이터가 호환됩니다. 모든 기계 세부 사항이 하드 코딩 된 경우 OS는 최상위 compatible 특성에서 “acme, coyotes-revenge”를 구체적으로 찾을 수 있습니다.

CPUs

다음 단계는 각 CPU에 대해 설명하는 것입니다. “cpus”라는 컨테이너 노드가 각 CPU의 자식 노드와 함께 추가됩니다. 이 경우 시스템은 ARM의 듀얼 코어 Cortex A9 시스템입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";

cpus {
cpu@0 {
compatible = "arm,cortex-a9";
};
cpu@1 {
compatible = "arm,cortex-a9";
};
};
};

각 CPU 노드의 compatible 속성은 최상위 수준의 compatible 속성과 마찬가지로 manufacturer,model 형식으로 정확한 CPU 모델을 지정하는 문자열입니다.

나중에 더 많은 속성이 CPU 노드에 추가되지만 먼저 더 많은 기본 개념에 대해 이야기해야합니다.

노드 이름

명명 규칙에 대해 잠시 이야기 할 가치가 있습니다. 모든 노드는 name@unit-address 형식의 이름을 가져야합니다.

name은 간단한 ASCII 문자열이며 최대 31 자까지 가능합니다. 일반적으로 노드는 노드의 종류에 따라 이름이 지정됩니다. 즉. 3com 이더넷 어댑터의 노드는 3com509가 아닌 ethernet 이름을 사용합니다.

노드가 주소가있는 장치를 설명하는 경우 장치 주소가 포함됩니다. 일반적으로 장치 주소는 장치에 액세스하는 데 사용되는 기본 주소이며 노드의 reg 속성에 나열됩니다. 이 문서의 뒷부분에서 reg 속성을 다룰 것입니다.

형제 노드의 이름은 고유해야하지만 주소가 다른 경우 (예 : serial@101f1000serial@101f2000) 둘 이상의 노드에서 동일한 일반 이름을 사용하는 것이 일반적입니다.

unit-adress는 장치에 접근하기 위해 사용되는 1 차 주소이고, 노드 내의 reg 속성에 나열되어 있는 정보에 해당한다.

unit address가 필요한 이유는 동일한 장치(예: uart)가 여럿 존재할 경우, 이를 구분해가 위해서임.

보통은 unit-adress는 노드의 reg 속성값으로 사용된 첫번째 주소값을 노드 이름의 장치 주소에 사용하도록 하고 있다.

Devices

시스템의 모든 장치는 장치 트리 노드로 표시됩니다. 다음 단계는 트리를 각 장치의 노드로 채우는 것입니다. 지금은 주소 범위와 irq를 처리하는 방법에 대해 이야기 할 때까지 새 노드가 비어 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";

cpus {
cpu@0 {
compatible = "arm,cortex-a9";
};
cpu@1 {
compatible = "arm,cortex-a9";
};
};

serial@101F0000 {
compatible = "arm,pl011";
};

serial@101F2000 {
compatible = "arm,pl011";
};

gpio@101F3000 {
compatible = "arm,pl061";
};

interrupt-controller@10140000 {
compatible = "arm,pl190";
};

spi@10115000 {
compatible = "arm,pl022";
};

external-bus {
ethernet@0,0 {
compatible = "smc,smc91c111";
};

i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
rtc@58 {
compatible = "maxim,ds1338";
};
};

flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
};
};
};

이 트리에서 시스템의 각 장치에 노드가 추가되었으며 계층은 장치가 시스템에 연결되는 방식을 반영합니다. 즉. extern 버스의 장치는 외부 버스 노드의 하위이고 i2c 장치는 i2c 버스 컨트롤러 노드의 하위입니다. 일반적으로 계층은 CPU 관점에서 시스템의 관점을 나타냅니다.

이 트리는 현재 유효하지 않습니다. 장치 간 연결에 대한 정보가 없습니다. 해당 데이터는 나중에 추가됩니다.

이 트리에서주의해야 할 사항 :

  • 모든 장치 노드에는 compatible 속성이 있습니다.
  • compatible 속성에는 플래시 노드에 2 개의 문자열이 있습니다. 다음 섹션을 읽고 이유를 알아보십시오.
  • 앞에서 언급했듯이 노드 이름은 특정 모델이 아닌 장치 유형을 반영합니다.

compatible 속성 이해

장치를 나타내는 트리의 모든 노드에는 compatible 속성이 있어야합니다. compatible은 운영 체제가 장치에 바인딩 할 장치 드라이버를 결정하는 데 사용하는 키입니다.

compatible은 문자열 목록입니다. 목록의 첫 번째 문자열은 노드가 <manufacturer>, <model>형식으로 나타내는 정확한 장치를 지정합니다. 다음 문자열은 장치와 호환되는 다른 장치를 나타냅니다.

예를 들어, 프리 스케일 MPC8349 SoC (System on Chip)에는 National Semiconductor ns16550 레지스터 인터페이스를 구현하는 직렬 장치가 있습니다. 따라서 MPC8349 직렬 장치의 compatible 속성은 compatible = “fsl, mpc8349-uart”, “ns16550”이어야합니다. 이 경우 fsl, mpc8349-uart는 정확한 장치를 지정하고 ns16550은 내셔널 세미 컨덕터 16550 UART와 레지스터 수준 호환 가능함을 나타냅니다.

참고 : ns16550에는 역사적 이유로 순수한 제조업체 접두사가 없습니다. 호환되는 모든 새 값은 제조업체 접두사를 사용해야합니다.

이를 통해 기존 장치 드라이버를 최신 장치에 바인딩하면서도 정확한 하드웨어를 고유하게 식별 할 수 있습니다.

경고 : “fsl, mpc83xx-uart”와 같은 와일드 카드 compatible 값을 사용하지 마십시오. 실리콘 공급 업체는 변경하기에 너무 늦은 순간 와일드 카드 가정을 위반하는 변경 작업을 항상 수행합니다. 대신 특정 실리콘 구현을 선택하고 후속 실리콘을 모두 호환되도록하십시오.

주소 지정 방법

주소를 지정할 수있는 장치는 다음 속성을 사용하여 주소 정보를 장치 트리로 인코딩합니다.

  • reg
  • #address-cells
  • #size-cells

각 주소 지정 가능 장치는 reg = <address1 length1 [address2 length2] [address3 length3] ...> 형식의 튜플 목록 인 reg를 가져옵니다. 각 튜플은 장치에서 사용하는 주소 범위를 나타냅니다. 각 주소 값은 셀이라고하는 하나 이상의 32 비트 정수 목록입니다. 마찬가지로 길이 값은 셀 목록이거나 비어있을 수 있습니다.

주소 및 길이 필드는 가변 크기로 가변적이므로 부모 노드의 #address-cells#size-cells 속성은 각 필드에 몇 개의 셀이 있는지를 나타내는 데 사용됩니다. 즉, reg 속성을 올바르게 해석하려면 부모 노드의 #address-cells#size-cells 값이 필요합니다.

#address-cells 속성과 #size-cells 속성은 reg속성의 데이터에 대한 갯수 규칙을 지정한다.
#address-cells 속성과 #size-cells 속성은 부모 노드에서 지정하고 reg속성은 자식 노드에서 지정한다.

reg는 주소와 길이가 셋트가 된다. 여기에는 몇가지 특징이 있다.

  • 하나의 주소 또는 여러개의 주소를 가지고 있을 수 있다.
  • 각 주소는 연속적일 수도 불연속 적일 수도 있다.

보통 reg의 셀들이 주소를 지정하기 위해서 몇개의 셀을 사용할지를 결정하고 길이를 지정하기 위해서 몇개의 셀들을 사용할지를 결정해 주는 것이 바로 #address-cells 속성과 #size-cells 속성이다.

  • 32비트 시스템에서는 #address-cells 값이 1, #size-cells 값이 1이된다.

  • 64비트 시스템에서는 #address-cells 값이 2, #size-cells 값이 2가된다.

  • #address-cells : reg 속성값에 시작 주소를 지정하기 위해서 몇개의 셀을 사용할 것인가를 지정한다.

  • #size-cells : reg 속성 값에 길이를 지정하기 위해서 몇개의 셀을 사용할 것인가를 지정한다.

CPU addressing

CPU 노드는 주소 지정에 관해 이야기 할 때 가장 간단한 경우를 나타냅니다. 각 CPU에는 고유 한 단일 ID가 할당되며 CPU ID와 연관된 크기가 없습니다.

1
2
3
4
5
6
7
8
9
10
11
12
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};

cpus 노드에서 #address-cells는 1로 설정되고 #size-cells는 0으로 설정됩니다. 이는 하위 reg 값이 크기 필드가없는 주소를 나타내는 단일 uint32임을 의미합니다. 이 경우 두 CPU에 주소 0과 1이 할당됩니다. 각 CPU에는 단일 주소 만 할당되므로 CPU 노드의 #size-cells는 0입니다.

또한 reg 값이 노드 이름의 값과 일치 함을 알 수 있습니다. 일반적으로 노드에 reg 속성이 있으면 노드 이름에 reg 속성의 첫 번째 주소 값인 단위 주소가 포함되어야합니다.

메모리 매핑 된 장치

CPU 노드에서 발견되는 단일 주소 값 대신 메모리 매핑 된 장치에는 응답 할 주소 범위가 할당됩니다. #size-cells는 각 자식 정규 표현식에서 길이 필드의 크기를 나타내는 데 사용됩니다. 다음 예에서 각 주소 값은 1 셀 (32 비트)이고 각 길이 값은 1 셀이기도하며 이는 32 비트 시스템에서 일반적입니다. 64 비트 시스템은 장치 트리에서 64 비트 주소 지정을 얻기 위해 #address-cells#size-cells에 2 값을 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/dts-v1/;

/ {
#address-cells = <1>;
#size-cells = <1>;

...

serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};

serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
};

gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};

interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
};

spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
};

...

};

각 장치에는 기본 주소가 할당되고 해당 영역의 크기가 할당됩니다. 이 예에서 GPIO 장치 주소에는 두 가지 주소 범위가 할당됩니다. 0x101f3000 … 0x101f3fff 및 0x101f4000..0x101f400f.

일부 장치는 다른 주소 지정 체계를 가진 버스에 있습니다. 예를 들어, 개별 칩 선택 라인이있는 외부 버스에 장치를 연결할 수 있습니다. 각 부모 노드는 자식 노드의 주소 지정 도메인을 정의하므로 시스템을 가장 잘 설명하기 위해 주소 매핑을 선택할 수 있습니다. 아래 코드는 칩 선택 번호가 주소로 인코딩 된 외부 버스에 연결된 장치의 주소 할당을 보여줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
external-bus {
#address-cells = <2>;
#size-cells = <1>;

ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};

i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
};
};

flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};

external-bus는 주소 값으로 2 개의 셀을 사용합니다. 하나는 칩 선택 번호를위한 것이고 다른 하나는 칩 선택의베이스로부터 오프셋을위한 것입니다. 주소의 오프셋 부분 만 범위를 가져야하므로 길이 필드는 단일 셀로 유지됩니다. 따라서이 예에서 각 reg 항목은 chipselect 번호, 오프셋 및 길이의 3개의 셀을 포함합니다.

주소 도메인은 노드와 그 자식에 포함되므로 부모 노드는 버스에 적합한 주소 지정 체계를 자유롭게 정의 할 수 있습니다. 직계 상위 및 하위 노드 외부의 노드는 일반적으로 로컬 주소 지정 도메인을 신경 쓸 필요가 없으며 한 도메인에서 다른 도메인으로 이동하기 위해 주소를 매핑해야합니다.

비 메모리 매핑 된 장치

다른 장치는 프로세서 버스에서 메모리 매핑되지 않습니다. 주소 범위를 가질 수 있지만 CPU에서 직접 액세스 할 수는 없습니다. 대신 상위 장치의 드라이버가 CPU 대신 간접 액세스를 수행합니다.

i2c 장치의 예를 들기 위해 각 장치에는 주소가 할당되지만 그와 관련된 길이나 범위는 없습니다. 이것은 CPU 주소 할당과 거의 같습니다.

1
2
3
4
5
6
7
8
9
10
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};

Ranges (주소 번환)

장치에 주소를 할당하는 방법에 대해 이야기했지만이 시점에서 해당 주소는 장치 노드에만 로컬입니다. 이 주소에서 CPU가 사용할 수있는 주소로 매핑하는 방법은 아직 설명하지 않았습니다.

루트 노드는 항상 주소 공간에 대한 CPU의 관점을 설명합니다. 루트의 자식 노드는 이미 CPU의 주소 도메인을 사용하고 있으므로 명시적인 매핑이 필요하지 않습니다. 예를 들어, serial@101f0000 장치에는 주소 0x101f0000이 직접 할당됩니다.

루트의 직계 자식이 아닌 노드는 CPU의 주소 도메인을 사용하지 않습니다. 메모리 매핑 된 주소를 얻으려면 장치 트리에서 한 도메인에서 다른 도메인으로 주소를 변환하는 방법을 지정해야합니다. ranges 속성은이 목적으로 사용됩니다.

다음은 ranges 속성이 추가 된 샘플 장치 트리입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
...
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash

ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};

i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};

flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};

ranges는 주소 변환 목록입니다. ranges 테이블의 각 항목은 자식 주소, 부모 주소 및 자식 주소 공간의 영역 크기를 포함하는 튜플입니다. 각 필드의 크기는 자녀의 #address-cells 값, 부모의 #address-cells 값 및 자녀의 #size-cells 값을 사용하여 결정됩니다.

ranges 속성의 값은 다음과 같은 형식으로 표현됩니다.

1
2
3
4
ranges = <자식주소1 부모주소1 자식주소크기1
자식주소2 부모주소2 자식주소크기2
자식주소3 부모주소3 자식주소크기3
자식주소4 부모주소4 자식주소크기4>;

이 예에서 external-bus의 경우 자식 주소는 2 셀이고 부모 주소는 1 셀이며 크기도 1 셀입니다.

ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet

여기서 0 0은 자식 주소 정보이고, 0x10100000은 부모 즉 CPU 입장에서 로컬 버스 주소가 됩니다. 0x10000은 자식 주소의 크기가 됩니다.

  • 칩 선택 0의 오프셋 0은 주소 범위 0x10100000..0x1010ffff에 매핑됩니다.
  • 칩 선택 1의 오프셋 0은 주소 범위 0x10160000..0x1016ffff에 매핑됩니다.
  • 칩 선택 2의 오프셋 0은 주소 범위 0x30000000..0x30ffffff에 매핑됩니다.

또는 부모와 자식 주소 공간이 동일한 경우 노드가 대신 빈 범위 속성을 추가 할 수 있습니다. 빈 범위 속성이 있으면 자식 주소 공간의 주소가 부모 주소 공간에 1 : 1로 매핑됩니다.

주소 변환이 모두 1 : 1 매핑으로 작성 될 수있을 때 왜 주소 변환이 사용되는지 묻습니다. PCI와 같은 일부 버스는 운영 체제에 세부 정보를 노출해야하는 주소 공간이 완전히 다릅니다. 다른 것들은 버스의 실제 주소를 알아야하는 DMA 엔진을 가지고 있습니다. 장치가 모두 동일한 소프트웨어 프로그래밍 가능 물리적 주소 매핑을 공유하기 때문에 장치를 그룹화해야하는 경우가 있습니다. 1 : 1 매핑 사용 여부는 운영 체제에 필요한 정보와 하드웨어 설계에 따라 다릅니다.

또한 i2c@1,0 노드에는 범위 특성이 없습니다. 이는 외부 버스와 달리 i2c 버스의 장치가 CPU의 주소 도메인에 메모리 매핑되지 않기 때문입니다. 대신, CPU는 i2c@1,0 장치를 통해 rtc@58 장치에 간접적으로 액세스합니다. ranges 속성이 없다는 것은 부모가 아닌 다른 장치에서 장치에 직접 액세스 할 수 없음을 의미합니다.

인터럽트 작동 방식

트리의 자연스러운 구조를 따르는 주소 범위 변환과 달리 인터럽트 신호는 시스템의 모든 장치에서 시작되고 종료 될 수 있습니다.

장치 트리에서 자연스럽게 표현되는 장치 주소 지정과 달리 인터럽트 신호는 트리와 무관 한 노드 간 링크로 표시됩니다.

보통 인터럽트는 디바이스의 하드웨어가 인터럽트를 발생하고 인터럽트 컨트롤러가 해당 신호를 수신하는 구조로 되어있습니다. 그래서 인터럽트는 디바이스 트리의 구조와 별도로 디바이스 노드간에 링크 구조로 표현됩니다. 인터럽트는 디바이스 노드의 속성 형태로 표현됩니다.

인터럽트 연결을 설명하기 위해 네 가지 속성이 사용됩니다.

  • interrupt-controller - 인터럽트 신호를 수신하는 장치로 노드를 선언하는 빈 속성
  • #interrupt-cells - 인터럽트 컨트롤러 노드의 속성입니다. 이 인터럽트 컨트롤러의 인터럽트 지정자에 몇 개의 셀이 있는지를 나타냅니다 (#address-cell#size-cell과 유사).
  • interrupt-parent - 연결된 인터럽트 컨트롤러에 대한 팬들을 포함하는 장치 노드의 속성. interrupt-parent 속성이없는 노드는 부모 노드에서 속성을 상속 할 수도 있습니다.
  • interrupts - 디바이스의 각 인터럽트 출력 신호마다 하나씩 인터럽트 지정자 목록을 포함하는 디바이스 노드의 특성입니다.

인터럽트 지정자는 디바이스가 연결된 인터럽트 입력을 지정하는 하나 이상의 데이터 셀 (#interrupt-cells로 지정)입니다.

아래 예와 같이 대부분의 장치에는 단일 인터럽트 출력 만 있지만 장치에 여러 개의 인터럽트 출력이있을 수 있습니다.

인터럽트 지정자의 의미는 전적으로 인터럽트 컨트롤러 장치의 바인딩에 달려 있습니다. 각 인터럽트 컨트롤러는 인터럽트 입력을 고유하게 정의하는 데 필요한 셀 수를 결정할 수 있습니다.

다음 코드는 Coyote’s revenge 샘플 장치에 인터럽트 연결을 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/dts-v1/;

/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;

cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};

serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};

serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};

gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};

intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};

spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};

external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash

ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};

i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};

flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
  • 이 기계에는 단일 인터럽트 컨트롤러 인 interrupt-controller@10140000이 있습니다.
  • intc레이블이 인터럽트 컨트롤러 노드에 추가되었으며이 레이블을 사용하여 루트 노드의 인터럽트 상위 속성에 phandle을 할당했습니다. 이 인터럽트 상위 값은 명시 적으로 재정의되지 않는 한 모든 하위 노드가이를 상속하므로 시스템의 기본값이됩니다.
  • 각 장치는 인터럽트 속성을 사용하여 다른 인터럽트 입력 라인을 지정합니다.
  • #interrupt-cells는 2이므로 각 인터럽트 지정자는 2 개의 셀을 갖습니다. 이 예제는 인터럽트 라인 번호를 인코딩하기 위해 첫 번째 셀을 사용하고 액티브 하이 대 액티브 로우 또는 에지 대 레벨 민감도와 같은 플래그를 인코딩하기 위해 두 번째 셀을 사용하는 일반적인 패턴을 사용합니다. 지정된 인터럽트 컨트롤러에 대해서는 컨트롤러 바인딩 문서를 참조하여 지정자가 인코딩되는 방법을 배우십시오.

사용자 추가 데이터

공통 속성 외에도 임의의 속성과 자식 노드를 노드에 추가 할 수 있습니다. 일부 규칙을 따르는 한 운영 체제에 필요한 모든 데이터를 추가 할 수 있습니다.

먼저, 새로운 장치 별 속성 이름은 기존 표준 속성 이름과 충돌하지 않도록 제조 접두사를 사용해야합니다.

둘째, 디바이스 드라이버 작성자가 데이터를 해석하는 방법을 알 수 있도록 특성 및 하위 노드의 의미를 바인딩으로 문서화해야합니다. 바인딩은 특정 호환 가능한 값의 의미, 보유해야 할 속성, 보유 할 자식 노드 및 해당 장치를 나타내는 문서를 문서화합니다. 각각의 고유 한 compatible 속성에는 고유 한 바인딩이 있어야합니다 (또는 다른 compatible 속성과의 호환성을 주장해야 함).

셋째, devicetree-discuss@lists.ozlabs.org 메일 링리스트에 검토 할 새 바인딩을 게시하십시오. 새로운 바인딩을 검토하면 나중에 문제를 일으킬 수있는 일반적인 실수가 많이 발생합니다.

특수 노드

aliases Node

특정 노드는 일반적으로 /external-bus/ethernet@0,0과 같은 전체 경로로 참조되지만 사용자가 실제로 알고 싶은 것이 “어떤 장치가 eth0입니까?” 별칭 노드를 사용하여 전체 장치 경로에 짧은 별칭을 할당 할 수 있습니다. 예를 들면 다음과 같습니다.

1
2
3
4
aliases {
ethernet0 = &eth0;
serial0 = &serial0;
};

운영 체제는 식별자를 장치에 할당 할 때 별칭을 사용할 수 있습니다.

여기에 새로운 구문이 사용됩니다. property = &label; 구문은 레이블이 참조하는 전체 노드 경로를 문자열 특성으로 지정합니다. 이것은 phandle = <&label>;과 다릅니다. phandle 값을 셀에 삽입하는 이전에 사용 된 양식입니다.

chosen Node

chosen 노드는 실제 장치를 나타내지 않지만 부팅 인수와 같이 펌웨어와 운영 체제간에 데이터를 전달하는 장소 역할을합니다. 선택한 노드의 데이터가 하드웨어를 나타내지 않습니다. 일반적으로 선택한 노드는 .dts 소스 파일에서 비어 있으며 부팅시 채워집니다.

예제 시스템에서 펌웨어는 선택한 노드에 다음을 추가 할 수 있습니다.

1
2
3
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};

고급 주제

고급 샘플 머신

기본 사항이 정의되었으므로 샘플 머신에 몇 가지 하드웨어를 추가하여 좀 더 복잡한 사용 사례를 논의 해 보겠습니다.

고급 샘플 머신은 제어 레지스터 메모리가 0x10180000에 매핑되고 BAR이 주소 0x80000000 이상에서 시작하도록 프로그래밍 된 PCI 호스트 브리지를 추가합니다.

장치 트리에 대해 이미 알고있는 것을 감안할 때 다음 호스트를 추가하여 PCI 호스트 브리지를 설명 할 수 있습니다.

1
2
3
4
5
pci@10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
};

위의 예제를 보면 PCI 호스트 브릿지는 제어를 위한 주소가 reg 속성으로 0x10180000 번지로 부터 크기는 0x1000 범위입니다. 이 번지영역을 이용 호스트 브릿지를 제어하게 됩니다. 이 호스트 브릿지는 시스템 인터럽트에 8번 인터럽트를 통해 처리하게 됩니다.

PCI Host Bridge

이 섹션에서는 호스트 / PCI 브리지 노드에 대해 설명합니다.

이 섹션에서는 PCI에 대한 기본 지식이 있다고 가정합니다.

peripheral component interconnect의 약어.
개인용 컴퓨터(PC)의 중앙 처리 장치(CPU)와 주변 장치를 연결하는 ISA나 EISA, VESA의 후속으로 개발된 로컬 버스 규격. PCI 버스 또는 PCI 로컬 버스로 널리 알려져 있으며, 이 규격의 PCI 슬롯이 대부분의 펜티엄 PC에 장착되어 있다. PCI 버스는 주소를 전달하는 신호와 데이터를 전달하는 신호를 시분할 다중화(TDM)하여 전송하기 때문에 신호선의 수가 적고, 32비트 또는 64비트 버스로서 접속 가능한 장치의 수는 10개가 권장되고 있다. PCI 버스는 CPU와 버스 사이에 브리지 회로를 두는 구조이기 때문에 VESA 로컬 버스와는 달리 CPU의 종류가 달라도 그에 대응하는 브리지 회로를 갖추기만 하면 어떤 CPU와도 연결할 수 있다. 또한 CPU와는 독자적으로 PCI 확장 카드가 작업을 동시 처리하는 버스 마스터링을 지원하므로 데이터 전송 속도가 서로 다른 여러 주변 장치가 접속되었을 때 동화(動畵) 등의 멀티미디어 데이터를 우선적으로 처리할 수 있게 한다.
[네이버 지식백과] PCI [peripheral component interconnect] (IT용어사전, 한국정보통신기술협회)

PCI Bus numbering

각 PCI 버스 세그먼트는 고유하게 번호가 지정되며 버스 번호는 두 개의 셀이 포함 된 버스 범위 특성을 사용하여 pci 노드에 노출됩니다. 첫 번째 셀은이 노드에 할당 된 버스 번호를 제공하고 두 번째 셀은 모든 하위 PCI 버스의 최대 버스 번호를 제공합니다.

샘플 시스템에는 단일 PCI 버스가 있으므로 두 셀이 모두 0입니다.

1
2
3
4
5
6
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-range = <0 0>;
};

PCI Address Translation

앞에서 설명한 로컬 버스와 유사하게 PCI 주소 공간은 CPU 주소 공간과 완전히 분리되어 있으므로 PCI 주소에서 CPU 주소로 가져 오려면 주소 변환이 필요합니다. 항상 그렇듯이 range, #address-cells#size-cells 속성을 사용하여 수행됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-range = <0 0>

#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
};

보다시피, 자식 주소 (PCI 주소)는 3 개의 셀을 사용하며 PCI 범위는 2 개의 셀로 인코딩됩니다. 첫 번째 질문은 PCI 주소를 지정하기 위해 3 개의 32 비트 셀이 필요한 이유 일 수 있습니다. 세 개의 셀에는 phys.hi, phys.mid 및 phys.low라는 레이블이 붙어 있습니다.

  • phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
  • phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
  • phys.low cell: llllllll llllllll llllllll llllllll

PCI 주소는 64 비트이며 phys.mid 및 phys.low로 인코딩됩니다. 그러나 실제로 흥미로운 것은 phys.high에 있으며 약간의 필드입니다.

  • n : 재배치 가능 지역 플래그 (여기서 역할을 수행하지 않음, 0이면 재배치 가능, 1이면 불가능)
  • p : 프리 페치 가능 (캐시 가능, 1이면 가능, 0이면 불가능) 리젼 플래그
  • t : aliased 주소 플래그 (여기에서 역할을 수행하지 않음)
  • ss : 공간 코드
    • 00 : 구성 공간
    • 01 : I / O 공간
    • 10:32 비트 메모리 공간
    • 11:64 비트 메모리 공간
  • bbbbbbbb : PCI 버스 번호. PCI는 계층 적으로 구성 될 수 있습니다. 따라서 서브 버스를 정의 할 PCI / PCI 브리지가있을 수 있습니다.
  • ddddd : 일반적으로 IDSEL 신호 연결과 관련된 장치 번호입니다.
  • fff : 기능 번호. 다기능 PCI 장치에 사용됩니다.
  • rrrrrrrr : 등록 번호; 구성주기에 사용됩니다.

PCI 주소 변환을 위해 중요한 필드는 pss입니다. phys.hi의 pss 값은 액세스 할 PCI 주소 공간을 결정합니다. 따라서 ranges 속성을 살펴보면 다음과 같은 세 가지 영역이 있습니다.

  • 호스트 CPU의 주소 0x80000000에 매핑 될 512MB 크기의 PCI 주소 0x80000000에서 시작하는 32 비트 프리 페치 가능 메모리 영역
  • 호스트 CPU의 주소 0xa0000000에 매핑 될 256MB 크기의 PCI 주소 0xa0000000에서 시작하는 32 비트 프리 페치 불가능 메모리 영역
  • 호스트 CPU의 주소 0xb0000000에 매핑 될 16MB 크기의 PCI 주소 0x00000000에서 시작하는 I / O 영역

렌치를 작업 물에 던지려면 phys.hi 비트 필드가 있음은 운영 체제가 노드가 PCI 브리지를 나타내므로 변환을 위해 관련없는 필드를 무시할 수 있음을 알아야합니다. OS는 추가 필드를 마스킹해야하는지 여부를 결정하기 위해 PCI 버스 노드에서 “pci”문자열을 찾습니다.

PCI DMA Address Translation

위의 범위는 CPU가 PCI 메모리를 보는 방법을 정의하고 CPU가 올바른 메모리 창을 설정하고 다양한 PCI 장치 레지스터에 올바른 매개 변수를 쓰도록 도와줍니다. 이것을 때때로 아웃 바운드 메모리라고합니다.

특별한 주소 변환 사례는 PCI 호스트 하드웨어가 시스템의 핵심 메모리를 보는 방법과 관련이 있습니다. 이는 PCI 호스트 컨트롤러가 마스터 역할을하고 시스템의 코어 메모리에 독립적으로 액세스 할 때 발생합니다. 이것은 종종 메모리 라인이 연결된 방식으로 인해 CPU와 다른 관점이므로 초기화시 PCI 호스트 컨트롤러에 프로그래밍해야 할 수도 있습니다. 이는 PCI 버스가 독립적으로 직접 메모리 액세스를 수행하기 때문에 일종의 DMA로 간주되며, 이러한 이유로 매핑 이름은 dma-ranges입니다. 이 유형의 메모리 매핑은 때때로 인바운드 메모리라고도하며 PCI 장치 트리 사양의 일부가 아닙니다.

경우에 따라 ROM (BIOS) 또는 이와 유사한 장치가 부팅시 이러한 레지스터를 설정하지만 다른 경우에는 PCI 컨트롤러가 완전히 초기화되지 않았으며 이러한 변환은 장치 트리에서 설정해야합니다. 그런 다음 PCI 호스트 드라이버는 일반적으로 dma-ranges 속성을 구문 분석하고 이에 따라 호스트 컨트롤러에 일부 레지스터를 설정합니다.

위 예제에서 확장 :

1
2
3
4
5
6
7
8
9
10
11
12
13
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-range = <0 0>

#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000
dma-ranges = <0x02000000 0 0x00000000 0x80000000 0 0x20000000>;
};

이 dma 범위 항목은 PCI 호스트 컨트롤러의 관점에서 PCI 주소 0x00000000의 512MB가 주소 0x80000000의 기본 코어 메모리에 나타납니다. 보시다시피 ss 주소 유형을 0x02로 설정하여 32 비트 메모리임을 나타냅니다.

Advanced Interrupt Mapping

이제 가장 흥미로운 부분 인 PCI 인터럽트 매핑에 대해 살펴 보겠습니다. PCI 장치는 와이어 #INTA, #INTB, #INTC#INTD를 사용하여 인터럽트를 트리거 할 수 있습니다.

인터럽트 이름 앞에있는 # 해시 기호는 활성이 낮음을 의미하며 이는 일반적인 규칙이며 PCI 인터럽트 라인은 항상 활성이 낮습니다.

단일 기능 장치는 인터럽트에 #INTA를 사용해야합니다. 다기능 장치는 단일 인터럽트 핀을 사용하는 경우 #INTA를 사용해야하고, 두 개의 인터럽트 핀을 사용하는 경우 #INTA#INTB를 사용해야합니다.

이러한 규칙으로 인해 #INTA는 일반적으로 #INTB, #INTC보다 많은 기능에서 사용됩니다. #INTD. #INTA를 통해 #INTD를 지원하는 4 개의 IRQ 라인에 부하를 분산시키기 위해 각 PCI 슬롯 또는 장치는 일반적으로 모든 #INTA 클라이언트가 동일한 수신 인터럽트 라인에 연결되지 않도록하기 위해 인터럽트 컨트롤러의 다른 입력에 회전 방식으로 연결됩니다.

이 절차를 인터럽트 스위 즐링이라고합니다. 따라서 장치 트리에는 각 PCI 인터럽트 신호를 인터럽트 컨트롤러의 입력에 매핑하는 방법이 필요합니다. #interrupt-cells, interrupt-mapinterrupt-map-mask 속성은 인터럽트 매핑을 설명하는 데 사용됩니다.

실제로 여기에 설명 된 인터럽트 매핑은 PCI 버스로 제한되지 않으며 모든 노드가 복잡한 인터럽트 맵을 지정할 수 있지만 PCI 사례가 가장 일반적입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-range = <0 0>

#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>

#interrupt-cells = <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0xc000 0 0 1 &intc 9 3 // 1st slot
0xc000 0 0 2 &intc 10 3
0xc000 0 0 3 &intc 11 3
0xc000 0 0 4 &intc 12 3
0xc800 0 0 1 &intc 10 3 // 2nd slot
0xc800 0 0 2 &intc 11 3
0xc800 0 0 3 &intc 12 3
0xc800 0 0 4 &intc 9 3>;
};

먼저 2 개의 셀을 사용하는 시스템 인터럽트 컨트롤러와 달리 PCI 인터럽트 번호는 하나의 셀만 사용한다는 것을 알 수 있습니다. 하나는 irq 번호이고 다른 하나는 플래그입니다. PCI 인터럽트는 항상 레벨이 낮도록 지정되므로 PCI는 인터럽트에 하나의 셀만 필요합니다.

예제 보드에는 각각 4 개의 인터럽트 라인이있는 2 개의 PCI 슬롯이 있으므로 8 개의 인터럽트 라인을 인터럽트 컨트롤러에 매핑해야합니다. 이것은 인터럽트 맵 속성을 사용하여 수행됩니다.

interrupt-map의 표현 방식은 아래와 같습니다.

interrupt-map = < 자식_인터럽트_정보1 부모_인터럽트_컨트롤러_정보1 부모_인터럽트_정보1
자식_인터럽트_정보2 부모_인터럽트_컨트롤러_정보2 부모_인터럽트_정보2
자식_인터럽트_정보3 부모_인터럽트_컨트롤러_정보3 부모_인터럽트_정보3

>;

위에서 정의한 interrupt-map = <0xc000 0 0 1 &intc 9 3을 살펴보면

  • 0xc000 0 0 1 : 자식_인터럽트_정보1
  • &intc : 부모_인터럽트_컨트롤러_정보1
  • 9 3 : 부모_인터럽트_정보1

여기서 자식_인터럽트_정보1을 살펴보면 여기서 설정이 #address-cells = <3>, #interrupt-cells = <1>;로 설정되어 있는 걸 볼 수 있습니다. #interrupt-cells로 표현되는 것은 자식 인터럽트의 번호를 의미합니다. 보통 PCI 디바이스 하드웨어의 인터럽트 번호는 0이 아닌 1로 시작됩니다.

#interrupt-cells가 2가 아닌 1인 이유는 PCI 인터럽트의 검출은 항상 로우 레벨일 경우만 검출되는 것으로 규정되어 있기 때문입니다.

부모_인터럽트_컨트롤러_정보1은 initc를 참조하게 되어있습니다.
전의 예제를 살펴보면

1
2
3
4
5
6
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};

#interrupt-cells = <2>; 부모의 #interrupt-cells는 2로 설정되어있는 것을 확인할 수 있습니다.
부모_인터럽트_정보1를 보면 9 3 두개의 셀로 되어있습니다. 이것의 의미는 인터럽트 번호가 9번이고 PCI의 인터럽트 검출 레벨은 항상 로우 액티브이므로 이것에 대한 표현 값으로 3을 지정하고 있습니다.

위 예제는 interrupt-map의 주석을 보면 알 수 있듯이 4개의 인터럽트를 가지는 2개의 PCI 슬롯을 가지고 있음을 알 수 있다. 그래서 8개의 인터럽트 라인을 인터럽트 콘트롤러에 매핑해야한다.

인터럽트 번호 (#INTA 등)는 단일 PCI 버스의 여러 PCI 장치를 구별하기에 충분하지 않기 때문에 어떤 PCI 장치가 인터럽트 라인을 트리거 했는지도 표시해야합니다. 다행히도 모든 PCI 장치에는 사용할 수있는 고유 한 장치 번호가 있습니다. 여러 PCI 장치의 인터럽트를 구별하려면 PCI 장치 번호와 PCI 인터럽트 번호로 구성된 튜플이 필요합니다. 보다 일반적으로 말하면, 우리는 4 개의 셀을 가진 단위 인터럽트 지정자를 구성합니다.

  • phys.hi, phys.mid, phys.low로 구성하기 위해서 #address-cells을 3으로 설정
  • 하나의 #interrupt-cell (#INTA, #INTB, #INTC, #INTD 중 하나를 지정하기 위해서).

PCI 주소의 장치 번호 부분 만 필요하기 때문에 인터럽트 맵 마스크 속성이 작동합니다. 인터럽트 맵 마스크는 또한 유닛 인터럽트 지정자와 같은 4 튜플입니다. 마스크의 1은 단위 인터럽트 지정자의 어느 부분을 고려해야하는지 나타냅니다. 이 예에서는 phys.hi의 장치 번호 부분 만 필요하며 4 개의 인터럽트 라인을 구분하기 위해 3 비트가 필요하다는 것을 알 수 있습니다 (카운팅 PCI 인터럽트 라인은 0이 아닌 1에서 시작합니다!).

앞에 보았던 phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrrddddd : 일반적으로 IDSEL 신호 연결과 관련된 장치 번호입니다. 부분을 mask하기 위해서 interrupt-map-mask0xf800 0 0로 설정합니다.
7은 인터럽트 번호를 추출하기 위한 비트 마스크 값이 된다. 이 마스크 값을 가지고 AND 논리연산을 합니다.

이제 인터럽트 맵 속성을 구성 할 수 있습니다. 이 특성은 테이블이며이 테이블의 각 항목은 하위 (PCI 버스) 단위 인터럽트 지정자, 상위 핸들 (인터럽트를 담당하는 인터럽트 제어기) 및 상위 단위 인터럽트 지정자로 구성됩니다. 따라서 첫 번째 줄에서 PCI 인터럽트 #INTA가 IRQ 9, 인터럽트 컨트롤러의 레벨에 민감한 IRQ 9에 매핑되어 있음을 알 수 있습니다.

현재 유일하게 누락 된 부분은 PCI 버스 장치 인터럽트 지정자에서 이상한 숫자입니다. 장치 인터럽트 지정자의 중요한 부분은 phys.hi 비트 필드의 장치 번호입니다. 장치 번호는 보드마다 다르며 각 PCI 호스트 컨트롤러가 각 장치에서 IDSEL 핀을 활성화하는 방법에 따라 다릅니다. 이 예에서 PCI 슬롯 1에는 장치 ID 24 (0x18)가 할당되고 PCI 슬롯 2에는 장치 ID 25 (0x19)가 할당됩니다. 각 슬롯에 대한 phys.hi 값은 다음과 같이 장치 번호를 11 비트 씩 비트 필드의 ddddd 섹션으로 이동하여 결정됩니다.

  • 슬롯 1의 phys.hi는 0xC000이며
  • 슬롯 2의 phys.hi는 0xC800입니다.

인터럽트 맵 속성 쇼를 모두 합하면 다음과 같습니다.

  • 슬롯 1의 #INTA는 IRQ9이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 1의 #INTB는 IRQ10이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 1의 #INTC는 IRQ11이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 1의 #INTD는 IRQ12이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.

  • 슬롯 2의 #INTA는 IRQ10이며 기본 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 2의 #INTB는 IRQ11이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 2의 #INTC는 IRQ12이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.
  • 슬롯 2의 #INTD는 IRQ9이며 1 차 인터럽트 컨트롤러에서 로우 레벨일때 인식됩니다.

interrupts = <8 0>; 속성은 호스트 / PCI 브리지 컨트롤러 자체가 트리거 할 수있는 인터럽트를 설명합니다. 이러한 인터럽트를 PCI 장치가 트리거 할 수있는 인터럽트 (INTA, INTB 등을 사용하여)와 혼용하지 마십시오.

마지막으로 유의해야 할 사항입니다. 인터럽트 상위 속성과 마찬가지로 노드에 인터럽트 맵 속성이 있으면 모든 자식 및 손자 노드에 대한 기본 인터럽트 컨트롤러가 변경됩니다. 이 PCI 예에서 이는 PCI 호스트 브리지가 기본 인터럽트 컨트롤러가됨을 의미합니다. PCI 버스를 통해 연결된 장치가 다른 인터럽트 컨트롤러에 직접 연결되어있는 경우 자체 인터럽트 상위 속성도 지정해야합니다.

공유하기