I2C Subsystem -1- (Basic)

 

I2C(Inter-Integrated Circuit)

I2C는 필립스에서 개발한 직렬 컴퓨터 버스이며 여러 개의 저속의 주변 기기를 연결하기 위해 사용된다. 시리얼 통신에 사용되는 UART 또는 SPI들보다 더 단순하여 심플한 회로 구성이 가능하다.

 

I2C 통신에 사용되는 2 개의 라인은 다음과 같은 기능을 수행한다.

  • SDA (Serial DAta)
    • 데이터의 직렬 전송에 사용된다.
  • SCL (Serial CLock)
    • 디바이스간 신호 동기화에 사용되는 클럭이다.

 

다음 그림은 I2C 버스에 연결되어 있는 디바이스들을 보여준다.

 

i2c 버스에 연결되는 디바이스 종류들은 수백~수천 가지로 매우 다양하다. 다음 그림은 몇 가지 종류의 디바이스들을 i2c 버스에 연결하여 사용할 때의 모습을 보여준다.

 

I2C Specification History

i2c 스펙은 다음과 같이 발전을 해왔다. 아래 노란 박스는 새 버전이 나올 때 마다 변경된 주요 항목이다.

  • 필립스에서 처음 I2C가 개발되었을 때 다음과 같은 specification으로 동작하였다.
    • 가장 널리 사용되는 스탠다드 모드가 100Khz로 사용되고, 저속 모드로 운영시에는 10Khz의 속도로 동작한다.
    • 7비트 주소를 사용하여 총 128개의 주소를 사용할 수 있지만 그 중 16개는 reserve되어 있고, 나머지 112개를 사용할 수 있다. 따라서 하나의 I2C 버스에는 최대 112개의 디바이스가 연결될 수 있다.
  • 그 후 추가 버전들은 속도가 증가되었고, 주소가 10비트로 확장되었다.
    • 속도 증가
      • Fast 모드에서 400Khz, High 모드에서 3.4Mhz로 속도가 증가됨을 알 수 있다.
    • 주소 확대
      • 10비트 모드가 사용되면서 최대 1008개의 디바이스를 연결할 수 있게 되었다.

  • (1) 3.4Mhz High-speed-mode를 사용하기 위해서는 SDAH SLCH를 사용하는 디바이스를 사용하고 arbitration 및 클럭 동기화를 사용하지 않아야 한다.
  • (2) 5Mhz Ultra Fast-speed-mode를 위해서는 USDA, USCL 라인이 추가되어야하고 Master → Slave 단방향만 지원된다.

 

멀티 마스터 지원

I2C는 다음 그림과 같이 1개의 버스에 2개 이상의 컨트롤러가 경쟁할 수 있다. 이 때 두 컨트롤러는 버스 Master 역할을 하기 위해 경쟁하게된다.

  • Multi-master
    • 2개 이상의 마스터가 메시지 손상 없이 동시에 버스를 제어하는 경우.
  • Arbitration
    • 2개 이상의 마스터가 버스를 동시에 통제할 때 오직 하나만이 허용되며 선택된 마스터의 메시지는 손상되지 않는다.
  • Synchronization
    • 두 개 이상의 디바이스의 클럭들을 동기화

 

I2C-Mux

다음과 같이 i2c 스위치 및 i2c 멀티플레서 두 종류로 나뉜다. 각각의 기능은 다음과 같다.

  • i2c 스위치
    • 1:N으로 구성 시 하위 채널 각각에 대해 select  및 deselect 할 수 있다. 즉 1개 이상을 select할 수 있다.
  • i2c 멀티플렉서
    • 1:N 또는 N:1로 구성 시 하위 채널 하나를 select 및 deselect 할 수 있다.  select 시 한 번에 하나의 채널만 select할 수 있다.

 

 

제품에 따라 다음과 같은 기능이 지원될 수도 있다.

  • HW reset 핀(option)
    • i2c 스위치에서 외부 HW reset(option) 핀이 지원되는 경우 버스 리커버리를 수행할 수 있다.
    • i2c 스위치 및 i2c 멀티플렉서 둘 다 내부에서 POR(Power On Reset) 기능을 지원한다.
  • 인터럽트(option)
    • 인터럽트를 받아 mux하여 전송할 수 있다.
  • HW addr 설정 핀
    • i2c-mux 디바이스를 하나의 버스에 여러 개를 사용할 경우 중복되지 않게 주소 하위 비트 몇 개(3~4)를 설정할 수 있는 핀이 패키지 외부로 제공된다.

 

다음 그림은 외부 reset, 인터럽트, HW 주소 설정핀 등이 지원되는 모습을 보여준다.

 

아래 그림은 2개의 i2c 컨트롤러와 그 중 하나에 연결된 4채널 i2c 멀티플렉서를 보여준다.

 

I2C 통신 프로토콜

2 개의 라인만을 사용하는 I2C 버스에서 I2C 디바이스들이 사용하는 통신 프로토콜을 알아보기로 하자.

 

Feature 리스트

다음 표와 같이 I2C 통신 프로토콜에 사용되는 Feature들이다. 디바이스들은 싱글 마스터, 멀티 마스터 또는 슬래이브 등의 각 구성(Configuration)에 따라 사용되는 Feature들이 다르다.

 

주요 Feature

i2c 버스에 어떠한 통신도 없는 경우 SDA 및 SCL 라인은 high 상태에 있다. 마스터나 디바이스가 통신을 시도할 때 전류가 흐르며 low 상태로 바뀐다.

 

Start & Stop condition

다음 그림과 같이 데이터를 송수신하기 위한 시작과 끝 조건을 알아본다.

  • Start condition
    • 클럭(SCL)이 high 시그널일 때 데이터(SDA)가 low 시그널로 변경될 때 Start condition으로 인식한다.
    • 표기법: S
  • Stop condition
    • 클럭(SCL)이 high 시그널일 때 데이터(SDA)가 high 시그널로 변경될 때 Stop condition으로 인식한다.
    • 표기법: P

 

i2c 데이터 통신 중에는 클럭이 high 시그널일 때에는 데이터(SDA) 시그널이 변경되면 안된다. 이러한 규칙으로 인해 클럭이 high인 상태에서는 데이터를 안정적으로 읽는 것을 보장한다.

 

그런데 아래 그림과 같이 클럭(SCL)이 high 상태일 때 데이터(SDA)가 변경되는 특수한 상태가 있다. 이러한 경우 데이터(SDA)가 변화한 방향에 따라 low 상태로 변경되면 Start condition이고 high 상태로 변경되면 Stop condition이라 한다.

 

1 바이트 전송 및 Acknowledge 처리

Start condition이 발생한 후 Stop condition이 나오기 전까지 디바이스들 간에 연속으로 바이트 단위의 통신을 할 수 있다. 1 바이트를 수신할 때마다 수신측 디바이스는 전송측을 위해 응답(Acknowledge) 비트를 전송해야 한다.

  • 전송측 디바이스는 1 바이트를 MSB부터 전송한다.
  • 수신측 디바이스는 1 바이트 수신 완료 후 1 비트의 Ack(low)를 전송한다.
  • 표기법: A 또는 ACK

 

아래 그림을 보면 클럭(SCL)을 마스터가 주기적으로 보내고 있음을 알 수 있다. 데이터(SDA)는 전송 디바이스측에서 8비트를 보낸 후 9번째에 high 상태를 유지하고 있다. 그리고 수신 디바이스측에서 9번째 클럭에 해당하는 위치가 오기전에 ACK(low) 상태를 유지하는 것을 알 수 있다.

Synchronization

하나의 i2c 버스에서 두 개의 마스터가 동작할 때 각각의 클럭을 and 하여 동기화 시키는 것으로 하나의 클럭을 만들어낸다.

 

Arbtration

하나의 i2c 버스가 free 상태일 때 두 개의 마스터가 동시에 버스를 차지하려고 각각 Start condition을 보낸 경우이다. 그 이후로 운좋게(?) 서로 데이터를 동일하게 보내다가 어느 시점에서 각자의 전송 비트가 달라지게 되는데 그 시점에서 데이터(SDA)를 high 상태로 바꾼 마스터가 arbtration의 패자가 된다. 즉 데이터(SDA)를 low 상태로 물고 있는 마스터가 승자이다.

 

아래 그림은 두 개의 마스터가 거의 동시에 start condtion을 보냈고, 첫 번째 마스터가 데이터를 high로 변경하였음에도 SDA 라인은 low 상태를 유지하고 있다. 이 때 첫 번째 마스터가 자신이 arbitration의 패자가 되었음을 인식한다.

 

Clock Streach (Clock low extending)

i2c 마스터가 규칙적으로 clock을 발생하여야 하고 이에 대해 i2c 디바이스는 클럭에 맞춰 데이터를 송/수신해야한다. 그러나 요청을 받은 후에 클럭 타임을 초과하는 처리 시간이 필요한 경우 slave 디바이스가 데이터를 준비하기 위해 클럭을 강제로 low로 설정해버린다. 그런 후 데이터가 준비되어 응답을 할 수 있으면 다시 클럭을 풀어준다. 그런 경우 마스터가 계속 하여 클럭을 진행할 수 있다.

  • 예) 주소 0x45를 갖는 EEPROM 디바이스에 offset 08번에 해당하는 데이터를 요청하였다. eeprom 장치에서 데이터를 가져오는 시간이 많이 걸릴 때가 있으므로 이러한 경우 잠시 클럭을 low로 묶어놓고 데이터가 다 준비된 경우 이를 응답하는 형식으로 처리를 하는 것을 clock streaching이라 한다.
  • ACK를 수신하면 인터럽트가 발생하고 이 인터럽트의 처리가 완료되기 전까지 클럭(SCL) 시그널은 low 상태로 고정되어 대기한다.
  • clock streach 기능을 사용하면 adapter가 I2C_FUNC_SLAVE 기능이 지원되어야 한다.
  • 클럭을 최대 100us까지 지연시킬 수 있다.
  • 두 가지 지연 방법이 있다.
    • Periodic clock stretching by slave
      • 주기적으로 매 번 클럭을 늘려 smbus 통신 속도를 10Khz로 줄일 수 있다.
    • Random clock stretching by master or slave
      • ISR 루틴에서 데이터 준비를 위해 지연을 시키는 경우이다.

 

다음 그림은 특정 디바이스로부터 무조건 1 바이트 값을 읽어오는 시나리오에서 디바이스가 clock streaching을 사용하는 유무에 대해 비교하였다.

  • 보통 주소를 전송하고 요청 offset 및 size를 요청하는데 이렇게 하면 길어지므로 아래 그림에서는 간단히 주소와 데이터를 수신하는 것으로 보여준다.
  • slave 디바이스가 scl을 low로 강제함으로써 마스터 디바이스가 계속하여 클럭을 발생시키지 못하게 홀드시킨다.

 

7-bit slave addresss (슬레이브 디바이스 선택 후 데이터 전송 또는 수신)

먼저 상대 디바이스를 선택한 후 데이터를 송/수신하는데 방법은 다음과 같다.

  • 7bit 주소를 전송하는 것으로 디바이스를 선택한다. 그 후 전송측을 결정하는 R/W 비트를 전송한다.
    • R/W 비트
      • 1=마스터가 수신
      • 0=마스터가 송신
  • 송수신 비트가 0인 경우 마스터가 슬레이브에 8 비트 데이터를 전송한다.
  • 송수신 비트가 1인 경우 슬레이브가 마스터에 8비트 데이터를 수신한다.
  • 그 후 수신측에서 응답(Ack) 1 비트를 전송한다.
  • 표기법:  Addr + R/W

 

특별 주소(Special Addresses)

다음의 16개 주소들은 다음과 같이 특별한 목적으로 사용된다.

  • R/W 비트 상태여부와 관계 없이 Address만을 보면 16개의 주소가 특별 주소로 사용된다.

  • smbus의 경우 다음 주소의 디바이스 용도는 다음과 같다.
    • 0001-000 (0x08)
      • SMBus host
      • SMBus를 이용할 때 host controller의 address이다.
      • 디바이스는 이 주소로 Host Notify 이벤트를 보낼 수 있다.
    • 0001-001 (0x09)
      • Smart Battery Charger
    • 0001-010
      • Smart Battery Selector
      • Smart Battery System Manager
    • 0001-011
      • Smart Battery
    • 0001-100
      • SMBus
    • 0001-100 (0x0c)
      • ARA(Alert Response Address)
      • SMBus alert 응답 주소
    • 0101-000 (0x28)
      • ACCESS.bus host
    • 0101-100 (0x2c)
      • LCD Contrast Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
    • 0101-101 (0x2d)
      • CCFL Backlight Driver로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
    • 0110-111 (0x37)
      • ACCESS.bus 디폴트 주소
    • 1000-0XX (0x40 ~ 0x43)
      • PCMCIA Socket Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
    • 1000-100 (0x44)
      • VGA Graphics Controller로 이전에 예약되어 있었지만 미래에 다시 설정할 예정이다.
    • 1001-0XX (0x48 ~ 0x4b)
      • 제한없이 사용할 수 있는 주소
    • 1100-001 (0x61)
      • SMBus 디바이스의 디폴트 주소

 

참고로 ACCESS.bus는 2-wire를 사용하여 외부 장치들을 연결하여 나갈 수 있도록 설계한 시도이다.

  • 전원까지 4개의 pin을 사용한다.
  • RS-232C 인터페이스를 대체할 목적이다.

 

10-bit addressing

10비트 주소를 사용하는 경우 다음과 같이 2 바이트를 사용하여 디바이스의 주소를 지정할 수 있다.

  • 첫 번째 바이트
    • 10비트 중 2비트 주소
  • 두 번째 바이트
    • 10비트 중 8비트 주소

 

 

General Call address

General call 주소가 사용되면 디바이스에 몇 가지 특별한 요청을 할 수 있다. 이 기능은 아래 Start BYTE와 같이 대부분의 시스템에서 잘 사용되지 않는 기능이다.  이 기능은 주로 i2c 디바이스들 중 EEPROM등에서 프로그래밍등이 요구될 때 사용되는 특수 기능이다.

  • 첫 번째 바이트 구성을 General call(0x0)로 사용한다.
  • 두 번째 바이트에 기능 코드 또는 마스터 주소를 부여할 수 있다.
    • LSB가 0일 때 다음과 같은 기능 코드가 있다.
      • 0x04: Write programmable part of slave address by hardware
      • 0x06: Reset and write programmable part of slave address by hardware
    • LSB가 1인 경우 마스터 주소를 사용하는 경우 Hardware Generall Call을 사용할 수 있다.

 

 

Start BYTE

Srart BYTE feature는 0x00 데이터와 dummy NACK(1)을 전송한다. 두 개 이상의 호스트 컨트롤러가 버스 Master를 경쟁하고 있는 경우 stop condition을 detect한 경우 두 호스트 컨트롤러 중 하나가 버스 마스터가 된다. 특별한 경우 어떤 호스트 컨트롤러가 계속하여 버스를 점유하여 명령(reset, EEPROM 기록 등)을 처리해야 하는 경우 stop condition을 사용하지 않아야 한다. stop condition을 하는 경우 버스를 빼앗길 염려가 있다. 이러한 경우에 stop condition 대신 START BYTE를 보내고 다시 start condition을 사용하는 방식으로 반복하여 계속 명령을 보낼 수 있다. 잘 사용되지 않는 기능이지만 드믈게 특정 시스템에서 사용된다.

 

Device ID

디바이스에 대한 제조사 id와 파트 id 및 리비전 정보를 알아올 수 있다.

 

디바이스를 알아오는 경우 다음과 같은 제조사 id를 읽어낼 수 있다.

 

i2c 전송 포맷

i2c 전송 포맷은 다음과 같이 3가지 방법이 있다.

  • 마스터 송신
    • 슬레이브로 데이터를 송신 중에 방향이 바뀌지 않는다.
  • 마스터 수신
    • 마스터가 주소 송신 후 슬레이브로 부터 데이터를 수신한다. 역시 방향이 바뀌지 않는다.
  • Combined 포맷
    • 마스터와 슬레이브의 복합 송수신이 가능하다.

 

마스터 송신

다음 그림은 마스터 디바이스가 슬레이브 디바이스로 N 바이트를  전송하는 모습이다.

  • 슬레이브가 바이트를 받을 때 마다 ACK를 보내는데 더 이상 받을 수 없는 상황인 경우 NACK를 보낸다.

 

마스터 수신

다음 그림은 마스터 디바이스가 슬레이브 디바이스로 1 바이트를 수신하는 모습이다.

  • 마지막 바이트를 수신한 후 마스터가 NACK를 보내야한다.

 

Combination 전송

다음 그림은 마스터 디바이스가 슬레이브 디바이스로 여러 개의 메시지를 송신 또는 수신하는 모습이다.

  • 마스터가 버스를 계속 점유한 채로 전송 및 수신들의 combination을 완료해야 하는 경우 사용한다.
  • 메시지의 전송이 끝난 후 반복 시작하고자 할 때 중간에 repeated start condition 을 전송한다.
    • 표기법: Sr

 

 

SMBus(System Management Bus)

필립스의 i2c를 기반으로 동작하도록 인텔과 듀라셀이 1994년 정의한 버스이다. 특징은 다음과 같다.

  • 주로 PC 마더보드에 연결된 전력 제어 장치(power on/off) 및 온도 센서등이 smbus에 연결되어 사용된다.
  • 참고: System Management Bus (SMBus) Specification Version 2.0 August 3, 2000 | smbus.org – 다운로드 pdf

 

SMBus가 I2C와의 다른 점

타이밍
  • Speed
    • smbus
      • 10~100Khz를 사용한다.
        • 실제 대부분의 구현은 50~100Khz를 사용한다.
      • PMBus로 발전되면서 400Khz 속도도 사용할 수 있다.
    • i2c
      • 최저 속도는 100Khz이다.
  • 클럭(scl) 라인 low 타임아웃
    • smbus
      • 타임아웃을 설정할 수 있다.
      • Slave 디바이스의 경우 최대 25ms 까지 low 시간을 연장할 수 있다. (Clock Streaching)
      • Master 디바이스의 경우 최대 10ms 까지 low 시간을 연장할 수 있다. (Clock Streaching)
    • i2c
      • 타임아웃을 설정할 수 없다.
  • 데이터(sda) 라인 홀드 시간
    • smbus
      • 최대 300ns까지 홀드 시킬 수 있다.
    • i2c
      • 홀드할 수 없다.
  • Power-On-Reset 후 디바이스 준비 시간
    • smbus
      • 최대 0.5초까지 대기해야 한다.
ACK 및 NACK
  • addr 전송 후 ACK 용도
    • smbus
      • 탈부착 smbus 디바이스가 버스에 연결되었는지 여부를 detect 하는 비트 용도로 항상 ACK를 요구한다.
      • 슬레이브 디바이스는 자신의 주소(Addr) 비트를 인식하면 ACK를 회신한다.
      • S | Addr + R/W | ACK | …
    • i2c
      • ACK가 없어도 된다.
  • NACK 의미
    • smbus
      • N잘못된 명령이나 데이터의 수신을 의미한다.
    • i2c
      • 더이상 데이터를 전송하지 않는다는 것을 의미한다.
Protocol
  • 탈부착 디바이스 연결
    • smbus
      • 통신 중 smbus 디바이스가 smbus에 연결될 수 있다.
    • i2c
      • 통신 중 i2c 디바이스가 i2c 버스에 연결될 수 없다.
  • ARP(Address Resolution Protocol)
    • smbus
      • ARP를 지원한다.
    • i2c
      • ARP를 지원하지 않는다.
  • PEC(Packet Error Checking)
    • smbus
      • PEC를 지원한다.
      • 에러 체킹을 위해 CRC-8 규격으로 1 바이트를 추가 사용한다.
    • i2c
      • PEC를 지원하지 않는다.
Alert
  • smbus에 연결된 디바이스들이 alert라는 옵션 신호를 사용하여 host에 전달할 수 있게 하였다.
전기 규격
  • 전기적인 특성이 I2C와 일부 다르다.

 

SMBus protocol

SMBus 프로토콜에 사용되는 명령들을 알아본다. 참고로 그림에서 사용하는 기호는 다음과 같다.

  • S
    • Start condition
  • P
    • Stop condition
  • Sr
    • Repeted Start condition
  • Addr
    • 7비트 주소
  • R/W
    • 1비트 (R=1, W=0)
  • A
    • Ack (0)
  • PEC
    • 8비트 Packet Error Checking

 

Quick, Send & Receive 명령

Quick

슬레이브 디바이스로 R/W 1 비트를 전달할 수 있고, Ack 1비트를 수신하여 detect 여부를 확인할 수 있다.

  • R/W 비트를 수신하여 장치의 on/off를 제어하거나, low-power 스탠바이 모드를 enable/disable하는데 사용할 수 있다.
  • 데이터 바이트의 송신 또는 수신은 없다.

 

Send byte

슬레이브 디바이스에 1 바이트를 송신한다. Write Byte와 다른 점은 command를 전송하지 않는다는 점이다.

  • pure smbus의 ACK는 디바이스에서의 처리 결과를 호스트에 반환하는 기능을 가진다.
  • pure i2c-bus의 ACK는 디바이스가 항상 1을 응답해야한다.

 

Receive byte

슬레이브 디바이스로부터 1 바이트를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

Write & Read 명령

Write byte

슬레이브 디바이스에 1 바이트 명령 + 1 바이트를 송신한다.

 

Write word

슬레이브 디바이스에 1 바이트 명령 + 워드(2 바이트)를 송신한다.

  • smbus에서 워드는 2바이트이다.

 

Read byte

슬레이브 디바이스에 1 바이트 명령을 송신하고 1 바이트를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

Read word

슬레이브 디바이스에 1 바이트 명령을 송신하고 워드(2 바이트)를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

Block 명령

Block write

슬레이브 디바이스에 명령을 전송한다. 그런 후 다시 슬레이브 디바이스에 길이 + 블럭(최대 32바이트) 데이터를 송신한다.

 

Block read

슬레이브 디바이스에 명령을 전송한다. 그런 후 다시 슬레이브 디바이스로부터 길이 + 블럭(최대 32바이트) 데이터를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

Process Call 명령

Process call

슬레이브 디바이스에 명령 + 워드(2 바이트)를 전송한다. 그런 후 다시 슬레이브 디바이스로부터 워드(2 바이트)를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

Block Process Call

슬레이브 디바이스에 명령 + 길이와 블럭 데이터(32 바이트까지)를 전송한다. 그런 후 다시 슬레이브 디바이스로부터 길이와 블럭(최대 32 바이트) 데이터를 수신한다.

  • 마지막 ACK는 NACK일 수 있고 이는 마지막 바이트를 의미한다.

 

이벤트

Host notify

슬레이브 디바이스가 호스트에 워드 데이터를 전달하고자 할 때 사용한다. 호스트는 워드 데이터를 수신하여 호스트에서 슬레이브를 담당하는 드라이버에 이벤트를 전달한다.

  • pure smbus에서 host notify는 반드시 구현되어야 한다.

 

smbalert#

슬레이브 디바이스가 smbalert# 인터럽트를 호스트에 발생시킨다. 발생 시킨 디바이스를 확인하기 위해 아래와 같이 ARA(Alert Response Address)로부터 디바이스 주소를 알아올 수 있다.

  • 만일 HW 구성 시 별도의 smbalert# 라인이 호스트에 연결되어 구성되지 않은 경우 호스트가 alert를 주기적으로 폴링하여 알아내는 방법도 사용할 수 있다.
  • pure smbus에서 alert는 option 사항이다.
  • alert callback을 위해 i2c_setup_smbus_alert() 함수를 사용하여 callback용 함수를 등록하여 사용한다.

 

I2C 서브시스템

I2C 디바이스와 드라이버를 등록하면 I2C 서브시스템은 유저 스페이스에 다양한 인터페이스를 제공한다. (i2c-N은 i2c adapter로 N은 특별히 지정하지 않으면 0번부터 시작된다.)

  • i2c-core가 크게 두 가지를 관리한다.
    • i2c adapter 디바이스
      • /sys/devices/…/<i2c 호스트 디바이스>/i2c-N
        • i2c 어댑터 디바이스 인터페이스
      • /sys/class/i2c-adapter
        • i2c-adapter 클래스 디바이스 인터페이스
      • /sys/bus/i2c/devices/i2c-N
        • 버스에 등록된 i2c 어댑터 디바이스 인터페이스
    • i2c client 디바이스 및 드라이버
      • /sys/devices/…/<i2c 호스트 디바이스>/i2c-N/<client device>
        • i2c 클라이언트 디바이스 인터페이스
        • <client device>의 기본 포맷은 <버스번호>-<4자리의 addr 숫자>
          • 예) 0x3f 주소를 가진 디바이스가 1번 i2c 버스에 연결된 경우 -> 1-003f
      • /sys/bus/i2c/devices/<client device>
  • i2c-dev는 i2c 클라이언트 디바이스를 위해 2 개의 인터페이스를 제공한다.
    • /dev/i2c-N
      • 캐릭터 디바이스 인터페이스
      • i2cdetect, i2cdump, i2cset, i2cget 등의 i2c-tools를 사용하려면 i2c 캐릭터디바이스가 동작해야 한다.
    • /sys/class/i2c-dev/i2c-N
      • i2c-dev 클래스 디바이스 인터페이스
      • i2c adapter에 대한 character 디바이스를 사용할 때 생성된다.(CONFIG_I2C_CHARDEV)

 

SoC 외부에 위치한 i2c 호스트 컨트롤러

임베디드 시스템에서 i2c 호스트 컨트롤러들은 보통 SoC 내부에 위치하여 플랫폼 디바이스로 곧바로 등록할 수 있다. 그러나 이를 사용하지 못하는 경우에는 속도가 빠른 버스에 연결하여 사용할 수 있다. 일반적인 PC에는 i2c 호스트 컨트롤러가 내장되지 않았다. 이러한 PC에는 pci 버스와 usb 버스가 연결되어 있는데 pci 버스 또는 usb 버스에 i2c 호스트 컨트롤러를 연결하여 사용하는 경우가 많다. 다음 그림은 usb 버스에 i2c 버스가 연결된 경우를 보여준다. 좌측 그림이 H/W 연결 구성이고 우측 그림이 이에 대한 각 버스 서브 시스템과 core 및 드라이버 위치를 보여준다.

  • 예) PCI 버스  —–>  USB 버스  —–>  I2C 버스

 

i2c sysfs

다음 그림은 i2c adapater 디바이스 및 드라이버가 등록된 모습을 sysfs 디렉토리로 보여준다.

  • 2 개의 i2c adapter가 플랫폼 디바이스로 등록되어있고, 2 개의 드라이버 역시 바인딩 되어 동작중인 상태이다.

 

다음 그림은 여러 개의 i2c client 디바이스 및 드라이버가 추가 등록된 모습을 sysfs 디렉토리로 보여준다.

  • 여러 가지 종류의 디바이스 및 드라이버가 등록되어 있는 모습을 보여준다.

 

i2c 호스트 컨트롤러(Adapter) 및 i2 디바이스(Client) 등록

 

i2c 디바이스는 버스 마스터 기능 유무에 따라 i2c 호스트 컨트롤러와 i2c 클라이언트의 역할을 한다.

  • i2c 호스트 컨트롤러(included i2c adapter)는 상위 버스와 i2c 버스간의 브릿지 기능을 수행한다.
  • i2c 클라이언트는 i2c 버스에 연결되는 consumer 디바이스이다.

 

i2c 버스는 pci나 usb 버스와 다르게 i2c 버스를 스캔하여 디바이스를 찾는 기능이 없다. 따라서 사전에 연결하고자 하는 디바이스의 주소를 미리 등록해두고 이 주소를 사용하여 디바이스가 있는지 HW detect를 시도할 수 있다.

 

다음 그림은 상위 버스에 i2c 호스트 컨트롤러(included i2c adapter)를 등록시키고 i2c 클라이언트 디바이스가 등록된 모습을 보여준다.

  • i2c 클라이언트 디바이스
    • 하드웨어 센서 모니터링 디바이스인 lm75를 예를 들었다.

 

i2c 호스트 컨트롤러 등록

i2c 호스트 컨트롤러 디바이스와 드라이버를 등록하기 위해 다음과 같은 API들을 사용한다.

예) 플랫폼 버스에 등록

  • 디바이스
    • platform_device_register() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
      • of_platform_device_create_pdata()
  • 드라이버
    • platform_register_driver() 함수를 사용하여 등록시킨다.
    • 드라이버 내부 probe 함수에서 i2c_add_adapter() 함수를 사용하여 i2c 버스를 제어할 i2c_adapter 구조체를 등록시킨다.

 

다음 그림은 broadcom 사의 i2c 호스트 컨트롤러 드라이버가 등록되는 주요 코드 과정을 보여준다.

 

i2c 클라이언트 등록

  • 디바이스 부분
    • i2c_new_device() 함수를 사용하여 등록시키거나 디바이스 트리를 파싱하여 등록을 한다.
      • of_i2c_register_device()
    • 유저 스페이스에서 sysfs를 사용하여 등록하는 방법도 있다.
      • 예) echo <device> 0x71 > /sys/bus/i2c/devices/i2c-0/new_device
  • 드라이버 부분
    • i2c_add_driver() 또는 i2c_register_driver() 함수를 사용하여 등록시킨다.

 

다음 그림은 wm8750 디바이스가 i2c 클라이언트 디바이스 및 드라이버로 등록되는 과정에서의 주요 코드 부분을 보여준다.

  • i2c 노드에 포함된 wm8750@1a 노드가 파싱되어 i2c_client 구조체에 포함된 후 i2c 버스에 디바이스로 이미 포함된 상태이다.
  • i2c_driver 구조체가 wm8750@1a 디바이스에 대응하는

 

유저 스페이스 바인딩

드라이버를 i2c 디바이스와 attach하는 방법

  • 기존(old) 방법
    • modprobe <driver> probe=1,0×71
    • modprobe <driver> force=1,0×71
    • modprobe <driver> force_<device>=1,0×71
  • 새로운(new) 방법
    • echo <device> 0x71 > /sys/bus/i2c/devices/i2c-0/new_device

 

드라이버를 i2c 디바이스와 attach하지 못하게 하는 방법

  • 기존(old) 방법
    • modprobe <driver> ignore=1,0×71
  • 새로운(new) 방법
    • 드라이버를 dummy 디바이스와 연결해두는 방법을 사용한다.
      • echo dummy 0x2f > /sys/bus/i2c/devices/i2c-1/new_device
      • modprobe <driver>

 

I2C Tools

i2c 툴을 설치하기 위해서는 다음과 같이 i2c-tools를 설치한다.

  • sudo apt-get install i2c-tools

 

i2cdetect

명령어 사용법

$ i2cdetect
Error: No i2c-bus specified!
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
i2cdetect -F I2CBUS
i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range.

 

i2c 버스 및 i2c Adapter명 확인

x86 서버

다음과 같이 pc server는 두 개의 i2c adapter가 서로 다름을 알 수 있다.

$ i2cdetect -l
i2c-0   unknown         AST i2c bit bus                         N/A
i2c-1   unknown         SMBus I801 adapter at 0580              N/A

 

broadcom ns2(Cortex-A57)
$ i2cdetect -l
i2c-0 i2c Broadcom iProc I2C adapter I2C adapter
i2c-1 i2c Broadcom iProc I2C adapter I2C adapter

 

rpi3(Cortex-A53)
$ i2cdetect -l
i2c-1 i2c 3f804000.i2c I2C adapter

 

i2c 버스별 Functionalities 확인

x86 서버

0번 버스는 i2c  전송이 가능하고 smbus 전송도 에뮬레이션 처리가 가능하다. 반면에 1번 버스는 smbus 전송만 가능하다.

$ i2cdetect -F 0
Functionalities implemented by /dev/i2c-0:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read yes
SMBus Block Process Call yes
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes

$ sudo i2cdetect -F 1
Functionalities implemented by /dev/i2c-1:
I2C no
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call no
SMBus Block Write yes
SMBus Block Read yes
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes

 

broadcom ns2(Cortex-A57)
$ i2cdetect -F 0
Functionalities implemented by /dev/i2c/0:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read no
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes

$ i2cdetect -F 0
  (상동)

 

rpi3(Cortex-A53)
$ i2cdetect -F 1
Functionalities implemented by /dev/i2c-1:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read no
SMBus Block Process Call no
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes

 

i2c 버스별 디바이스 스캔

x86 서버
$ sudo i2cdetect -y -r 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

$ sudo i2cdetect -y -r 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- 44 -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

 

broadcom ns2(Cortex-A57)
  • HW 스캔된 디바이스는 숫자로 표기되고, UU로 표기된 디바이스는 이미 바인딩(dummy 포함)된 디바이스이다.
$ i2cdetect -y -r 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- --
50: 50 51 -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
70: UU -- UU -- -- -- -- --

# i2cdetect -y -r 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- UU
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

 

rpi3(Cortex-A53)
$ i2cdetect -y -r 0
Error: Could not open file `/dev/i2c-0' or `/dev/i2c/0': No such file or directory

$ i2cdetect -y -r 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

 

i2cdump

명령어 사용법
$ i2cdump
Error: No i2c-bus specified!
Usage: i2cdump [-f] [-o] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
b (byte, default)
w (word)
W (word on even register addresses)
s (SMBus block)
i (I2C block)
c (consecutive byte)

 

데이터 dump

1번 버스의 0x4f 디바이스 덤프

$ i2cdump -f -y 1 0x4f
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "?.........?....
10: 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?.............
…
e0: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "?.........?....
f0: 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?.............

 

i2cget

명령어 사용법
$ i2cget 
Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
b (read byte data, default)
w (read word data)
c (write byte/read byte)
Append p for SMBus PEC

 

1 바이트 읽기

0번 버스의 0x4f 디바이스에서 offset 0x0b에 해당하는 바이트를 읽어온다.

$ i2cget -f -y 0 0x4c 0x0b
0x0f

 

i2cset

명령어 사용법
$ i2cset
Usage: i2cset [-f] [-y] [-m MASK] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
c (byte, no value)
b (byte data, default)
w (word data)
i (I2C block data)
s (SMBus block data)
Append p for SMBus PEC

 

1바이트 쓰기

1번 버스의 0x72 디바이스 0 offset에 0x01을 기록한다. (i2c 스위치에 0번 채널을 선택하게 한다.)

$ i2cset -f -y 1 0x72 0 0x01

 

i2c trace

i2c를 trace하기 위해 ftrace를 사용해본다. ftrace를 사용하기 위해 다음의 커널 옵션이 사용된다.

  • CONFIG_FTRACE
  • CONFIG_ENABLE_DEFAULT_TRACERS

 

먼저 tracefs를 /sys/kernel/tracing 디렉토리에 마운트한다.

$ mount -t tracefs nodev /sys/kernel/tracing

 

마운트한  후 해당 디렉토리를 살펴보면 다음과 같다.

# cd /sys/kernel/tracing/
# ls -la
total 0
drwx------    6 root     root             0 Jan  1 09:00 .
drwxr-xr-x    8 root     root             0 Jan  1 09:31 ..
-r--r--r--    1 root     root             0 Jan  1 09:00 README
-r--r--r--    1 root     root             0 Jan  1 09:00 available_events
-r--r--r--    1 root     root             0 Jan  1 09:00 available_tracers
-rw-r--r--    1 root     root             0 Jan  1 09:00 buffer_size_kb
-r--r--r--    1 root     root             0 Jan  1 09:00 buffer_total_size_kb
-rw-r--r--    1 root     root             0 Jan  1 09:00 current_tracer
drwxr-xr-x   45 root     root             0 Jan  1 09:00 events
--w-------    1 root     root             0 Jan  1 09:00 free_buffer
drwxr-xr-x    2 root     root             0 Jan  1 09:00 instances
drwxr-xr-x    2 root     root             0 Jan  1 09:00 options
drwxr-xr-x    6 root     root             0 Jan  1 09:00 per_cpu
-r--r--r--    1 root     root             0 Jan  1 09:00 printk_formats
-r--r--r--    1 root     root             0 Jan  1 09:00 saved_cmdlines
-rw-r--r--    1 root     root             0 Jan  1 09:00 saved_cmdlines_size
-rw-r--r--    1 root     root             0 Jan  1 09:00 set_event
-rw-r--r--    1 root     root             0 Jan  1 09:00 set_event_pid
-rw-r--r--    1 root     root             0 Jan  1 09:00 trace
-rw-r--r--    1 root     root             0 Jan  1 09:00 trace_clock
--w--w----    1 root     root             0 Jan  1 09:00 trace_marker
-rw-r--r--    1 root     root             0 Jan  1 09:00 trace_options
-r--r--r--    1 root     root             0 Jan  1 09:00 trace_pipe
-rw-r--r--    1 root     root             0 Jan  1 09:00 tracing_cpumask
-rw-r--r--    1 root     root             0 Jan  1 09:00 tracing_on
-rw-r--r--    1 root     root             0 Jan  1 09:00 tracing_thresh

 

사용할 수 있는 i2c 이벤트를 확인해본다.

$ cat available_events | grep i2c
i2c:smbus_result
i2c:smbus_reply
i2c:smbus_read
i2c:smbus_write
i2c:i2c_result
i2c:i2c_reply
i2c:i2c_read
i2c:i2c_write

 

다음 명령을 통해 i2c 이벤트 전체를 trace 하도록 enable 한다.

$ echo 1 > events/i2c/enable

 

만일 i2c-adapter(i2c-bus 호스트 컨트롤러)가 여러 개 준비되어 있는 경우 그 중 하나만 선택하려면 다음과 같은 명령을 사용할 수 있다.

$ echo adapter_nr==0 > events/i2c/filter

 

다음은 0x72 주소를 사용하는 i2c 스위치 디바이스에 0x80 데이터를 기록하여 총 8개 채널 중 마지막 채널을 연결한다.

  • trace에 0 값을 기록하는 경우 기존 trace 로그를 삭제한다.
$ echo 0 > trace; i2cset -f -y 0 0x72 0x80; cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 4/4   #P:2
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          i2cset-168   [000] ....  2027.045809: smbus_write: i2c-0 a=072 f=0000 c=80 BYTE l=0 []
          i2cset-168   [000] ....  2027.045814: i2c_write: i2c-0 #0 a=072 f=0000 l=1 [80]
          i2cset-168   [000] ....  2027.046032: i2c_result: i2c-0 n=1 ret=1
          i2cset-168   [000] ....  2027.046033: smbus_result: i2c-0 a=072 f=0000 c=80 BYTE wr res=0

 

기록된 값을 확인해본다.

$ echo 0 > trace; i2cget -f -y 0 0x72; cat trace
0x80
# tracer: nop
#
# entries-in-buffer/entries-written: 6/6   #P:2
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          i2cget-176   [000] ....  2326.033639: smbus_read: i2c-0 a=072 f=0000 c=0 BYTE
          i2cget-176   [000] ....  2326.033641: i2c_read: i2c-0 #0 a=072 f=0001 l=1
          i2cget-176   [000] ....  2326.033856: i2c_reply: i2c-0 #0 a=072 f=0001 l=1 [80]
          i2cget-176   [000] ....  2326.033857: i2c_result: i2c-0 n=1 ret=1
          i2cget-176   [000] ....  2326.033857: smbus_reply: i2c-0 a=072 f=0000 c=0 BYTE l=1 [80]
          i2cget-176   [000] ....  2326.033858: smbus_result: i2c-0 a=072 f=0000 c=0 BYTE rd res=0

 

위의 방법과 유사하지만 tracefs를 마운트하지 않고 /sys/kernel/debug/tracing 디렉토리에서 동일한 동작을 할 수도 있다.

 

U-Boot에서 I2C 제어

명령어 헬프
u-boot> i2c
i2c - I2C sub-system

Usage:
i2c crc32 chip address[.0, .1, .2] count - compute CRC32 checksum
i2c dev [dev] - show or set current I2C bus
i2c loop chip address[.0, .1, .2] [# of objects] - looping read of device
i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device
i2c mm chip address[.0, .1, .2] - write to I2C device (auto-incrementing)
i2c mw chip address[.0, .1, .2] value [count] - write to I2C device (fill)
i2c nm chip address[.0, .1, .2] - write to I2C device (constant address)
i2c probe [address] - test for and show device(s) on the I2C bus
i2c read chip address[.0, .1, .2] length memaddress - read to memory
i2c write memaddress chip address[.0, .1, .2] length [-s] - write memory
          to I2C; the -s option selects bulk write in a single transaction
i2c reset - re-init the I2C Controller
i2c speed [speed] - show or set I2C bus speed

 

버스 선택 및 probe
u-boot> i2c dev 0
Setting bus to 0
u-boot> i2c probe
Valid chip addresses: 00 4C 68 70 72

 

u-boot> i2c dev 1
Setting bus to 1
u-boot> i2c probe
Valid chip addresses: 4F

 

데이터 덤프
u-boot> i2c md 0x4f 0
0000: 22 11 ff 00 00 ff 00 00 ff 00 ff 0f 00 00 00 00 "............…

u-boot> i2c md 0x4f 2 1
0002: ff

 

데이터 기록(0x4f 디바이스 2 offset에 0xf7 기록)
u-boot> i2c mw 0x4f 2 f7

 

참고

 

 

48 thoughts to “I2C Subsystem -1- (Basic)”

  1. 정말 감사합니다 !! 디바이스 드라이버 관련해서 이해가 되지 않는 부분들이 너무 많았었는데, 정리가… 존경스럽습니다. 이런 고급 정보들을 쉽게 알려주셔서 감사합니다!!

  2. 잘봤습니다. 한가지 궁금한 사항이 있는데, 혹시 i2c 드라이버상에서 SDA값을 사용자가 컨트롤 할 수 있나요?

  3. 안녕하세요? 기본적으로 i2c는 바이트 단위로 TX FIFO 레지스터에 값을 쓰는 것으로 전송하므로 비트 단위의 조작은 불가능합니다. 다만 특별한 하드웨어 환경에서 충분히 가능하게 만들 수 있는 방법이 두 가지가 있습니다.
    첫 번째, scl 및 sda에 해당하는 2개의 gpio 핀을 사용하여 i2c를 에뮬레이션 하여 사용하는 방법이 있습니다. (soft serial 처럼 soft i2c가 되겠네요.)
    두 번째 SoC에서 gpio와 i2c가 같은 핀을 사용하는 경우 i2c로 사용하다가 장애 시 pinctrl 시스템의 도움을 받아 gpio 모드로 변경한 후 리커버리를 목적으로 gpio api로 sda 및 scl을 에뮬레이션 합니다. (또한 이렇게 직접 scl 및 sda를 제어할 수 있도록 i2c에는 bit 알고리즘이 준비되어 있습니다.)

    어려운 방법이지만 위와 같이 gpio를 이용할 수 있으면 직접 sda 및 scl을 제어할 수 있습니다.
    도움이 되시길 바랍니다.

    1. 아니면 제가 NXP I2C관련 문서를 보면서 생각해 본건데, SLAVE단이 TRANSMITTER MODE에 갖히게 되면 SDA도 LOW로 갖히게 된다는데… 이걸 가능하게 하려면 I2C 커널 드라이브단에서 무엇을 건드려야 되나요? 계속 여기 저기 건드려 봐도 잘 모르겠네요…

      1. 그래서 sda가 low에 갖히는 해결하기 위해서 i2c-bus recovery 를 해주는 것으로 알고 있는데…

  4. i2c 버스 리커버리는 대부분의 i2c 드라이버에 구현되어 있지 않습니다. i2c 버스가 물려서 장애 시에 버스 초기화만을 제공하는 일부 드라이버도 있고, gpio를 사용하여 full로 sda 및 scl을 컨트롤하는 방법도 있습니다. 만일 gpio를 사용할 수 있으면 i2c-imx.c 드라이버 파일을 참고하셔서 nxp 드라이버를 수정하시기 바랍니다.
    struct i2c_bus_recovery_info 구조체에 구현 후크 함수를 연결하면 되는데, 간단하게 각 후크를 설명하자면
    (*recover_bus) 후크는 버스를 리커버리하기 위한 구현이 있어야 합니다. (gpio가 없는 시스템에서는 i2c 레지스터를 사용하여 초기화 등…)
    (*get_scl) 후크는 scl 값을 읽어오는 구현으로 주로 scl에 해당하는 gpio 값을 읽어옵니다.
    (*set_scl) 후크는 scl 값을 설정하는 구현으로 주로 scl에 해당하는 gpio 값을 설정합니다.
    (*get_sda) 후크는 sda 값을 읽어오는 구현으로 주로 sda에 해당하는 gpio 값을 읽어옵니다.
    (*prepare_recovery)) 후크는 리커버리 동작을 하기 전에 수행해야 하는 구현으로, 주로 pinctrl을 사용하여 gpio 모드로 전환합니다.
    (*unprepare_recovery)) 후크는 리커버리 동작을 완료한 후에 수행하는 구현으로, 주로 pinctrl을 사용하여 원래 i2c 모드로 전환합니다.

    개발 시에는 usb 로직 아날라이저 등을 사용하면 pc에서 sda 및 scl의 각 파형들을 분석할 수 있습니다.

  5. 문c 블로그 – I2C Subsystem -3- (Transfer) 글에서 gpio를 사용하는 경우는 그림과 설명을 해놓았으므로 참고하시기 바랍니다.

    추가로 아주 중요한 디바이스를 안정적으로 컨트롤 해야 하는 상황일 때에는
    gpio를 사용한 i2c 버스 리커버리 외에 별도의 gpio 핀을 사용하여 연결된 i2c 디바이스들을 리셋할 수 있도록 구현하는 경우도 있습니다. (리셋을 위한 hw 변경 필요)

    1. 안녕하세요. 추가적으로 궁금한 사항이 있는데요…
      제가 가지고 있는 i2c 소스 코드중에서 i2c-dev.c를 보면, i2cdev_ioctl_rdrw라는 함수가 있습니다.
      그런데 문제는요… 해당 함수로 입력되는 매개변수 2가지 중에서 arg값에 들어가는 특정 2 종류의 값들이 일련의 수행 순서에서
      꼬이게 되면 sda가 low로 갖히게 되더라고요…
      결론으로, 질문은 i2c-dev.c에 있는 i2cdev_ioctl_rdrw의 매개변수 arg는 무슨 역할을 하는 건가요? (솔직히… i2cdev_ioctl_rdrw 얘도 무슨 역할을 하는지… 대충 알고 있어서 이해가 잘 안되네요…)

  6. 안녕하세요? 오늘 이상하게 스팸 댓글 수백개가 공격해왔는데, 조원님 댓글마저도 자동 분류되어 휩쓸린것을 우연히 보고 옮겨왔습니다. ^^;
    궁금하신 내용 답변 드립니다.
    i2c-dev.c 파일은 커널이 아닌 유저 application을 위해 i2c 기능에 대응하는 캐릭터 디바이스 인터페이스를 제공합니다.
    유저 application에서 /dev/i2c-N 파일을 open한 후 I2C_RDWR command로 ioctl 호출합니다. 이 때 i2c_msg 구조체를 arg 인자로 커널로 전달하여 i2c_transfer() 함수를 이용하여 i2c 전송이 이루어집니다.
    참고로 i2c_msg 구조체는 송신과 수신 데이터가 담깁니다.

    데이터를 보낼 때 i2c 디바이스가 스마트하지 못한 경우가 대부분입니다. 따라서 정확한 데이터를 보내지 못한 경우 i2c 디바이스가 stuck 되는 경우가 발생합니다.
    이러한 경우 위의 리커버리 루틴으로 해결할 수 있습니다. 만일 i2c 디바이스가 i2c 버스 리커버리 루틴에도 동작하지 않는 최악의 경우는 i2c 디바이스를 gpio로 리셋하여 해결해야 합니다.

    1. 아.. 그랬었나요? 어쩐지 어제 안 올라 가더라구요…ㅠ;;
      답변 정말 감사드립니다. 이제는 어느정도 이해가 되는거 같아요.

      그런데, 설명해 주신 것에서 조금 보태서 궁금한 것이 있는데요…
      1) 우선 “i2c 기능에 대응하는 캐릭터 디바이스 인터페이스”라고 하셨는데, 이게 그러면 i2c sda 파형에서 나타나는 slave 주소나 read/write 신호 등을 나타내면서 양 디바이스간에 인터페이스 역할을 해준다.. 모 이런 말인가요?
      2) 그리고, “I2C_RDWR command로 ioctl호출”이라고 하셨는데요. 이것은 rdwr 명령어로 i2cdev_ioctl_rdrw()함수를 호출한다는 의미인가요?
      3) 마지막으로,

      struct i2c_msg {
      __u16 addr; /* slave address */
      __u16 flags;
      #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
      #define I2C_M_RD 0x0001 /* read data, from slave to master */
      #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
      #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
      #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
      #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
      #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
      #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
      __u16 len; /* msg length */
      __u8 *buf; /* pointer to msg data */
      };

      위 구조체가 i2c_msg 인데, 이 구조체가 arg로 전달되잖아요? 그런데, 앞 서 제가 말씀드린 것 처럼 순서가 섞였더니 low stuck이 됬다는 것은 i2c를 이용한 데이터 송신/수신이 섞였다고 이해하면 되나요?
      아… 제가 너무 두리뭉실하게 물어보고 또 애매하게 답하니.. 죄송스럽네요…
      다시 한번 답변은 정말 도움됬고, 감사합니다.

  7. 네. 번호 순서대로 답변드리겠습니다.

    1) i2c 드라이버는 커널에서 동작합니다. 커널 API를 사용하여 i2c 데이터를 송수신할 수 있습니다. 커널이 아닌 유저 application에서 커널용 i2c 호스트 드라이버를 이용할 때 가장 쉽게 사용할 수 있는 방법이 i2c-dev라고 하는 캐릭터 디바이스(/dev/i2c-N)를 사용하는 방법입니다. 샘플은 인터넷에 여러 개가 있고, 그 중 몇 개를 소개합니다.
    http://forum.falinux.com/zbxe/index.php?document_srl=504163&mid=ezboard
    http://wittgens.pe.kr/blog/blog_wittgens/4620
    https://m.blog.naver.com/PostView.nhn?blogId=khsrdc&logNo=110165550647&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F
    위와 같이 캐릭터 디바이스를 사용하여 slave 주소를 지정하고 i2c 호스트 컨트롤러를 통해 ioctl 또는 read/write 등을 수행할 수 있습니다.

    2) 번 항목은 위의 유저 application 샘플에서 I2C_RDWR 명령을 사용하여 combined format으로 쓰고, 읽고를 할 수 있습니다. 아래는 이에 대한 원문이고, 인터넷을 통해 샘플 소스들을 몇 개 보시면 이해하실 겁니다. (유저에서 I2C_RDWR 명령을 사용하면 커널 쪽 i2cdev_ioctl_rdrw() 함수가 호출됩니다.)
    https://www.kernel.org/doc/Documentation/i2c/dev-interface

    3) low stuck이 되는 이유는 디바이스가 원하는 포맷을 사용하지 않고 없는 곳에 읽거나 기록하려고 할 때, 또는 읽고 쓰고 순서를 잘못했을 때 즉 장치가 사용하는 프로토콜을 사용하지 않아 발생할 수 있습니다. 그 외에도 여러 가지 원인이 있습니다만 처음 프로그래밍할 때 발생하는 문제들은 대부분 읽고 쓰는 순서 및 규칙이 잘못되었을 때 발생합니다. i2c 데이터를 송수신하는 방법이 여러 가지가 있고, i2c 디바이스 종류마다 쓰거나 읽을 데이터가 모두 다르므로 해당 디바이스에 대한 규칙(프로토콜)을 지켜야 합니다.만일 read만 허용하는 장비에서 I2C_RDWR 같은 것을 사용하면 디바이스가 stuck이 될 수도 있습니다.

    참고로 전문적인 분석이 필요한 경우 도움이 되는 방법은 다음과 같습니다.
    – HW에 송/수신 되는 비트들을 분석하려면 커널 트레이스를 이용하는 방법이 있습니다. (커널 옵션도 바꿔야 하고 커널 컴파일도 해야하므로 복잡합니다)
    – 간단히 USB 로직 아날라이저 장비를 사용하는 방법이 있습니다. (중국제는 불과 수천원이더군요. https://www.youtube.com/watch?v=rR5cEFRO9_s)

    열심히 해결책을 찾기 바랍니다.

    1. 이런 애매한 질문에 이렇게 성실히 답해 주셔서 정말 감사합니다.
      이해하는데, 많은 도움이 되었습니다. 감사합니다.

    1. 안녕하세요. 문영일님… 또 왔습니다. 궁금한게 있어서요… (정말 커널을 모르는 사람이다 라고 생각해 주세요.)
      low stuck이 발생하고 recovery 함수로 다시 심폐소생술 하듯이 빠져나오잖아요? 그런데, 궁금한게 SDA LOW로 되고 나서
      어떻게 recovery 함수로 넘어가나요?

  8. 각 i2c host adapter에 구현된 i2c 전송 함수에서 전송에 문제가 있을 때 i2c 리커버리 커널 API인 i2c_recover_bus() 함수를 호출하도록 구현합니다.

    예) drivers/i2c/busses/i2c-imx.c
    static int i2c_imx_xfer(struct i2c_adapter *adapter,
    struct i2c_msg *msgs, int num)
    {
    . unsigned int i, temp;
    . int result;
    . bool is_lastmsg = false;
    . struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
    .
    . dev_dbg(&i2c_imx->adapter.dev, “< %s>\n”, __func__);
    .
    . /* Start I2C transfer */
    . result = i2c_imx_start(i2c_imx);
    . if (result) {
    . if (i2c_imx->adapter.bus_recovery_info) {
    . i2c_recover_bus(&i2c_imx->adapter);
    . result = i2c_imx_start(i2c_imx);
    . }
    . }

    1. 제가 가지고 있는 i2c-imx.c에서는
      /* Start I2C transfer */
      result = i2c_imx_start(i2c_imx);
      if (result)
      goto fail0;

      정도만 정의되어 있는데, 이럴 경우 i2c_bus_recovery()함수로
      못 뛰지 않나요? 지금 보면 fail()함수로 가게 되어 있는데…

      그리고, 명절 잘 보네세요. ^^

    1. 안녕하세요. 추석은 잘 보내셨습니까? 다름이 아니라 앞서 알려주신 참고내용에 대해서
      감사하다는 말씀부터 전하고 싶네요. 감사합니다.

      그후 계속 소스코드 역으로 타고가면서 확인하고 있는데요. 이해가 안 되는 부분이 있어서요… (결론은 질문이…;;;)
      i2c host adapter에 구현된 i2c 전송 함수에서 보면,
      /* Start I2C transfer */
      result = i2c_imx_start(i2c_imx);
      이렇게 문장이 시작하잖아요..?
      여기서 i2c_imx_start()함수를 보면 다음 과 같은 함수(i2c_imx_bus_busy())가 있는 것을 볼 수 있었습니다.
      (커널 v4.4에서 /drivers/i2c/busses/i2c-imx.c)

      그래서 질문으로는 해당 함수에서 EAGAIN, ETIMEDOUT 이것들을 반환하기도 하는데 이것들을 또 역으로 타고 가면
      주석으로 설명이 되있긴 한데… 제 실력으로 이해가 안 되서….;;; 무슨 말인가요?
      이것들이 혹시 recovery 함수를 트리거 시키는 것과 연관이 있나요?

      * 질문정리 *
      1. i2c_imx_bus_busy()의 EAGAIN, ETIMEDOUT 들은 무엇인가요?
      2. 이것들이 recovery 함수를 트리거 시키는 것과 연관이 있나요?

      static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
      {
      unsigned long orig_jiffies = jiffies;
      unsigned int temp;

      dev_dbg(&i2c_imx->adapter.dev, “\n”, __func__);

      while (1) {
      temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);

      /* check for arbitration lost */
      if (temp & I2SR_IAL) {
      temp &= ~I2SR_IAL;
      imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
      return -EAGAIN;
      }

      if (for_busy && (temp & I2SR_IBB))
      break;
      if (!for_busy && !(temp & I2SR_IBB))
      break;
      if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
      dev_dbg(&i2c_imx->adapter.dev,
      ” I2C bus is busy\n”, __func__);
      return -ETIMEDOUT;
      }
      schedule();
      }

      return 0;
      }

      1. 안녕하세요? 먼저 질문하신 내용에 대해 답변을 드립니다.

        i2c 전송은 i2c 관련 레지스터를 조작하여 start 시그널 -> 전송 -> stop 시그널 단계로 구성됩니다.
        먼저 전송하기 전에 i2c 버스에 start 시그널을 보내야하는데 전송하기 전에 i2c 상태 레지스터를 확인하여 문제가 없는 경우에만 전송을 할 수 있습니다.
        i2c 컨트롤러 칩마다 에러 종류는 다양한데 위의 imx 에서는 다음과 같은 에러를 반환합니다.
        1) 버스 경합 실패 에러
        2개 이상의 i2c 호스트 컨트롤러가 버스 마스터가 되어 버스를 먼저 소유하려고 경합(arbitration)을 할 수 있습니다. 이 때 버스 마스터가 되지 못한 i2c 호스트 컨트롤러는 -EAGAIN 에러를 돌려줍니다. 즉 다시 시도하라는 것이죠. (2개의 i2c 호스트 컨트롤러를 하나의 i2c 버스를 share하여 사용하는 HW 구성에서만 발생합니다)
        2) 타임 아웃 에러
        두 번째 -ETIMEDOUT은 버스를 소유하려고 시도 하였지만 0.5초 동안이나 버스를 소유할 수 없을 때에 발생하는 에러입니다.

        위와 같이 전송을 시작하기 전에 start 시그널을 처리하할 수 없는 경우 imx에서는 버스 리커버리를 한 번 시도한 후, 재차 start 시그널을 보냅니다.
        만일 버스 리커버리 후에도 start 시그널을 보낼 수 없으면 더 이상 처리하지 않고 해당 에러를 반환합니다.
        (타임아웃의 경우 0.5초 x 2번의 시간이 소요되므로 최장 1초가 걸립니다.)

        1. 답변 감사합니다.
          답변해주신 내용 계속해서 이해하려고 노력해 보겠습니다.

  9. 안녕하세요. 설명 내용중에 Multi Master 에 대한 내용이 있어서 올리신 글까지 보게 되었는데, 감사합니다. I2C 백과 사전 같습니다.

    질문이 있는데
    두개 이상의 Multi Master 가 동일 I2C 버스 상에 있을때,
    리눅스의 i2c-dev.c 가 이것을 컨트롤 할 수 있도록 되어 있는건가요?
    /drivers/i2c/i2c-dev.c 를 봤는데, Arbitration이 발생했을 경우 어떻게 처리 되는지 찾을 수 없어서
    문의 드립니다.

  10. 안녕하세요?

    i2c core에서는 arbitration 실패에 대해 별도로 처리하는 로직이 없습니다.
    단순히 i2c_transfer() 함수 호출에 대한 전송 실패만 리턴합니다.

    리턴 값은 각 제조사의 i2c 드라이버의 (*.master_xfer) 후크 함수가 돌려줍니다.
    (물론 HW가 i2c 전송 후에 arbitration 실패를 감지합니다)

    그 리턴 값 중 arbitration 실패는 M_CMD_STATUS_LOST_ARB 입니다.
    전송을 시도했는데 전송 실패 사유가 경쟁에서 졌다는 뜻입니다.

    도움이 되시길 바랍니다.

  11. 안녕하세요 하나 궁금한게 있어서 댓글 하나 남기겠습니다
    혹시 standard mode, fast mode, high speed mode로 어떤 기준하에 왜 나누어진건가요?

    1. 안녕하세요?

      i2c H/W도 버전에 따라 속도가 향상됩니다.
      버전이 올라갈 수록 지원하는 스피드모드가 두 개 이상이 될 수도 있습니다.
      이러한 경우 모드를 선택하여 사용할 수 있습니다.
      스탠다드 모드보다 더 빠른 속도 모드들은 I2C 컨트롤러와 I2C 디바이스 둘 다 해당 스피트 모드를 지원할 때 사용할 수 있습니다.
      그런데 대부분의 경우 100Khz 표준 모드를 사용하고, 조금 빠른 처리가 필요한 곳에서는 400Khz Fast 모드를 사용하기도 합니다. (그 이상은 저도 사용해보지 않았습니다)

      1. 답변 감사합니다 그럼 혹시 표준모드랑 Fast모드를 어떤 곳에 쓰셨는지 여쭤봐도 될까요??
        아니면 혹시 이를 사용하는 제품 예라도 아시는 거 있으신가요??

        1. Fast 모드는 Broadcom North-Star 2 cpu에 내장된 i2c controller와
          FPGA(LXMXO2-2000)를 연결할 때 사용하였습니다.

          1. 답변 감사합니다 또 여쭤보고 싶은게 생겼습니다
            그렇다면 fast mode랑 standard mode의 차이는 전송 속도 말고는 차이가 없는 건지 궁금합니다
            또한 high-speed mode로 넘어갈때 fast mode에서 넘어가는 것으로 알고 있는데
            standard mode에서는 못 넘어가는지 여쭤보고 싶습니다.

            1. 제가 참고 문서로 올려놓은 i2c spec 문서를 보시면 high-speed 모드는 standard 또는 fast 모드 슬레이브와 같이 운영할 때 가능하면 브릿지로 분리해서 사용하라고 되어 있습니다.
              (브릿지 없이 사용시 p42, 브릿지로 분리할 때 p43에 관련 그림이 있습니다)

              속도가 다른 standard 또는 fast 모드를 사용할 때를 위해 낮은 속도 모드의 클럭에서 Start condition을 전송한 후 high speed 모드 전송을 의미하는 마스터 코드 0000 1XXX를 송신합니다. 그 후 high speed 모드로 바꾸어 Start Repeat 부호를 이어 전송한 후 디바이스와 통신하는 것을 볼 수 있을 것입니다. 통신이 완료되는 Stop condition을 만나면 다시 클럭이 저속 모드로 바뀌는군요.

              high speed 모드로 넘어가는 것은 standard 모드도 넘어갈 수 있는 것으로 보입니다.

              전체적으로 p35부터 자세히 보셔야 할 것 같습니다.

  12. 안녕하세요. i2c 통신 관련해서 질문 하나만 드려도 될까요??

    마스터를 stm32f429 를 사용하고 있고 datasheet상 i2c 가 400khz fast mode를 지원한다고 되어있습니다.
    헌데 제가 사용하는 slave는 1Mhz 까지 지원한다고 하였을 때, 마스터의 clock을 오버클럭? 을 할 수 있는지 궁금합니다.

    또한, 그런 오버클럭이 가능 할 때, slave의 클럭도 3.4Mhz 까지 오버클럭이 가능한지도 궁금합니다.

    1. 안녕하세요?

      Fm+(Fast-mode Plus)가 지원되는 디바이스라도 Fm+가 지원되지 않는 컨트롤러라면 Fm까지만 지원되리라 판단합니다. (단순히 클럭뿐만 아니라 제어타이밍도 달라집니다)
      혹여 오버클럭으로 동작시킨다고 해도 타이밍이 Fm모드로 보내므로 쉽게 안정되지 못하리라 생각합니다.

      Hs(High Spped)의 지원은 더 다릅니다. ^^;

      정확한 이해를 위해서는 i2c bus spec v2.1 이상을 살펴보시기 바랍니다.

      수고하세요.

      문영일 드림.

  13. 안녕하세요? I2C관련 하나 질문 드리고 싶어 댓글 남깁니다.

    Arbitration기능에서 두개의 마스터에서 보내는 라인이 Low에서 High으로 먼저 바뀌는 마스터가 탈락을 의미하는건가요?
    만약 그렇다면 High으로 바뀌기 전에는 어떤 마스터가 경쟁에서 이기는 마스터인지는 모르는 상태인걸 말씀하시는거겠죠
    그러면 데이터가 High일때 두개의 데이터가 하나는 high상태를 유지하고 하나는 Low상태로 달라지면 어떻게 되는건가요?
    추가로 마스터 디바이스가 자신이 경쟁에서 졌다는 사실을 어떻게 알아내는지도 의문이 듭니다.(마스터는 클럭을 계속 송신하는 중인데 SDA와 자신의 데이터가
    달라졌다는 사실을 어떻게 캐치 하는지)

    답변 꼭 부탁드립니다.. 감사합니다

    1. 안녕하세요?

      네. low에서 high로 먼저 바뀌는 마스터가 경합(arbitration)에서 탈락한 것입니다.

      i2c가 사용하는 SDA, SCL 두 라인은 모두 low active 시그널로 움직입니다.
      즉 high 전압 상태에서 거의 전류가 흐르지 않는 상태이고, low 전압 상태가 되면 전류가 개통되는 상태입니다.
      참고로 두 개의 마스터가 SDA 라인을 서로 연결하여 시그널을 보낼 때 두 마스터 중 하나라도 low를 전송하고 있으면 실제 SDA 라인의 전압은 low가 유지됩니다.

      만일 두 마스터가 동시에 같은 비트들을 연속으로 보내는 경우 두 마스터 모두 성공하고 있는 것으로 착각합니다. 이렇게 경합이 되는지도 모른 체 비트들을 전송하다가 중간에 어떤 한 호스트 컨트롤러가 다른 비트를(누군가는 high를 보내고, 다른 누군가는 low를 보낼 때) 보낼 때 high를 보내는 마스터가 결국 경합에서 탈락합니다. 동일하게 보내고 있는 동안에는 두 마스터 컨트롤러는 경합이 시작되었는지도 인지하지 못합니다.

      마스터 A의 SDA 출력과 마스터 B의 SDA 출력을 서로 연결하여 동시에 같은 데이터 출력을 할 때 처음에 경합(arbitration)을 인지하지 못한 채 진행하고 있습니다. 각자의 마스터는 자신이 출력하고 있는 전압 상태와 두 마스터가 연결되어 실제 SDA 라인에 흐르고 있는 전압을 두 마스터 컨트롤러들은 하드웨어 로직으로 feed-back 모니터링하여 비교합니다. 이 때 자신이 의도한 시그널 출력이 아닌 경우 경합에서 졌음을 알게 됩니다. 이러면 전송 실패로 끝나므로, 경합에서 진 마스터 컨트롤러는 다시 전송을 시도해야 합니다.

      실제 경합에서 탈락하여 전송 실패한 경우 대부분의 i2c controller들은 자신의 레지스터 중 status 레지스터에 결과 비트를 남깁니다.
      보통 LAB(Lost Arbitration Bit) 또는 LOST_ARB라는 이름의 비트 상태를 보고 확인할 수 있습니다.
      보통 전송 실패 후 i2c 호스트 컨트롤러 드라이버가 이의 에러 코드를 반환하므로 에러 코드를 보고 알 수 있습니다.
      i2c 컨트롤러 중에는 경합 결과에 대한 에러 코드가 분류되어 있지 않은 경우도 있는데, 이러한 경우는 직접 호스트 드라이버를 조금 수정해서 에러 코드를 추가하여 사용해야 합니다.

      수고하세요.

      문영일 드림.

      1. 안녕하세요? 상세한 답변 정말 감사드립니다. 많은 도움이 되었습니다.
        하나라도 LOW를 전송하면 SDA라인의 전압이 LOW가 되는 사실을 모르고 있었네요.
        실제 SDA라인의 전압을 하드웨어 로직으로 Feed-back모니터링이 사실 이해는 잘 가지 않습니다. ㅜㅜ

        추가로 하나만 더 질문 드리겠습니다…
        clock streatch 기능을 사용할때 마스터에서 클럭을 보내다가 슬레이브가 강제로 low로 만드는데 이때 마스터쪽에서는 클럭을 계속 보내는 중이지만 low로 표시가 되는건가요?? 아니면 클럭을 보내는것 자체를 stop시키는 건가요??

        그다음 클럭 지연을 끝낸 후 슬레이브 쪽에서 클럭을 생성하여 마스터쪽으로 보내는건가요?? 아니면 마스터에서 다시 클럭을 보내올때까지 대기했다가 클럭이 도착했을때 보내는건가요?? SCL라인은 마스터만이 클럭을 생성하는지 양방향인지 궁금합니다. Logic쪽을 공부하면서 사소한 클럭에도 신경이 쓰이네요.

        답변 기다리겠습니다. 감사합니다!

        1. 안녕하세요?

          i2c 마스터 컨트롤러 hw 로직은 SDA와 SCL 두 라인을 출력하면서 모니터링도 합니다. 내가 원하는 출력이 실제 라인에서도 반영이 되었는지를 실시간 확인하고 있습니다. 만일 경합이 없는 상태에서는 컨트롤러가 만드는 시그널이 그대로 SDA와 SCL 라인에 실리는 것을 보장합니다.

          다음은 클록 스트리칭에 대해 답변드립니다.

          이번에는 마스터 컨트롤러가 클록을 low로 출력할 때 슬레이브도 중간에 개입하여 클록을 low로 출력합니다. 마스터 컨트롤러끼리 경합하는 것과 비슷하죠?

          마스터 컨트롤러는 자신이 출력한 클록이 SCL 라인에 잘 반영이 되는 경우에만 다음 클록을 발생시킵니다.
          마스터 컨트롤러가 low 시그널을 SCL 라인에 출력하고 있는데, 클록 스트레칭을 요구하는 디바이스가 그 타이밍에 SCL 라인에
          low 출력을 보냅니다. 그러면 마스터 컨트롤러는 low -> high로 시그널이 바뀌는 순간에 자신이 high로 변경하였는데, 여전히 SCL 라인에 low 출력이 있는 것을 감지합니다. 마스터 컨트롤러는 이렇게 슬레이브 디바이스가 클록을 low로 스트리칭하고 있다는 것을 인지합니다. 그러면 컨트롤러는 SCL 라인을 모니터링하면서 슬레이브가 클록 스트리칭이 끝나서 출력을 high로 바꿀 때 까지 대기합니다.

          결국 클록 스트리칭이 발생하는 동안 마스터 컨트롤러는 클록을 보내는 것을 잠시 유보하고 슬레이브가 클록을 놔줄 때(high로 되돌려질 때)까지 대기하게 됩니다.

          슬레이브는 클록을 스스로 먼저 만들지는 않습니다. 마스터가 클록을 low로 전송하고 있을 때 스트레칭 기간이 필요한 경우에만 중간에 개입하여 SCL에 low 전압을 출력합니다.

          SCL 라인과 SDA 라인의 출력은 마스터 컨트롤러든 슬레이브든 모두 다 low로 출력할 수 있다는 것을 이해하겠죠?
          (모든 슬레이브 디바이스가 클록 스트레칭을 하는 것이 아닙니다. 마스터 컨트롤러가 i2c 주소를 전송 후 read 동작에서 지연이 필요한 EPROM이 내장된 SFP 광모듈 같은 특정 느린 특성을 가진 디바이스들만이 클록 스트레칭 기능을 사용합니다.)

          감사합니다.

          1. 설명 정말 감사합니다. 정말 완벽하게 설명 해주셨습니다.

            하나만 더 여쭈어보고 싶은데 혹시 I2C를 사용하는 마스터 컨트롤러는 SDA와 SCL 두 라인을 모니터링하는 기능을 기본적으로 포함하고 있는건가요 아니면 위에
            질문처럼 어떠한 기능을 가지고 있는 컨트롤러에 한해서 기능을 가지고 있는걸까요?

            추가로 모니터링 하는 방법을 간단하게나마 설명해 주시면 정말 너무 감사하겠습니다. ㅜㅜ

            답변 기다리겠습니다. 감사합니다.

            1. 대부분의 i2c 호스트 컨트롤러들이 arbitration을 검출할 수 있을 것입니다.
              사용하실 제품의 데이터 시트 또는 가이드 문서에서 multi-master 또는 arbitration으로 검색이 되는 제품들은 다 사용할 수 있습니다.
              i2c 최초 표준 스펙에도 기재되어 있기 때문에 대부분의 i2c 호스트 컨트롤러 하드웨어는 지원할 것 입니다.
              (오래된 드라이버 소프트웨어에서 에러 코드를 정의하지 않았을 수도 있습니다.)

              모니터링하는 방법은 소프트웨어 로직이 아니라 하드웨어 로직이잖아요?
              실제 arbitrator 로직은 간단하지만 블럭 다이어그램에 arbitrator가 있다고만 하고, 내부는 보통 자세히 공개되어 있지 않습니다.

              https://slideplayer.com/slide/1508785/ < - 10 페이지 https://www.allaboutcircuits.com/technical-articles/the-i2c-bus-firmware-implementation-details/

              위의 그림을 보시고 참고하시기 바랍니다. 감사합니다.

  14. 감사합니다. 덕분에 I2C 개념 이해에 근접한 것 같습니다.
    작년글이지만 질문을 드리고 싶습니다.
    repeated start condition을 한다고 가정했을 때,
    Read –> repeated start condition –> Read
    Write –> repeated start condition –> Write
    위 경우처럼 같은 반복 조건만 되는 것인가요?

    Read –> repeated start condition –> Write (X로 예상 됨)
    Read –> Stop –> Write로 해야하는 것인지 질문드립니다.

  15. 안녕하세요?
    I2C 디바이스가 read 동작과 write 동작을 combination할 때에도 repeated start condition이 가능합니다.
    Read –> repeated start condition –> Write (O)
    즐거운 하루 되세요

  16. 안녕하세요.
    궁금한 점이 있어서 여쭈어 봅니다. 구글링을 통해 정보를 찾기가 쉽지 않아서요.
    SMBus Protocol에서 Block Read/write를 확인 하고 싶은데요
    슬레이브 디바이스가
    일반 smbus 지원하는 Device면 다 지원한다고 생각 하면 되나요?
    아니면 Block Read/Write 를 지원하는 디바이스가 따로 있는 것인가요?
    예로 제가 0x00번지 부터 0x03번지를 읽고 싶을 때 어떻게 할지 몰라서요…
    Block Read를 하면 첫번째 주소의 데이터가 계속 나오는거 같아서 여쭈어 봅니다.
    확인하려면 어떤 device로 확인 하면 좋을까요?

    1. 안녕하세요?
      먼저 해당 i2c 버스에 대응하는 i2c adapter 드라이버가 SMBus Block Read 기능을 지원하는지 확인합니다.

      예) 1번 I2C 버스의 기능 확인
      $ i2cdetect -F 1
      Functionalities implemented by /dev/i2c-1:
      I2C no
      SMBus Quick Command yes
      SMBus Send Byte yes
      SMBus Receive Byte yes
      SMBus Write Byte yes
      SMBus Read Byte yes
      SMBus Write Word yes
      SMBus Read Word yes
      SMBus Process Call no
      SMBus Block Write yes
      SMBus Block Read yes
      SMBus Block Process Call no
      SMBus PEC yes
      I2C Block Write yes
      I2C Block Read yes

      모든 디바이스에 대해 Block Read를 할 수 있는것은 아닙니다.
      광모듈(SFP, XFP, …)에 내장된 EEPROM 같은 장치를 읽어낼 때 block read를 사용합니다.

      다음 전송 API를 참고하세요. 예) 광모듈(EEPROM)의 0x00 ~ 0x03까지 4바이트를 읽어온다.
      struct i2c_adapter *adapter = _adapter; /* 전송할 i2c 버스에 해당하는 i2c adapter */
      union i2c_smbus_data data;

      u16 addr = 0x004f; /* i2c address (chip address) */
      u8 offset = 0x00; /* EEPROM은 command 대신 읽고자 하는 시작 offset */
      u8 len = 4; /* 수신할 바이트 수 */
      u8 flags = 0;

      data.block[0] = len;

      i2c_smbus_xfer(adapter, addr, flags, I2C_SMBUS_READ, offset, I2C_SMBUS_I2C_BLOCK_DATA, &data);

      동작 디버깅은 본문의 i2c trace를 활용하시기 바랍니다.

      예) EEPROM의 32바이트 블럭 읽기
      cat-5515 [000] …. 4339.827273: i2c_write: i2c-0 #0 a=051 f=0000 l=1 [00]
      cat-5515 [000] …. 4339.827275: i2c_read: i2c-0 #1 a=051 f=0001 l=32

      감사합니다.

  17. 안녕하세요
    smbus와 i2c차이점에서
    timeout의 정의가 무엇인지 알고싶습니다.
    구글에 검색해도 도무지 이해가 가지않아 여쭈어봅니다.

  18. 안녕하세요?

    I2C-bus specification and user manual Rev.6 4 April 2014 문서 기준으로 p33의 다음 목차를 보시면 나와 있습니다.
    4.2 SMBus – System Management Bus
    4.2.2 Time-out feature
    SMBus has a time-out feature which resets devices if a communication takes too long.
    This explains the minimum clock frequency of 10 kHz to prevent locking up the bus. I2C can be a ‘DC’ bus, meaning that a slave device stretches the master clock when performing some routine while the master is accessing it. This notifies the master that the slave is busy but does not want to lose the communication. The slave device will allow continuation after its task is complete. There is no limit in the I2C-bus protocol as to how long this delay can be, whereas for a SMBus system, it would be limited to 35 ms.
    SMBus protocol just assumes that if something takes too long, then it means that there is a problem on the bus and that all devices must reset in order to clear this mode. Slave devices are not then allowed to hold the clock LOW too long.

    I2C 버스는 응답을 느리게 해도 되지만, SMBUS는 최대 35ms (타임아웃) 이내에 응답을 하지않으면 리셋하도록 규격이 되어 있습니다.

    감사합니다.

  19. 안녕하세요

    i2c setting 관련해서 질문이 있어서 댓글 남깁니다.

    보통 특정 모듈에서 사용하는 i2c line을 device tree에서 셋팅하고 module init되면서 i2c로 셋팅이 되는걸로 알고 있는데요,

    1. driver module init 측면에서, i2c setting이 setup 되는 시점은 언제 쯤 되는 걸까요?
    2. i2c setting이 되고나서 실제 i2c read/write가 한번이라도 일어나기 전까지는 해당 gpio line이 INPUT으로 셋팅되지 않는게 맞을까요?

    감사합니다.

  20. 안녕하세요?

    질문에서 “i2c setting”에 대해 i2c 호스트 컨트롤러 설정으로 이해하겠습니다.

    질문 1. driver module init 측면에서, i2c setting이 setup 되는 시점은 언제 쯤 되는 걸까요?
    답변 1. i2 호스트 컨트롤러의 설정은 다음과 같은 수순으로 진행합니다.
    1) 디바이스 트리에서 i2c 호스트 컨트롤러의 디바이스 정보를 parsing하여 등록합니다.(부팅 중 커널이 parsing하여 자동으로 등록합니다.)
    2) 커널이 부팅 중 i2c 드라이버 모듈의 init을 호출하고 i2c 호스트 컨틀롤러들을 등록합니다.(i2c의 경우 i2c_add_adapter())
    3) i2c 컨트롤러 등록 시 매치(디바이스트리의 compatible 속성과 비교)되는 디바이스인 경우 해당 드라이버의 probe 함수를 호출합니다.
    4) 해당 i2c 컨트롤러의 probe 함스에서는 i2c 장치의 클럭, 인터럽트 설정, 레지스터 매핑한 후 i2c 호스트 디바이스 관련 설정을 설정합니다.

    질문 2. i2c setting이 되고나서 실제 i2c read/write가 한번이라도 일어나기 전까지는 해당 gpio line이 INPUT으로 셋팅되지 않는게 맞을까요?
    답변: 먼저 i2c가 설정되는 타이밍을 알아봅니다.
    1) hw 부트스트랩에서 결정 (전원 인가 시점에서 결정)
    2) 부트로더에서 sw로 결정
    3) 리눅스 커널에서 sw로 결정(pinctrl 디바이스 트리 정보를 읽어서 결정)

    3)번에서 결정해야 하는 경우 하드웨어마다 다르지만 i2c 라인들은 보통 초기에 gpio input 모드로 설정되어, i2c 디바이스에 전류 및 전압 충격이 가해지지 않도록 보호합니다. 이후 pinctrl 드라이버를 통해 해당 핀들을 i2c 라인으로 변경한 후 i2c 장치가 동작하도록 합니다.

    디폴트로 설정되어 있는 핀들을 언제 i2c로 변경하는지는 디바이스 트리를 통해 다음과 같이 진행합니다.
    1) pinctrl 디바이스 드라이버가 먼저 호출될 때 우선적으로 디바이스 트리에서 지정한 디바이스들 모두를 설정합니다.
    2) 각각의 디바이스 드라이버 로드 후 probe가 호출될 때 필요 시(option) 해당 드라이버 코드에서 디바이스 트리의 pin 모드 설정을 읽어 i2c로 변경하도록 pinctrl 관련 함수를 호출합니다.

    pinctrl 관련된 정보는 다음을 참조하시기 바랍니다.
    http://jake.dothome.co.kr/pinctrl/
    http://jake.dothome.co.kr/pinctrl-2/

    감사합니다.

댓글 남기기

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