[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 안에 구현된 것이 각 디바이스 드라이버 각자가 구현한 것보다는
훨신 더 효율적이고, 또한 여러분이 셋팅을 바꾸기 위한 드라이버를 다시 로드하지
않아도 된다는 이점을 가집니다. 여러분은 또한 드라이버가 로드되기 전이나 이용
가능하기 전에조차 디바이스를 인스턴스로 만들 수 있고, 그 디바이스가
무슨 드라이버를 필요로 하는지 알 필요도 없습니다.

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

  1. 안녕하세요 다솜돌이님
    좋은정보 감사합니다
    그런데 platform_data를 쓰는 이유가 무엇인지 궁금합니다.

    1. 플랫폼마다 다른 데이터를 등록해서 해당 데이터로 무언가를 하기 위해서겠죠~ 거의 대개 드라이버에서 이걸 가지고 데이터에 맞는 처리, 즉 플랫폼 별 처리를 합니다. 🙂

댓글 남기기

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