[Linux:Kernel] 원형 버퍼(Circular Buffer)

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

      =========
      원형 버퍼
      =========
작성: David Howells <dhowells@redhat.com>
      Paul E. McKenney <paulmck@linux.vnet.ibm.com>
번역: 양정석 <dasomoli@gmailREMOVETHIS.com>
리눅스는 원형 버퍼를 구현하는데 사용할 수 있는 많은 기능을 제공합니다.
다음의 두가지 셋이 있습니다:
 (1) 2의 거듭제곱 크기의 버퍼에 대한 정보를 결정하기 위한 편리한 함수들
 (2) 버퍼 안의 객체의 생산자와 소비자가 하나의 락(lock)을 공유하지 않기를
     원할 때를 위한 메모리 장벽
이들 기능을 사용하기 위해서, 아래에서 논의되는 것처럼, 적어도 하나의 생산자와
하나의 소비자가 있어야 합니다. 여러 생산자를 처리하는 것도 그들을 연속적으로
일렬로 늘어놓는 것으로, 여러 소비자를 처리하는 것도 그들을 연속적으로 일렬로
늘어놓는 것으로써 처리 가능합니다.
내용:
 (*) 원형 버퍼가 뭔가요?
 
 (*) 2의 거듭제곱 버퍼 측정.
 (*) 원형 버퍼와 함께 메모리 장벽 사용하기
     – 생산자.
     – 소비자.
===================
원형 버퍼가 뭔가요?
===================
가장 먼저, 원형 버퍼가 뭘까요? 원형 버퍼는 그 안에 두가지 인덱스가 있는 유한한
크기의 고정된 버퍼입니다:
 (1) ‘헤드(head)’ 인덱스 – 생산자가 항목을 버퍼로 집어넣는 곳
 (2) ‘테일(tail)’ 인덱스 – 소비자가 다음 항목을 버퍼에서 찾는 곳
일반적으로 테일 포인터가 헤드 포인터와 같을 때, 그 버퍼는 빈 것입니다; 그리고 그
버퍼는 헤드 포인터가 테일 포인터보다 하나 작을 때, 가득 찬 것입니다. 
헤드 인덱스는 아이템들이 추가될 때, 테일 인덱스는 아이템들이 제거될 때 증가합니다.
테일 인덱스는 헤드 인덱스를 절대로 넘어설 수 없고, 두 인덱스 모두 그들이 버퍼의
끝에 다다랐을 때, 0으로 다시 돌아와야 합니다. 그래서 무한한 양의 데이터가
그 버퍼를 통해 흐를 수 있습니다.
일반적으로 항목들은 모두 같은 단위 크기이지만, 아래 테크닉을 사용하는 것이
엄격하게 요구되지는 않습니다. 인덱스들은 여러 항목이나 가변 크기의 항목들이
두 인덱스 모두 다른 것을 추월하지 않도록 제공되는 그 버퍼 안으로 포함된다면,
1보다 더 많이 증가할 수 있습니다. 그러나 그 구현자는 한 단위 크기 이상의
한 부분은 버퍼의 끝을 돌 수 있고, 두 세그먼트로 쪼개질 수 있기 때문에 조심해야
합니다.
=======================
2의 거듭제곱 버퍼 측정
=======================
제멋대로인 크기의 원형 버퍼의 사용하거나 남아있는 양의 계산은 보통 나머지
(나누기) 명령의 사용을 필요로 하는 느린 동작이 됩니다. 그러나 버퍼가 2의
거듭제곱 크기라면, 훨씬 빠른 비트-앤드(bitwise-AND) 명령을 대신 사용할 수
있습니다.
리눅스는 2의 거듭 제곱 원형 버퍼를 처리하기 위한 매크로 셋을 제공합니다. 이들은
다음을 통해 사용할 수 있습니다:
#include <linux/circ_buf.h>
그 매크로들은:
  (*) 버퍼의 남은 양 측정:
CIRC_SPACE(head_index, tail_index, buffer_size);
     이것은 항목들이 넣어질 수 있는 그 버퍼[1] 안에 남은 공간의 양을 반환합니다.
 (*) 버퍼 안에 최대로 연이어 붙어있는 공간 측정:
CIRC_SPACE_TO_END(head_index, tail_index, buffer_size);
     이것은 항목들이 다시 그 버퍼의 처음으로 돌아가는 것 없이 즉시 삽입되어질
     수 있는 그 버퍼[1] 안에 남은 연이은 공간의 양을 반환합니다.
 (*) 버퍼의 사용량 측정:
CIRC_CNT(head_index, tail_index, buffer_size);
     이것은 현재 버퍼[2]를 사용하는 항목 수를 반환합니다
 (*) 처음으로 돌아가는 것이 없을 때(non-wrapping)의 버퍼 사용량 측정:
CIRC_CNT_TO_END(head_index, tail_index, buffer_size);
     이것은 다시 그 버퍼의 처음으로 돌아가는 것 없이 뽑아 낼 수 있는 연이은
     항목들[2]의 수를 반환합니다.
이들 매크로 각각은 명목상으로 0 에서 buffer_size-1 사이의 값을 반환할 것입니다.
그러나:
 [1] CIRC_SPACE*() 들은 생산자 안에서 사용되도록 의도되었습니다. 생산자에게
     그들은 생산자가 헤드 인덱스를 제어하기 때문에 하한값을 반환할 것입니다.
     그러나 소비자는 여전히 다른 CPU 상에서 그 버퍼를 감소시키고, 테일 인덱스를
     옮기고 있을 수 있습니다.
     생산자가 그 공간을 바쁘게 감소시킬 수 있기 때문에, 소비자에게 그것은
     상한값을 보여줄 것입니다.
 [2] CIRC_CNT*() 들은 소비자 안에서 사용되도록 의도되었습니다. 소비자에게
     그들은 소비자가 테일 인덱스를 제어하기 때문에 하한값을 반환할 것입니다.
     그러나 생산자는 여전히 다른 CPU 상에서 그 버퍼를 채우고, 헤드 인덱스를
     옮기고 있을 수 있습니다.
     소비자가 그 버퍼를 바쁘게 비울 수 있기 때문에, 생산자에게 그것은 상한값을
     보여줄 것입니다.
 [3] 서드 파티에게는, 생산자와 소비자가 보여질 수 있게 되어감에 의해, 그
     인덱스들에 쓰는 순서는 그들이 독립적이고, 다른 CPU 상에서 생성될 있기 때문에
     보장될 수 없습니다. 그래서 이런 상황에서의 결과는 그저 추정이 될 것이고, 아예
     틀릴 수도 있습니다.
=======================================
원형 버퍼와 함께 메모리 장벽 사용하기
=======================================
원형 버퍼와 함께 메모리 장벽을 사용함으로써, 여러분은 다음을 위한 욕구를
피할 수 있습니다.
 (1) 그 버퍼의 양 끝으로의 접근을 다스리기 위한 단일 락(lock) 사용, 그래서
     그 버퍼가 동시에 채우고 비울 수 있는; 그리고
 (2) 어토믹(atomic) 카운터 연산 사용
이를 위한 두 편이 있습니다: 그 버퍼를 채우는 생산자, 그를 비우는 소비자.
어느 한번에 하나만 버퍼를 채워야 하고, 어느 한번에 하나만 버퍼를 비워야 합니다만,
두 편은 동시에 수행할 수 있습니다.
생산자
——
생산자는 이처럼 보일 것입니다:
spin_lock(&producer_lock);
unsigned long head = buffer->head;
unsigned long tail = ACCESS_ONCE(buffer->tail);
if (CIRC_SPACE(head, tail, buffer->size) >= 1) {
/* 버퍼로 아이템 하나를 넣어라 */
struct item *item = buffer[head];
produce_item(item);
smp_wmb(); /* 헤드를 증가시키기 전에 항목을 넣어라 */
buffer->head = (head + 1) & (buffer->size – 1);
/* wake_up() 은 어느 하나가 깨기 전에 헤드가 제출됐음을 확인할
* 것이다 */
wake_up(consumer);
}
spin_unlock(&producer_lock);
이는 CPU가 새로운 항목의 내용을 헤드 인덱스가 소비자에게 사용가능하게 만들기
전에 반드시 쓰여져야 한다는 것을 지시할 것이고, 그 후 그 CPU가 소비자가 깨기
전에 바뀐 헤드 인덱스가 쓰여져야만 함을 지시합니다.
wake_up() 이 꼭 사용하는 그 메카니즘일 필요는 없지만, 만약 상태 변경이 일어난다면,
사용되는 아무 것이나 헤드 인덱스의 갱신과 소비자의 상태 변경 사이에 반드시 한번의
(쓰기) 메모리 장벽을 보장해야 함을 명심하세요.
소비자
——
소비자는 이처럼 보일 것입니다:
spin_lock(&consumer_lock);
unsigned long head = ACCESS_ONCE(buffer->head);
unsigned long tail = buffer->tail;
if (CIRC_CNT(head, tail, buffer->size) >= 1) {
/* 그 인덱스에 있는 내용을 읽기 전에 인덱스를 읽어라 */
smp_read_barrier_depends();
/* 버퍼로부터 하나의 항목을 꺼내라 */
struct item *item = buffer[tail];
consume_item(item);
smp_mb(); /* 테일을 증가시키기 전에 서술자 읽기를 끝내라 */
buffer->tail = (tail + 1) & (buffer->size – 1);
}
spin_unlock(&consumer_lock);
이는 CPU가 새로운 항목을 읽기 전에 그 인덱스가 올라갔음을 확인할 것을 지시할
것이고, 그 후 CPU가 그 항목을 지울 새로운 테일 포인터를 쓰기 전에 그 항목
읽기를 끝냈음을 확인하도록 합니다.
반대 인덱스를 읽기 위한 두 알고리듬 안에 ACCESS_ONCE() 의 사용에 주의하세요.
이것은 컴파일러가 그들의 캐시된 값-어떤 컴파일러는 smp_read_barrier_depends()를
가로질러 수행하는-을 버리고 재로딩하는 것을 막습니다. 여러분이 반대 인덱스가
한 번만 사용될 거라고 알 수 있다면, 이것이 엄격히 필요하지는 않습니다.
=============
더 읽을 거리
=============
리눅스의 메모리 장벽 기능에 대한 설명을 위해 Documentation/memory-barriers.txt도
보세요.

[Linux:Kernel] 어떻게 I2C 디바이스를 인스턴스로 만드는가(How to instantiate I2C devices)

이 문서의 라이센스는 GPL 을 따릅니다(This document is released under the GPL license).

Documentation/i2c/instantiating-devices

번역 : 양정석(dasomoli@gmailREMOVETHIS.com)

어떻게 I2C 디바이스를 인스턴스로 만드는가

=========================================
PCI나 USB 디바이스와 다르게, I2C 디바이스들은 하드웨어 레벨에서 열겨되지
않습니다. 대신에, 소프트웨어가 어떤 디바이스가 각 I2C 버스 세그먼트에
붙어 있는지, 어떤 주소를 이 디바이스가 사용하는지 알아야만 합니다. 이런
이유로, 커널 코드는 I2C 디바이스들을 명시적으로 인스턴스로 만듭니다.
컨텍스트와 요구 사항에 따라 이렇게 할 수 있는 여러 방법이 있습니다.
방법 1: I2C 디바이스를 버스 번호로 선언하기
——————————————-
이 방법은 많은 임베디드 시스템을 위한 경우처럼, I2C 버스가 하나의 시스템
버스일 때, 적절합니다. 이런 시스템 상에서, 각 I2C 버스는 미리 알려진
번호를 가집니다. 그래서 이 버스 상에서 활동하는 I2C 디바이스를 미리 선언
(pre-declare)하는 것이 가능합니다. 이것은 i2c_register_board_info() 를
호출함으로써 등록되는 struct i2c_board_info 의 배열로 수행됩니다.
예제(omap2 h4):
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO(“isp1301_omap”, 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
{ /* EEPROM on mainboard */
I2C_BOARD_INFO(“24c01”, 0x52),
.platform_data = &m24c01,
},
{ /* EEPROM on cpu card */
I2C_BOARD_INFO(“24c01”, 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(…)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(…)
}
위의 코드는 I2C 버스 1 상에 그들 각각의 주소와 그들 드라이버가 필요로 하는
각각에 맞는 데이터를 포함하는 3개의 디바이스를 선언합니다. 해당 I2C 버스가
등록될 때, I2C 디바이스들은 i2c-core에 의해 자동으로 인스턴스로 될 것입니다.
디바이스들은 그들이 장착된 I2C 버스가 (만약) 없어진다면 자동으로 해제되고
소멸됩니다.
방법 2: 디바이스를 명시적으로 인스턴스로 만들기
———————————————–
이 방법은 좀 더 큰 디바이스가 I2C 버스를 내부 통신 용으로 사용할 때
적절합니다. 일반적인 경우는 TV 어댑터입니다. 이들은 보통 I2C 버스로 메인
칩에 연결되는 튜너, 비디오 디코더, 오디오 디코더, 기타 등을 가질 수
있습니다. 여러분은 미리 I2C 버스의 개수를 알 수 없을텐데, 그래서 위에서
설명한 방법 1 은 사용할 수 없습니다. 대신에 여러분은 여러분의 I2C 디바이스를
명시적으로 인스턴스로 만들 수 있습니다. 이것은 struct i2c_board_info 를
채우고, i2c_new_device() 를 호출함으로써 수행합니다.
예제 (sfe4001 네트워크 드라이버):
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO(“max6647”, 0x4e),
};
int sfe4001_init(struct efx_nic *efx)
{
(…)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
(…)
}
위의 코드는 진행 중에 네트워크 어댑터의 I2C 버스 상의 1개의 I2C 디바이스를
인스턴스로 만듭니다.
다른 경우는 I2C 디바이스가 있는지 없는지를 모를 때, 혹은 한 보드로부터
(제조자가 공지없이 그 디자인을 바꾼) 다음 보드로 다른 주소를 가질 때 입니다.
이 경우, 여러분은 i2c_new_device() 대신 i2c_new_probed_device()를 호출할
수 있습니다.
예제 (nxp OHCI 드라이버):
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static int usb_hcd_nxp_probe(struct platform_device *pdev)
{
(…)
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;
(…)
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.type, “isp1301_nxp”, I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
  normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(…)
}
위의 코드는 진행 중에 OHCI 어댑터에 있는 I2C 버스 상에 1개의 I2C 디바이스를
인스턴스로 만듭니다. 그것은 먼저 0x2c 주소로 시도하고 아무것도 찾지 못하면,
0x2d 주소로 시도하고 나서도 여전히 아무 것도 찾지 못하면, 간단히 포기합니다.
I2C 디바이스를 인스턴스로 만드는 드라이버가 소멸을 위한 청소 작업을
책임집니다. 이는 i2c_new_device() 나 i2c_new_probed_device() 에 의해
반환됐던 포인터 상의 i2c_unregister_device() 를 호출함으로써 수행됩니다.
방법 3: 특정 디바이스를 위한 I2C 버스 감지하기
———————————————-
i2c_new_probed_device() 를 호출한다 할지라도, 어떤 때, 여러분은 I2C
디바이스에 대한 충분한 정보를 갖고 있지 못합니다. 일반적인 경우는 PC 메인보드
상의 하드웨어 모니터링 칩입니다. 25 개의 다른 주소로 장착될 수 있는 많은
모델이 있습니다. 거기다 어마어마한 숫자의 메인보드가 있어, 사용되는 하드웨어
모니터링 칩의 하나도 빠뜨리지 않은 철저한 리스트를 빌드하는 것은 불가능합니다.
운좋게도 이들 대부분의 칩은 제조사와 디바이스 ID 레지스터를 가지기 때문에,
그들은 감지(probing)에 의해서 식별될 수 있습니다.
이런 경우, I2C 디바이스들은 명시적으로 선언되지 않거나, 인스턴스로 되지
않습니다. 대신에, 이런 디바이스를 위한 i2c-core 는 그 드라이버가 로드되자
마자, 어떤 것이 찾아지면, 감지할 것이고, I2C 디바이스는 자동으로 인스턴스로
만들어질 것입니다. 이 메카니즘의 어떠한 오동작도 막기 위해서, 다음 제약 사항이
적용됩니다:
* I2C 디바이스 드라이버는 임의의 레지스터를 읽음으로써 지원되는 디바이스를
  식별할 수 있는 detect() 메소드를 구현해야만 합니다.
* 오직 지원하는 디바이스를 가질 듯하고, 감지를 허용하는 버스들만 감지될
  것입니다. 예를 들면 이것은 TV 어댑터 상의 하드웨어 모니터링 칩을 위한 감지는
  피합니다.
  
예제:
drivers/hwmon/lm90.c 안의 lm90_driver 와 lm90_detect() 를 보세요.
성공적인 감지의 결과로서 인스턴스로 만들어진 I2C 디바이스는 그들을 감지했던
드라이버가 제거될 때, 혹은 아래 깔려있는 I2C 버스 그 스스로가 소멸될 때,
어느 것이 먼저 일어나든지, 자동으로 소멸될 것입니다.
2.4 커널의 i2c 서브 시스템과 초기 2.6 커널에 익숙한 여러분들은 이 방법 3 이
이전에 했던 방법과 기본적으로 비슷한 것을 알 수 있을 것입니다. 두 가지 명확한
차이가 있습니다:
* 감지하는 것은 그 당시에는 I2C 디바이스를 인스턴스로 만드는 유일한 방법이었던
  데 반해, 지금은 그저 하나의 방법일 뿐입니다. 가능한 곳에서는 방법 1 과 2 가
  더 우선되어야 합니다. 방법 3 은 원치 않는 부작용이 있을 수 있기 때문에 다른
  방법이 없을 때에만 사용되어야 합니다.
* 그 당시에는 모든 I2C 버스가 기본값으로 감지되었었던데 반해, I2C 버스들은
  지금은 명시적으로 어떤 I2C 드라이버 클래스가 (그 클래스 비트 필드를 이용해서)
  그들을 감지할 수 있는지 알려줘야만 합니다. 기본값은 아무 감지도 하지 않음을
  의미하는 텅빈 클래스입니다. 그 클래스 비트 필드의 목적은 앞서 언급했던 원치
  않는 부작용을 제한하기 위함입니다.
  
다시 한번, 방법 3은 가능한 곳에서 피하는 것이 좋습니다. 명시적인 디바이스
인스턴스로 만들기(방법 1 과 2)가 더 안전하고, 더 빠른, 훨씬 좋은 방법입니다.
방법 4: 유저-스페이스(user-space)에서 인스턴스로 만들기
——————————————————-
일반적으로, 커널은 어떤 I2C 디바이스가 연결되었고, 어떤 주소를 그들이
사용하는지 알아야만 합니다. 그러나 특정 경우에는 그렇지 않기 때문에
사용자들이 그 정보를 제공하게 하도록 sysfs 인터페이스가 추가되었습니다.
이 인터페이스는 모든 I2C 버스 디렉토리 안에 생성되는 2개의 속성 파일을
생성합니다: new_device 와 delete_device, 두 파일 모두 쓰기 전용이고,
여러분은 그들에 적절하게 I2C 디바이스를 인스턴스를 만들고, 각각 삭제하도록
옳은 파라미터를 써야만 합니다.
new_device 파일은 2 파라미터를 가집니다: 그 I2C 디바이스의 이름(문자열)과
그 I2C 디바이스의 주소(숫자, 일반적으로 0x로 시작하는 16진수, 그러나
10진수로도 표현 가능함)
delete_device 파일은 하나의 파라미터를 가집니다: 그 I2C 디바이스의 주소.
두 디바이스가 주어진 I2C 세그먼트 상에 같은 주소로 장착될 수 없듯이,
그 주소는 삭제하기 위한 디바이스를 유일하게 식별할 수 있는 충분조건입니다.
예제:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
이 인터페이스는 커낼 내부(in-kernel) 디바이스 선언이 수행될 수 없을 때에만
사용되어져야 하지만, 유용할 수 있는 여러 경우가 있습니다:
* I2C 드라이버가 일반적으로 디바이스를 감지하지만(방법 3) 여러분의 디바이스가
  장착된 그 버스 세그먼트는 더 적절한 클래스 비트 셋을 가지지 않고, 그래서
  감지가 되지 않을 때.
* I2C 드라이버가 일반적으로 디바이스를 감지하지만, 여러분의 디바이스가 알 수 없는
  주소에 장착될 때.
* I2C 드라이버가 일반적으로 디바이스를 감지하지만, 감지 루틴이 너무 엄격하거나,
  여러분의 디바이스가 공식적으로 아직 지원되지 않기 때문에 여러분의 디바이스가
  감지되지 않지만 여러분은 그와 호환되는 것을 알 때.
* I2C 디바이스를 여러분 스스로 납땜해서 테스트 보드 상에서 드라이버를 개발
  중일 때.
  
이 인터페이스는 몇몇 I2C 드라이버가 구현한 force_* 모듈 파라미터를 대체합니다.
i2c-core 안에 구현된 것이 각 디바이스 드라이버 각자가 구현한 것보다는
훨신 더 효율적이고, 또한 여러분이 셋팅을 바꾸기 위한 드라이버를 다시 로드하지
않아도 된다는 이점을 가집니다. 여러분은 또한 드라이버가 로드되기 전이나 이용
가능하기 전에조차 디바이스를 인스턴스로 만들 수 있고, 그 디바이스가
무슨 드라이버를 필요로 하는지 알 필요도 없습니다.

[WinMerge] Linux Kernel 디렉토리 비교를 위한 Filter

커널은 빌드하면 .cmd 같은게 나와서 디렉토리로 비교하면 저런거 때문에 더럽다.

그래서 필터에 .cmd 같은거 추가해서 쓰면 된다~

다음을 Linux.flt 같은걸로 저장해서 필터 있는데다 같이 넣어주자~

## This is a directory/file filter template for WinMerge
name: Linux
desc: for Linux kernel
## Select if filter is inclusive or exclusive
## Inclusive (loose) filter lets through all items not matching rules
## Exclusive filter lets through only items that match to rule
## include or exclude
def: include
## Filters for filenames begin with f:
## Filters for directories begin with d:
## (Inline comments begin with ” ##” and extend to the end of the line)
f: \.o$
f: \.lib$
f: \.bak$ ## backup
f: \.cmd$
d: \\cvs$ ## CVS control directory

 

[Linux:Kernel] 리눅스와 디바이스 트리(Linux and device tree)

이 문서의 라이센스는 GPL 라이센스를 따릅니다(This document is released under the GPL license).

리눅스와 디바이스 트리

———————-
디바이스 트리 데이터를 위한 리눅스 사용 모델
저자: Grant Likely <grant.likely@secretlab.ca>
번역: 양정석 <dasomoli@gmailREMOVETHIS.com>
이 글은 리눅스가 디바이스 트리를 어떻게 사용하는지를 설명합니다.
디바이스 트리 데이터 포멧의 개요는 devicetree.org[1]에 있는 디바이스
트리 용법 페이지에서 찾을 수 있습니다.
[1] http://devicetree.org/Device_Tree_Usage
“오픈 펌웨어 디바이스 트리” 또는 간단히 디바이스 트리(DT)는 하드웨어를
서술하기 위한 데이터 구조와 언어입니다. 더 정확하게는, 어떤 운영 체제에
의해 읽혀져서 운영 체제가 그 머신의 상세 사항을 하드 코딩할 필요 없게
하는 한 하드웨어의 서술입니다.
구조적으로, DT는 이름있는 노드들로 이루어진 하나의 트리, 또는 사이클이
없는 그래프이고, 노드들은 임의의 데이터를 캡슐화하는 이름있는 프로퍼티
를 임의의 개수만큼 갖습니다. 또한 한 노드에서 자연스러운 트리 구조의
밖 다른 곳으로 임의의 연결을 만들 수 있는 메카니즘도 존재합니다.
개녑적으로, 데이터 버스, 인터럽트 라인, GPIO 연결, 그리고 부속 장치들을
포함하는 일반적인 하드웨어 속성을 서술하기 위해서 트리 안에서 데이터가
어떻게 나타나야 하는지를 위한 ‘바인딩’이라고 부르는 사용 용법의 공통
부분이 정의됩니다.
가능한 한 많이, 이미 존재하는 지원 코드의 사용을 최대화하기 위해서
이미 존재하는 바인딩을 사용해서 하드웨어가 서술됩니다. 그러나 프로퍼티와
코드 이름은 간단한 문자열이기 때문에, 이미 존재하는 바인딩을 확장하거나,
새로운 노드와 속성을 정의하는 것으로 새로운 것을 하나 생성하는 것은
쉽습니다. 그러나, 이미 존재하는 것에 대한 어떤 과제를 먼저 하지 않고,
새로운 바인딩을 생성하는 것은 조심하세요. 이전에 어떻게 기존 시스템에서
i2c 디바이스들이 열거되는지를 먼저 파악하지 않고 생성된 새로운 바인딩
때문에 현재 두가지 다른, 호환되지 않는 i2c 버스를 위한 바인딩이 있습니다.
1. 역사
——-
DT는 원래 오픈 펌웨어로부터 어떤 (운영 체제 같은)클라이언트 프로그램에게
데이터를 전달하기 위한 통신 방법의 일종으로 오픈 펌웨어에 의해서
생성되었습니다. 운영 체제는 런타임에 하드웨어의 구성을 알아내기 위해서
디바이스 트리를 사용했고, 그럼으로써 하드 코딩된 정보 없이 (모든
디바이스를 위한 드라이버가 가용함을 가정하여) 이용 가능한 하드웨어의
한 주류를 지원하였습니다.
오픈 펌웨어는 PowerPC 와 SPARC 플랫폼에서 일반적으로 사용되었기 때문에,
이들 아키텍처의 리눅스의 지원은 오랫동안 디바이스 트리를 사용했습니다.
2005년에, PowerPC 리눅스가 많은 코드 정리와 32비트와 64비트 지원을
통합하기 시작했을 때, 오픈 펌웨어를 사용하느냐와 상관없이 모든 PowerPC
플랫폼 상에서 DT 지원이 필요하다는 결정이 내려집니다. 이것을 하기 위해서
진짜 오픈 펌웨어 구현을 필요로 하지 않는 바이너리 덩이로 커널로 전달할 수
있는 평면 디바이스 트리(FDT: Flattened Device Tree)라고 불리는
DT 표현방법이 만들어 졌습니다. U-Boot, kexec, 그리고 다른 부트로더들이
디바이스 트리 바이너리(dtb: Device Tree Binary)를 전달하기 위한, 그리고
부팅 타임에 dtb를 수정하기 위한 두가지를 지원하기 위해서 수정되었습니다.
DT는 또한 PowerPC 부트 래퍼 (arch/powerpc/boot/*)에 추가되어 dtb가 기존의
DT가 아닌 것을 아는 펌웨어의 부팅을 지원하기 위한 커널 이미지와 함께
싸여질 수 있게 되었습니다.
조금 지나서부터는, FDT 구조는 모든 아키텍처에 사용가능하도록 일반화
되었습니다. 이 것이 쓰여지는 시점에, 6개의 메인 라인 아키텍처(arm,
microblaze, mips, powerpc, sparc, 그리고 x86)와 1개의 메인 라인은
아닌 아키텍처(nios)가 여러 단계의 DT 지원을 하고 있습니다.
2. 데이터 모델
————–
만약 여러분이 이미 디바이스 트리 용법[1] 페이지를 읽지 않았다면,
지금 가서 읽으세요. 괜찮아요. 내가 기다리죠….
2.1 하이 레벨 뷰
—————-
이해를 위해 가장 중요한 것은, DT가 간단히 하드웨어를 서술하는 하나의
데이터 구조라는 것입니다. 그에 관한 마법 같은 것은 없어요. 그리고, 
마법을 사용해서 모든 하드웨어 설정 문제를 날려버리도록 하지도 않습니다.
그게 하는 것은 리눅스 커널 안에서 (혹은 그 문제를 위한 다른 운영체제
안에서) 그 보드로부터의 하드웨어 설정과 디바이스 드라이버 지원의
결합을 끊는 언어를 제공하는 것입니다. DT를 사용하는 것은 보드와 디바이스
지원이 데이터 중심(Data driven)이 되도록 합니다; 머신마다 하드 코딩된
선택들 대신 커널로 전달된 데이터에 기조한 셋업 결정을 내리기.
이상적으로는, 데이터 중심 플랫폼 셋업은 더 적은 코드 중복을 가져오고,
하나의 커널 이미지로 광범위한 하드웨어 지원을 쉽게 만듭니다.
리눅스는 DT 데이터를 다음 세가지 주요 목적을 위해 사용합니다:
1) 플랫폼 식별,
2) 런타임 환경 설정,
3) 디바이스 실제 장착
2.2 플랫폼 식별
—————
무엇보다도 먼저, 커널은 그 특정 머신을 식별하기 위해서 DT 안의 데이터를
사용할 것입니다. 완벽한 세상에서, 모든 플랫폼의 상세 사항은 디바이스
트리에 의에서 완벽히 일관되고 신뢰성있게 서술될 것이므로, 그 특정
플랫폼은 커널에게 문제가 되지 않습니다. 하드웨어가 완벽하지 않음에도,
커널은 초기 부팅 동안 그 머신을 식별해야만 하고 그로 인해 그 머신
의존적인 수정 사항을 실행할 수 있는 기회를 가집니다.
대부분의 경우에, 그 머신 식별은 엉뚱하고, 그 커널은 대신 머신의 코어
CPU나 SoC에 기초한 셋업 코드를 선택할 것입니다. ARM 을 예로 들면,
arch/arm/kernel/setup.c 안의 setup_arch() 는 machine_desc 테이블을
검색하고, 디바이스 트리 데이터와 가장 알맞은 machine_desc를 고르는
arch/arm/kernel/devtree.c 안에 있는 setup_machine_fdt()를 호출할
것입니다. 루트 디바이스 트리 노드 안에 있는 ‘compatible’ 프로퍼티에서
찾고 (궁금하다면, arch/arm/include/asm/mach/arch.h 내에 정의된)
struct machine_desc 안의 dt_compat 리스트와 비교함으로써 가장 알맞은
것을 결정합니다.
‘compatible’ 프로퍼티는 그 머신의 정확한 이름으로 시작하는 문자열의
정렬된 리스트를 포함하고, 그 보드와 가장 크게 호환되는 것에서부터
가장 적은 순으로 정렬된 보드들의 부가 리스트가 뒤따릅니다. 예를 들면,
TI BeagleBoard를 위한 루트 compatible 프로퍼티와 BeagleBoard xM 보드
같은 그 후계자는 각각:
compatible = “ti,omap3-beagleboard”, “ti,omap3450”, “ti,omap3”;
compatible = “ti,omap3-beagleboard-xm”, “ti,omap3450”, “ti,omap3”;
여기서 “ti,omap3-beagleboard-xm” 은 정확한 모델을 지정합니다. 그것은
또한 OMAP 3450 SoC, 그리고 일반적인 omap3 계열과 호환된다고 주장합니다.
여러분은 그 리스트를 가장 크게 특징적인 것(정확한 보드)에서 가장 적게
특징적인 것(SoC 계열)로 정렬함을 알아챌 수 있을 것입니다.
영리한 독자는 Bealge xM 은 또한 원래의 Beagle 보드와 호환된다고 주장함에
주목할 수 있습니다. 그러나, 같은 제품 라인이고, 한 보드가 다른 것과
호환된다고 주장할 때, 무엇을 의미하는지 정확히 못박기 어려울 지라도,
보통 한 보드에서 다른 것으로의 하이 레벨의 변경이 있는 한, 하나는 그
보드 레벨에서 그렇게 하는 것에 대한 경고를 받아야 합니다. 최고 레벨에서
한 쪽의 경고가 잘못되고, 한 보드가 다른 것과 호환된다고 주장하지 않는
것이 더 낫습니다. 유의할만한 예외는 싣는 보드로 한 CPU 모듈이 붙여졌을
때 같이, 한 보드가 다른 보드를 싣고 있을 때 입니다.
compatible 값에 대해 하나 더. compatible 프로퍼티 안에 사용되는 어떤
문자열도 그게 나타내는 것에 대해서 문서화되어야만 합니다. compatible
문자열에 대한 문서를 Documentation/devicetree/bindings 안에 추가하세요.
ARM 으로 돌아가서, 각각의 machine_desc에 대해서, 커널은 compatible
프로퍼티 안에 어떤 dt_compat 리스트 항목이 나타나지는 않는지 살펴봅니다.
하나가 그렇다면, 그 machine_desc는 그 머신으로 들어가기 위한 후보가
됩니다. machine_desc 의 전체 테이블을 검색한 후에, setup_machine_fdt()는
각 machine_desc 와 비슷한 compatible 프로퍼티 내의 항목을 기초로
‘가장 호환되는’ machine_desc를 반환합니다. 비슷한 machine_desc을
아무것도 찾지 못했다면, NULL 을 반환합니다.
이 동작 방식 뒤에 숨어있는 이유는 대부분의 경우에 그들이 모두 같은
SoC나 Soc의 같은 계열을 사용한다면, 하나의 machine_desc가 많은 수의
보드를 지원할 수 있다는 관측 때문입니다. 그러나 변함없이 특정 보드는
일반적인 경우에는 쓸데없는 특정 셋업 코드를 필요로 한다는 예외가
있습니다. 특별한 경우들은 일반적인 셋업 코드 안의 문제를 일으킬 수 있는
보드(들)을 위한 명시적인 검사에 의해 처리될 수 있습니다만, 크게 서둘러
그렇게 하는 것은 몇가지 경우보다 더 많아질 경우, 추하고 (또는)
관리할 수 없게 됩니다.
대신에 compatible 리스트는 dt_compat 리스트 안에 “더 적은 compatible”
값을 지정함으로써 일반적인 machine_desc가 광범위한 공용 보드들을 지원할
수 있도록 합니다. 위의 예에서, 일반적인 보드 지원은 “ti,omap3” 또는
“ti,omap3450″과 호환된다고 주장할 수 있습니다.. 원래의 beagleboard 에서
부팅 초기에 특별한 특수 조치 코드가 필요한 버그가 발견된다면, 새로운
machine_desc 는 그 특수 조치를 구현해서 “ti,omap3-beagleboard” 에만
맞게 되도록 추가할 수 있습니다.
PowerPC는 각 machine_desc 로부터 .probe() 를 호출하고, TRUE를 반환하는
첫번째 것이 사용되는 약간 다른 동작 방식을 사용합니다. 그러나, 이 접근법은
compatible 리스트의 우선 순위를 반영하지 않고, 아마도 새로운 아키텍처
지원을 위해서는 피해야 합니다.
2.3 런타임 환경 설정
——————–
대부분의 경우에, 하나의 DT는 펌웨어에서 커널로 데이터를 주고 받을 수
있는 하나밖에 없는 방법이 될 것이고, 그래서 또한 런타임에 initrd 이미지의
위치와 커널 파라미터 문자열 같은 환경 설정 데이터를 넘기는데 사용되기
시작합니다.
이 데이터의 대부분은 /chosen 노드 안에 포함되고, 리눅스가 부팅될 때,
이와 같은 것들이 보일 것입니다:
chosen {
bootargs = “console=ttyS0,115200 loglevel=8”;
initrd-start = <0xc8000000>;
initrd-end = <0xc8200000>;
};
bootargs 프로퍼티는 커널 인자들을 포함하고, initrd-* 프로퍼티들은 initrd
덩이의 주소와 크기를 정의합니다. initrd-end 는 initrd 이미지 이후의
첫번째 주소임에 유의하세요. 그래서 이것은 struct resource의 일반적인
용법과 일치하지 않습니다. chosen 노드는 또한 옵션으로 플랫폼 의존적인
환경 설정 데이터를 위한 임의의 개수의 추가 프로퍼티들을 포함할 수 있습니다.
초기 부팅 동안, 아키텍처 셋업 코드는 페이징이 셋업되기 전에 디바이스
트리를 파싱하는 다른 헬퍼 콜백들과 of_scan_flat_dt()를 여러번 호출합니다.
of_scan_flat_dt() 코드는 디바이스 트리를 스캔하고 초기 부팅 동안 필요한
정보를 추출하는 헬퍼를 사용합니다. 일반적으로 early_init_dt_scan_chosen()
헬퍼는 커널 파라미터를 포함하는 chosen 노드를 파싱하는데,
early_init_dt_scan_root()는 DT 주소 공간 모델을 초기화하는데, 그리고,
early_init_dt_scan_memory()는 사용 가능한 RAM의 위치와 크기를 결정하는데
사용됩니다.
ARM 상에서 setup_machin_fdt() 함수는 맞는 그 보드를 지원하는
machine_desc를 선택한 후에 디바이스 트리의 초기 스캐닝을 책임집니다.
2.4 디바이스 실제 장착
———————-
보드가 식별되고 난 후에, 그리고 초기 환경 설정 데이터가 파싱된 후에는,
커널 초기화가 보통의 방법으로 수행될 수 있습니다. 이 프로세스의 어떤
시점에, unflatten_device_tree()가 더 효율적인 런타임 표현 방식으로 데이터를
변환하기 위해서 호출됩니다. 이것은 또한 ARM 에서의 machine_desc
.init_early(), .init_irq(), 그리고 init_machine() 훅 같은 머신 의존적인
셋업 훅 때 실행되기 시작할 것입니다. 이 절의 나머지는 ARM 구현으로부터의
예제를 사용합니다. 그러나 모든 아키텍처에서 DT를 사용할 때 이와 같은
것들을 잘 수행할 것입니다.
이름에서 추측할 수 있듯이, .init_early() 는 부트 프로세스 내에서 일찍
실행될 필요가 있는 어떤 머신 의존적인 셋업을 위해 사용되고, .init_irq() 는
인터럽트 처리를 셋업하는데 사용됩니다. DT를 사용하는 것은 이들 함수들의
행위를 실제로 바꾸지 않습니다. DT가 제공되면 .init_early() 와 .init_irq()
모두 플랫폼에 대한 추가 데이터를 얻기 위해서 아무 DT 쿼리 함수
(include/linux/of*.h 안의 of_*)나 호출할 수 있습니다.
DT 안에서 가장 흥미로운 훅은 플랫폼에 대한 데이터로 리눅스 디바이스
모델을 실제 장착시키는 주된 책임을 가지는 .init_machine() 입니다.
역사적으로, 이것은 정적인 클럭 구조체, platform_devices, 그리고 보드 지원
.c 파일 안의 다른 데이터의 합을 정의함, 그리고 .init_machine() 안에서
일제히 등록되는 것으로써 임베디드 플랫폼 상에서 구현되었습니다. DT가
사용될 때, 각 플랫폼을 위한 하드 코딩된 정적 디바이스들 대신, 디바이스의
리스트가 DT를 파싱함으로써 얻어질 수 있고, 디바이스 구조체들을 동적으로
할당할 수 있습니다.
가장 간단한 경우는 .init_machine() 이 그냥 platform_devices 뭉치를 등록할
책임만을 가지고 있을 때 입니다. 한 platform_device 는 리눅스에서 하드웨어에
의해 찾을 수 없는 메모리 또는 I/O 매핑된 디바이스, 그리고 ‘복합적인’ 또는
‘가상의’ (이들 이후에는 또 다른) 디바이스로 사용되는 하나의 개념입니다.
DT 에서는 ‘플랫폼 디바이스’ 용어가 없는 반면, 플랫폼 디바이스는 대충 그
트리의 루트에 있는 디바이스 노드와 간단한 메모리 매핑된 버스 노드들의 자식에
해당합니다.
지금이 예제를 펼쳐 볼 좋은 시간인 것 같네요. 여기 NVIDIA Tegra 보드에 대한
디바이스 트리의 일부가 있습니다.
/{
compatible = “nvidia,harmony”, “nvidia,tegra20”;
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
chosen { };
aliases { };
memory {
device_type = “memory”;
reg = <0x00000000 0x40000000>;
};
soc {
compatible = “nvidia,tegra20-soc”, “simple-bus”;
#address-cells = <1>;
#size-cells = <1>;
ranges;
intc: interrupt-controller@50041000 {
compatible = “nvidia,tegra20-gic”;
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >;
};
serial@70006300 {
compatible = “nvidia,tegra20-uart”;
reg = <0x70006300 0x100>;
interrupts = <122>;
};
i2s1: i2s@70002800 {
compatible = “nvidia,tegra20-i2s”;
reg = <0x70002800 0x100>;
interrupts = <77>;
codec = <&wm8903>;
};
i2c@7000c000 {
compatible = “nvidia,tegra20-i2c”;
#address-cells = <1>;
#size-cells = <0>;
reg = <0x7000c000 0x100>;
interrupts = <70>;
wm8903: codec@1a {
compatible = “wlf,wm8903”;
reg = <0x1a>;
interrupts = <347>;
};
};
};
sound {
compatible = “nvidia,harmony-sound”;
i2s-controller = <&i2s1>;
i2s-codec = <&wm8903>;
};
};
.init_machine() 때, Tegra 보드 지원 코드는 이 DT를 살펴보고, 어떤 노드를
platform_devices 로 생성해야 할 지를 결정할 필요가 있을 것입니다.
그러나, 이 트리를 살펴보고, 즉시 어떤 종류의 디바이스가 각 노드로
표현된 것인지는 한 노드가 한 디바이스를 표현한 것이 전부일 지라도
명확하지 않을 것입니다. /chosen, /aliases, 그리고 /memory 노드는
디바이스를 나타내지 않는(아마 분명히, 메모리는 디바이스로 고려될 수
있겠지만) 정보를 전달하기 위한 노드들 입니다. /soc 노드의 자식들은
메모리 매핑된 디바이스지만, codec@1a 는 i2c 디바이스이고, sound 노드는
디바이스가 아니지만, 그보다는 다른 디바이스들이 오디오 서브 시스템을
만들기 위해서 함께 연결되어 있다고 표현합니다. 저는 이 보드 설계에
매우 익숙하기 때문에 각 디바이스가 무엇인지 압니다만, 커널은 어떻게
이 노드들로 무엇을 해야 할 지 알 수 있을까요?
그 트릭은 커널이 트리의 루트에서 시작하고, ‘compatible’ 프로퍼티를 가진
노드들을 살피는 겁니다. 첫째, 일반적으로 ‘compatilbe’ 프로퍼티를 가진
어떤 노드는 어떤 종류의 디바이스를 표현한다고 가정하고, 둘째, 그 트리의
루트에 있는 어떤 노드는 직접 프로세서 버스에 붙거나, 또는 다른 방식으로
설명할 수 없는 기타 시스템 디바이스라고 가정할 수 있습니다. 각 트리
노드에 대해, 리눅스는 차례차례 platform_driver 로 향하게 될
platform_device 를 할당하고 등록합니다.
왜 이들 노드를 위해 platform_device 를 사용하는 것이 안전한 가정일까요?
글쎄요, 리눅스가 디바이스를 모델링하는 방법에서, 거의 모든 bus_types 는
그 디바이스들이 버스 컨트롤러의 자식이라고 가정합니다. 예를 들면,
각 i2c_client 는 i2c_master 의 한 자식입니다. 각 spi_device 는 SPI 버스의
한 자식이고요. 유사하게 USB, PCI, MDIO, 기타도 그렇습니다. 같은 계층 구조는
DT에서도 역시 I2C 디바이스 노드들은 오직 I2C 버스 노드의 한 자식으로만
나타나는 데서 찾을 수 있습니다. SPI, MDIO, USB. 기타도 동일합니다. 부모
디바이스의 특정 타입을 필요로 하지 않는 디바이스는 오직 리눅스
/sys/devices 트리의 바닥에서 행복하게 살 platform_devices(그리고,
amba_devices, 이후에 나올 다른 것들)입니다. 그래서 한 DT 노드가 그 트리의
루트 노드라면, 정말로 아마도 그것은 하나의 platform_device로 등록되는 것이
최선입니다.
리눅스 보드 지원 코드는 그 트리의 루트에 있는 디바이스를 찾기 시작하기
위해서 platform_populate(NULL, NULL, NULL, NULL) 를 호출합니다. 그 트리의
루트로부터 시작할 때, 시작 노드(첫번째 NULL), 부모 struct device(마지막
NULL)을 줄 필요가 없고, 우리는 매치 테이블을 (아직) 사용하고 있지 않기
때문에, 그 파라미터들은 모두 NULL 입니다. 오직 디바이스를 등록할 필요가
있는 한 보드를 위해서, .init_machine() 은 of_platform_populate() 함수를
위해서를 제외하면 완전히 텅 빌 수 있습니다.
Tegra 예제에서, 이것이 /soc 와 /sound 노드의 이유입니다. 그러나 SoC
노드의 자식에 대해서는 뭘까요? 그들도 플랫폼 디바이스로 등록되어야만
하지 않을까요? 리눅스 DT 지원에서, 자식 디바이스를 위한 일반적인 동작은
그 부모의 디바이스 드라이버에 의해서 드라이버 .probe() 때 등록되는
것입니다. 그래서 i2c 버스 디바이스 드라이버는 각 자식 노드를 위한
i2c_client 를 등록할 것이고, SPI 버스 드라이버는 그 spi_device 자식을
등록할 것이고, 유사하게 다른 bus_types도 그럴 겁니다. 이 모델에 따라
드라이버는 SoC 노드로 바인드하고, 그 자식들 각 노드를 간단히
platform_devices로 등록하도록 작성될 수 있습니다. 보드 지원 코드는
SoC 디바이스를 할당하고 등록할 것이고, (이론적인) SoC 디바이스
드라이버는 SoC 디바이스로 바인드할 수 있고, 그 .probe() 훅 안에서
soc/interrupt-controller, /soc/serial, /soc/i2s, 그리고 /soc/i2c 를 위한
platform_devices 를 등록합니다. 쉽죠, 어때요?
실제로, 더 많은 platform_devices 처럼 어떤 platform_devices의 자식을
등록하는 것은 일반적인 패턴으로 나타납니다. 그리고 그 디바이스 트리 지원
코드는 그를 반영하고 위의 예제를 더 간단하게 만듭니다.
of_platform_populate() 의 두번째 인자는 of_device_id 테이블이고, 그 테이블
안의 한 항목과 매치되는 어떤 노드는 또한 그 등록된 자식 노드를 얻을
것입니다. Tegra 의 경우에 그 코드는 이것처럼 볼 수 있습니다:
static void __init harmony_init_machine(void)
{
/* … */
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
“simple-bus” 는 ePAPR 1.0 스펙 안에 간단한 메모리 매핑된 버스를 나타내는
한 프로퍼티로 정의됩니다. 그래서 of_platform_populate() 코드는
simple-bus 호환 노드는 언제나 가로지를 것임을 그저 가정하도록 작성될 수
있습니다. 그러나, 우리는 한 인자를 그 안으로 넣을 수 있어서 보드 지원
코드는 언제나 그 기본 동작을 오버라이드할 수 있습니다.
[i2c/spi/기타 자식 디바이스 추가에 대한 논의가 추가될 필요가 있음]
부록 A: AMBA 디바이스
———————
ARM Primecells 들은 하드웨어 찾기와 전력 관리를 위한 지원을 포함하는
ARM AMBA 버스에 붙는 특정 종류의 디바이스입니다. 리눅스에서,
struct amba_device 와 amba_bus_type 은 Primecell 디바이스를 표현하는데
사용됩니다. 그러나, 약간 성가신 것은 ABMA 버스 상의 모든 디바이스가
Primecells은 아니고, 리눅스를 위해 amba_device 와 platform_device
인스턴스 둘 다 같은 버스 세그먼트의 형제가 되는 것이 일반적입니다. 
DT를 사용할 때, 이것은 platform_device 또는 amba_device 중에 어느 하나로
각 노드를 등록해야 하는지 결정해야만 하기 때문에 of_platform_populate()
에 문제를 만듭니다. 이것은 불행히 그 디바이스 생성 모델이 약간
복잡합니다. 그러나 그 해결책은 너무 깊이 퍼져 있어서 나타나지 않습니다.
만약 “arm,amba-primecell”과 호환되는 한 노드가 있으면,
of_platform_populate() 는 그것을 platform 디바이스 대신 amba_device 로
등록할 것입니다.

[Linux:Kernel] 레귤레이터 드라이버 인터페이스

이 문서의 저작권은 GPL license를 따릅니다(This document is released under the GPL license).

Documentation/power/regulator/regulator.txt

번역: 양정석 <dasomoli@gmailREMOVETHIS.com>

레귤레이터 드라이버 인터페이스

==============================

레귤레이터 드라이버 인터페이스는 비교적 간단하고, 레귤레이터 드라이버가

그들의 서비스들을 코어 프레임워크에 등록하도록 디자인되었습니다.

등록

====

드라이버들은 레귤레이터를 다음 호출로 등록할 수 있습니다 :-

struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
const struct regulator_config *config);

이것은 그 레귤레이터 수용능력과 동작들을 레귤레이터 코어에 등록할 것입니다.

레귤레이터들은 다음 호출로 등록을 해제할 수 있습니다 :-

void regulator_unregister(struct regulator_dev *rdev);

레귤레이터 이벤트

=================

레귤레이터들은 이벤트들(예를 들면, 과열, 저전압, 기타)을 컨슈머 드라이버로

다음 호출을 통해 보낼 수 있습니다 :-

int regulator_notifier_call_chain(struct regulator_dev *rdev,
 unsigned long event, void *data);

[Linux:Kernel] 레귤레이터 머신 드라이버 인터페이스

이 문서의 저작권은 GPL license를 따릅니다(This document is released under the GPL license).
Documentation/power/regulator/machine.txt
번역: 양정석 <dasomoli@gmailREMOVETHIS.com>
레귤레이터 머신 드라이버 인터페이스
===================================
레귤레이터 머신 드라이버 인터페이스는 보드/머신 의존적인 초기화 코드가
레귤레이터 서브 시스템을 설정하도록 의도되었습니다.
다음 머신을 생각해봅시다 :-
  레귤레이터-1 -+-> 레귤레이터-2 –> [컨슈머 A @ 1.8 – 2.0V]
                |
                +-> [컨슈머 B @ 3.3V]
                
컨슈머 A 와 B 를 위한 드라이버는 그들의 전원 공급 제어에 따라 그에 맞는
레귤레이터에 맵핑되어야만 합니다. 이 맵핑은 각 레귤레이터를 위한
struct regulater_consumer_supply 생성에 의한 머신 초기화 코드 안에 담겨 있을
수 있습니다.
struct regulator_consumer_supply {
        const char *dev_name;   /* consumer dev_name() */
        const char *supply;     /* consumer supply – e.g. “vcc” */
};
예를 들면, 위의 머신을 위해서는
static struct regulator_consumer_supply regulator1_consumers[] = {
{
        .dev_name       = “dev_name(consumer B)”,
        .supply         = “Vcc”,
},};
static struct regulator_consumer_supply regulator2_consumers[] = {
{
        .dev    = “dev_name(consumer A”),
        .supply = “Vcc”,
},};
이 것은 레귤레이터-1을 ‘Vcc’ 공급원으로 컨슈머 B를 위해 맵핑하고,
레귤레이터-2를 ‘Vcc’ 공급원에 컨슈머 A를 위해 맵핑합니다.
각 레귤레이터 파워 도메인을 위한 제약 사항들은 바로 struct regulator_init_data를
정의함으로써 등록될 수 있습니다. 이 구조체는 또한 컨슈머를 그들의 공급
레귤레이터로 맵핑합니다 :-
static struct regulator_init_data regulator1_data = {
        .constraints = {
                .name = “Regulator-1”,
                .min_uV = 3300000,
                .max_uV = 3300000,
                .valid_modes_mask = REGULATOR_MODE_NORMAL,
        },
        .num_consumer_supplies = ARRAY_SIZE(regulator1_consumers),
        .consumer_supplies = regulator1_consumers,
};
그 name 필드는 다른 레귤레이터들을 위한 공급원들의 설정을 위해, 그리고
로그 기록과 다른 분석 출력 결과를 위한 용도를 위해서 그 보드를 실제로
설명하는 것으로 셋팅되어야 합니다. 보통 그 회로도 내의 공급 선로를 위한
이름이 좋습니다. name이 주어지지 않으면 서브 시스템이 하나를 선택할 것입니다.
레귤레이터-1은 레귤레이터-2로 전력을 공급합니다. 레귤레이터-1이 컨슈머 A가
그 공급원(레귤레이터-2)을 켤 때 켜질 수 있도록 이 관계는 그 코어에 반드시
등록되어야 합니다. 그 공급 레귤레이터는 아래의 supply_regulator 필드에 의해
셋팅됩니다:-
static struct regulator_init_data regulator2_data = {
        .supply_regulator = “Regulator-1”,
        .constraints = {
                .min_uV = 1800000,
                .max_uV = 2000000,
                .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
                .valid_modes_mask = REGULATOR_MODE_NORMAL,
        },
        .num_consumer_supplies = ARRAY_SIZE(regulator2_consumers),
        .consumer_supplies = regulator2_consumers,
};
마지막으로, 레귤레이터 디바이스들이 일반적인 관례대로 등록되어야만 합니다.
static struct platform_device regulator_devices[] = {
{
        .name = “regulator”,
        .id = DCDC_1,
        .dev = {
                .platform_data = &regulator1_data,
        },
},
{
        .name = “regulator”,
        .id = DCDC_2,
        .dev = {
                .platform_data = &regulator2_data,
        },
},
};
/* register regulator 1 device */
platform_device_register(&regulator_devices[0]);
/* register regulator 2 device */
platform_device_register(&regulator_devices[1]);

[Linux:Kernel] 레귤레이터 컨슈머 드라이버 인터페이스

이 문서의 저작권은 GPL license를 따릅니다(This document is released under the GPL license).

Documentation/power/regulator/consumer.txt

번역: 양정석 <dasomoli@gmailREMOVETHIS.com>

레귤레이터 컨슈머 드라이버 인터페이스
=====================================

이 문서는 컨슈머 디바이스 드라이버를 위한 레귤레이터 인터페이스에 대해
서술하고 있습니다. 이 문서 내에 사용된 용어의 설명은 overview.txt를 봐주세요.
1. 컨슈머 레귤레이터 접근 (정적 & 동적 드라이버)
================================================

컨슈머 드라이버는 그 공급 레귤레이터를 다음 호출로 접근 할 수 있습니다 :-

regulator = regulator_get(dev, “Vcc”);

컨슈머는 그 struct device 포인터와 전원 공급자 ID를 넘깁니다. 코어는 해당하는
레귤레이터를 머신 의존적인 탐색 테이블을 뒤져서 찾습니다. 탐색이 성공하면 이
호출은 이 컨슈머에 전원을 공급하는 struct regulator의 포인터를 반환할 것입니다.

레귤레이터를 컨슈머 드라이버에서 제거하기 위해서는 다음을 호출해야 합니다 :-

regulator_put(regulator);

컨슈머들은 하나 이상의 레귤레이터에 의해 전원이 공급될 수 있습니다. 예를 들면,
아날로그와 디지털 전원 공급을 함께 받는 코덱 컨슈머 :-

digital = regulator_get(dev, “Vcc”);  /* digital core */
analog = regulator_get(dev, “Avdd”);  /* analog */

그 레귤레이터 접근 함수 regulator_get()과 regulator_put() 는 일반적으로
여러분의 디바이스 드라이버의 probe() 와 remove() 함수 안에서 각각 호출됩니다.
2. 레귤레이터 출력 켜고 끄기 (정적 & 동적 드라이버)
===================================================

컨슈머는 그 전원 공급을 다음을 통해 켤 수 있습니다:-

int regulator_enable(regulator);

알림: 그 공급자는 이미 regulator_enabled()가 호출되기 전에 켜져 있을 겁니다.
그 컨슈머가 레귤레이터를 공유하거나 그 레귤레이터가 이전에 부트로더나 커널 보드
초기화 코드에 의해서 켜져 있다만 이렇게 될 수 있습니다.

컨슈머는 다음 호출을 통해 레귤레이터가 켜져 있는지 알아볼 수 있습니다 :-

int regulator_is_enabled(regulator);

이것은 레귤레이터가 켜져 있으면, 0보다 큰 값을 반환할 것입니다.
컨슈머는 그 전원 공급이 더이상 필요없을 때 다음을 통해 끌 수 있습니다 :-

int regulator_disable(regulator);

알림: 이것은 만약 그것을 다른 컨슈머와 공유하고 있으면, 그 전원 공급을 끄지
않을 것입니다. 그 레귤레이터는 켜진 것의 참조 카운트가 0일 때만 끌 것입니다.

마지막으로, 레귤레이터는 긴급한 경우에 강제로 끌 수 있습니다 :-

int regulator_force_disable(regulator);

알림: 이것은 즉시 그리고 강제로 레귤레이터 출력을 끌 겁니다. 모든 컨슈머는
전원이 꺼질 것입니다.

3. 레귤레이터 전압 제어 & 상태(동적 드라이버)
=============================================

어떤 컨슈머 드라이버는 시스템 동작 시점에 맞게 동적으로 그들의 전압 공급을
바꾸고 싶을 수 있습니다. 예를 들면, CPUfreq 드라이버들은 전력을 아끼기 위해서
주파수와 함께 전압을 조정할 수 있고, SD 드라이버들은 해당하는 카드 전압을
선택할 필요가 있을 수 있고, 기타 등등.

컨슈머들은 그들의 전압 공급을 다음을 통해 제어할 수 있습니다 :-

int regulator_set_voltage(regulator, min_uV, max_uV);

여기서 min_uV 와 max_uV 는 최소 그리고 최대 허용가능한 마이크로 볼트 전압입니다.

알림: 이것은 레귤레이터가 켜져있거나 꺼져 있을 때, 호출될 수 있습니다. 켜져
있을 때 호출되면, 전압은 즉시 바뀔 것이고, 아니라면, 전압 설정이 바뀌고
레귤레이터가 다음에 켜질 때 전압이 물리적으로 셋팅됩니다.

설정된 레귤레이터의 출력 전압은 다음을 통해 얻을 수 있습니다 :-

int regulator_get_voltage(regulator);

알림: get_voltage() 는 레귤레이터가 켜져있건 꺼져있건 설정된 전압을 반환합니다.
그리고 레귤레이터 출력 상태를 알아내는데는 사용하지 말아야 합니다. 그러나
그 레귤레이터의 물리적 출력 전압을 알아내기 위해서 이것은 is_enabled() 와의
조합으로 사용할 수 있습니다.
4. 레귤레이터 전류 제한 제어 & 상태(동적 드라이버)
==================================================

어떤 컨슈머 드라이버는 그 공급 전류 제한을 시스템 동작 시점에 맞춰 바꿔야 할
수도 있습니다. 예를 들면, LCD 백라이트 드라이버는 다양한 백라이트 밝기에 맞게
전류 제한을 바꿀 수 있고, USB 드라이버는 전원 공급 때 500mA 로 제한하기를 원할
겁니다.

컨슈머들은 그 공급 전류 제한을 다음으로 제어할 수 있습니다 :-

int regulator_set_current_limit(regulator, min_uA, max_uA);

여기서 min_uA 와 max_uA 는 최소, 최대 허용가능한 마이크로 암페어 전류 제한입니다.

알림: 이것은 레귤레이터가 켜져 있거나 꺼져 있을 때 호출 될 수 있습니다. 만약 켜져
있을 때 호출되면, 그 전류 제한은 즉시 바뀔 것이고, 아니면, 그 전류 제한 설정이
바뀌고 다음에 켜질 때 물리적으로 전류 제한이 셋팅됩니다.

레귤레이터는 전류 제한 값을 다음으로 얻을 수 있습니다 :-

int regulator_get_current_limit(regulator);

알림: get_current_limit() 는 그 레귤레이터가 켜져 있거나 꺼져 있거나 상관없이
전류 제한값을 반환할 것입니다. 그리고 레귤레이터 전류 로드값을 알아내기 위해서는
사용되어서는 안됩니다.
5. 레귤레이터 동작 모드 제어 & 상태(동적 드라이버)
==================================================

어떤 컨슈머들은 컨슈머들의 동작 상태 변경 때 더 효율적이도록 전원 공급
레귤레이터의 동작 모드를 바꿈으로써 더 많은 시스템 전력을 절약할 수 있습니다.
예를 들면, 컨슈머 드라이버는 대기 상태이고 그 후에는 더 적은 전류를 먹습니다.

레귤레이터 동작 모드는 직접 혹은 간접적으로 변경될 수 있습니다.

간접 동작 모드 제어
——————-
컨슈머 드라이버는 그 공급 레귤레이터 동작 모드의 변경을 다음을 통해 요청할 수
있습니다 :-

int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);

이것은 코어가 레귤레이터 상의 (모든 그 컨슈머에 기초해서) 총 부하를 재계산하도록
하는 원인이 될 것이고, 그 전류 동작 부하에 가장 알맞은 동작 모드로 (필요하고
허용된다면) 변경할 것입니다.

load_uA 값은 컨슈머 데이터시트로부터 결정될 수 있습니다. 예를 들면, 대부분의
데이터시트는 특정 상태에서 최대 전류 소모량을 보여주는 표를 가집니다.

대부분의 컨슈머는 그들이 레귤레이터에 대해 모를 때 또는 그 레귤레이터가
다른 컨슈머와 공유되는지 모를 때는 간접 동작 모드 제어를 사용할 것입니다.

직접 동작 모드 제어
——————-
맞추어진 또는 강하게 결합된 드라이버들은 직접 레귤레이터 동작 모드를 그들의
동작 시점에 따라 제어하기를 원할 것입니다. 이것은 다음을 통해 모을 수
있습니다 :-

int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator);

직접 모드는 그 레귤레이터에 대해서, 그리고 다른 컨슈머들과 공유되지 않음을
*아는* 컨슈머에 의해서만 사용될 것입니다.
6. 레귤레이터 이벤트
====================
레귤레이터들은 컨슈머들에게 외부 이벤트를 알릴 수 있습니다. 이벤트들은 컨슈머에
의해서 레귤레이터 부하 또는 실패 컨디션 하에서 수신될 수 있습니다.

컨슈머들은 다음 호출을 통해 레귤레이터 이벤트에 대한 관심을 등록할 수 있습니다 :-

int regulator_register_notifier(struct regulator *regulator,
     struct notifier_block *nb);

컨슈머들은 다음 호출을 통해 그들의 관심을 해제할 수 있습니다 :-

int regulator_unregister_notifier(struct regulator *regulator,
struct notifier_block *nb);

레귤레이터들은 그들에게 관심있어 하는 컨슈머들에게 이벤트를 보내는데 커널
노티파이어 프레임워크를 사용합니다.

[Linux:Kernel] 레귤레이터 API 디자인 노트

이 문서의 저작권은 GPL license를 따릅니다(This document is released under the GPL license).

Documentation/power/regulator/design.txt

번역: 양정석 <dasomoli@gmailREMOVETHIS.com>

레귤레이터 API 디자인 노트

==========================

이 문서는 부분적으로 구조화된 요약, 레귤레이터 API 디자인에 영향을 준

디자인 고려점들의 개요를 제공합니다.

안전성

——

 – 레귤레이터 설정 내의 에러들은 잠재적으로 영구적인 하드웨어 손상을

   포함하는 시스템에 매우 심각한 결과를 가져올 수 있습니다.

 – 시스템의 전원 설정을 자동으로 결정하는 것은 불가능합니다 – 다른 전력

   요구사항을 가지는 같은 칩의 소프트웨어적으로 동등한 변종들과 전력

   요구사항을 가지는 일부 컴포넌트들은 소프트웨어에 보입니다.

   

  => 그 API 는 이들 변경이 이 특정 시스템 상에서 안전하게 수행될 수 있다는

     것을 알게 되기 전까지는 하드웨어 상태에 변경을 가하지 않습니다.

컨슈머 유즈 케이스

——————

 – 시스템 내의 디바이스들의 압도적인 수의 대부분은 그들의 전원을 켜고 끄는 것을

   넘는 어떤 런타임 설정을 하도록 하는 요구 사항을 갖지는 않을 겁니다.

 – 많은 시스템 내의 전원 공급자는 여러 다른 컨슈머들 사이에 공유될 겁니다.

  => 컨슈머 API는 이들 유즈 케이스가 처리되는데 매우 쉽도록, 그래서 컨슈머가

     공유된 공급원으로 별다른 추가적인 노력없이 동작하도록 구조화되어야

     합니다.

[Linux:Kernel] 리눅스 전압과 전류 레귤레이터 프레임워크

이 문서의 저작권은 GPL license를 따릅니다(This document is released under the GPL license).


Documentation/power/regulator/overview.txt


리눅스 전압과 전류 레귤레이터 프레임워크

========================================

이것에 관하여

=============

이 프레임워크는 전압과 전류 레귤레이터를 제어하기 위한 표준 커널 인터페이스를

제공하기 위해서 디자인 되었습니다.

그 의도는 시스템으로 하여금 전력을 절약하고 더 긴 배터리 수명을 위해 동적으로

레귤레이터의 전원 출력을 제어할 수 있도록 하는 데 있습니다. 이 것은 (전압 출력을

제어 가능한 곳에서)전압 조절 장치와 (전류 제한이 제어 가능한)전류 제어, 둘 다

적용합니다.

(C) 2008  Wolfson Microelectronics PLC.

저자: Liam Girdwood <lrg@slimlogic.co.uk>

번역: 양정석 <dasomoli@gmailREMOVETHIS.com>

용어

====

이 문서 안에서는 몇몇 용어가 사용됩니다:-

  o 레귤레이터   – 다른 디바이스로 전력을 공급하는 전자 장치.

                   어떤 것들은 그들의 출력 전압과 (또는) 전류를 제어할 수 있는

                   데 반해 대부분의 레귤레이터는 그들의 출력을 켜고 끌 수 있습니다.

                   입력 전압 -> 레귤레이터 -> 출력 전압

                   

  o PMIC         – 전력 관리 칩(Power Management IC). 한 IC는 여러개의 레귤레이터와

                   종종 다른 서브 시스템을 포함합니다.

  o 컨슈머       – 레귤레이터에 의해 전력이 공급되는 전자 장치.

                   컨슈머는 두가지 타입으로 분류할 수 있습니다:-

                   

                   정적: 컨슈머는 그 공급 전압이나 전류 제한을 바꾸지 않습니다.

                   그저 그 전원 공급을 켜거나 끄는 것만을 필요로 합니다. 그 공급

                   전압은 하드웨어, 부트로더, 펌웨어나 커널 보드 초기화 코드에

                   의해서 결정됩니다.

  o 파워 도메인  – 레귤레이터, 스위치의 출력 전원 또는 다른 파워 도메인에 의한

                   그 입력 전원이 공급되는 전자 회로.

                   

                   그 공급 레귤레이터는 스위치(들) 뒤에 있을 것입니다. 예를 들면,

                   

                   레귤레이터 -+-> 스위치-1 -+-> 스위치-2 –> [컨슈머 A]

                               |             |

                               |             +-> [컨슈머 B], [컨슈머 C]

                               |

                               +-> [컨슈머 D], [컨슈머 E]

                

                   저것은 하나의 레귤레이터와 세 개의 파워 도메인입니다:

                   

                   도메인 1: 스위치-1, 컨슈머 D와 E.

                   도메인 2: 스위치-2, 컨슈머 B와 C.

                   도메인 3: 컨슈머 A.

                   

                   그리고 이것은 “공급자들” 관계를 나타냅니다:

                   

                   도메인-1 –> 도메인-2 –> 도메인-3.

                   

                   하나의 파워 도메인은 다른 레귤레이터들에 의해 전원이 공급되는

                   레귤레이터들을 갖을 것입니다. 예를 들면,

                   

                   레귤레이터-1 -+-> 레귤레이터-2 -+-> [컨슈머 A]

                                 |

                                 +-> [컨슈머 B]

                                 

                   이 것은 우리에게 두 개의 레귤레이터와 두 개의 파워 도메인을 줍니다:

                   

                   도메인 1: 레귤레이터-2, 컨슈머 B

                   도메인 2: 컨슈머 A

                   

                   그리고 하나의 “공급자들” 관계:

                   

                   도메인-1 –> 도메인-2

  o 제약 사항    – 제약 사항은 성능과 하드웨어 보호를 위한 전원 레벨을 정의하는데

                   사용되고는 합니다. 제약 사항은 세 개의 레벨이 존재합니다:

                   

                   레귤레이터 레벨: 이것은 레귤레이터 하드웨어 동작 파라미터에

                   의해서 정의되고, 레귤레이터 데이터 시트 내에서 정해집니다.

                   예를 들면,

                   

                     – 전압 출력은 800mV -> 3500mV 범위 안 입니다.

                     – 레귤레이터 전류 출력 제한은 20mA @ 5V 아니면 10mA @10V 입니다.

                     

                   파워 도메인 레벨: 이것은 커널 레벨 보드 초기화 코드에 의해서

                   소프트웨어 내에서 정의됩니다. 그것은 파워 도메인을 특정 전원

                   범위로 제약하는데 사용되곤 합니다. 예를 들면,

                   

                     – 도메인-1 전압은 3300mV

                     – 도메인-2 전압은 1400mV -> 1600mV

                     – 도메인-3 전류 제한은 0mA -> 20mA.

                     

                   컨슈머 레벨: 이것은 컨슈머 드라이버가 동적으로 전압이나 전류 제한

                   레벨을 셋팅함에 의해서 정의됩니다.

                   

                   예를 들면, 컨슈머 백라이트 드라이버가 전류 증가를 위해 5mA 에서

                   10mA 로 LCD 광도 증가를 위해 요청을 합니다. 이 것은 다음과 같은

                   레벨을 통해 진행됩니다 :-

                   

                   컨슈머: LCD 밝기를 증가할 필요가 있다. 밝기 테이블(컨슈머 드라이버는

                   같은 레퍼런스 디바이스 상에서를 기초로 여러 다른 개인 설정을 사용하기도

                   한다) 안에서 살펴보고 다음 전류 mA 값을 요청하라.

                   

                   파워 도메인: 새로운 전류 제한이 이 도메인과 시스템 상태(예를 들면,

                   배터리 전원, USB 전원)를 위한 동작 제한들 내에 있는가

                   

                   레귤레이터 도메인: 새로운 전류 제한이 입력/출력 전압을 위한 레귤레이터

                   동작 파라미터 내에 있는가

                   

                   만약 그 레귤레이터가 모든 제약사항 테스트를 동과하면 새로운 레귤레이터

                   값이 적용됩니다.

디자인

======

프레임워크는 SoC 기반의 디바이스들을 대상으로 하고 디자인되었습니다만, SoC가

아닌 디바이스들과도 관련이 있고, 다음 네가지 인터페이스에 따라 나뉩니다:-

   1. 컨슈머 드라이버 인터페이스

      이것은 컨슈머 드라이버가 레귤레이터를 (클럭 atm으로 할 수 있는 것 같이)

      얻거나 내려 놓을 수 있고, 전류 제한, 모드, 켜고 끄기, 전압을 읽고/셋팅할

      수 있다는 것에서 커널 클럭 인터페이스와 비슷한 API 를 사용합니다. 컨슈머

      에게 그 공급 전압과 전류 제한의 완전한 제어를 허용하여야 합니다. 또한

      사용 중이 아니면 꺼져서 드라이버들이 전원 제어를 위한 레귤레이터 없이

      시스템 안에서 재사용될 수 있도록 합니다.

      

        Documentation/power/regulator/consumer.txt 를 보세요.

        

   2. 레귤레이터 드라이버 인터페이스

      이것은 레귤레이터 드라이버가 그들의 레귤레이터를 등록하고, 그 코어에

      동작을 제공할 수 있도록 합니다. 또한 레귤레이터 이벤트를 클라이언트에게

      퍼뜨리기 위한 노티파이어 호출 체인을 가집니다.

      

        Documentation/power/regulator/regulator.txt 를 보세요.

        

   3. 머신 인터페이스

      이 인터페이스는 머신 의존적인 코드를 위해서 존재하고, 각 레귤레이터를

      위한 전압/전류 도메인의 (제약사항과 함께) 생성을 가능하도록 합니다.

      버그가 있는 클라이언트 드라이버에 의한 과전압 또는 과전류에 따른

      디바이스 손상을 막는 레귤레이터 제약사항을 제공할 수 있습니다. 또한

      어떤 레귤레이터가 다른 것들에 의해 공급되는지를 나타내는 (클럭 트리와

      비슷한) 레귤레이터 트리의 생성을 하도록 합니다.

      

        Documentation/power/regulator/machine.txt 를 보세요.

        

   4. 유저스페이스 ABI.

      그 프레임워크는 또한 많은 유용한 전압/전류/동작모드 데이터를 유저스페이스에

      sysfs를 통해 드러냅니다. 이것은 디바이스 전원 소비와 상태를 들여다 보는데

      사용될 수 있습니다.

      

        Documentation/ABI/testing/sysfs-class-regulator 를 보세요.