안녕하세요? 문c 블로그입니다.
2018년 9월 5일 금일의 트래픽이 초과되어 수 시간동안 문c 블로그 페이지가 열리지 않았었습니다.
다시 확인하여 서비스를 계속하고 있습니다.
어제부터 트래픽이 폭주하던데 지속적으로 모니터링을 하겠습니다.
불편을드려 죄송합니다.
문영일 드림.
안녕하세요? 문c 블로그입니다.
2018년 9월 5일 금일의 트래픽이 초과되어 수 시간동안 문c 블로그 페이지가 열리지 않았었습니다.
다시 확인하여 서비스를 계속하고 있습니다.
어제부터 트래픽이 폭주하던데 지속적으로 모니터링을 하겠습니다.
불편을드려 죄송합니다.
문영일 드림.
최근 PC 및 Embedded 시스템에서 가장 많이 사용하는 고속 Bus인 PCI Express를 위주로 설명한다. 먼저 PCI Bus에 대해 간략히 알아보고 PCI Express를 더 자세히 비교하여 설명하기로 한다.
PCI(Peripheral Component Interconnect) 아키텍처는 주변 장치 연결을 위해 ISA(Industry Standard Architecture bus), EISA, MCA, VESA 등의 버스 표준을 대체하기 위해 설계되었다. 오늘날 PC에서 가장 널리 사용되는 버스 규격이다.
다음 그림은 버스 타입별로 비트 수, 클럭 및 핀 수를 비교하였다.
PCI는 다음과 같은 주요 목표로 설계되었다.
32비트 PC가 처음 출현했을 때 사용하던 방식으로 ISA 호환을 위해 PCI 버스 하단에서 ISA 버스를 지원함을 알 수 있다.
8비트 및 16비트 ISA 버스가 채택된 ISA 슬롯 모습을 알아본다.
32비트 PCI 및 64비트 PCI 버스가 채택된 PCI 및 PCI/X 슬롯 모습을 알아본다.
1 ~ 수 개의 pci 슬롯을 통해 pci 버스를 공유한다.
다음은 버스 컨트롤러가 발생시키는 시그널 라인들에 대한 설명이다. (# 기호는 low active로 동작함을 의미한다.)
버스 마스터가 발생시키는 시그널 라인들에 대한 설명이다.
다음 파란색 시그널은 타겟 디바이스가 발생시키는 시그널 라인들에 대한 설명이다. (# 기호는 low active로 동작함을 의미한다.)
PCI Bus Arbiter의 구성은 다음과 같이 Centralized 또는 Daisy Chain 방식을 사용한다.
다음 시그널은 PCI 버스 arbiter 회로가 수신또는 발생시키는 시그널 라인들에 대한 설명이다.
PCI 전송 에러를 감지하는 회로이다. 오래된 pc에서는 단순하게 처리하기 위해 전송 에러를 감지하면 NMI를 발생시킨 후 시스템을 정지시키기도 했다.
Bus Access Latency는 다음 3가지를 포함한다. 참고로 MLT(Master Latency Timer)는 configuration 레지스터 중 offset=14 바이트 위치에 있다.
4비트를 사용하여 명령 타입을 구분한다.
PCI H/W는 다음과 같이 3가지 종류의 버스를 사용한다.
PCI는 다음과 같이 3가지 주소 영역을 지원한다. (주의: PCI 고유의 주소 공간이며 CPU가 access할 수 있는 물리 주소 영역이 아니다)
시스템에 여러 개의 PCI 슬롯을 지원하는 경우 각 슬롯의 INTA ~ INTD 라인들의 라우팅은 시스템 설계에 따라 다르다.
PCI 디바이스가 레거시 인터럽트 대신 MSI를 사용하면 다음과 같은 장점들이 있다.
PCI Express 버스는 여러 개의 슬롯을 공유하지 않고, 1 vs 1로 연결하여 사용한다. 이러한 특성으로 인해 여러 개의 슬롯이 필요한 경우 PCI express 스위치를 사용하여 확장한다.
각각의 lane 수를 가지는 PCIe 슬롯 및 PCIe 카드를 알아본다.
PCI 및 PCIe의 미니 사이즈로도 사용되고 있다. 이 폼팩터들은 점점 사용되지 않고 있으며 이들은 m.2 폼팩터로 사용 추세가 이동하고 있다.
M.2 소켓(슬롯)을 통해 여러 가지 버스를 제공하는데 이의 제공 여부 구분을 정의한다.
M.2 카드에서 가장 많이 사용하는 key ID는 다음과 같다.
아래 그림을 보고 key B+M과 key M 타입을 구분하는 방법을 알아두자.
다음은 여러 가지 SSD 타입에 대해 설명하였다.
위에서 구분한 내용을 아래 그림을 보고 실제 제품 형태와 비교해보자.
패킷을 주고(Transmit) 받을(Receive) 수 있는 하나의 lane으로 PCI Express 링크를 구성할 수 있다. 또한 2 개 이상의 lane을 묶어(aggregation) 하나의 링크로 만들면 통신 속도를 통합한 lane 만큼 높일 수 있다.
다음 그림에서 SoC가 Root Complex 가상 PCI Bridge를 통해 PCIe 버스를 3개 제공하는 것을 보여준다. 각각의 버스를 다음과 같이 사용하고 있다.
PCI 버스가 CPU와 연결되는 가장 상위는 Root Complex Device이고 버스를 확장하기 위해 PCI Switch를 사용할 수 있다.
루트 컴플렉스(RC)는 CPU/메모리 서브시스템을 I/O 연결할 수 있는 I/O 계층 구조의 루트이다.
PCI Express 루트 컴플렉스 디바이스를 더 자세히 살펴보면 다수의 내부 PCI-PCI 브리지 구조로 구성된다.
스위치는 다 수의 가상 PCI-to-PCI 브리지 디바이스들로 내부적으로 구성되어 있다.
PCI 전송에 다음 3가지의 전송 계층을 사용한다.
3 계층의 TLP(Transaction Layer Packet) 또는 2 계층의 DLLP(Data Link Layer Packet)을 전송하며 각 계층에서 취급하는 데이터는 다음과 같다.
트랜잭션 레이어 패킷은 다음과 같은 구성을 사용한다. 전송 시 Byte 0 위치부터 시작한다.
다음 그림은 TLP 헤더와 패킷 포맷을 보여준다. 패킷 포맷에 따라 1~4바이트의 TLP 헤더를 갖는다.
다음 표에서 트랜잭션 타입을 보여준다.
다음과 같이 두 가지 타입의 라우팅이 지원된다.
아래 그림은 4 DW 길이의 TLP 헤더이다. 포맷이 0 x 0 인 경우 3 DW 길이의 TLP 헤더를 사용한다.
TH 비트가 설정된 메모리 읽기 요청은 다음표와 같이 첫번째와 마지막 DW에 대한 바이트 enable을 표시한다.
다음 그림은 메모리, I/O 및 Configuration 트랜잭션 데이터를 요청할 때 사용하는 TLP 헤더이다. (Memory 32비트는 생략)
데이터 전송 계층의 MAC과 물리 계층의 PHY간의 인터커넥트
물리 전송 계층의 3가지 서브 레이어를 각 기능별로 송/수신 흐름을 알아본다.
다음과 같은 엔코딩을 사용하여 5개 이상의 비트가 연속되지 않게 제한시킨다. 이렇게 하므로써 출력이 커지는 것을 방지하여 EMI 방사(radiation)를 줄여준다.
PCI Express Configuration 모델은 두 개의 Configuration Space access 매커니즘을 지원한다.
PCI Express는 PCI와 유사하지만 다음과 같은 변화가 있다.
PCI Express 하나를 담당하는 Configuration Space를 알아본다.
다수의 PCI Express 버스를 관리하기 위해 아래와 같은 주소 체제로 나누어 사용한다.
다음 그림은 Root Complex 가상 PCI Bridge가 3개의 PCIe 버스를 제공하며 이에 대한 pci 주소 공간을 보여준다.
디바이스 타입에 따라 Configuration Space Header는 다음과 같이 2 가지 타입을 지원한다.
다음 그림은 i2c 버스에 연결된 i2c 멀티플레서를 블럭 다이아그램으로 표현하였다.
include/linux/i2c-mux.h
struct i2c_mux_core { struct i2c_adapter *parent; struct device *dev; unsigned int mux_locked:1; unsigned int arbitrator:1; unsigned int gate:1; void *priv; int (*select)(struct i2c_mux_core *, u32 chan_id); int (*deselect)(struct i2c_mux_core *, u32 chan_id); int num_adapters; int max_adapters; struct i2c_adapter *adapter[0]; };
drivers/i2c/muxes/i2c-mux.c
/* multiplexer per channel data */ struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; struct i2c_mux_core *muxc; u32 chan_id; };
drivers/i2c/muxes/i2c-mux.c
struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, struct device *dev, int max_adapters, int sizeof_priv, u32 flags, int (*select)(struct i2c_mux_core *, u32), int (*deselect)(struct i2c_mux_core *, u32)) { struct i2c_mux_core *muxc; muxc = devm_kzalloc(dev, sizeof(*muxc) + max_adapters * sizeof(muxc->adapter[0]) + sizeof_priv, GFP_KERNEL); if (!muxc) return NULL; if (sizeof_priv) muxc->priv = &muxc->adapter[max_adapters]; muxc->parent = parent; muxc->dev = dev; if (flags & I2C_MUX_LOCKED) muxc->mux_locked = true; if (flags & I2C_MUX_ARBITRATOR) muxc->arbitrator = true; if (flags & I2C_MUX_GATE) muxc->gate = true; muxc->select = select; muxc->deselect = deselect; muxc->max_adapters = max_adapters; return muxc; } EXPORT_SYMBOL_GPL(i2c_mux_alloc);
i2c_mux_core를 할당한다.
다음 그림은 8개의 adapter를 위한 i2c_mux_core가 준비됨을 알 수 있다.
drivers/i2c/muxes/i2c-mux.c -1/3-
int i2c_mux_add_adapter(struct i2c_mux_core *muxc, u32 force_nr, u32 chan_id, unsigned int class) { struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; char symlink_name[20]; int ret; if (muxc->num_adapters >= muxc->max_adapters) { dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); return -EINVAL; } priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* Set up private adapter data */ priv->muxc = muxc; priv->chan_id = chan_id; /* Need to do algo dynamically because we don't know ahead * of time what sort of physical adapter we'll be dealing with. */ if (parent->algo->master_xfer) { if (muxc->mux_locked) priv->algo.master_xfer = i2c_mux_master_xfer; else priv->algo.master_xfer = __i2c_mux_master_xfer; } if (parent->algo->smbus_xfer) { if (muxc->mux_locked) priv->algo.smbus_xfer = i2c_mux_smbus_xfer; else priv->algo.smbus_xfer = __i2c_mux_smbus_xfer; } priv->algo.functionality = i2c_mux_functionality; /* Now fill out new adapter structure */ snprintf(priv->adap.name, sizeof(priv->adap.name), "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id); priv->adap.owner = THIS_MODULE; priv->adap.algo = &priv->algo; priv->adap.algo_data = priv; priv->adap.dev.parent = &parent->dev; priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; if (muxc->mux_locked) priv->adap.lock_ops = &i2c_mux_lock_ops; else priv->adap.lock_ops = &i2c_parent_lock_ops; /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) dev_err(&parent->dev, "Segment %d behind mux can't share classes with ancestors\n", chan_id); else priv->adap.class = class;
adapter를 추가한 후 i2c_mux_core에 연결한다.
drivers/i2c/muxes/i2c-mux.c -2/3-
. /* * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. */ if (muxc->dev->of_node) { struct device_node *dev_node = muxc->dev->of_node; struct device_node *mux_node, *child = NULL; u32 reg; if (muxc->arbitrator) mux_node = of_get_child_by_name(dev_node, "i2c-arb"); else if (muxc->gate) mux_node = of_get_child_by_name(dev_node, "i2c-gate"); else mux_node = of_get_child_by_name(dev_node, "i2c-mux"); if (mux_node) { /* A "reg" property indicates an old-style DT entry */ if (!of_property_read_u32(mux_node, "reg", ®)) { of_node_put(mux_node); mux_node = NULL; } } if (!mux_node) mux_node = of_node_get(dev_node); else if (muxc->arbitrator || muxc->gate) child = of_node_get(mux_node); if (!child) { for_each_child_of_node(mux_node, child) { ret = of_property_read_u32(child, "reg", ®); if (ret) continue; if (chan_id == reg) break; } } priv->adap.dev.of_node = child; of_node_put(mux_node); }
drivers/i2c/muxes/i2c-mux.c -3/3-
/* * Associate the mux channel with an ACPI node. */ if (has_acpi_companion(muxc->dev)) acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(muxc->dev), chan_id); if (force_nr) { priv->adap.nr = force_nr; ret = i2c_add_numbered_adapter(&priv->adap); if (ret < 0) { dev_err(&parent->dev, "failed to add mux-adapter %u as bus %u (error=%d)\n", chan_id, force_nr, ret); goto err_free_priv; } } else { ret = i2c_add_adapter(&priv->adap); if (ret < 0) { dev_err(&parent->dev, "failed to add mux-adapter %u (error=%d)\n", chan_id, ret); goto err_free_priv; } } WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, "mux_device"), "can't create symlink to mux device\n"); snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, symlink_name), "can't create symlink for channel %u\n", chan_id); dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); muxc->adapter[muxc->num_adapters++] = &priv->adap; return 0; err_free_priv: kfree(priv); return ret; } EXPORT_SYMBOL_GPL(i2c_mux_add_adapter);
다음 그림은 i2c_mux_add_adapter() 함수가 4번 호출되어 4개의 i2c 채널(i2c_mux_priv)이 생성되고 그 내부에 adapter와 algorithm이 있는 것을 확인할 수 있다.
다음 그림과 같이 i2c-mux의 경우 child 노드의 파란색 reg 속성 값이 adapter 번호가 된다. 즉 i2c 버스 번호가 된다. 그러나 적색으로된 i2c-arb 또는 i2c-gate 노드의 경우 i2c 버스가 하나이므로 reg 속성이 주어지지 않는 것을 알 수 있다.
다음 그림은 i2c_transfer() 함수를 사용 시 버스 락 처리 및 i2c-mux의 채널 선택과 전송을 수행하는 과정을 보여준다.
drivers/i2c/i2c-mux.c
static int __i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_mux_priv *priv = adap->algo_data; struct i2c_mux_core *muxc = priv->muxc; struct i2c_adapter *parent = muxc->parent; int ret; /* Switch to the right mux port and perform the transfer. */ ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = __i2c_transfer(parent, msgs, num); if (muxc->deselect) muxc->deselect(muxc, priv->chan_id); return ret; }
adapter에 해당하는 채널을 선택한 채로 부모 adapter를 통해 메시지를 전송한다. 사용이 완료되면 deselect 하여야 한다.
drivers/i2c/i2c-mux.c
static void i2c_parent_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { struct i2c_mux_priv *priv = adapter->algo_data; struct i2c_adapter *parent = priv->muxc->parent; rt_mutex_lock(&parent->mux_lock); i2c_lock_bus(parent, flags); }
mux 뮤텍스 락을 건채로 부모 adapter 버스 락을 건다.
include/linux/i2c.h
/** * i2c_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT * locks only this branch in the adapter tree */ static inline void i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { adapter->lock_ops->lock_bus(adapter, flags); }
adapter에 해당하는 버스 락 오퍼레이션을 수행한다.
drivers/i2c/muxes/i2c-mux-pca954x.c
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) { struct pca954x *data = i2c_mux_priv(muxc); struct i2c_client *client = data->client; const struct chip_desc *chip = data->chip; u8 regval; int ret = 0; /* we make switches look like muxes, not sure how to be smarter */ if (chip->muxtype == pca954x_ismux) regval = chan | chip->enable; else regval = 1 << chan; /* Only select the channel if its different from the last channel */ if (data->last_chan != regval) { ret = pca954x_reg_write(muxc->parent, client, regval); data->last_chan = ret < 0 ? 0 : regval; } return ret; }
i2c-mux의 요청받은 채널 번호에 해당하는 i2c 버스를 HW 선택한다.
drivers/i2c/muxes/i2c-mux-pca954x.c
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() for this as they will try to lock adapter a second time */ static int pca954x_reg_write(struct i2c_adapter *adap, struct i2c_client *client, u8 val) { int ret = -ENODEV; if (adap->algo->master_xfer) { struct i2c_msg msg; char buf[1]; msg.addr = client->addr; msg.flags = 0; msg.len = 1; buf[0] = val; msg.buf = buf; ret = __i2c_transfer(adap, &msg, 1); if (ret >= 0 && ret != 1) ret = -EREMOTEIO; } else { union i2c_smbus_data data; ret = adap->algo->smbus_xfer(adap, client->addr, client->flags, I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, &data); } return ret; }
i2c 전송 후크가 있는 경우 i2c 버스에 인자로 요청한 1 바이트 값을 전송한다. i2c 전송 후크가 없는 경우 smbus에 인자로 요청한 1 바이트 값을 전송한다.
가장 많이 사용되는 필립스 반도체의 i2c-mux 제품들 중 954X 시리즈를 기능별로 구분할 수 있는 테이블이다.
각 제품마다 하위 2~4개의 비트를 설정하여 주소를 설정할 수 있다.
다음과 같이 채널을 선택한다.
다음 그림은 some device와 통신하기 위해 i2c-mux 칩인 pca9545a 디바이스에 해당하는 주소 0x72로 0x8 값을 전송하여 #3번 채널을 선택하는 모습을 보여준다.
<kernel v4.14>
전송에 관여하는 다음 항목들에 대해 알아본다.
Kernel v4.1-rc1에서 추가된 기능으로 특정 i2c 호스트 컨트롤러들에서 HW 전송 제한이 걸려 있는 경우 이에 대한 전송 제한 체크를 수행할 수 있도록 하였다.
drivers/i2c/base/i2c-core-base.c
static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { const struct i2c_adapter_quirks *q = adap->quirks; int max_num = q->max_num_msgs, i; bool do_len_check = true; if (q->flags & I2C_AQ_COMB) { max_num = 2; /* special checks for combined messages */ if (num == 2) { if (q->flags & I2C_AQ_COMB_WRITE_FIRST && msgs[0].flags & I2C_M_RD) return i2c_quirk_error(adap, &msgs[0], "1st comb msg must be write"); if (q->flags & I2C_AQ_COMB_READ_SECOND && !(msgs[1].flags & I2C_M_RD)) return i2c_quirk_error(adap, &msgs[1], "2nd comb msg must be read"); if (q->flags & I2C_AQ_COMB_SAME_ADDR && msgs[0].addr != msgs[1].addr) return i2c_quirk_error(adap, &msgs[0], "comb msg only to same addr"); if (i2c_quirk_exceeded(msgs[0].len, q->max_comb_1st_msg_len)) return i2c_quirk_error(adap, &msgs[0], "msg too long"); if (i2c_quirk_exceeded(msgs[1].len, q->max_comb_2nd_msg_len)) return i2c_quirk_error(adap, &msgs[1], "msg too long"); do_len_check = false; } } if (i2c_quirk_exceeded(num, max_num)) return i2c_quirk_error(adap, &msgs[0], "too many messages"); for (i = 0; i < num; i++) { u16 len = msgs[i].len; if (msgs[i].flags & I2C_M_RD) { if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len)) return i2c_quirk_error(adap, &msgs[i], "msg too long"); } else { if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len)) return i2c_quirk_error(adap, &msgs[i], "msg too long"); } } return 0; }
송/수신할 메시지에 대해 메시지 수 및 길이 제한을 체크한다. 성공 시 0을 반환한다.
다음 그림은 quirks 체크를 수행하는 모습을 보여준다.
/** * struct i2c_adapter_quirks - describe flaws of an i2c adapter * @flags: see I2C_AQ_* for possible flags and read below * @max_num_msgs: maximum number of messages per transfer * @max_write_len: maximum length of a write message * @max_read_len: maximum length of a read message * @max_comb_1st_msg_len: maximum length of the first msg in a combined message * @max_comb_2nd_msg_len: maximum length of the second msg in a combined message * * Note about combined messages: Some I2C controllers can only send one message * per transfer, plus something called combined message or write-then-read. * This is (usually) a small write message followed by a read message and * barely enough to access register based devices like EEPROMs. There is a flag * to support this mode. It implies max_num_msg = 2 and does the length checks * with max_comb_*_len because combined message mode usually has its own * limitations. Because of HW implementations, some controllers can actually do * write-then-anything or other variants. To support that, write-then-read has * been broken out into smaller bits like write-first and read-second which can * be combined as needed. */
struct i2c_adapter_quirks { u64 flags; int max_num_msgs; u16 max_write_len; u16 max_read_len; u16 max_comb_1st_msg_len; u16 max_comb_2nd_msg_len; };
I2C 버스 리커버리는 kernel v3.10-rc1에서 소개되었다.
i2c 프로토콜 규격의 섹션 3.1.16에서 “Bus clear”라는 항목이 구현되었다.
3.1.16 Bus clear In the unlikely event where the clock (SCL) is stuck LOW, the preferential procedure is to reset the bus using the HW reset signal if your I2C devices have HW reset inputs. If the I2C devices do not have HW reset inputs, cycle power to the devices to activate the mandatory internal Power-On Reset (POR) circuit. If the data line (SDA) is stuck LOW, the master should send nine clock pulses. The device that held the bus LOW should release it sometime within those nine clocks. If not, then use the HW reset or cycle power to clear the bus.
i2c 버스를 리커버리하는 방법은 다음과 같은 방법들이 있다.
drivers/i2c/base/i2c-core-base.c
static void i2c_init_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; char *err_str; if (!bri) return; if (!bri->recover_bus) { err_str = "no recover_bus() found"; goto err; } /* Generic GPIO recovery */ if (bri->recover_bus == i2c_generic_gpio_recovery) { if (!gpio_is_valid(bri->scl_gpio)) { err_str = "invalid SCL gpio"; goto err; } if (gpio_is_valid(bri->sda_gpio)) bri->get_sda = get_sda_gpio_value; else bri->get_sda = NULL; bri->get_scl = get_scl_gpio_value; bri->set_scl = set_scl_gpio_value; } else if (bri->recover_bus == i2c_generic_scl_recovery) { /* Generic SCL recovery */ if (!bri->set_scl || !bri->get_scl) { err_str = "no {get|set}_scl() found"; goto err; } } return; err: dev_err(&adap->dev, "Not using recovery: %s\n", err_str); adap->bus_recovery_info = NULL; }
i2c 버스 리커버리를 준비한다.
다음 그림은 i2c_init_recovery() 함수에서 3가지 타입의 (*recover_bus) 후크에 대해 처리하는 과정을 보여준다.
include/linux/i2c.h
/** * struct i2c_bus_recovery_info - I2C bus recovery information * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or * i2c_generic_scl_recovery() or i2c_generic_gpio_recovery(). * @get_scl: This gets current value of SCL line. Mandatory for generic SCL * recovery. Used internally for generic GPIO recovery. * @set_scl: This sets/clears SCL line. Mandatory for generic SCL recovery. Used * internally for generic GPIO recovery. * @get_sda: This gets current value of SDA line. Optional for generic SCL * recovery. Used internally, if sda_gpio is a valid GPIO, for generic GPIO * recovery. * @prepare_recovery: This will be called before starting recovery. Platform may * configure padmux here for SDA/SCL line or something else they want. * @unprepare_recovery: This will be called after completing recovery. Platform * may configure padmux here for SDA/SCL line or something else they want. * @scl_gpio: gpio number of the SCL line. Only required for GPIO recovery. * @sda_gpio: gpio number of the SDA line. Only required for GPIO recovery. */
struct i2c_bus_recovery_info { int (*recover_bus)(struct i2c_adapter *); int (*get_scl)(struct i2c_adapter *); void (*set_scl)(struct i2c_adapter *, int val); int (*get_sda)(struct i2c_adapter *); void (*prepare_recovery)(struct i2c_adapter *); void (*unprepare_recovery)(struct i2c_adapter *); /* gpio recovery */ int scl_gpio; int sda_gpio; };
다음 그림은 i2c 버스가 stuck 상태일 때 100Khz의 클럭 펄스를 9번 만들어 i2c 버스가 리커버리를 시도하는 모습을 보여준다.
drivers/i2c/base/i2c-core-base.c
int i2c_recover_bus(struct i2c_adapter *adap) { if (!adap->bus_recovery_info) return -EOPNOTSUPP; dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); return adap->bus_recovery_info->recover_bus(adap); } EXPORT_SYMBOL_GPL(i2c_recover_bus);
i2c 버스 리커버리를 시도한다.
drivers/i2c/base/i2c-core-base.c
int i2c_generic_scl_recovery(struct i2c_adapter *adap) { return i2c_generic_recovery(adap); } EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
scl 핀에 9번의 클럭을 만들어 i2c 버스 리커버리를 시도한다.
drivers/i2c/base/i2c-core-base.c
/* * We are generating clock pulses. ndelay() determines durating of clk pulses. * We will generate clock with rate 100 KHz and so duration of both clock levels * is: delay in ns = (10^6 / 100) / 2 */ #define RECOVERY_NDELAY 5000 #define RECOVERY_CLK_CNT 9 static int i2c_generic_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; int i = 0, val = 1, ret = 0; if (bri->prepare_recovery) bri->prepare_recovery(adap); bri->set_scl(adap, val); ndelay(RECOVERY_NDELAY); /* * By this time SCL is high, as we need to give 9 falling-rising edges */ while (i++ < RECOVERY_CLK_CNT * 2) { if (val) { /* Break if SDA is high */ if (bri->get_sda && bri->get_sda(adap)) break; /* SCL shouldn't be low here */ if (!bri->get_scl(adap)) { dev_err(&adap->dev, "SCL is stuck low, exit recovery\n"); ret = -EBUSY; break; } } val = !val; bri->set_scl(adap, val); ndelay(RECOVERY_NDELAY); } if (bri->unprepare_recovery) bri->unprepare_recovery(adap); return ret; }
scl 핀에 9번의 클럭을 만들어 i2c 버스 리커버리를 시도한다.
drivers/i2c/base/i2c-core-base.c
int i2c_generic_gpio_recovery(struct i2c_adapter *adap) { int ret; ret = i2c_get_gpios_for_recovery(adap); if (ret) return ret; ret = i2c_generic_recovery(adap); i2c_put_gpios_for_recovery(adap); return ret; } EXPORT_SYMBOL_GPL(i2c_generic_gpio_recovery);
gpio 모드로 변경하여 scl을 준비하고 9번의 클럭을 만들어 i2c 버스 리커버리를 시도한다. 처리한 후 다시 gpio 모드를 해제한다.
다음 그림은 pinmux를 통해 i2c 펑션 모드로 사용하다가 i2c 버스 리커버리를 시도하는 모습을 보여준다.
drivers/i2c/base/i2c-core-base.c
static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; struct device *dev = &adap->dev; int ret = 0; ret = gpio_request_one(bri->scl_gpio, GPIOF_OPEN_DRAIN | GPIOF_OUT_INIT_HIGH, "i2c-scl"); if (ret) { dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio); return ret; } if (bri->get_sda) { if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) { /* work without SDA polling */ dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n", bri->sda_gpio); bri->get_sda = NULL; } } return ret; }
scl_gpio 번 핀을 scl로 사용하기 위해 open-drain 설정으로 초기 high 출력 상태로 요청한다. (*get_sda) 후크가 사용되는 경우 sda_gpio 번 핀을 sda로 input mode로 사용 요청한다.
drivers/i2c/base/i2c-core-base.c
static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; if (bri->get_sda) gpio_free(bri->sda_gpio); gpio_free(bri->scl_gpio); }
리커버리를 종료하였으므로 scl_gpio 및 sda_gpio 번 핀을 gpio 모드에서 해제한다.
drivers/i2c/base/i2c-core-base.c
static int get_scl_gpio_value(struct i2c_adapter *adap) { return gpio_get_value(adap->bus_recovery_info->scl_gpio); }
scl_gpio 번 핀의 값을 알아온다.
drivers/i2c/base/i2c-core-base.c
static void set_scl_gpio_value(struct i2c_adapter *adap, int val) { gpio_set_value(adap->bus_recovery_info->scl_gpio, val); }
scl_gpio 번 핀에 값을 출력한다.
drivers/i2c/base/i2c-core-base.c
static int get_sda_gpio_value(struct i2c_adapter *adap) { return gpio_get_value(adap->bus_recovery_info->sda_gpio); }
sda_gpio 번 핀의 값을 알아온다.
i2c 호스트 컨트롤러를 통해 i2c 버스에 데이터를 송/수신할 때 먼저 버스를 점유하기 위해 i2c 버스 락을 걸고 사용한다. 이 때 대부분의 호스트 컨트롤러의 adapter 인터페이스는 lock_ops가 주어지지 않았을 때 디폴트로 adapter의 bus_lock 뮤텍스를 사용한다.
다음 그림은 i2c 전송 전에 디폴트 i2c 버스 락 오퍼레이션인 adapter의 bus_lock 뮤텍스를 사용하는 것을 보여준다.
drivers/i2c/i2c-core-base.c
/** * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT * locks only this branch in the adapter tree */ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { rt_mutex_lock(&adapter->bus_lock); }
adapter에 있는 버스 뮤텍스 락을 건다.
drivers/i2c/i2c-core-base.c
/** * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT * trylocks only this branch in the adapter tree */ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) { return rt_mutex_trylock(&adapter->bus_lock); }
adapter에 있는 버스 뮤텍스 락을 시도한다.
drivers/i2c/i2c-core-base.c
/** * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT * unlocks only this branch in the adapter tree */ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) { rt_mutex_unlock(&adapter->bus_lock); }
adapter에 있는 버스 뮤텍스 락을 해제한다.
i2c 전송은 i2c_algorithm에 구현되어 있는 i2c용 전송 후크를 이용한다. 만일 smbus 전송이 필요한 경우 smbus용 후크도 지정하여 사용할 수 있다.
다음 그림은 i2c 전송에 관여하는 lock operation과 알고리즘 및 전송 제한 값 및 i2c 전송 함수의 호출 단계를 보여준다.
drivers/i2c/i2c-core-base.c
/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret; /* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that's the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it's unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */ if (adap->algo->master_xfer) { #ifdef DEBUG for (ret = 0; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W', msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); } #endif if (in_atomic() || irqs_disabled()) { ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } ret = __i2c_transfer(adap, msgs, num); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -EOPNOTSUPP; } } EXPORT_SYMBOL(i2c_transfer);
i2c 버스에 락을 건채 요청한 메시지 수 만큼 전송을 수행한다. 성공 시 0을 반환한다.
drivers/i2c/i2c-core-base.c
/** * __i2c_transfer - unlocked flavor of i2c_transfer * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Adapter lock must be held when calling this function. No debug logging * takes place. adap->algo->master_xfer existence isn't checked. */
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { unsigned long orig_jiffies; int ret, try; if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets * enabled. This is an efficient way of keeping the for-loop from * being executed when not needed. */ if (static_key_false(&i2c_trace_msg)) { int i; for (i = 0; i < num; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_read(adap, &msgs[i], i); else trace_i2c_write(adap, &msgs[i], i); } /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (ret = 0, try = 0; try <= adap->retries; try++) { ret = adap->algo->master_xfer(adap, msgs, num); if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } if (static_key_false(&i2c_trace_msg)) { int i; for (i = 0; i < ret; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_reply(adap, &msgs[i], i); trace_i2c_result(adap, i, ret); } return ret; } EXPORT_SYMBOL(__i2c_transfer);
i2c 버스에 요청한 메시지 수 만큼 전송을 수행한다. 성공 시 0을 반환한다.
broadcom ns2에 내장된 i2c에 사용하는 bcm-i2c-iproc 드라이버에서 사용하는 전송함수을 예로 알아본다.
drivers/i2c/busses/i2c-bcm-iproc.c
static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) { struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); int ret, i; /* go through all messages */ for (i = 0; i < num; i++) { ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); if (ret) { dev_dbg(iproc_i2c->device, "xfer failed\n"); return ret; } } return num; }
요청한 메시지 수 만큼 메시지들을 전송한다. 성공 시 메시지 수가 반환된다. 실패의 경우 음수를 반환한다.
drivers/i2c/busses/i2c-bcm-iproc.c -1/2-
static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, struct i2c_msg *msg) { int ret, i; u8 addr; u32 val; unsigned int tx_bytes; unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC); /* check if bus is busy */ if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) { dev_warn(iproc_i2c->device, "bus is busy\n"); return -EBUSY; } iproc_i2c->msg = msg; /* format and load slave address into the TX FIFO */ addr = i2c_8bit_addr_from_msg(msg); writel(addr, iproc_i2c->base + M_TX_OFFSET); /* * For a write transaction, load data into the TX FIFO. Only allow * loading up to TX FIFO size - 1 bytes of data since the first byte * has been used up by the slave address */ tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1); if (!(msg->flags & I2C_M_RD)) { for (i = 0; i < tx_bytes; i++) { val = msg->buf[i]; /* mark the last byte */ if (i == msg->len - 1) val |= 1 << M_TX_WR_STATUS_SHIFT; writel(val, iproc_i2c->base + M_TX_OFFSET); } iproc_i2c->tx_bytes = tx_bytes; } /* mark as incomplete before starting the transaction */ reinit_completion(&iproc_i2c->done); iproc_i2c->xfer_is_done = 0; /* * Enable the "start busy" interrupt, which will be triggered after the * transaction is done, i.e., the internal start_busy bit, transitions * from 1 to 0. */ val = BIT(IE_M_START_BUSY_SHIFT); /* * If TX data size is larger than the TX FIFO, need to enable TX * underrun interrupt, which will be triggerred when the TX FIFO is * empty. When that happens we can then pump more data into the FIFO */ if (!(msg->flags & I2C_M_RD) && msg->len > iproc_i2c->tx_bytes) val |= BIT(IE_M_TX_UNDERRUN_SHIFT); writel(val, iproc_i2c->base + IE_OFFSET);
1개의 메시지를 전송한다. 실패 시 음수를 반환한다.
drivers/i2c/busses/i2c-bcm-iproc.c -2/2-
/* * Now we can activate the transfer. For a read operation, specify the * number of bytes to read */ val = BIT(M_CMD_START_BUSY_SHIFT); if (msg->flags & I2C_M_RD) { val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | (msg->len << M_CMD_RD_CNT_SHIFT); } else { val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); } writel(val, iproc_i2c->base + M_CMD_OFFSET); time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); /* disable all interrupts */ writel(0, iproc_i2c->base + IE_OFFSET); /* read it back to flush the write */ readl(iproc_i2c->base + IE_OFFSET); /* make sure the interrupt handler isn't running */ synchronize_irq(iproc_i2c->irq); if (!time_left && !iproc_i2c->xfer_is_done) { dev_err(iproc_i2c->device, "transaction timed out\n"); /* flush FIFOs */ val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); return -ETIMEDOUT; } ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); if (ret) { /* flush both TX/RX FIFOs */ val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); return ret; } /* * For a read operation, we now need to load the data from FIFO * into the memory buffer */ if (msg->flags & I2C_M_RD) { for (i = 0; i < msg->len; i++) { msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> M_RX_DATA_SHIFT) & M_RX_DATA_MASK; } } return 0; }
다음 그림은 broadcom ns2 SoC 내부에 임베드된 i2c 호스 컨트롤러에서 사용되는 주요 레지스터들이다.
<kernel v4.14>
i2c-core-base.c
static int __init i2c_init(void) { int retval; retval = of_alias_get_highest_id("i2c"); down_write(&__i2c_board_lock); if (retval >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = retval + 1; up_write(&__i2c_board_lock); retval = bus_register(&i2c_bus_type); if (retval) return retval; is_registered = true; #ifdef CONFIG_I2C_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter"); if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; } #endif retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif is_registered = false; bus_unregister(&i2c_bus_type); return retval; } postcore_initcall(i2c_init);
i2c 버스를 사용할 수 있도록 준비한다.
i2c 버스에 디바이스와 드라이버가 등록될 때 동작하는 i2c 코어를 먼저 설명하고 i2c 호스트 컨트롤러의 등록은 잠시 후에 다루기로 한다.
i2c 클라이언트 디바이스 및 i2c 드라이버의 등록방법은 다음과 같다.
다음 API를 통해 등록한다.
drivers/i2c/i2c-core-base.c
struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, }; EXPORT_SYMBOL_GPL(i2c_bus_type)
i2c 버스에 등록되는 디바이스 및 드라이버가 있을 때마다 i2c_device_match() 함수가 호출된다. 이의 결과가 성공(1)인 경우 i2c_device_probe() 함수가 호출된다.
i2c 버스에 디바이스나 드라이버가 등록될 때마다 버스에 등록되어 있는 모든 디바이스와 드라이버들이 서로 매치될 수 있는지 이 함수를 호출한다.
예를 들어 i2c 버스에 다음 디바이스 및 드라이버들이 등록되어 있다고 가정하자.
+ i2c 버스 | +---- 디바이스 리스트 | +---------- A-device | +---------- B-device +---- 드라이버 리스트 +---------- C-driver +---------- D-driver +---------- E-driver
위와 같은 경우 매치 여부를 테스트하기 위해 A디바이스를 C,D,E 드라이버와 비교해야 하고, B 디바이스도 역시 C, D, E 드라이버와 비교해야 한다. 즉 등록된 디바이스들과 드라이버들의 조합으로 디바이스 및 드라이버 인자를 가지고 i2c_device_match() 함수가 호출된다.
다음과 같이 3가지 방식을 사용하여 아래 순서대로 매치 여부를 결정한다.
drivers/i2c/i2c-core-base.c
static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* Attempt an OF style match */ if (i2c_of_match_device(drv->of_match_table, client)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* Finally an I2C match */ if (i2c_match_id(driver->id_table, client)) return 1; return 0; }
디바이스와 드라이버가 매치되는 경우 성공(1)을 반환한다.
i2c 버스에 등록된 디바이스와 드라이버들 중 하나가 매치되는 경우 이 함수가 호출된다.
drivers/i2c/i2c-core-base.c
static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); if (!client->irq && !driver->disable_i2c_core_irq_mapping) { int irq = -ENOENT; if (client->flags & I2C_CLIENT_HOST_NOTIFY) { dev_dbg(dev, "Using Host Notify IRQ\n"); irq = i2c_smbus_host_notify_to_irq(client); } else if (dev->of_node) { irq = of_irq_get_byname(dev->of_node, "irq"); if (irq == -EINVAL || irq == -ENODATA) irq = of_irq_get(dev->of_node, 0); } else if (ACPI_COMPANION(dev)) { irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); } if (irq == -EPROBE_DEFER) return irq; if (irq < 0) irq = 0; client->irq = irq; } /* * An I2C ID table is not mandatory, if and only if, a suitable OF * or ACPI ID table is supplied for the probing device. */ if (!driver->id_table && !i2c_acpi_match_device(dev->driver->acpi_match_table, client) && !i2c_of_match_device(dev->driver->of_match_table, client)) return -ENODEV; if (client->flags & I2C_CLIENT_WAKE) { int wakeirq = -ENOENT; if (dev->of_node) { wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); if (wakeirq == -EPROBE_DEFER) return wakeirq; } device_init_wakeup(&client->dev, true); if (wakeirq > 0 && wakeirq != client->irq) status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); else if (client->irq > 0) status = dev_pm_set_wake_irq(dev, client->irq); else status = 0; if (status) dev_warn(&client->dev, "failed to set up wakeup irq\n"); }
디바이스에 바인딩된 드라이버의 (*probe_new) 후크 함수를 호출한다. 후크 함수가 없는 경우 (*probe) 후크 함수를 호출한다. 이 과정에서 사용할 irq를 클라이언트 디바이스에 지정하고 클럭 설정을 한다. 그리고 디바이스에 절전 기능이 있는 경우 pm 도메인을 어태치한다.
dev_dbg(dev, "probe\n"); status = of_clk_set_defaults(dev->of_node, false); if (status < 0) goto err_clear_wakeup_irq; status = dev_pm_domain_attach(&client->dev, true); if (status == -EPROBE_DEFER) goto err_clear_wakeup_irq; /* * When there are no more users of probe(), * rename probe_new to probe. */ if (driver->probe_new) status = driver->probe_new(client); else if (driver->probe) status = driver->probe(client, i2c_match_id(driver->id_table, client)); else status = -EINVAL; if (status) goto err_detach_pm_domain; return 0; err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); err_clear_wakeup_irq: dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); return status; }
i2c 호스트 컨트롤러가 어떠한 버스에 연결되는지에 따라 모듈의 드라이버 등록부분의 매크 함수가 바뀐다.
참고로 i2c 호스트 컨트롤러는 대부분이 플랫폼 버스에 연결되어 사용된다. 그리고 나머지는 pci 버스에 연결되어 사용된다. usb, isa 및 amba 버스에 연결되어 사용되는 i2c 컨트롤러 드라이버는 몇 개 없다.
이제 버스별로 드라이버를 등록하는 과정을 살펴보자.
i2c 호스트 컨트롤러를 플랫폼 버스에 붙이는 방법이다.
static const struct of_device_id foo_of_match[] = { {.compatible = "company,foo-i2c", }, {}, }; MODULE_DEVICE_TABLE(of, foo_of_match); static struct platform_driver foo_i2c_driver = { .driver = { .name = "foo-i2c", .of_match_table = foo_i2c_of_match, }, .probe = foo_i2c_probe, .remove = foo_i2c_remove, }; module_platform_driver(foo_i2c_driver);
플랫폼 디바이스 정보들은 디바이스 트리를 사용하는 시스템에서는 대부분 디바이스 트리에서 플랫폼 리소스 정보와 드라이버 명이 지정된다. PC 서버와 같이 ACPI를 사용하는 시스템에서는 ACPI 펌웨어로부터 디바이스에 대한 정보를 알아올 수 있다. 다음은 디바이스 트리를 사용하여 플랫폼 디바이스가 등록된 샘플이다.
i2c0: i2c@66080000 { compatible = "company,foo-i2c"; reg = <0x66080000 0x100>; #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI 394 IRQ_TYPE_NONE>; clock-frequency = <100000>; };
i2c 호스트 컨트롤러를 pci 버스에 붙이는 방법이다. pci 버스에서는 pci 벤더 id와 pci 디바이스 id를 사용하여 매치 테이블을 구성한다.
#define PCI_VENDOR_ID_FOO 0x1234 #define PCI_DEVICE_ID_FOO 0x5678 static const struct pci_device_id foo_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_FOO) }, { 0, } }; MODULE_DEVICE_TABLE (pci, hydra_ids); static struct pci_driver foo_driver = { .name = "foo", .id_table = foo_ids, .probe = foo_probe, .remove = foo_remove, }; module_pci_driver(hydra_driver);
i2c 호스트 컨트롤러를 usb 버스에 붙이는 방법이다. usb 버스에서는 벤더 id와 제품 id를 사용하여 매치 테이블을 구성한다.
static const struct usb_device_id foo_table[] = { { USB_DEVICE(0x1234, 0x5678) }, { } }; static struct usb_driver foo_driver = { .name = "foo-i2c", .probe = foo_probe, .disconnect = foo_disconnect, .id_table = foo_table, }; module_usb_driver(foo_driver);
probe 함수가 호출되면 먼저 디바이스 트리를 통해 플랫폼 디바이스에 등록되어 있던 플랫폼 리소스 정보를 알아와서 아래 구조체에 등록하고 HW 설정등을 수행한다.
struct foo_i2c_dev { struct device *device; struct clk *clk; /* ic2 클럭이 필요한 경우에만 */ u32 bus_clk_rate; void __iomem *base; /* 매핑하여 사용할 i2c 레지스터 */ int irq; /* 인터럽트가 사용되는 경우에만 */ struct i2c_adapter adapter; /* 등록할 i2c adapter */ unsigned int bus_speed; /* 디바이스 트리에서 읽어온 버스 속도 */ ... };
플랫폼 드라이버에서의 probe 함수는 다음과 같은 형식으로 작성된다.
static int foo_probe(struct platform_device *pdev) { int irq, ret = 0; struct foo_i2c_dev *foo_i2c; struct i2c_adapter *adap; struct resource *res; foo_i2c = devm_kzalloc(&pdev->dev, sizeof(*foo_i2c), GFP_KERNEL); if (!foo_i2c) return -ENOMEM; platform_set_drvdata(pdev, foo_i2c); foo_i2c->device = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); foo_i2c->base = devm_ioremap_resource(foo_i2c->device, res); if (IS_ERR(foo_i2c->base)) return PTR_ERR(foo_i2c->base); ret = foo_i2c_init(foo_i2c); /* HW 초기화 설정이 필요한 경우 */ if (ret) return ret; ret = foo_i2c_cfg_speed(foo_i2c); /* 버스 속도 HW 설정 */ if (ret) return ret; irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(foo_i2c->device, "no irq resource\n"); return irq; } foo_i2c->irq = irq; ret = devm_request_irq(foo_i2c->device, irq, foo_i2c_isr, 0, pdev->name, foo_i2c); if (ret < 0) { dev_err(foo_i2c->device, "unable to request irq %i\n", irq); return ret; } foo_i2c_enable(foo_i2c); /* i2c enable HW 설정 */ adap = &foo_i2c->adapter; i2c_set_adapdata(adap, foo_i2c); strlcpy(adap->name, "foo i2c adapter", sizeof(adap->name)); adap->algo = &foo_algo; adap->quirks = &foo_quirks; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; return i2c_add_adapter(adap); }
i2c adapter에서 i2c 전송에 대한 구현은 i2c chip마다 모두 다르다. 가장 많이 사용하는 방법으로 i2c_algorithm 구조체에 i2c 및 smbus 프로토콜 전송 함수를 구현하여 등록하고 이를 adapter->algo에서 지정하면 된다.
include/linux/i2c.h
/** * struct i2c_algorithm - represent I2C transfer method * @master_xfer: Issue a set of i2c transactions to the given I2C adapter * defined by the msgs array, with num messages available to transfer via * the adapter specified by adap. * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this * is not present, then the bus layer will try and convert the SMBus calls * into I2C transfers instead. * @functionality: Return the flags that this algorithm/adapter pair supports * from the I2C_FUNC_* flags. * @reg_slave: Register given client to I2C slave mode of this adapter * @unreg_slave: Unregister given client from I2C slave mode of this adapter * * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. * * The return codes from the @master_xfer field should indicate the type of * error code that occurred during the transfer, as documented in the kernel * Documentation file Documentation/i2c/fault-codes. */
struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); int (*unreg_slave)(struct i2c_client *client); #endif };
또 다른 방법으로는 다음과 같이 커널에 미리 준비된 3 가지 방법 알고리즘을 사용하는 방법도 있다.
아래에 bit 알고리즘 방식으로 사용하는 구조체만 설명하기로 한다. gpio를 사용하여 i2c 호스트 컨트롤러를 구현해보고자 하는 경우 반드시 이해를 해야 한다.
include/linux/i2c-algo-bit.h
/* --- Defines for bit-adapters --------------------------------------- */ /* * This struct contains the hw-dependent functions of bit-style adapters to * manipulate the line states, and to init any hw-specific features. This is * only used if you have more than one hw-type of adapter running. */ struct i2c_algo_bit_data { void *data; /* private data for lowlevel routines */ void (*setsda) (void *data, int state); void (*setscl) (void *data, int state); int (*getsda) (void *data); int (*getscl) (void *data); int (*pre_xfer) (struct i2c_adapter *); void (*post_xfer) (struct i2c_adapter *); /* local settings */ int udelay; /* half clock cycle time in us, minimum 2 us for fast-mode I2C, minimum 5 us for standard-mode I2C and SMBus, maximum 50 us for SMBus */ int timeout; /* in jiffies */ };
위의 구조체를 구현하여 동작시키는 gpio 드라이버를 작성하는 방법을 알아본다. (전체 소스를 만드는 것이 아니라 주요 부분만 설명하였다.)
struct i2c_gpio_platform_data { unsigned int sda_pin; <- i2c sda 용도의 gpio 핀 번호 unsigned int scl_pin; <- i2c scl 용도의 gpio 핀 번호 int udelay; int timeout; unsigned int sda_is_open_drain:1; unsigned int scl_is_open_drain:1; unsigned int scl_is_output_only:1; };
위의 구조체에 들어갈 값들은 i2c 디바이스가 디바이스 트리를 통해 플랫폼 디바이스로 등록된 경우 아래와 같이 플랫폼 데이터로 전달받을 수 있다.
아래와 같이 gpio의 23번을 i2c sda 라인으로 사용하고, gpio 24번을 i2c scl 라인으로 사용하게 한다. 둘 다 gpio핀 상태를 open-drain으로 설정한다.
i2c@0 { compatible = "i2c-gpio"; gpios = <&pioA 23 0 /* sda */ &pioA 24 0 /* scl */> i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; i2c-gpio,delay-us = <2>; /* ~100 kHz */ #address-cells = <1>; #size-cells = <0>; rv3029c2@56 { compatible = "rv3029c2"; reg = <0x56>; }; };
만일 그렇지 않은 경우 직접 다음과 같은 방법으로 디바이스 트리 노드를 파싱하여 알아올 수 있다.
그 후 bit 알고리즘 처리를 위한 후크 함수와 i2c_algo_bit_data 구조체를 채워 구현한다.
static void i2c_gpio_setsda_val(void *data, int state) { struct i2c_gpio_platform_data *pdata = data; gpio_set_value(pdata->sda_pin, state); } static void i2c_gpio_setscl_val(void *data, int state) { struct i2c_gpio_platform_data *pdata = data; gpio_set_value(pdata->scl_pin, state); } static int i2c_gpio_getsda(void *data) { struct i2c_gpio_platform_data *pdata = data; return gpio_get_value(pdata->sda_pin); } static int i2c_gpio_getscl(void *data) { struct i2c_gpio_platform_data *pdata = data; return gpio_get_value(pdata->scl_pin); } static struct i2c_algo_bit_data { .data = &pdata .setsda = i2c_gpio_setsda_val, .setscl = i2c_gpio_setscl_val, .getsda = i2c_gpio_getsda, .getscl = i2c_gpio_getscl, .udelay = 5, /* 디바이스 트리에서 읽은 값(us) */ .timeout = 100, /* 디바이스 트리에서 읽은 값(ms) */ } foo_bit;
위의 정보들을 모아 i2c_adapter 구조체를 만든 후 다음 api를 통해 adapter를 등록하는 것으로 호스트 컨트롤러 드라이버가 완성되다.
i2c 호스트 컨트롤러 드라이버가 상위 버스(플랫폼 버스나 pci 버스 등)에 등록된 후 그 아래에 위치한 i2c 버스를 control하는 칩과 연동하여 사용하기 위해 i2c adapter 디바이스를 구현하여 사용한다.
다음 그림은 pci 버스 아래에 i2c bus가 연결되어 있는 모습이다. i2c adapter는 상위 버스와 i2c 버스 중간에 위치한다.
include/linux/i2c.h
/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; struct rt_mutex mux_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; struct irq_domain *host_notify_domain; };
다음 그림은 adapter 디바이스를 등록하고 해당 i2c 버스에서 검출되는 i2c 디바이스들을 등록하는 과정을 보여준다.
drivers/i2c/i2c-core-base.c
/** * i2c_add_adapter - declare i2c adapter, use dynamic bus number * @adapter: the adapter to add * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * doesn't matter or when its bus number is specified by an dt alias. * Examples of bases when the bus number doesn't matter: I2C adapters * dynamically added by USB links or PCI plugin cards. * * When this returns zero, a new bus number was allocated and stored * in adap->nr, and the specified adapter became available for clients. * Otherwise, a negative errno value is returned. */
int i2c_add_adapter(struct i2c_adapter *adapter) { struct device *dev = &adapter->dev; int id; if (dev->of_node) { id = of_alias_get_id(dev->of_node, "i2c"); if (id >= 0) { adapter->nr = id; return __i2c_add_numbered_adapter(adapter); } } mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id; adapter->nr = id; return i2c_register_adapter(adapter); } EXPORT_SYMBOL(i2c_add_adapter);
i2c adapter 디바이스를 등록하고 해당 i2c 버스의 일부 디바이스들을 HW 디텍트 시도하여 등록한다. adapter id는 디바이스 트리에서 지정할 수도 있다. 결정된 i2c adapter id는 adapter->nr에 저장된다.
drivers/i2c/i2c-core-base.c
/** * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1 * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * See i2c_add_numbered_adapter() for details. */ static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id == -ENOSPC ? -EBUSY : id; return i2c_register_adapter(adap); }
지정한 i2c adapter id 번호로 id를 발급받아 i2c adapter 디바이스를 등록한다. 그런 후 해당 i2c 버스에서 검출되는 i2c 디바이스들을 등록한다.
drivers/i2c/i2c-core-base.c
static int i2c_register_adapter(struct i2c_adapter *adap) { int res = -EINVAL; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) { res = -EAGAIN; goto out_list; } /* Sanity checks */ if (WARN(!adap->name[0], "i2c adapter has no name")) goto out_list; if (!adap->algo) { pr_err("adapter '%s': no algo supplied!\n", adap->name); goto out_list; } if (!adap->lock_ops) adap->lock_ops = &i2c_adapter_lock_ops; rt_mutex_init(&adap->bus_lock); rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; /* register soft irqs for Host Notify */ res = i2c_setup_host_notify_irq_domain(adap); if (res) { pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", adap->name, res); goto out_list; } dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); goto out_list; } dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
i2c adapter 디바이스를 등록한다. 이 후 지정된 주소에 대해 HW 디텍트를 시도하여 자동 등록한다.
pm_runtime_no_callbacks(&adap->dev); pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); #ifdef CONFIG_I2C_COMPAT res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent); if (res) dev_warn(&adap->dev, "Failed to create compatibility class link\n"); #endif i2c_init_recovery(adap); /* create pre-declared device nodes */ of_i2c_register_devices(adap); i2c_acpi_register_devices(adap); i2c_acpi_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); /* Notify drivers */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); return 0; out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res; }
drivers/i2c/i2c-core-base.c
static int __process_new_adapter(struct device_driver *d, void *data) { return i2c_do_add_adapter(to_i2c_driver(d), data); }
아래 함수로 연결된다.
drivers/i2c/i2c-core-base.c
static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { /* Detect supported devices on that bus, and instantiate them */ i2c_detect(adap, driver); /* Let legacy drivers scan this bus for matching devices */ if (driver->attach_adapter) { dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n", driver->driver.name); dev_warn(&adap->dev, "Please use another way to instantiate your i2c_client\n"); /* We ignore the return code; if it fails, too bad */ driver->attach_adapter(adap); } return 0; }
adapter에 해당하는 i2c 버스에서 detect 지원되는 디바이스들을 HW detect 후 발견되면 등록한다. 그리고 legacy 드라이버가 디바이스를 디텍트할 수 있도록 (*attach_adapter) 후크가 있는 경우 호출한다.
drivers/i2c/i2c-core-base.c
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) { const unsigned short *address_list; struct i2c_client *temp_client; int i, err = 0; int adap_id = i2c_adapter_id(adapter); address_list = driver->address_list; if (!driver->detect || !address_list) return 0; /* Warn that the adapter lost class based instantiation */ if (adapter->class == I2C_CLASS_DEPRECATED) { dev_dbg(&adapter->dev, "This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. " "If you need it, check 'Documentation/i2c/instantiating-devices' for alternatives.\n", driver->driver.name); return 0; } /* Stop here if the classes do not match */ if (!(adapter->class & driver->class)) return 0; /* Set up a temporary client to help detect callback */ temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (!temp_client) return -ENOMEM; temp_client->adapter = adapter; for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { dev_dbg(&adapter->dev, "found normal entry for adapter %d, addr 0x%02x\n", adap_id, address_list[i]); temp_client->addr = address_list[i]; err = i2c_detect_address(temp_client, driver); if (unlikely(err)) break; } kfree(temp_client); return err; }
adapter에 해당하는 i2c 버스에서 detect 지원되는 디바이스들을 HW detect 후 발견되면 등록한다.
drivers/i2c/i2c-core-base.c
static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver) { struct i2c_board_info info; struct i2c_adapter *adapter = temp_client->adapter; int addr = temp_client->addr; int err; /* Make sure the address is valid */ err = i2c_check_7bit_addr_validity_strict(addr); if (err) { dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", addr); return err; } /* Skip if already in use (7 bit, no need to encode flags) */ if (i2c_check_addr_busy(adapter, addr)) return 0; /* Make sure there is something at this address */ if (!i2c_default_probe(adapter, addr)) return 0; /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); info.addr = addr; err = driver->detect(temp_client, &info); if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ return err == -ENODEV ? 0 : err; } /* Consistency check */ if (info.type[0] == '\0') { dev_err(&adapter->dev, "%s detection function provided no name for 0x%x\n", driver->driver.name, addr); } else { struct i2c_client *client; /* Detection succeeded, instantiate the device */ if (adapter->class & I2C_CLASS_DEPRECATED) dev_warn(&adapter->dev, "This adapter will soon drop class based instantiation of devices. " "Please make sure client 0x%02x gets instantiated by other means. " "Check 'Documentation/i2c/instantiating-devices' for details.\n", info.addr); dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", info.type, info.addr); client = i2c_new_device(adapter, &info); if (client) list_add_tail(&client->detected, &driver->clients); else dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", info.type, info.addr); } return 0; }
주소로 HW 디텍트를 시도하여 검출되는 디바이스를 등록한다.
drivers/i2c/i2c-core-base.c
/* And this is a strict address validity check, used when probing. If a * device uses a reserved address, then it shouldn't be probed. 7-bit * addressing is assumed, 10-bit address devices are rare and should be * explicitly enumerated. */ int i2c_check_7bit_addr_validity_strict(unsigned short addr) { /* * Reserved addresses per I2C specification: * 0x00 General call address / START byte * 0x01 CBUS address * 0x02 Reserved for different bus format * 0x03 Reserved for future purposes * 0x04-0x07 Hs-mode master code * 0x78-0x7b 10-bit slave addressing * 0x7c-0x7f Reserved for future purposes */ if (addr < 0x08 || addr > 0x77) return -EINVAL; return 0; }
요청한 타겟 주소가 0x08~0x77 범위이내인 경우 성공(0)을 반환한다.
drivers/i2c/i2c-core-base.c
/* ---------------------------------------------------- * the i2c address scanning function * Will not work for 10-bit addresses! * ---------------------------------------------------- */ /* * Legacy default probe function, mostly relevant for SMBus. The default * probe method is a quick write, but it is known to corrupt the 24RF08 * EEPROMs due to a state machine bug, and could also irreversibly * write-protect some EEPROMs, so for address ranges 0x30-0x37 and 0x50-0x5f, * we use a short byte read instead. Also, some bus drivers don't implement * quick write, so we fallback to a byte read in that case too. * On x86, there is another special case for FSC hardware monitoring chips, * which want regular byte reads (address 0x73.) Fortunately, these are the * only known chips using this I2C address on PC hardware. * Returns 1 if probe succeeded, 0 if not. */
static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) { int err; union i2c_smbus_data dummy; #ifdef CONFIG_X86 if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE_DATA, &dummy); else #endif if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_QUICK, NULL); else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy); else { dev_warn(&adap->dev, "No suitable probing method supported for address 0x%02X\n", addr); err = -EOPNOTSUPP; } return err >= 0; }
레거시 default probe 펑션으로 7비트 주소를 사용하며 대부분 SMBus에서 사용된다. 제한된 범위의 주소를 사용하는 디바이스들을 대상으로 SMBus에 읽거나 쓰는 것으로 HW detect를 하기 위해 probe 준비를 한다. 성공의 경우 1을 반환한다.
drivers/i2c/i2c-core-base.c
static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result = 0; if (parent) result = i2c_check_mux_parents(parent, addr); if (!result) result = device_for_each_child(&adapter->dev, &addr, i2c_check_mux_children); return result; }
주소 busy 체크를 수행한다. i2c mux가 사용될 수 있으므로 윗 방향과 아랫 방향을 계속 조사한다. 성공의 경우 0을 반환한다.
drivers/i2c/i2c-core-base.c
/* walk up mux tree */ static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result; result = device_for_each_child(&adapter->dev, &addr, __i2c_check_addr_busy); if (!result && parent) result = i2c_check_mux_parents(parent, addr); return result; }
mux 트리의 윗 방향으로 이동하면서 주소 busy 체크를 수행한다. 성공의 경우 0을 반환한다.
drivers/i2c/i2c-core-base.c
/* recurse down mux tree */ static int i2c_check_mux_children(struct device *dev, void *addrp) { int result; if (dev->type == &i2c_adapter_type) result = device_for_each_child(dev, addrp, i2c_check_mux_children); else result = __i2c_check_addr_busy(dev, addrp); return result; }
디바이스가 adapter인 경우 자식 디바이스들을 대상으로 busy 체크하기 위해 재귀 호출읋하고, 클라이언트 디바이스인 경우 busy 체크를 수행한다. 성공의 경우 0을 반환한다.
drivers/i2c/i2c-core-base.c
static int __i2c_check_addr_busy(struct device *dev, void *addrp) { struct i2c_client *client = i2c_verify_client(dev); int addr = *(int *)addrp; if (client && i2c_encode_flags_to_addr(client) == addr) return -EBUSY; return 0; }
클라이언트 디바이스의 주소가이미 엔코딩되어 있는 경우 -EBUSY를 반환한다. 그 외 성공(0)을 반환한다.
drivers/i2c/i2c-core-base.c
/* Return a unique address which takes the flags of the client into account */ static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client) { unsigned short addr = client->addr; /* For some client flags, add an arbitrary offset to avoid collisions */ if (client->flags & I2C_CLIENT_TEN) addr |= I2C_ADDR_OFFSET_TEN_BIT; if (client->flags & I2C_CLIENT_SLAVE) addr |= I2C_ADDR_OFFSET_SLAVE; return addr; }
클라이언트 디바이스에 사용하는 주소를 엔코딩한다.
include/linux/i2c.h
/** * struct i2c_client - represent an I2C slave device * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address; * I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking * @addr: Address used on the I2C bus connected to the parent adapter. * @name: Indicates the type of the device, usually a chip name that's * generic enough to hide second-sourcing and compatible revisions. * @adapter: manages the bus segment hosting this I2C device * @dev: Driver model device node for the slave. * @irq: indicates the IRQ generated by this device (if any) * @detected: member of an i2c_driver.clients list or i2c-core's * userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter * calls it to pass on slave events to the slave driver. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver * managing the device. */
struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif };
다음 그림은 i2c 클라이언트 디바이스 및 드라이버가 등록되어 매치되는 경우 드라이버의 probe 함수가 호출되는 과정을 보여준다.
drivers/i2c/i2c-core-base.c
/** * i2c_new_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * Create an i2c device. Binding is handled through driver model * probe()/remove() methods. A driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe hotplugging will * load the driver module). This call is not appropriate for use by mainboard * initialization logic, which usually runs during an arch_initcall() long * before any i2c_adapter could exist. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return NULL; client->adapter = adap; client->dev.platform_data = info->platform_data; if (info->archdata) client->dev.archdata = *info->archdata; client->flags = info->flags; client->addr = info->addr; client->irq = info->irq; if (!client->irq) client->irq = i2c_dev_irq_from_resources(info->resources, info->num_resources); strlcpy(client->name, info->type, sizeof(client->name)); status = i2c_check_addr_validity(client->addr, client->flags); if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; }
인자로 전달받은 adapter와 i2c 보드 정보로 i2c 디바이스를 생성한 후 등록한다. 매치되는 드라이버가 있는 경우 드라이버의 probe 후크를 호출한다.
/* Check for address business */ status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); if (status) goto out_err; client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client); if (info->properties) { status = device_add_properties(&client->dev, info->properties); if (status) { dev_err(&adap->dev, "Failed to add properties to client %s: %d\n", client->name, status); goto out_err; } } status = device_register(&client->dev); if (status) goto out_free_props; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; out_free_props: if (info->properties) device_remove_properties(&client->dev); out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n", client->name, client->addr, status); out_err_silent: kfree(client); return NULL; } EXPORT_SYMBOL_GPL(i2c_new_device);
include/linux/i2c.h
/** * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @attach_adapter: Callback for bus addition (deprecated) * @probe: Callback for device binding - soon to be deprecated * @probe_new: New callback for device binding * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @alert: Alert callback, for example for the SMBus alert protocol * @command: Callback for bus-wide signaling (optional) * @driver: Device driver model driver * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) * @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. * * For automatic device detection, both @detect and @address_list must * be defined. @class should also be set, otherwise only devices forced * with module parameters will be created. The detect function must * fill at least the name field of the i2c_board_info structure it is * handed upon successful detection, and possibly also the flags field. * * If @detect is missing, the driver will still work fine for enumerated * devices. Detected devices simply won't be supported. This is expected * for the many I2C/SMBus devices which can't be detected reliably, and * the ones which can always be enumerated in practice. * * The i2c_client structure which is handed to the @detect callback is * not a real i2c_client. It is initialized just enough so that you can * call i2c_smbus_read_byte_data and friends on it. Don't do anything * else with it. In particular, calling dev_dbg and friends on it is * not allowed. */
struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared. You should avoid * using this, it will be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *) __deprecated; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* New driver model interface to aid the seamless removal of the * current probe()'s, more commonly unused than used second parameter. */ int (*probe_new)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). * For the SMBus Host Notify protocol, the data corresponds to the * 16-bit payload data reported by the slave device acting as master. */ void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; bool disable_i2c_core_irq_mapping; };
i2c_client 구조체에 임베드되어 있는 device 구조체의 driver_data 필드를 사용하여 extra 클라이언트 데이터를 사용할 수 있도록 다음 API들이 사용된다.
간단히 1 바이트 데이터 송신 및 수신하는 방법은 다음과 같다.
int foo_read_value(struct i2c_client *client, u8 reg) { if (reg < 0x10) /* byte-sized register */ return i2c_smbus_read_byte_data(client, reg); else /* word-sized register */ return i2c_smbus_read_word_data(client, reg); } int foo_write_value(struct i2c_client *client, u8 reg, u16 value) { if (reg == 0x10) /* Impossible to write - driver error! */ return -EINVAL; else if (reg < 0x10) /* byte-sized register */ return i2c_smbus_write_byte_data(client, reg, value); else /* word-sized register */ return i2c_smbus_write_word_data(client, reg, value); }
drivers/i2c/i2c-core-base.c
/* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. */ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) return -EAGAIN; /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; INIT_LIST_HEAD(&driver->clients); /* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. */ res = driver_register(&driver->driver); if (res) return res; pr_debug("driver [%s] registered\n", driver->driver.name); /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); return 0; } EXPORT_SYMBOL(i2c_register_driver);
i2c 드라이버를 등록한다. 매치되는 디바이스가 있는 경우 드라이버의 probe 후크를 호출한다.
모든 i2c adapter가 i2c 전송 또는 smbus를 지원하는 것은 아니다. 따라서 adapter가 지원하는 기능을 비트별로 노출하여 i2c 클라이언트가 이를 확인하여 동작할 수 있게 지원한다.
i2c_adapter 구조체의 functionality 필드의 각 비트는 다음과 같다.
다음 그림은 functionality에 사용하는 플래그들과 복합 플래그들을 보여준다.
smbus만 지원하는 adapter의 경우 일반적으로 다음과 같은 기능을 지원한다. 예) i2c-piix4 드라이버
full-I2C 기능을 갖는 adapter의 경우 일반적으로 다음과 같은 기능을 지원한다.
클라이언트가 adapter에 attach를 시도할 때 adapter에서 이 디바이스가 사용하는 기능이 있는지 먼저 체크해야 한다.
static int lm75_detect(...) { (...) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) goto exit; (...) }
위의 코드 체크에서 통과하는 경우 이제 i2c_smbus_read_byte_data() 함수 및 i2c_smbus_write_byte_data() 함수를 사용할 수 있다는 것을 의미한다.
유저 스페이스에서 i2c adapter를 control할 수 있도록 다음 파일명으로 i2c 캐릭터 디바이스가 노출된다.
i2c 버스에 i2c adapter 디바이스가 등록/해제될 때 다음 함수를 통해 i2c-dev 클래스 디바이스가 생성/해제되며 이 때 i2c 캐릭터 디바이스도 생성/삭제된다.
참고로 i2c adapter에 대한 클래스 디바이스는 다음과 같이 adater 디바이스 바로 뒤에 생성된다.
생성된 캐릭터 디바이스 파일에서 ioctl() 함수를 통해 다음과 같은 기능이 지원된다.
include/uapi/linux/i2c-dev.h
/* This is the structure as used in the I2C_RDWR ioctl call */ struct i2c_rdwr_ioctl_data { struct i2c_msg __user *msgs; /* pointers to i2c_msgs */ __u32 nmsgs; /* number of i2c_msgs */ };
include/uapi/linux/i2c.h
/** * struct i2c_msg - an I2C transaction segment beginning with START * @addr: Slave address, either seven or ten bits. When this is a ten * bit address, I2C_M_TEN must be set in @flags and the adapter * must support I2C_FUNC_10BIT_ADDR. * @flags: I2C_M_RD is handled by all adapters. No other flags may be * provided unless the adapter exported the relevant I2C_FUNC_* * flags through i2c_check_functionality(). * @len: Number of data bytes in @buf being read from or written to the * I2C slave address. For read transactions where I2C_M_RECV_LEN * is set, the caller guarantees that this buffer can hold up to * 32 bytes in addition to the initial length byte sent by the * slave (plus, if used, the SMBus PEC); and this value will be * incremented by the number of block data bytes received. * @buf: The buffer into which data is read, or from which it's written. * * An i2c_msg is the low level representation of one segment of an I2C * transaction. It is visible to drivers in the @i2c_transfer() procedure, * to userspace from i2c-dev, and to I2C adapter drivers through the * @i2c_adapter.@master_xfer() method. * * Except when I2C "protocol mangling" is used, all I2C adapters implement * the standard rules for I2C transactions. Each transaction begins with a * START. That is followed by the slave address, and a bit encoding read * versus write. Then follow all the data bytes, possibly including a byte * with SMBus PEC. The transfer terminates with a NAK, or when all those * bytes have been transferred and ACKed. If this is the last message in a * group, it is followed by a STOP. Otherwise it is followed by the next * @i2c_msg transaction segment, beginning with a (repeated) START. * * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then * passing certain @flags may have changed those standard protocol behaviors. * Those flags are only for use with broken/nonconforming slaves, and with * adapters which are known to support the specific mangling options they * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). */
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_RD 0x0001 /* read data, from slave to master */ /* I2C_M_RD is guaranteed to be 0x0001! */ #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ };
다음 그림은 유저 스페이스에서 i2c 프로토콜로 전송하기 위해 데이터를 준비한 모습을 보여준다.
i2c_msg 구조체에서 사용하는 플래그 중 표준 전송과 다른 방법을 사용하기 위해 사용하는 플래그들은 다음과 같다. 특정 디바이스와의 통신에 문제가 있어 이를 해결하기 위한 워크 어라운드 형태로 사용된다.
include/uapi/linux/i2c-dev.h
/* This is the structure as used in the I2C_SMBUS ioctl call */ struct i2c_smbus_ioctl_data { __u8 read_write; __u8 command; __u32 size; union i2c_smbus_data __user *data; };
/* * Data for SMBus Messages */ #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ union i2c_smbus_data { __u8 byte; __u16 word; __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ /* and one more for user-space compatibility */ };
다음 그림은 유저 스페이스에서 smbus 프로토콜로 전송하기 위해 데이터를 준비한 모습을 보여준다.
아래 코드와 같이 유저 스페이스에서 캐릭터 디바이스를 통해 접근하는 경우에도 i2c functionality 체크를 수행하고 사용해야 한다.
int file; if (file = open("/dev/i2c-0", O_RDWR) < 0) { /* Some kind of error handling */ exit(1); } if (ioctl(file, I2C_FUNCS, &funcs) < 0) { /* Some kind of error handling */ exit(1); } if (!(funcs & I2C_FUNC_SMBUS_QUICK)) { /* Oops, the needed functionality (SMBus write_quick function) is not available! */ exit(1); }