[ BBB - yocto (9) ] 장치트리(DEVICE TREE)
0) 참조하면 좋은 문서
spruh73q.pdf (am335x reference manual)
https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf
am3358-ep.pdf(핀 속성 보려고 다운로드)
https://www.ti.com/lit/ds/symlink/am3358-ep.pdf
BBB_SCH.pdf (비글본 회로도)
https://github.com/beagleboard/beaglebone-black/blob/master/BBB_SCH.pdf
디바이스 트리를 설명해둔 문서
https://developer.toradex.com/software/linux-resources/device-tree/device-tree-overview1) 장치트리란?
장치트리란 커널이 사용하도록 할 장치들의 정보를 기술해놓은 파일이다.
장치 트리 파일을 바탕으로 커널은 적절한 드라이버를 로드하기도 하고 핀 멀티플랙싱(핀의 역할을 지정)등을 한다.
그럼 이 장치 트리에 대해서 알아보자.
본인의 poky 디렉토리로 이동한다. 그리고 dts 파일 쪽으로 이동하여 보자.
$ cd /path/to/poky
$ source oe-init-build-env
$ cd build/tmp/work-shared/beaglebone/kernel-source/arch/arm/boot/dtsdts 디렉토리에서 다음 명령어를 실행한다.$ code .명령어를 실행하고 vscode 에서 ctrl + p를 눌러 다음 3개 파일을 열어보자.이것이 장치 트리이다.우리가 사용하고 있는 am335x-boneblack.dtb 파일은 위 4개를 컴파일 하여 dtb(디바이스 트리를바이너리로 컴파일)한 것이다.안의 문법을 보면 특이하게 되어 있다.구조는 위와 같으며 안의 문법을 보면 특이하게 되어 있다.하나씩 알아가 보자.1. 디바이스 트리 버전- 디바이스 트리 버전이다. 디바이스 트리를 컴파일 할 때 여기에 명시된 버전을 기준으로 컴파일 한다.2. 인클루드 헤더와 장치 트리 파일- 인클루드 헤더 : c에서와 마찬가지로 인클루드 하여 사용할 수 있다. c 문법이 사용되어 있다.- 인클루드 헤더의 위치 : 포키디렉토리/tmp/work-shared/beaglebone/kernel-source/include- 장치 트리 파일 : 다른 장치트리 파일을 포함한다.3. 머신 호환성- 이 디바이스 트리에 호환되는 머신의 이름을 적어놓는다.- 커널은 머신 이름에 맞는 드라이버를 로드할 것이다.4. 노드- 노드는 장치에 대한 정보를 기술해 놓는다.- 네이밍 규칙은 다음과 같다.[레이블]@[하드웨어 주소]즉 node0@0 는 레이블이 node0, 하드웨어 주소는 0이라는 뜻이다.- 레이블은 상대 참조될 수 있다. (&[레이블 이름]를 사용하여 참조한다.)5. 노드 속성- 장치의 속성을 지정하거나, 노드의 활성 비활성 속성을 설정하는 등 커널이 디바이스를 사용자가설정한 노드 속성대로 사용하게 한다.- 노드 속성에는 스트링을 적거나 16진수 값을 적을 수 있다.6. 노드 호환성- 머신 호환성과 마찬가지로 커널이 노드에 적힌 장치의 드라이버를 로드한다.- 노드에 적힌 '노드 호환성'을 기준으로 커널이 드라이버를 찾는다.7. 자식 노드 속성- 자식 노드의 속성을 설정한다.8. alias 노드- alias 노드는 다른 장치 트리에서도 참조할 수 있는 노드를 의미한다.- 선언은 이렇게 한다. alias [참조 노드 이름] = &[참조할 노드]- 이렇게 선언하면 다른 디바이스 트리에서 인클루드 해서 &[참조 노드 이름] 써서 노드 속성을 덮어씌우거나 추가할 수 있다.호환성에 대해서 설명을 좀 더 덧붙이자면 커널이 드라이버를 찾을 때compatible = "driver name" 이런식으로 주는데요 드라이버는 이 경로에서 찾을 수 있다.poky디렉토리/build/tmp/work-shared/beaglebone/kernel-source/driversspi를 예로 들어보자면,poky디렉토리/build/tmp/work-shared/beaglebone/kernel-source/drivers/spi/spidev.c 를 열어보면, 다음과 같은 속성이 있다.static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv", .data = &spidev_of_check }, { .compatible = "lineartechnology,ltc2488", .data = &spidev_of_check }, { .compatible = "semtech,sx1301", .data = &spidev_of_check }, { .compatible = "lwn,bk4", .data = &spidev_of_check }, { .compatible = "dh,dhcom-board", .data = &spidev_of_check }, { .compatible = "menlo,m53cpld", .data = &spidev_of_check }, { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, { .compatible = "micron,spi-authenta", .data = &spidev_of_check }, {}, };구조체에서 장치 트리의 compatible에 해당하는 드라이버를 로드하게 된다... 우선은 이 정도로만 이해하자.3) 핀 멀티플랙싱디바이스 트리에 대해서 대충 알았으니 핀 멀티 플랙싱에 대해 알아보자.핀 멀티 플랙싱에 관해서는 위 '참조하면 좋은 문서' 항목의 spruh73q.pdf의 핀 멀티플랙싱 챕터를 읽어봐도 좋을 것이다멀티 플랙싱이란 핀이 하나에 여러개의 역할을 할 수 있는 것을 말한다. 한번에 하나의 역할을 할 수 있다.이를테면, uart로 사용하는 핀은 gpio로도 사용할 수 있고 목적에 따라 다른 역할을 하는 핀으로 사용가능하다.우리는 핀이 어떻게 무엇으로 어떻게 동작할 것인지 디바이스 트리에 기술해 주어야 한다.그래야 핀이 사용자의 의도대로 올바르게 동작한다.핀 속성에 대해서는 am3358-ep.pdf 이 문서를 참조하면 된다.이를 테면 다음 챕터에서 다루게 되겠지만, uart1을 사용하고 싶다고 하자.비글본에서 proc에 있는 serial을 찍어서 어떤 시리얼 핀이 사용 가능한지 봐보자.$ cat /proc/tty/driver/serial serinfo:1.0 driver revision: 0: uart:8250 mmio:0x44E09000 irq:42 tx:8471 rx:105 RTS|CTS|DTR|DSR 1: uart:8250 mmio:0x48022000 irq:20 tx:0 rx:0 CTS|DSR|CD|RI 2: uart:unknown port:00000000 irq:0 3: uart:unknown port:00000000 irq:0 4: uart:unknown port:00000000 irq:0 5: uart:unknown port:00000000 irq:0지금 디바이스 트리를 수정해서 uart 1이 할당되어서 이렇게 보이지만, 원래는 uart2, 3번과 마찬가지로unkown 포트로 나온다.이는 디바이스 트리에 uart1의 장치를 활성화하지 않았기 때문이며, 장치트리에서 활성화 해주어야 한다.4) 패드 컨르롤 레지스터패드 control register에 대한 설명이다.1. 비트 6 : slew rate(신호 변화하는 속도) 설정하는 비트다.- 0 : 빠르게- 1 : 느리게2. 비트 5 : pad를 위해 입력을 활성화 한다. output으로만 쓸 때는 0으로 설정한다. 1은 인풋, 아웃풋 둘다로 쓸 수 있다.- 0 : 수신기 비활성화- 1 : 수신기 활성화3. 비트 4 : 풀업 스타일을 설정한다.- 0 : 풀다운 선택- 1 : 풀업 선택4. 비트 3 : 풀업에 대한 활성화 여부를 설정한다.- 0 : 풀업/다운 활성화- 1 : 비활성화5. 비트 0~2 : 모드 설정이다. 핀의 역할을 지정하는 부분으로 보면 되겠다.- mode에 대해서는 문서 am3358-ep.pdf의 4.2 핀 속성 항목을 참조하자.6. 비트 8 : 인풋 아웃풋 설정이다.문서에서는 찾을 수 없었지만, omap.h에 가보면 패드 컨트롤 레지스터의 상위 비트에 대한 추가설정 항목이 있다.경로 : 포키디렉토리/tmp/work-shared/beaglebone/kernel-source/include/dt-bindings/pinctrl/omap.h/* omap3/4/5 specific mux bit defines */ #define INPUT_EN (1 << 8) #define OFF_EN (1 << 9) #define OFFOUT_EN (1 << 10) #define OFFOUT_VAL (1 << 11) #define OFF_PULL_EN (1 << 12) #define OFF_PULL_UP (1 << 13) #define WAKEUP_EN (1 << 14) #define WAKEUP_EVENT (1 << 15)우선 8번째 비트의 INPUT_EN이 있는데 이것이 am33xx.h에서 #undef되고 새롭게 정의된 것을 볼 수 있다./* am33xx specific mux bit defines */ #undef PULL_ENA #undef INPUT_EN #define PULL_DISABLE (1 << 3) #define INPUT_EN (1 << 5) #define SLEWCTRL_SLOW (1 << 6) #define SLEWCTRL_FAST 0 /* update macro depending on INPUT_EN and PULL_ENA */ #undef PIN_OUTPUT #undef PIN_OUTPUT_PULLUP #undef PIN_OUTPUT_PULLDOWN #undef PIN_INPUT #undef PIN_INPUT_PULLUP #undef PIN_INPUT_PULLDOWN #define PIN_OUTPUT (PULL_DISABLE) #define PIN_OUTPUT_PULLUP (PULL_UP) #define PIN_OUTPUT_PULLDOWN 0 #define PIN_INPUT (INPUT_EN | PULL_DISABLE) #define PIN_INPUT_PULLUP (INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (INPUT_EN) /* undef non-existing modes */ #undef PIN_OFF_NONE #undef PIN_OFF_OUTPUT_HIGH #undef PIN_OFF_OUTPUT_LOW #undef PIN_OFF_INPUT_PULLUP #undef PIN_OFF_INPUT_PULLDOWN #undef PIN_OFF_WAKEUPENABLE패드 control 레지스터와 일치한다.
여기 문서 내용을 보면 아웃풋으로 고정하여 핀을 쓸 때는 레지스터에 풀업 저항을 달지 마라고 나와있으니주의하여 사용하도록 하자.5) 핀 멀티플랙싱 설정하기이제 멀티 플랙싱을 할 특정 핀을 지정하려면 어떻게 해야할지 의문이 들 것이다.또한 모드에는 어떤 모드가 있는지도 말이다.비글본의 회로도를 보며 이야기 하겠다.익스펜션 헤더핀의 녹색 박스의 저 2개의 핀을 설정하려 한다고 치자ctrl + f 를 눌러 검색하여 보자.코어 쪽을 살펴보면 코어의 안쪽에 기술되어있는 핀 모드 속성이 보일 것이다. 이는 순서대로mode0/mode1/mode2/mode3/.../mode7인 것을 알 수 있다.
문서 am3358-ep.pdf의 pin 속성을 보면 알 수 있다.위 정보를 바탕으로 핀을 멀티 플랙싱하는 방법은1. 핀의 오프셋을 지정한다.2. 모드, 핀의 풀업, 인풋 아웃풋을 설정한다.이렇게 해주면 된다.am33xx의 오프셋은 여기에 기술되어있다.경로 : 포키디렉토리/tmp/work-shared/beaglebone/kernel-source/include/dt-bindings/pinctrl/am33xx.h#define AM335X_PIN_OFFSET_MIN 0x0800U #define AM335X_PIN_GPMC_AD0 0x800 ...자 이제 필요한 내용을 모두 알았으니... uart1의 핀을 설정해보자.우선 uart1의 노드를 상대 참조([&uart1] 이런 방식으로 &를 사용해 노드를 쓰는 것을 상대참조, 주소를그대로 가져다 쓰는 경우를 절대 참조라 함) 하여 속성을 덮어 씌워보자.am335x-boneblack.dtb를 수정하여 보자.am33xx-l4.dtsi에서 ctrl + f 키를 눌러 uart1를 검색하면 보이는 노드 속성이다.status 의 노드 속성 스트링이 diabled로 되어 있는데 노드가 비활성 상태이다. 활성 상태로 덮어 씌워야 한다.//am335x-boneblack.dtb의 맨 아래에 적자&uart1 {status = "okay";};자 이렇게 하면 uart1의 핀 속성을 덮어 씌울 수 있다. status 속성을 okay로 해서 활성화 상태로 만들었다.이제 핀 멀티 플랙싱만 설정해주면 된다.am335x-bone-common.dtsi를 참조하면,&am33xx_pinmux {user_leds_s0: user-leds-s0-pins {pinctrl-single,pins = <AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7)AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7)AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7)AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7)>;};}이런식으로 되어 있는데, 하나씩 살펴보자.- am33xx_pinmux : 핀 먹스를 설정하는 드라이버로 생각하자. am33xx-l4.dtsi 에서 찾을 수 있다.- 자식 노드에 멀티플랙싱 할 핀의 이름과 참조될 이름을 기술할 수 있다.- pinctrl-single,pins 속성을 사용해 여러개 핀의 멀티 플랙싱 설정을 한번에 지정할 수 있다.- [레이블] : [노드 이름] 형식으로 자식 노드를 선언한다. 이때 레이블이 상대 참조 될 수 있다.자 이것을 am335x-boneblack.dtb에서 참조하여 uart를 위한 자식노드를 작성하여 보자.&am33xx_pinmux {uart1_pins: uart1-pins {pinctrl-single,pins = <0x180 0x0010 /* uart1_rx mux mode 0 input */0x184 0x0000 /* uart1_tx mux mode 0 output */>;};};pinctrl-single,pins = < [오프셋] [패드 컨트롤 레지스터 값]>;이렇게 적어주면 된다.오프셋의 계산은 am33xx.h의 상수 'AM335X_PIN_OFFSET_MIN' 의 0x800 값을 빼주면 된다.#define AM335X_PIN_UART1_RXD 0x980 #define AM335X_PIN_UART1_TXD 0x984저 2개의 상수 값에서 오프셋을 빼주면 된다.이를태면,AM335X_PIN_UART1_RXD의 경우는 0x980 - 0x800 = 0x180AM335X_PIN_UART1_TXD의 경우는 0x984 - 0x800 = 0x184패드 컨트롤 레지스터는 아까 위에서 4) 패드 컨트롤 레지스터 항목을 참조해서 적어주면 된다.주의) 핀 먹스 드라이버는 칩 제조사마다 다른 드라이버를 쓰기 때문에 레퍼런스 메뉴얼을 참조해야 함에주의하자 비글본에서 쓰는 핀 멀티플랙싱 설정 하는 방식을 라즈베리파이에 그대로 쓸 수 없다는 이야기이다.이 과정은 매우 귀찮으며, 실수할 여지도 다분하다. 하지만 ti사에서 제공하는 매크로 함수가 있으니 이걸 사용하면무척이나 편리하다.&am33xx_pinmux {uart1_pins: uart1-pins {pinctrl-single,pins = <AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0)AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_OUTPUT, MUX_MODE0)>;};};가능하다면 위 방식을 사용하자.자 이제 핀 멀티플랙싱 설정을 uart1 노드가 사용할 수 있도록 핀 컨트롤을 매핑해주면 된다.&uart1 {pinctrl-names = "default";pinctrl-0 = <&uart1_pins>;status = "okay";};최종 am335x-boneblack.dtb의 모습// SPDX-License-Identifier: GPL-2.0-only/** Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/*//dts-v1/;#include "am33xx.dtsi"#include "am335x-bone-common.dtsi"#include "am335x-boneblack-common.dtsi"#include "am335x-boneblack-hdmi.dtsi"/ {model = "TI AM335x BeagleBone Black";compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";chosen {base_dtb = "am335x-boneblack.dts";base_dtb_timestamp = __TIMESTAMP__;};};&cpu0_opp_table {/** All PG 2.0 silicon may not support 1GHz but some of the early* BeagleBone Blacks have PG 2.0 silicon which is guaranteed* to support 1GHz OPP so enable it for PG 2.0 on this board.*/oppnitro-1000000000 {/* OPP Nitro */opp-supported-hw = <0x06 0x0100>;};};&gpio0 {gpio-line-names ="[mdio_data]","[mdio_clk]","P9_22 [spi0_sclk]","P9_21 [spi0_d0]","P9_18 [spi0_d1]","P9_17 [spi0_cs0]","[mmc0_cd]","P9_42A [ecappwm0]","P8_35 [lcd d12]","P8_33 [lcd d13]","P8_31 [lcd d14]","P8_32 [lcd d15]","P9_20 [i2c2_sda]","P9_19 [i2c2_scl]","P9_26 [uart1_rxd]","P9_24 [uart1_txd]","[rmii1_txd3]","[rmii1_txd2]","[usb0_drvvbus]","[hdmi cec]","P9_41B","[rmii1_txd1]","P8_19 [ehrpwm2a]","P8_13 [ehrpwm2b]","NC","NC","P8_14","P8_17","[rmii1_txd0]","[rmii1_refclk]","P9_11 [uart4_rxd]","P9_13 [uart4_txd]";};&gpio1 {gpio-line-names ="P8_25 [mmc1_dat0]","[mmc1_dat1]","P8_5 [mmc1_dat2]","P8_6 [mmc1_dat3]","P8_23 [mmc1_dat4]","P8_22 [mmc1_dat5]","P8_3 [mmc1_dat6]","P8_4 [mmc1_dat7]","NC","NC","NC","NC","P8_12","P8_11","P8_16","P8_15","P9_15A","P9_23","P9_14 [ehrpwm1a]","P9_16 [ehrpwm1b]","[emmc rst]","[usr0 led]","[usr1 led]","[usr2 led]","[usr3 led]","[hdmi irq]","[usb vbus oc]","[hdmi audio]","P9_12","P8_26","P8_21 [emmc]","P8_20 [emmc]";};&gpio2 {gpio-line-names ="P9_15B","P8_18","P8_7","P8_8","P8_10","P8_9","P8_45 [hdmi]","P8_46 [hdmi]","P8_43 [hdmi]","P8_44 [hdmi]","P8_41 [hdmi]","P8_42 [hdmi]","P8_39 [hdmi]","P8_40 [hdmi]","P8_37 [hdmi]","P8_38 [hdmi]","P8_36 [hdmi]","P8_34 [hdmi]","[rmii1_rxd3]","[rmii1_rxd2]","[rmii1_rxd1]","[rmii1_rxd0]","P8_27 [hdmi]","P8_29 [hdmi]","P8_28 [hdmi]","P8_30 [hdmi]","[mmc0_dat3]","[mmc0_dat2]","[mmc0_dat1]","[mmc0_dat0]","[mmc0_clk]","[mmc0_cmd]";};&gpio3 {gpio-line-names ="[mii col]","[mii crs]","[mii rx err]","[mii tx en]","[mii rx dv]","[i2c0 sda]","[i2c0 scl]","[jtag emu0]","[jtag emu1]","[mii tx clk]","[mii rx clk]","NC","NC","[usb vbus en]","P9_31 [spi1_sclk]","P9_29 [spi1_d0]","P9_30 [spi1_d1]","P9_28 [spi1_cs0]","P9_42B [ecappwm0]","P9_27","P9_41A","P9_25","NC","NC","NC","NC","NC","NC","NC","NC","NC","NC";};&baseboard_eeprom {vcc-supply = <&ldo4_reg>;};&am33xx_pinmux {uart1_pins: uart1-pins {pinctrl-single,pins = <0x180 0x00880x184 0x0008>;};};&uart1 {pinctrl-names = "default";pinctrl-0 = <&uart1_pins>;status = "okay";};이제 이것을 빌드하자.$ cd poky폴더 $ source oe-init-build-env $ bitbake -c compile -f virtual/kernel $ bitbake -c deploy -f virtual/kernel-f는 강제로 compile 과 deploy를 수행하라는 것이며, 이 과정 이후에 나온 am335x-boneblack.dtb를비글본 sd카드의 파티션 1번에 있는 것과 교체해 부팅해 주면 다시 /proc/tty/driver/serial을 확인해보면 활성화 된 것을 볼 수 있을 것이다.이렇게 디바이스 트리를 직접 수정하여 써도 되지만, 최근에 추가된 오버레이를 다음번에 사용해보려고 한다.오버레이는 다음 챕터에서 자세히 다루겠다.
댓글
댓글 쓰기