Tüm eğitimler
TEKNİK REHBERGÖMÜLÜ LİNUXSENSÖR VERİ YOLU2026

I3C
Gelişmiş Sensör Veri Yolu Protokolü

I3C protokolünün I2C üzerindeki avantajları, Linux I3C subsystem API'si, controller ve cihaz sürücüsü geliştirme.

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:

Düşük hızStandart mod 100 kHz, hızlı mod 400 kHz, hızlı artı mod 1 MHz; modern sensörler için yetersiz
Statik adresler7-bit adresler sabit ve konfigürasyona göre pinden çekilir; aynı busta iki özdeş cihaz kullanılamaz
Kesme için ek pinHer sensör için ayrı INT pini gerekir; çok sensörlü tasarımlarda GPIO açığı oluşur
Bant genişliği paylaşımıMaster/slave mimarisi; slave cihaz veri hazır olduğunda master'ı uyaramaz (polling gerekir)
Güç tüketimiAçık-toplayıcı çekme dirençleri statik güç tüketir; yüksek frekansa çıkıldıkça güç artışı belirginleşir

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:

  1. Controller, ENTDAA CCC komutunu yayınlar.
  2. Tüm adressiz I3C cihazlar yanıt mekanizmasına girer.
  3. Her cihaz 64-bit Provisional ID (PID) ve BCR/DCR değerlerini gönderir.
  4. Controller bir adres atar; bu cihaz susar, bir sonrakine geçilir.
  5. 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 KoduAdAçıklama
0x06RSTDAADinamik adresleri sıfırla — tüm cihazlar
0x07ENTDAADinamik adres atama işlemini başlat
0x08DEFSLVSSlave listesini tanımla
0x09SETMWLMaks. yazma veri uzunluğunu ayarla
0x0ASETMRLMaks. okuma veri uzunluğunu ayarla
0x28ENECEvent/IBI'ı etkinleştir
0x29DISECEvent/IBI'ı devre dışı bırak
0x61SETDASAStatik adresten dinamik adres ata

HDR Modu

HDR (High Data Rate) modları standart SDR'nin ötesinde bant genişliği sunar:

HDR-DDRÇift veri hızı; SCL'nin hem yükselen hem alçalan kenarında veri; ~25 Mbps
HDR-TSLTernary symbol legacy; 3 seviyeli sinyal; ~37.5 Mbps
HDR-TSPTernary symbol pure; gelişmiş 3 seviyeli sinyal; ~50 Mbps
HDR-BTBulk transport; sürekli veri akışı senaryoları için

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:

I2C polling 1 kHzCPU her 1ms'de bir uyanır; 1000 uyanış/sn; uyku gecikmesi artar
I3C IBI olay tabanlıCPU yalnızca sensör verisi hazır olduğunda uyanır; gereksiz uyanış yok
Güç tasarrufuTipik giyilebilir senaryoda %30-50 CPU uyanış döngüsü azaltımı

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ı

Hız sınırıI2C cihaz varlığı I3C push-pull hızını kısıtlamaz; ancak I2C transferleri sırasında bus yavaşlar
Çekme direnciI2C cihazlar için çekme direnci hâlâ gereklidir; bu I3C yüksek frekans kapasitansını artırır
IBI uyumluluğuI2C cihazlar IBI gönderemez; yalnızca I3C cihazlar IBI yapabilir
HDR engellemeBus'ta I2C cihaz varsa HDR modu kullanılamaz; I3C cihazlar SDR ile sınırlı kalır

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

ÖzellikTürAçıklama
i3c-scl-hzu32I3C SCL frekansı Hz (SDR modu)
i2c-scl-hzu32I2C SCL frekansı Hz (mixed-bus)
assigned-addressu8DAA sırasında bu adres verilmesi tercih edilir
#address-cellsu32I3C master için 3 olmalıdır
jdec-spdboolJEDEC SPD protokol desteği etkinleştir
mctp-cccboolMCTP 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

SorunOlası NedenÇözüm
DAA tamamlanmıyorCihaz PID benzersiz değil ya da sinyal bütünlüğü sorunuLogic analyzer ile SCL/SDA izle; terminasyon ve pull-up kontrol et
IBI alınamıyorENEC CCC gönderilmedi ya da cihaz IBI desteği yokBCR'nin IBI bayrağını kontrol et; ENEC komutunu dmesg'de izle
I2C cihaz görünmüyorMixed-bus open-drain moduna geçilmiyori2c_xfers callback'ini ve mode switching mantığını kontrol et
priv_xfers ETIMEDOUTSCL frekansı çok yüksek veya cihaz yanıt vermiyori3c-scl-hz'i düşür; cihaz veri hazırlama süresini kontrol et
Adres çakışmasıİki cihaz aynı statik/dinamik adrese sahipRSTDAA 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 */