00 I3C Neden Var?
I3C (Improved Inter Integrated Circuit), MIPI Alliance tarafından geliştirilen ve I2C'nin sınırlarını aşmak için tasarlanan modern bir seri veri yolu protokolüdür. IoT, giyilebilir cihazlar ve mobil sensör sistemleri için optimize edilmiştir.
I2C'nin Sınırları
I2C, 1982'de Philips tarafından geliştirilen köklü bir protokoldür. Onlarca yıllık kullanımla birlikte ciddi kısıtlamalar ortaya çıkmıştır:
I3C'nin Çözümleri
I2C: I3C:
Max 1 MHz vs 12.5 MHz SDR (Single Data Rate)
25 Mbps DDR (Double Data Rate)
HDR-TSL, HDR-TSP modları
Sabit 7-bit vs Dinamik adres atama (DAA)
adres 64-bit benzersiz kimlik (PID)
Ayri INT pini vs IBI — In-Band Interrupt
(SDA hattı üzerinden)
Yalnizca I2C vs I2C geriye donuk uyumluluk
(karma bus destegi)
12.5 MHz SDR ve IBI Avantajı
I3C, SDR (Single Data Rate) modunda 12.5 MHz'e ulaşır; bu I2C hızlı modunun yaklaşık 31 katıdır. Daha önemlisi, IBI (In-Band Interrupt) sayesinde sensörler veri hazır olduğunda SDA hattını kendi başlarına düşürüp controller'ı uyarabilir. Bu özellik, periyodik polling'i ortadan kaldırır ve CPU uyanma döngüleri açısından önemli güç tasarrufu sağlar.
Uygulama Alanları
I3C öncelikli olarak şu alanlarda öne çıkar: IMU (İvme, Jiroskop), basınç ve sıcaklık sensörleri, kamera IMU köprüleri, giyilebilir biyometrik sensörler ve endüstriyel çok sensörlü sistemler. JESD300 (MIPI I3C HCI) standardı, donanım kontrolör register haritasını tanımlar ve Linux sürücüsü bu standarda dayanır.
01 I3C Protokol Temelleri
I3C, I2C sinyallemesini (SCL + SDA) temel alır; ancak üzerine DAA, CCC, HDR ve IBI gibi köklü yenilikler ekler. Protokolün temel mekanizmalarını anlamak, sürücü geliştirme için zorunludur.
DAA — Dinamik Adres Atama
I3C cihazları bus'a bağlandıklarında 7-bit dinamik adres alırlar. DAA işlemi şu adımları izler:
- Controller,
ENTDAACCC komutunu yayınlar. - Tüm adressiz I3C cihazlar yanıt mekanizmasına girer.
- Her cihaz 64-bit Provisional ID (PID) ve BCR/DCR değerlerini gönderir.
- Controller bir adres atar; bu cihaz susar, bir sonrakine geçilir.
- Tüm cihazlar adres alana kadar döngü sürer.
Controller Cihaz A (addr yok) Cihaz B (addr yok)
| | |
|--ENTDAA----------->| |
| |--PID[63:32]------->|
| |--PID[31:0]+BCR+DCR>|
|--ADDR=0x08 atandi->| |
| (Cihaz A susar) | |
| | Cihaz B yanit verir
| | (PID göndеrir)
|--ADDR=0x09 atandi------------------------->|
CCC Komutları
CCC (Common Command Code) komutları, tüm cihazlara (broadcast) veya belirli bir cihaza (unicast) yönlendirilebilen yönetim komutlarıdır.
| CCC Kodu | Ad | Açıklama |
|---|---|---|
| 0x06 | RSTDAA | Dinamik adresleri sıfırla — tüm cihazlar |
| 0x07 | ENTDAA | Dinamik adres atama işlemini başlat |
| 0x08 | DEFSLVS | Slave listesini tanımla |
| 0x09 | SETMWL | Maks. yazma veri uzunluğunu ayarla |
| 0x0A | SETMRL | Maks. okuma veri uzunluğunu ayarla |
| 0x28 | ENEC | Event/IBI'ı etkinleştir |
| 0x29 | DISEC | Event/IBI'ı devre dışı bırak |
| 0x61 | SETDASA | Statik adresten dinamik adres ata |
HDR Modu
HDR (High Data Rate) modları standart SDR'nin ötesinde bant genişliği sunar:
Bus Durumları ve Zamanlaması
I3C, I2C'den farklı olarak push-pull çıkış aşaması kullanır. I2C'de hem master hem slave açık-toplayıcı çıkış kullanırken I3C'de controller SCL ve SDA'yı push-pull sürer; bu sayede daha yüksek frekansa çıkılabilir ve çekme direncine bağımlılık azalır.
02 Linux I3C Subsystem
Linux kernel 5.0 ile birlikte tam I3C subsystem desteği sunulmaya başlandı. Alt sistem, I2C'deki i2c_adapter / i2c_client yapısına benzer biçimde controller ve cihaz soyutlamalarını tanımlar.
Temel Veri Yapıları
#include <linux/i3c/master.h>
#include <linux/i3c/device.h>
/* Controller (bus master) soyutlaması */
struct i3c_master_controller {
struct i3c_bus bus;
struct i3c_master_controller_ops *ops;
struct device dev;
bool secondary; /* İkincil master mı? */
/* ... iç alanlar ... */
};
/* Bus üzerindeki her I3C cihazı */
struct i3c_device {
struct i3c_master_controller *bus;
struct i3c_device_info info;
struct device dev;
};
/* Cihaz bilgisi */
struct i3c_device_info {
uint64_t pid; /* 64-bit Provisional ID */
uint8_t dyn_addr; /* Dinamik adres (DAA'dan) */
uint8_t static_addr; /* Statik adres (0 = yok) */
uint8_t bcr; /* Bus Characteristics Register */
uint8_t dcr; /* Device Characteristics Register */
uint8_t hdr_cap; /* Desteklenen HDR modları */
uint8_t max_read_ds; /* Maks. okuma veri hızı */
uint8_t max_write_ds; /* Maks. yazma veri hızı */
};
i3c_driver: Cihaz Sürücüsü Yapısı
struct i3c_driver {
struct device_driver driver;
const struct i3c_device_id *id_table; /* Eşleştirme tablosu */
int (*probe)(struct i3c_device *i3cdev);
void (*remove)(struct i3c_device *i3cdev);
};
/* Cihaz kimlik tablosu (PID + BCR + DCR) */
static const struct i3c_device_id my_sensor_i3c_ids[] = {
I3C_DEVICE(0x04, 0x123, 0x456), /* manuf_id, part_id, extra */
I3C_DEVICE_EXTRA_INFO(0x04, 0x789, 0x000, NULL),
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i3c, my_sensor_i3c_ids);
Alt Sistemin Katman Yapısı
Kullanici Alani (sysfs, /dev)
|
+------v---------------------------------------+
| I3C Core (drivers/i3c/core.c) |
| i3c_master_register / i3c_driver_register |
| DAA yonetimi / CCC gonderme |
+------+-------------------+--------------------+
| |
+------v------+ +-------v-------+
| Controller | | Cihaz |
| Surucusu | | Surucusu |
| (i3c_master | | (i3c_driver) |
| _controller| | |
| _ops) | | |
+------+------+ +---------------+
|
+------v------+
| Donanim |
| (SoC I3C |
| kontrolor) |
+-------------+
Transfer API'si
I3C transferleri, SDR ve CCC komutları için iki farklı yapıyla gerçekleştirilir:
/* SDR veri transferi */
struct i3c_priv_xfer {
bool rnw; /* true = okuma, false = yazma */
u16 len; /* Bayt sayisi */
union {
const void *out; /* Yazma tamponu */
void *in; /* Okuma tamponu */
} data;
};
int i3c_device_do_priv_xfers(struct i3c_device *dev,
struct i3c_priv_xfer *xfers,
int nxfers);
/* CCC komutu gönderme */
struct i3c_ccc_cmd; /* Tek/yayın hedefli */
int i3c_master_send_ccc_cmd_locked(
struct i3c_master_controller *master,
struct i3c_ccc_cmd *cmd);
03 Controller Sürücüsü Yazımı
I3C controller sürücüsü, SoC'taki donanım I3C kontrolörünü Linux I3C alt sistemine bağlar. i3c_master_controller_ops yapısı uygulanarak tüm gerekli callback'ler sağlanır.
i3c_master_controller_ops Yapısı
#include <linux/i3c/master.h>
struct i3c_master_controller_ops {
/* Bus'ı başlat, DAA gerçekleştir */
int (*bus_init)(struct i3c_master_controller *master);
/* Bus'ı kapat */
void (*bus_cleanup)(struct i3c_master_controller *master);
/* I3C cihaz eklendiğinde çağrılır */
int (*attach_i3c_dev)(struct i3c_dev_desc *dev);
/* I3C cihaz adres güncelleme */
int (*reattach_i3c_dev)(struct i3c_dev_desc *dev,
u8 old_dyn_addr);
/* I3C cihaz ayrıldığında */
void (*detach_i3c_dev)(struct i3c_dev_desc *dev);
/* I2C cihaz eklendiğinde (uyumluluk için) */
int (*attach_i2c_dev)(struct i2c_dev_desc *dev);
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
/* CCC komutu gönder */
int (*send_ccc_cmd)(struct i3c_master_controller *master,
struct i3c_ccc_cmd *cmd);
/* Özel SDR transferi */
int (*priv_xfers)(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *xfers,
int nxfers);
/* I2C transferi */
int (*i2c_xfers)(struct i2c_dev_desc *dev,
const struct i2c_msg *xfers,
int nxfers);
/* IBI desteği */
int (*request_ibi)(struct i3c_dev_desc *dev,
const struct i3c_ibi_setup *req);
void (*free_ibi)(struct i3c_dev_desc *dev);
int (*enable_ibi)(struct i3c_dev_desc *dev);
int (*disable_ibi)(struct i3c_dev_desc *dev);
};
bus_init Callback
bus_init, kontrolörü başlatır ve DAA işlemini tetikler. I3C alt sistemi bus_init'i platform_driver probe()'ından sonra çağırır.
static int my_i3c_bus_init(struct i3c_master_controller *master)
{
struct my_i3c_ctrl *ctrl = to_my_ctrl(master);
int ret;
/* Donanim kontrolorunu etkinlestir */
ret = clk_prepare_enable(ctrl->clk);
if (ret)
return ret;
/* SCL frekansini ayarla */
ret = my_i3c_set_speed(ctrl, I3C_BUS_SDR0_SCL_RATE);
if (ret)
goto err_clk;
/* Bus'i open-drain modda baslat (I2C geriye uyumluluk) */
my_i3c_set_mode(ctrl, MODE_OPEN_DRAIN);
/* I2C cihazlari kesfet */
ret = i3c_master_bus_init(master);
if (ret)
goto err_clk;
/* Push-pull moda gec, DAA baslt */
my_i3c_set_mode(ctrl, MODE_PUSH_PULL);
ret = i3c_master_do_daa(master);
if (ret)
dev_warn(&master->dev, "DAA hatasi: %d\n", ret);
return 0;
err_clk:
clk_disable_unprepare(ctrl->clk);
return ret;
}
attach_i3c_dev Callback
DAA sırasında yeni bir I3C cihazı keşfedildiğinde çağrılır. Sürücü burada cihaza özgü donanım kaynaklarını ayırabilir.
static int my_i3c_attach_dev(struct i3c_dev_desc *dev)
{
struct my_i3c_ctrl *ctrl =
to_my_ctrl(dev->common.master);
struct my_i3c_dev_data *devdata;
/* Cihaz basina ozel kaynak ayir */
devdata = kzalloc(sizeof(*devdata), GFP_KERNEL);
if (!devdata)
return -ENOMEM;
devdata->dyn_addr = dev->info.dyn_addr;
devdata->ibi_pool = NULL;
i3c_dev_set_master_data(dev, devdata);
return 0;
}
Platform Sürücüsü Kaydı
static const struct i3c_master_controller_ops my_i3c_ops = {
.bus_init = my_i3c_bus_init,
.bus_cleanup = my_i3c_bus_cleanup,
.attach_i3c_dev = my_i3c_attach_dev,
.detach_i3c_dev = my_i3c_detach_dev,
.send_ccc_cmd = my_i3c_send_ccc,
.priv_xfers = my_i3c_priv_xfers,
.i2c_xfers = my_i3c_i2c_xfers,
.request_ibi = my_i3c_request_ibi,
.enable_ibi = my_i3c_enable_ibi,
.disable_ibi = my_i3c_disable_ibi,
.free_ibi = my_i3c_free_ibi,
};
static int my_i3c_probe(struct platform_device *pdev)
{
struct my_i3c_ctrl *ctrl;
int ret;
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
ctrl->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ctrl->regs))
return PTR_ERR(ctrl->regs);
ctrl->clk = devm_clk_get(&pdev->dev, "i3c-clk");
if (IS_ERR(ctrl->clk))
return PTR_ERR(ctrl->clk);
platform_set_drvdata(pdev, ctrl);
return i3c_master_register(&ctrl->master,
&pdev->dev,
&my_i3c_ops,
false); /* secondary=false */
}
static const struct of_device_id my_i3c_of_match[] = {
{ .compatible = "vendor,my-i3c-ctrl" },
{ }
};
MODULE_DEVICE_TABLE(of, my_i3c_of_match);
04 I3C Cihaz Sürücüsü
I3C cihaz sürücüsü, I3C subsystem aracılığıyla sensörler, IMU'lar ve diğer I3C cihazlarıyla haberleşir. Yapısı I2C cihaz sürücüsüne benzer ama IBI ve CCC komut desteği eklenmiştir.
Sürücü Kaydı ve probe
#include <linux/i3c/device.h>
#include <linux/module.h>
struct my_sensor_data {
struct i3c_device *i3cdev;
struct iio_dev *indio_dev;
/* ... cihaza özgü alanlar ... */
};
static int my_sensor_probe(struct i3c_device *i3cdev)
{
struct my_sensor_data *data;
const struct i3c_device_info *info;
int ret;
data = devm_kzalloc(&i3cdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->i3cdev = i3cdev;
i3cdev_set_drvdata(i3cdev, data);
/* Cihaz bilgisini al */
i3c_device_get_info(i3cdev, &info);
dev_info(&i3cdev->dev, "PID=0x%llx Adres=0x%02x\n",
info->pid, info->dyn_addr);
/* Sensör ilk yapılandırması */
ret = my_sensor_init(data);
if (ret)
return ret;
return 0;
}
static void my_sensor_remove(struct i3c_device *i3cdev)
{
struct my_sensor_data *data = i3cdev_get_drvdata(i3cdev);
my_sensor_deinit(data);
}
static const struct i3c_device_id my_sensor_ids[] = {
I3C_DEVICE(0x04, 0xABC, 0x001), /* Vendör ID, parça kodu */
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i3c, my_sensor_ids);
static struct i3c_driver my_sensor_driver = {
.driver = {
.name = "my-i3c-sensor",
},
.probe = my_sensor_probe,
.remove = my_sensor_remove,
.id_table = my_sensor_ids,
};
module_i3c_driver(my_sensor_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Ornek I3C sensor surucusu");
SDR Veri Okuma ve Yazma
/* Tek register okuma (SDR modu) */
static int my_sensor_read_reg(struct my_sensor_data *data,
u8 reg, u8 *val)
{
struct i3c_priv_xfer xfers[2];
int ret;
/* Yazma: register adresi gönder */
xfers[0].rnw = false;
xfers[0].len = 1;
xfers[0].data.out = ®
/* Okuma: veriyi al */
xfers[1].rnw = true;
xfers[1].len = 1;
xfers[1].data.in = val;
ret = i3c_device_do_priv_xfers(data->i3cdev, xfers, 2);
return ret;
}
/* Çoklu bayt okuma (burst read) */
static int my_sensor_read_burst(struct my_sensor_data *data,
u8 reg, u8 *buf, size_t len)
{
struct i3c_priv_xfer xfers[2];
xfers[0].rnw = false;
xfers[0].len = 1;
xfers[0].data.out = ®
xfers[1].rnw = true;
xfers[1].len = len;
xfers[1].data.in = buf;
return i3c_device_do_priv_xfers(data->i3cdev, xfers, 2);
}
CCC Komutu Gönderme
#include <linux/i3c/ccc.h>
/* Unicast CCC: belirli bir cihaza MRL (Max Read Length) ayarla */
static int my_sensor_set_max_read_len(struct my_sensor_data *data,
u16 mrl)
{
struct i3c_ccc_mrl mrl_val = {
.len = cpu_to_be16(mrl),
};
struct i3c_ccc_cmd_dest dest = {
.addr = data->i3cdev->info.dyn_addr,
.payload.len = sizeof(mrl_val),
.payload.data = &mrl_val,
};
struct i3c_ccc_cmd cmd = {
.id = I3C_CCC_SETMRL(false), /* unicast */
.dests = &dest,
.ndests = 1,
};
return i3c_device_send_ccc_cmd(data->i3cdev, &cmd);
}
05 IBI: In-Band Interrupt
IBI (In-Band Interrupt), I3C'nin en çarpıcı özelliklerinden biridir. Sensör, SDA hattını master'ın START koşulundan önce düşürerek controller'ı uyarabilir. Bu mekanizma, polling gerektirmeden olay tabanlı veri alımı sağlar.
IBI Çalışma Prensibi
Zaman çizgisi:
SCL ____|-------|-------|-------|_______
SDA ______________________________|____
Sensor SDA'yi dusurur (controller mesgul degil)
Bu IBI arbitrasyon baslatir:
- Sensor kendi dinamik adresini gonderir
- Controller IBI'i kabul eder (ACK) veya reddeder (NACK)
- Kabul edilirse sensor MDB (Mandatory Data Byte) gonderir
- Ardından opsiyonel ilave veri gelir
IBI Kurulumu: Cihaz Sürücüsünde
#include <linux/i3c/device.h>
/* IBI geldiğinde çağrılacak handler */
static void my_sensor_ibi_handler(struct i3c_device *i3cdev,
const struct i3c_ibi_payload *payload)
{
struct my_sensor_data *data = i3cdev_get_drvdata(i3cdev);
dev_dbg(&i3cdev->dev, "IBI alindi, MDB=0x%02x len=%zu\n",
((u8 *)payload->data)[0],
payload->len);
/* Çalışma kuyruğuna veri okuma işi gönder */
schedule_work(&data->ibi_work);
}
/* probe() içinde IBI kurulumu */
static int my_sensor_setup_ibi(struct my_sensor_data *data)
{
struct i3c_ibi_setup ibi_req = {
.max_payload_len = 2, /* MDB + 1 ek bayt */
.num_slots = 4, /* IBI kuyruk derinliği */
.handler = my_sensor_ibi_handler,
};
int ret;
/* IBI kaynağı ayır */
ret = i3c_device_request_ibi(data->i3cdev, &ibi_req);
if (ret) {
dev_err(&data->i3cdev->dev,
"IBI ayirma hatasi: %d\n", ret);
return ret;
}
/* IBI'ı etkinleştir */
ret = i3c_device_enable_ibi(data->i3cdev);
if (ret) {
i3c_device_free_ibi(data->i3cdev);
return ret;
}
dev_info(&data->i3cdev->dev, "IBI etkin\n");
return 0;
}
IBI ve Polling Güç Karşılaştırması
IMU sensörlerinde tipik polling stratejileri CPU'yu periyodik olarak uyandırır. IBI ile CPU yalnızca gerçekten veri hazır olduğunda uyanır:
IBI'ı Devre Dışı Bırakma
/* remove() veya suspend() içinde */
static void my_sensor_teardown_ibi(struct my_sensor_data *data)
{
i3c_device_disable_ibi(data->i3cdev);
i3c_device_free_ibi(data->i3cdev);
}
Controller Tarafında IBI Desteği
Controller sürücüsü, IBI arbitrasyon mantığını donanım kesmesi aracılığıyla yönetir ve yazılım işleyicisini çağırır:
/* Controller IRQ handler — IBI geldi */
static irqreturn_t my_i3c_isr(int irq, void *dev_id)
{
struct my_i3c_ctrl *ctrl = dev_id;
u32 status = readl(ctrl->regs + INT_STATUS);
if (status & INT_IBI_RECEIVED) {
/* IBI adresini ve payload'ı oku */
u8 ibi_addr = readl(ctrl->regs + IBI_ADDR) & 0x7F;
u8 mdb = readl(ctrl->regs + IBI_DATA);
/* I3C core'u bilgilendir */
i3c_master_handle_ibi(ctrl->master_ext, ibi_addr, mdb);
writel(INT_IBI_RECEIVED, ctrl->regs + INT_CLEAR);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
06 I2C Geriye Dönük Uyumluluk
I3C, I2C ile fiziksel seviyede uyumludur: aynı iki kablo (SCL + SDA) kullanılır. Bu sayede aynı bus üzerinde hem I3C hem I2C cihazları barındırılabilir (mixed-bus).
Mixed-Bus Çalışma Prensibi
I3C controller, bus'ı açık-toplayıcı (open-drain) modda başlatarak I2C cihazlarla haberleşir. DAA tamamlandıktan sonra I3C cihazlar push-pull moduna geçer; I2C cihazlar yine açık-toplayıcı olarak kalır. Controller, I2C ve I3C transferlerini uygun bus modunda sırayla gerçekleştirir.
Bus Baslatma Akisi: 1. Open-drain modu aktif 2. I2C cihazlarla haberlesme (statik adresler) 3. SETDASA CCC: I2C statik adresine sahip I3C cihazlara dinamik adres ata 4. ENTDAA: Kalan I3C cihazlara DAA uygula 5. Push-pull moda gec (I3C hizli mod) 6. I2C cihazlar: yine open-drain, daha yavaz
Linux'ta Mixed-Bus Yapılandırması
I3C bus'a bağlı I2C cihazları DT'de I3C master düğümünün altına i2c-device olarak tanımlanır:
/* Device Tree mixed-bus yapılandırması */
i3c_master: i3c-master@e8000000 {
compatible = "vendor,my-i3c-master";
reg = <0xe8000000 0x1000>;
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i3c_clk>;
/* I3C bus parametreleri */
i3c-scl-hz = <12500000>; /* 12.5 MHz SDR */
i2c-scl-hz = <400000>; /* 400 kHz I2C hızlı mod */
#address-cells = <3>;
#size-cells = <0>;
/* I3C cihaz: adres ve PID ile tanımlanır */
imu@4a,5400000000001 {
compatible = "invensense,icm42605";
reg = <0x4a 0x05400000 0x00001>;
/* [dyn_addr, pid_hi, pid_lo] */
};
/* Karma bus: I2C cihaz aynı bus'ta */
pressure@77 {
compatible = "bosch,bmp280";
reg = <0x77>;
};
};
I2C Cihaz Erişimi (i2c_xfers)
Controller sürücüsünün i2c_xfers callback'i, I2C mesajlarını I3C bus üzerinde açık-toplayıcı modda iletir.
static int my_i3c_i2c_xfers(struct i2c_dev_desc *dev,
const struct i2c_msg *xfers,
int nxfers)
{
struct my_i3c_ctrl *ctrl = to_my_ctrl(dev->common.master);
int i, ret;
/* Open-drain moduna gec */
my_i3c_set_mode(ctrl, MODE_OPEN_DRAIN);
for (i = 0; i < nxfers; i++) {
ret = my_i3c_do_i2c_xfer(ctrl,
dev->addr,
&xfers[i]);
if (ret)
break;
}
/* Push-pull moduna geri don */
my_i3c_set_mode(ctrl, MODE_PUSH_PULL);
return ret;
}
Karma Bus Kısıtlamaları
07 Device Tree Yapılandırması
I3C controller ve cihazların DT tanımlamaları, I2C'den farklı birkaç özellik gerektirir. I3C cihaz adresleme #address-cells = 3 ile yapılır ve PID dahil edilir.
Controller DT Düğümü
i3c0: i3c@e8000000 {
compatible = "vendor,my-i3c-controller";
reg = <0x0 0xe8000000 0x0 0x1000>;
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
/* Saat kaynakları */
clocks = <&cru CLK_I3C0>, <&cru PCLK_I3C0>;
clock-names = "i3c-clk", "pclk";
/* I3C ve I2C bus hızları */
i3c-scl-hz = <12500000>; /* SDR0 = 12.5 MHz */
i2c-scl-hz = <400000>; /* I2C hızlı mod */
/* Adres hücresi sayısı: I3C cihazlar için 3 */
#address-cells = <3>;
#size-cells = <0>;
status = "okay";
};
I3C Cihaz DT Tanımı
I3C cihazlar için reg özelliği üç değerden oluşur: dinamik adres (0 = DAA'dan atansın), PID üst 32-bit, PID alt 16-bit.
&i3c0 {
/* IMU: ICM-42605, PID = 0x0000000000000001
Dinamik adres: 0x0A (önerilir, DAA'da atanır)
Atanmış adres DT'de yalnızca öneri niteliğindedir */
imu@a,0,1 {
compatible = "invensense,icm42605";
reg = <0x0a 0x00000000 0x00000001>;
/* 0x0a: onerilen dinamik adres
0x00000000: PID bits [47:16]
0x00000001: PID bits [15:0] */
};
/* Manyetometre: I3C (dinamik adres 0x0B) */
mag@b,40000000,1234 {
compatible = "st,lsm6dso";
reg = <0x0b 0x40000000 0x00001234>;
assigned-address = <0x0b>;
};
/* Opsiyonel: I2C cihaz aynı bus üzerinde */
eeprom@50 {
compatible = "atmel,24c64";
reg = <0x50>;
pagesize = <32>;
};
};
Önemli DT Özellikleri
| Özellik | Tür | Açıklama |
|---|---|---|
| i3c-scl-hz | u32 | I3C SCL frekansı Hz (SDR modu) |
| i2c-scl-hz | u32 | I2C SCL frekansı Hz (mixed-bus) |
| assigned-address | u8 | DAA sırasında bu adres verilmesi tercih edilir |
| #address-cells | u32 | I3C master için 3 olmalıdır |
| jdec-spd | bool | JEDEC SPD protokol desteği etkinleştir |
| mctp-ccc | bool | MCTP over I3C desteği |
assigned-address Davranışı
assigned-address özelliği, DAA sürecinde kontrolörün bu cihaza hangi dinamik adresi atamaya çalışacağını belirtir. Ancak bu bir garanti değildir; adres çakışması varsa farklı bir adres atanabilir. Statik adresli I3C cihazlar için static-addr özelliği kullanılır ve SETDASA CCC ile adres atanır.
08 Hata Ayıklama
I3C alt sistemi, sysfs üzerinden bus ve cihaz bilgilerini sunar. i3cdetect aracı, i2cdetect'e benzer biçimde bus üzerindeki cihazları listeler. Mantık analizörü ile protokol seviyesinde izleme yapmak da mümkündür.
sysfs I3C Bus Bilgisi
# I3C bus listesi
ls /sys/bus/i3c/devices/
# 0-5400000000001 (bus 0, PID = 0x5400000000001)
# 0-4000000001234
# Cihaz bilgisi
cat /sys/bus/i3c/devices/0-5400000000001/modalias
# i3c:manuf0004part0123ext0001
# Dinamik adres
cat /sys/bus/i3c/devices/0-5400000000001/dynamic_address
# BCR değeri
cat /sys/bus/i3c/devices/0-5400000000001/bcr
# DCR değeri
cat /sys/bus/i3c/devices/0-5400000000001/dcr
# HDR modları
cat /sys/bus/i3c/devices/0-5400000000001/hdr_capable
i3cdetect Aracı
i3cdetect, i2c-tools paketine benzer biçimde i3c-tools paketinde bulunur. Bus'taki tüm cihazları ve türlerini listeler.
# i3c-tools kurulumu (kaynak derleme)
git clone https://github.com/I3CToolsOrg/i3c-tools
cd i3c-tools
make && make install
# Bus 0 üzerindeki tüm cihazları tara
i3cdetect -l
# I3C bus 0: ...
i3cdetect 0
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
# 00: 0a 0b
# ...
# 0a: I3C cihaz (dinamik adres)
# 0b: I3C cihaz (dinamik adres)
dmesg ile DAA Takibi
# DAA sürecini dmesg'de izle
dmesg | grep -i i3c
# Tipik başarılı DAA çıktısı:
# [ 5.234] i3c-master e8000000.i3c: DAA baslatildi
# [ 5.236] i3c-master e8000000.i3c: Cihaz bulundu PID=0x5400000000001
# [ 5.237] i3c-master e8000000.i3c: Adres 0x0a atandi
# [ 5.238] i3c-master e8000000.i3c: DAA tamamlandi, 2 cihaz
Mantık Analizörü ile Protokol Analizi
Sigrok / PulseView veya Saleae Logic ile I3C protokolünü doğrudan izlemek mümkündür. I3C dekoderü SCL/SDA sinyallerini DAA, CCC ve veri transferlerine göre ayrıştırır.
# sigrok-cli ile I3C yakalama örneği
# (Sigrok 0.6+ I3C desteği gerektirir)
sigrok-cli \
--driver=fx2lafw \
--config samplerate=50MHz \
--channels D0=SCL,D1=SDA \
--protocol-decoders i3c \
--output-format ascii \
--time 100ms
Yaygın Sorunlar ve Çözümleri
| Sorun | Olası Neden | Çözüm |
|---|---|---|
| DAA tamamlanmıyor | Cihaz PID benzersiz değil ya da sinyal bütünlüğü sorunu | Logic analyzer ile SCL/SDA izle; terminasyon ve pull-up kontrol et |
| IBI alınamıyor | ENEC CCC gönderilmedi ya da cihaz IBI desteği yok | BCR'nin IBI bayrağını kontrol et; ENEC komutunu dmesg'de izle |
| I2C cihaz görünmüyor | Mixed-bus open-drain moduna geçilmiyor | i2c_xfers callback'ini ve mode switching mantığını kontrol et |
| priv_xfers ETIMEDOUT | SCL frekansı çok yüksek veya cihaz yanıt vermiyor | i3c-scl-hz'i düşür; cihaz veri hazırlama süresini kontrol et |
| Adres çakışması | İki cihaz aynı statik/dinamik adrese sahip | RSTDAA ile sıfırla ve DAA'yı yeniden başlat |
IBI Test Akışı
/* Basit IBI test kodu — cihaz sürücüsünde */
static ssize_t ibi_test_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct my_sensor_data *data = dev_get_drvdata(dev);
int enable;
if (kstrtoint(buf, 10, &enable))
return -EINVAL;
if (enable)
i3c_device_enable_ibi(data->i3cdev);
else
i3c_device_disable_ibi(data->i3cdev);
return count;
}
static DEVICE_ATTR_WO(ibi_test);
/* sysfs: echo 1 > /sys/bus/i3c/devices/.../ibi_test */