Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX REGMAP 2026

regmap
SoC Register Erişim Çatısı

I2C, SPI ve MMIO register erişimini tek bir soyut API ile yönet — cache, IRQ ve debugfs entegrasyonu dahil kapsamlı rehber.

00 regmap Neden Var

regmap çatısı, her sürücünün ayrı ayrı yazdığı register erişim kodunun tekrarını ortadan kaldırmak için 2011 yılında (Linux 3.1) Mark Brown tarafından eklendi. Bugün kernel'de 1000'den fazla sürücü regmap kullanır.

Regmap Öncesi Problem

regmap yokken her sürücü şu kalıbı tekrarlıyordu:

/* KÖTÜ: I2C sürücüsünde her register erişimi için boilerplate */

/* Okuma */
static int mydev_reg_read(struct mydev_priv *priv, u8 reg, u8 *val)
{
    struct i2c_msg msgs[2];
    int ret;

    msgs[0].addr  = priv->client->addr;
    msgs[0].flags = 0;
    msgs[0].len   = 1;
    msgs[0].buf   = ®

    msgs[1].addr  = priv->client->addr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].len   = 1;
    msgs[1].buf   = val;

    ret = i2c_transfer(priv->client->adapter, msgs, 2);
    return (ret == 2) ? 0 : ret;
}

/* Yazma */
static int mydev_reg_write(struct mydev_priv *priv, u8 reg, u8 val)
{
    u8 buf[2] = { reg, val };
    return i2c_master_send(priv->client, buf, 2);
}

/* Bit güncelleme */
static int mydev_reg_update(struct mydev_priv *priv, u8 reg, u8 mask, u8 val)
{
    u8 old, new_val;
    int ret;

    ret = mydev_reg_read(priv, reg, &old);
    if (ret)
        return ret;

    new_val = (old & ~mask) | (val & mask);
    if (new_val == old)
        return 0;

    return mydev_reg_write(priv, reg, new_val);
}
/* Her sürücü bu 3 fonksiyonu yeniden yazıyordu... */

regmap ile Aynı İşlevsellik

/* İYİ: regmap ile — 3 boilerplate fonksiyon yerine standart API */

/* Okuma */
regmap_read(priv->regmap, REG_ADDR, &val);

/* Yazma */
regmap_write(priv->regmap, REG_ADDR, val);

/* Bit güncelleme — otomatik read-modify-write */
regmap_update_bits(priv->regmap, REG_ADDR, MASK, val);

regmap'in Sağladıkları

Bus soyutlamasıAynı sürücü kodu I2C, SPI veya MMIO üzerinde çalışabilir — sadece init satırı değişir. Çekirdek donanım testi için MMIO, üretim için I2C kullanılabilir.
Register cacheTekrarlanan okumalar bus trafiği oluşturmaz. Yazma da önbelleğe alınabilir. Güç yönetimi senaryolarında kritik önem taşır.
IRQ yönetimiregmap_irq_chip ile donanım IRQ demux'u otomatik — her bit için sanal IRQ domain oluşturulur.
debugfs entegrasyonuOtomatik olarak /sys/kernel/debug/regmap/ altında register dump arayüzü oluşturulur.
Güvenli erişimRead-only, write-only ve volatile register tanımlamaları — yanlış erişim otomatik reddedilir veya uyarı üretir.
Lock yönetimiYerleşik mutex/spinlock ile thread-safe erişim. Fast I/O path için spinlock, normal için mutex.

Desteklenen Bus Türleri

BusInit FonksiyonuBaşlık Dosyası
MMIOdevm_regmap_init_mmio()linux/regmap.h
I2Cdevm_regmap_init_i2c()linux/regmap.h
SPIdevm_regmap_init_spi()linux/regmap.h
I3Cdevm_regmap_init_i3c()linux/regmap.h
SPMIdevm_regmap_init_spmi_base()linux/regmap.h
W1 (1-Wire)devm_regmap_init_w1()linux/regmap.h
AC97devm_regmap_init_ac97()linux/regmap.h

01 regmap_config Yapısı

struct regmap_config, regmap'in tüm davranışını tanımlar: adres/veri genişliği, cache politikası, izin verilen register'lar ve erişim kısıtları.

Temel Alanlar

#include <linux/regmap.h>

static const struct regmap_config mydev_regmap_config = {
    /*
     * reg_bits: register adres genişliği (bit cinsinden)
     * I2C codec: 8, bazı PMIC: 16, MMIO 32-bit: 32
     */
    .reg_bits     = 8,

    /*
     * val_bits: register değer genişliği (bit cinsinden)
     * Tipik değerler: 8, 16, 32
     */
    .val_bits     = 8,

    /*
     * reg_stride: ardışık register'lar arası adres farkı
     * Varsayılan: 1. MMIO 32-bit register'lar için: 4
     */
    .reg_stride   = 1,

    /*
     * max_register: en yüksek geçerli register adresi
     * Sınır kontrolü için kullanılır — üstündekilere erişim reddedilir
     */
    .max_register = 0xFF,

    /* Cache türü (s6'da detaylandırılır) */
    .cache_type   = REGCACHE_RBTREE,

    /* Register başlangıç değerleri (cache doldurmak için) */
    .reg_defaults     = mydev_reg_defaults,
    .num_reg_defaults = ARRAY_SIZE(mydev_reg_defaults),
};

Erişim Kontrol Tabloları

/*
 * Hangi register'ların okunabilir/yazılabilir/volatile/precious olduğunu
 * tanımlamak için iki yöntem var:
 *
 * 1. Callback fonksiyonları (esnek, büyük register haritaları için)
 * 2. regmap_access_table ile bool tablo (hızlı, küçük haritalar için)
 */

/* Yöntem 1: Callback */
static bool mydev_readable(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case REG_STATUS:
    case REG_DATA_LOW:
    case REG_DATA_HIGH:
    case REG_ID:
        return true;
    default:
        return false;
    }
}

static bool mydev_writeable(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case REG_CTRL:
    case REG_CONFIG:
    case REG_THRESHOLD:
        return true;
    default:
        return false;
    }
}

static bool mydev_volatile(struct device *dev, unsigned int reg)
{
    /*
     * Volatile register: cache'lenMEZ — her okumada gerçek donanım okunur.
     * Status, interrupt, FIFO register'ları genellikle volatile'dir.
     */
    switch (reg) {
    case REG_STATUS:
    case REG_IRQ_STATUS:
    case REG_FIFO_DATA:
        return true;
    default:
        return false;
    }
}

static bool mydev_precious(struct device *dev, unsigned int reg)
{
    /*
     * Precious register: regmap tarafından asla okunmaz (örn. FIFO'yu boşaltır).
     * Cache miss olsa bile hardware'dan okumaz.
     */
    return reg == REG_FIFO_DATA;
}

static const struct regmap_config mydev_regmap_config = {
    .reg_bits       = 8,
    .val_bits       = 8,
    .max_register   = 0x7F,
    .readable_reg   = mydev_readable,
    .writeable_reg  = mydev_writeable,
    .volatile_reg   = mydev_volatile,
    .precious_reg   = mydev_precious,
    .cache_type     = REGCACHE_RBTREE,
};

regmap_reg_default ile Başlangıç Değerleri

static const struct reg_default mydev_reg_defaults[] = {
    { REG_CTRL,      0x00 },   /* reset değeri: kapalı */
    { REG_CONFIG,    0x80 },   /* reset değeri: varsayılan ayar */
    { REG_THRESHOLD, 0x40 },
    { REG_RATE,      0x0A },
};

static const struct regmap_config mydev_regmap_config = {
    /* ... */
    .reg_defaults     = mydev_reg_defaults,
    .num_reg_defaults = ARRAY_SIZE(mydev_reg_defaults),
    /* Cache bu değerlerle başlar — ilk okumada bus trafiği oluşmaz */
};

Endianness Kontrolü

static const struct regmap_config be16_config = {
    .reg_bits      = 8,
    .val_bits      = 16,
    /*
     * val_format_endian: REGMAP_ENDIAN_BIG veya REGMAP_ENDIAN_LITTLE
     * Varsayılan: REGMAP_ENDIAN_DEFAULT (native CPU endianness)
     * I2C cihazları genellikle big-endian veri gönderir
     */
    .val_format_endian = REGMAP_ENDIAN_BIG,
    .reg_format_endian = REGMAP_ENDIAN_BIG,
};

02 MMIO regmap

MMIO regmap, bellek eşlemeli SoC register'larına erişim için kullanılır. Platform sürücüleri ve AMBA/APB cihazları için standart yaklaşımdır.

devm_regmap_init_mmio Kullanımı

#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/of.h>

struct mydev_priv {
    struct regmap   *regmap;
    struct clk      *clk;
};

/* 32-bit MMIO register'lar için tipik config */
static const struct regmap_config mydev_mmio_config = {
    .reg_bits   = 32,
    .val_bits   = 32,
    .reg_stride = 4,      /* 32-bit register'lar 4 byte arayla */
    .max_register = 0x1FC,

    /* MMIO erişimlerinde genellikle cache gerekmez (donanım state maşinesi) */
    /* Ancak suspend/resume senaryolarında cache kullanışlıdır */
    .cache_type = REGCACHE_NONE,

    /* MMIO'da lock genellikle spin_lock ile yapılır (IRQ bağlamından erişim) */
    .fast_io    = true,   /* mutex yerine spinlock kullan */
};

static int mydev_probe(struct platform_device *pdev)
{
    struct mydev_priv *priv;
    struct resource *res;
    void __iomem *base;
    int ret;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /* ioremap — regmap init'ten önce yapılır */
    base = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(base))
        return PTR_ERR(base);

    /*
     * devm_regmap_init_mmio:
     *   @dev: platform_device'ın &pdev->dev
     *   @regs: ioremap edilmiş base pointer
     *   @config: regmap_config yapısı
     *
     * devm varyantı: sürücü kaldırıldığında otomatik temizlenir
     */
    priv->regmap = devm_regmap_init_mmio(&pdev->dev,
                                          base,
                                          &mydev_mmio_config);
    if (IS_ERR(priv->regmap)) {
        dev_err(&pdev->dev, "regmap MMIO init hatası: %ld\n",
                PTR_ERR(priv->regmap));
        return PTR_ERR(priv->regmap);
    }

    platform_set_drvdata(pdev, priv);

    /* İlk donanım konfigürasyonu */
    ret = regmap_write(priv->regmap, REG_CTRL, CTRL_RESET);
    if (ret)
        return ret;

    /* Reset tamamlanmasını bekle */
    ret = regmap_read_poll_timeout(priv->regmap, REG_STATUS,
                                   val, !(val & STATUS_RESET),
                                   100,      /* uyku: 100 us */
                                   10000);   /* timeout: 10 ms */
    if (ret)
        dev_warn(&pdev->dev, "Donanım reset timeout\n");

    return ret;
}

regmap_read_poll_timeout

/*
 * regmap_read_poll_timeout — register değeri beklenen duruma gelene kadar poll et
 *
 * @map:      regmap handle
 * @addr:     register adresi
 * @val:      okunacak değer için geçici değişken (kendisi tanımlanır)
 * @cond:     bekleme koşulu (val kullanılarak ifade edilir)
 * @sleep_us: iki deneme arasındaki uyku süresi (mikrosaniye)
 * @timeout_us: toplam timeout süresi (mikrosaniye, 0=bekleme yok)
 *
 * Başarıda 0, timeout'ta -ETIMEDOUT döner.
 */

/* Örnek: TX FIFO boş olana kadar bekle */
u32 status;
ret = regmap_read_poll_timeout(priv->regmap, TX_STATUS_REG,
                                status,
                                status & TX_FIFO_EMPTY,
                                10,       /* 10 µs aralık */
                                1000000); /* 1 saniyelik timeout */
if (ret == -ETIMEDOUT) {
    dev_err(dev, "TX FIFO timeout\n");
    return ret;
}

MMIO regmap ile Suspend/Resume

static int mydev_suspend(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);

    /*
     * regcache_cache_only: sonraki yazma işlemleri sadece cache'e gider,
     * donanıma iletilmez. Donanım güç kesilecek.
     */
    regcache_cache_only(priv->regmap, true);

    clk_disable_unprepare(priv->clk);
    return 0;
}

static int mydev_resume(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);
    int ret;

    ret = clk_prepare_enable(priv->clk);
    if (ret)
        return ret;

    regcache_cache_only(priv->regmap, false);

    /*
     * regcache_sync: cache'deki tüm değerleri donanıma yaz (dirty olanlar).
     * Donanım resetlendiyse tüm register'ları geri yükler.
     */
    ret = regcache_sync(priv->regmap);
    return ret;
}

03 I2C regmap

I2C codec, sensor ve PMIC sürücülerinin büyük çoğunluğu regmap ile yazılır. devm_regmap_init_i2c ile I2C erişimi tamamen soyutlanır.

devm_regmap_init_i2c Kullanımı

#include <linux/i2c.h>
#include <linux/regmap.h>

/* WM8731 ses codec benzeri 9-bit register, 9-bit değer, big-endian */
static const struct regmap_config wm_like_regmap_config = {
    .reg_bits   = 7,   /* 7-bit register adresi */
    .val_bits   = 9,   /* 9-bit değer */
    /*
     * reg_format_endian ve val_format_endian değerleri cihaz veri sayfasına göre.
     * Bazı ses codec'leri I2C frame'i farklı düzenler:
     *   [7-bit reg addr][9-bit value] = 16 bit toplam
     */
    .reg_format_endian = REGMAP_ENDIAN_BIG,
    .val_format_endian = REGMAP_ENDIAN_BIG,
    .max_register      = 0x17,
    .cache_type        = REGCACHE_RBTREE,
    .reg_defaults      = wm_reg_defaults,
    .num_reg_defaults  = ARRAY_SIZE(wm_reg_defaults),
};

static int wm_like_probe(struct i2c_client *client)
{
    struct wm_priv *priv;

    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /*
     * devm_regmap_init_i2c:
     *   @client: i2c_client pointer
     *   @config: regmap_config
     *
     * Dahili olarak i2c_smbus_read_byte_data / i2c_transfer kullanır.
     */
    priv->regmap = devm_regmap_init_i2c(client, &wm_like_regmap_config);
    if (IS_ERR(priv->regmap))
        return PTR_ERR(priv->regmap);

    i2c_set_clientdata(client, priv);
    return 0;
}

Çok Baytlı Register Okuma

/* Tekil register okuma */
unsigned int val;
regmap_read(priv->regmap, REG_TEMP_HIGH, &val);

/*
 * regmap_bulk_read: ardışık register'ları tek seferinde oku
 * I2C auto-increment destekleyen cihazlar için etkin
 */
u8 raw[4];
ret = regmap_bulk_read(priv->regmap,
                        REG_DATA_BASE,   /* başlangıç adresi */
                        raw,             /* hedef buffer */
                        ARRAY_SIZE(raw)); /* okunacak register sayısı */

/* 16-bit big-endian değer birleştir */
s16 temp = (raw[0] << 8) | raw[1];

Tipik I2C Sensor Sürücüsü Şablonu

/* TMP112 benzeri sıcaklık sensörü */
#define TMP_REG_TEMP   0x00
#define TMP_REG_CONFIG 0x01
#define TMP_REG_TLOW   0x02
#define TMP_REG_THIGH  0x03

#define TMP_CONFIG_SD   BIT(0)    /* Shutdown mode */
#define TMP_CONFIG_TM   BIT(1)    /* Thermostat mode */
#define TMP_CONFIG_OS   BIT(15)   /* One-shot */

static bool tmp_volatile(struct device *dev, unsigned int reg)
{
    /* Sıcaklık register'ı her okumada güncelleniyor */
    return reg == TMP_REG_TEMP;
}

static const struct regmap_config tmp_regmap_config = {
    .reg_bits          = 8,
    .val_bits          = 16,
    .val_format_endian = REGMAP_ENDIAN_BIG,
    .max_register      = TMP_REG_THIGH,
    .volatile_reg      = tmp_volatile,
    .cache_type        = REGCACHE_RBTREE,
};

static int tmp_read_temp(struct tmp_priv *priv, int *temp_mc)
{
    unsigned int raw;
    int ret;

    /* volatile register — her çağrıda I2C okuma yapılır */
    ret = regmap_read(priv->regmap, TMP_REG_TEMP, &raw);
    if (ret)
        return ret;

    /*
     * TMP112: [15:4] = 12-bit two's complement sıcaklık (0.0625°C/LSB)
     * raw >> 4 işaretli kayan nokta → milli-Celsius dönüşümü
     */
    *temp_mc = sign_extend32(raw >> 4, 11) * 625 / 10;
    return 0;
}

04 SPI regmap

SPI cihazları, okuma/yazma ayrımı için genellikle adres byte'ının en yüksek bitini kullanır. regmap bu konvansiyonu spi_read_flag_mask ile soyutlar.

devm_regmap_init_spi ve spi_read_flag_mask

#include <linux/spi/spi.h>
#include <linux/regmap.h>

/*
 * Yaygın SPI register konvansiyonu:
 *   Yazma: [0|7-bit addr][8-bit data]
 *   Okuma: [1|7-bit addr][8-bit data]
 *
 * spi_read_flag_mask: adres byte'ına OR'lanan okuma bayrağı
 * Çoğu cihaz: 0x80 (bit7 = okuma)
 */
static const struct regmap_config adxl_spi_config = {
    .reg_bits          = 8,
    .val_bits          = 8,
    .max_register      = 0x39,
    .cache_type        = REGCACHE_RBTREE,

    /*
     * SPI okuma işleminde adres byte'ına eklenecek maske
     * ADXL345: 0xC0 (bit7=okuma, bit6=multi-byte okuma)
     */
    .read_flag_mask    = 0x80,
    .write_flag_mask   = 0x00,  /* Yazma: bit7 = 0 */
};

static int adxl_probe(struct spi_device *spi)
{
    struct adxl_priv *priv;

    priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /*
     * devm_regmap_init_spi:
     *   @spi: spi_device pointer
     *   @config: regmap_config
     *
     * read_flag_mask otomatik uygulanır — sürücü kodu bus detaylarını bilmez
     */
    priv->regmap = devm_regmap_init_spi(spi, &adxl_spi_config);
    if (IS_ERR(priv->regmap))
        return PTR_ERR(priv->regmap);

    spi_set_drvdata(spi, priv);

    /* Cihaz ID kontrolü */
    return adxl_check_id(priv);
}

Çoklu Byte Okuma — burst read

/* ADXL345: X/Y/Z ivme verisi — 6 ardışık register */
#define ADXL_REG_DATA_X0  0x32
#define ADXL_REG_DATA_X1  0x33
#define ADXL_REG_DATA_Y0  0x34
#define ADXL_REG_DATA_Y1  0x35
#define ADXL_REG_DATA_Z0  0x36
#define ADXL_REG_DATA_Z1  0x37

static int adxl_read_xyz(struct adxl_priv *priv,
                          s16 *x, s16 *y, s16 *z)
{
    __le16 buf[3];
    int ret;

    /*
     * regmap_bulk_read: tek SPI transfer ile 6 byte oku
     * read_flag_mask VE multi-byte flag otomatik uygulanır
     * (config'de read_flag_mask = 0xC0: bit7=read, bit6=multi-byte)
     */
    ret = regmap_bulk_read(priv->regmap,
                            ADXL_REG_DATA_X0,
                            buf,
                            ARRAY_SIZE(buf));
    if (ret)
        return ret;

    *x = le16_to_cpu(buf[0]);
    *y = le16_to_cpu(buf[1]);
    *z = le16_to_cpu(buf[2]);
    return 0;
}

SPI ve I2C İçin Ortak Sürücü

/*
 * Bazı cihazlar hem I2C hem SPI bağlantısı destekler (örn. BMI160).
 * regmap soyutlaması sayesinde aynı sürücü kodu her iki bus'ta çalışır.
 */
struct mydev_priv {
    struct regmap *regmap;
    /* bus-bağımsız ortak alanlar */
};

/* Ortak init mantığı */
static int mydev_init(struct mydev_priv *priv)
{
    /* Bu kod SPI mi I2C mi olduğunu bilmez */
    regmap_write(priv->regmap, REG_RESET, RESET_VAL);
    regmap_read_poll_timeout(priv->regmap, REG_STATUS,
                              val, val & STATUS_READY,
                              1000, 100000);
    return regmap_update_bits(priv->regmap, REG_CONFIG,
                               CONFIG_EN, CONFIG_EN);
}

/* I2C probe */
static int mydev_i2c_probe(struct i2c_client *client)
{
    struct mydev_priv *priv = devm_kzalloc(&client->dev,
                                             sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->regmap = devm_regmap_init_i2c(client, &mydev_regmap_cfg);
    if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap);
    i2c_set_clientdata(client, priv);
    return mydev_init(priv);  /* aynı kod */
}

/* SPI probe */
static int mydev_spi_probe(struct spi_device *spi)
{
    struct mydev_priv *priv = devm_kzalloc(&spi->dev,
                                             sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->regmap = devm_regmap_init_spi(spi, &mydev_spi_regmap_cfg);
    if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap);
    spi_set_drvdata(spi, priv);
    return mydev_init(priv);  /* aynı kod — bus soyutlandı */
}

05 regmap_read, regmap_write, regmap_update_bits

regmap'in günlük kullanım API'sı — tek register erişiminden toplu işlemlere, polling'den bit maskelemeye kadar tüm yaygın kullanım kalıpları.

Temel Okuma/Yazma

/* Tekil okuma — val: unsigned int* */
unsigned int val;
int ret = regmap_read(priv->regmap, REG_STATUS, &val);
if (ret) {
    dev_err(dev, "REG_STATUS okunamadı: %d\n", ret);
    return ret;
}

/* Tekil yazma */
ret = regmap_write(priv->regmap, REG_CTRL, CTRL_ENABLE | CTRL_MODE_A);
if (ret)
    return ret;

/*
 * regmap_update_bits: read-modify-write (atomic, thread-safe)
 *
 * @map:   regmap handle
 * @reg:   register adresi
 * @mask:  hangi bitlerin değiştirileceği
 * @val:   yeni değer (mask ile AND'lenir)
 *
 * Etkisi: reg = (reg & ~mask) | (val & mask)
 */
/* Sadece bit 2 ve 3'ü güncelle, diğerlerine dokunma */
ret = regmap_update_bits(priv->regmap, REG_CONFIG,
                          BIT(3) | BIT(2),   /* mask */
                          BIT(3));            /* val: bit3=1, bit2=0 */

regmap_update_bits_check

/*
 * regmap_update_bits_check: update_bits gibi ama gerçekten değişti mi söyler
 * change: true = register değeri değişti, false = zaten aynıydı
 */
bool change;
ret = regmap_update_bits_check(priv->regmap, REG_CONFIG,
                                BIT(0), BIT(0),
                                &change);
if (!ret && change)
    dev_dbg(dev, "Config register güncellendi\n");

Toplu İşlemler

/* regmap_bulk_write: ardışık register'lara tek seferinde yaz */
static const u8 init_seq[] = {
    0x01,  /* REG_BASE+0 */
    0x80,  /* REG_BASE+1 */
    0x00,  /* REG_BASE+2 */
    0x0F,  /* REG_BASE+3 */
};
ret = regmap_bulk_write(priv->regmap, REG_BASE,
                         init_seq, ARRAY_SIZE(init_seq));

/*
 * regmap_multi_reg_write: ardışık olmayan register'lara yazı listesi
 * Tek I2C/SPI trafiği değil, arka arkaya tekil yazma işlemleri
 */
static const struct reg_sequence init_regs[] = {
    { REG_RESET,   0x01,  10 },  /* 10 ms gecikme sonra devam */
    { REG_CTRL,    0x80,   0 },
    { REG_CONFIG,  0x0F,   0 },
    { REG_ENABLE,  0x01,   5 },  /* 5 ms gecikme */
};
ret = regmap_multi_reg_write(priv->regmap,
                              init_regs,
                              ARRAY_SIZE(init_regs));

Bit Testi Yardımcıları

/*
 * regmap_test_bits: register'da belirtilen bitlerin TÜMÜ set mi?
 * Başarıda 1 (set), 0 (clear) veya negatif hata döner.
 */
ret = regmap_test_bits(priv->regmap, REG_STATUS, STATUS_READY | STATUS_LOCKED);
if (ret == 1)
    dev_info(dev, "Donanım hazır ve kilitli\n");
else if (ret == 0)
    dev_warn(dev, "Beklenen bitler set değil\n");

regmap_noinc_read / regmap_noinc_write

/*
 * regmap_noinc_read: adres artırmadan çoklu okuma
 * Kullanım: FIFO register'ları — aynı adresten N kez oku
 */
u8 fifo_buf[64];
ret = regmap_noinc_read(priv->regmap,
                         FIFO_DATA_REG,   /* adres artmaz */
                         fifo_buf,
                         sizeof(fifo_buf));

/* regmap_noinc_write: FIFO'ya yazma */
ret = regmap_noinc_write(priv->regmap,
                          FIFO_DATA_REG,
                          tx_buf,
                          tx_len);

06 Register Cache

Register cache, yazılım tarafından son yazılan değerleri bellekte tutar. Cache hit durumunda donanım bus'u hiç kullanılmaz — özellikle güç tasarrufu ve hızlı okuma senaryolarında kritik önem taşır.

Cache Türleri Karşılaştırması

TürVeri YapısıHafızaArama HızıNe Zaman Kullan
REGCACHE_NONEYokSıfırHer erişim donanıma gider, cache istemiyorsun
REGCACHE_FLATDizi (array)max_register * val_bits/8O(1)Küçük, sık erişilen register haritası
REGCACHE_RBTREEKırmızı-siyah ağaçSadece yazılan register'larO(log n)Seyrek register haritası, büyük adres alanı
REGCACHE_MAPLEMaple treeDeğişkenO(log n)RBTREE yerine yeni tercih (kernel 6.1+)

Cache Politikası ve Volatile Register

/*
 * Cache davranış kuralları:
 *
 * 1. volatile_reg() true dönen register'lar ASLA cache'lenmez
 *    → Her regmap_read() donanımdan okur
 *
 * 2. Non-volatile register'lar ilk yazıldıktan sonra cache'e alınır
 *    → Sonraki regmap_read() bus'a gitmez
 *
 * 3. reg_defaults ile başlangıç cache değerleri verilebilir
 *    → Probe'da ilk okuma bus'a gitmez
 *
 * 4. precious_reg() true dönen register'lar cache miss'te bus'tan okunmaz
 *    → Sadece yazma cache'lenir
 */

/* Cache ne kadar verimliliği artırır? */
/* Örnek: sıcaklık sensörü, 10 konfigürasyon register, 1 veri register */

/* volatile_reg: sadece data register — 9 config okuması cache'den */
/* Tipik iyileştirme: ~%90 azalan I2C trafiği config okumalarında */

Cache Bypass ve Senkronizasyon

/* Tek seferlik cache bypass okuma — donanım durumunu doğrula */
unsigned int hw_val;
regmap_hw_read(priv->regmap, REG_CONFIG, &hw_val);

/* regcache_bypass: tüm işlemleri cache'e gitmeden donanıma yönlendir */
regcache_bypass(priv->regmap, true);
/* ... doğrudan donanım erişimi ... */
regcache_bypass(priv->regmap, false);

/* regcache_drop_region: belirli aralıktaki cache girdilerini sil */
regcache_drop_region(priv->regmap, REG_DATA_BASE, REG_DATA_END);

/*
 * regcache_mark_dirty: cache tüm register'ları "dirty" işaretle
 * Sonraki regcache_sync tüm cache'i donanıma yazdırır
 * Kullanım: donanım resetlendi, cache ile senkronize değil
 */
regcache_mark_dirty(priv->regmap);
regcache_sync(priv->regmap);

/* Belirli aralığı sync et */
regcache_sync_region(priv->regmap, REG_CONFIG_BASE, REG_CONFIG_END);

Suspend/Resume ile Tam Cache Döngüsü

static int mydev_suspend(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);

    /* Donanıma giden tüm yazmaları engelle — sadece cache güncellenir */
    regcache_cache_only(priv->regmap, true);

    /* Donanımı kapat */
    regulator_disable(priv->supply);
    clk_disable_unprepare(priv->clk);
    return 0;
}

static int mydev_resume(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);
    int ret;

    /* Donanımı aç */
    ret = clk_prepare_enable(priv->clk);
    if (ret) return ret;

    ret = regulator_enable(priv->supply);
    if (ret) return ret;

    /* Donanım resetlendi — cache'i dirty işaretle */
    regcache_mark_dirty(priv->regmap);

    /* cache_only modunu kapat */
    regcache_cache_only(priv->regmap, false);

    /* Cache'deki tüm dirty değerleri donanıma geri yaz */
    ret = regcache_sync(priv->regmap);
    if (ret)
        dev_err(dev, "regcache sync hatası: %d\n", ret);

    return ret;
}

07 regmap IRQ

regmap-irq çatısı, tek bir donanım IRQ'sundan birden fazla sanal IRQ oluşturmak için kullanılır. PMIC, çok fonksiyonlu codec ve multi-sensor cihazlarda tek IRQ hattı onlarca alt kaynağa hizmet eder.

regmap_irq_chip Kavramı

Donanım IRQ (tek hat, GPIO veya SPI/I2C IRQ)
        │
        ▼
  regmap_irq_chip handler
  ├─ IRQ_STATUS register'ı oku (hangi bit set?)
  ├─ Aktif bit'leri bul
  └─ Her bit için sanal IRQ tetikle
        │
        ▼
  ┌─────────────┬──────────────┬──────────────┐
  │  virq N     │  virq N+1    │  virq N+2    │
  │ (overcurrent)│  (overtemp) │  (poweroff)  │
  └─────────────┴──────────────┴──────────────┘
        │              │              │
  Alt sürücü A   Alt sürücü B   Alt sürücü C
  request_irq()  request_irq()  request_irq()
    

regmap_irq_chip Tanımı

#include <linux/regmap.h>

/* IRQ register bloğu tanımı — çoğu cihaz STATUS + MASK + ACK üçlüsü */
static const struct regmap_irq mydev_irqs[] = {
    /* IRQ indeksi 0: overcurrent */
    REGMAP_IRQ_REG(MYDEV_IRQ_OC,    0, BIT(0)),
    /* IRQ indeksi 1: overtemperature */
    REGMAP_IRQ_REG(MYDEV_IRQ_OT,    0, BIT(1)),
    /* IRQ indeksi 2: undervoltage */
    REGMAP_IRQ_REG(MYDEV_IRQ_UV,    0, BIT(2)),
    /* IRQ indeksi 3: power-good */
    REGMAP_IRQ_REG(MYDEV_IRQ_PG,    0, BIT(3)),
    /* Farklı status register (indeks 1) üzerindeki IRQ */
    REGMAP_IRQ_REG(MYDEV_IRQ_FAULT, 1, BIT(0)),
};

/*
 * regmap_irq_chip: IRQ demux'ün konfigürasyonu
 */
static const struct regmap_irq_chip mydev_irq_chip = {
    .name           = "mydev-irq",
    .irqs           = mydev_irqs,
    .num_irqs       = ARRAY_SIZE(mydev_irqs),
    .num_regs       = 2,          /* IRQ register bloğu sayısı */

    /*
     * status_base: IRQ durum register başlangıcı
     * Regmap bu adresten num_regs kadar register okur
     */
    .status_base    = MYDEV_REG_IRQ_STATUS0,

    /*
     * mask_base: IRQ maske register başlangıcı
     * Bit=1 → IRQ masked (kapalı), Bit=0 → aktif
     */
    .mask_base      = MYDEV_REG_IRQ_MASK0,

    /*
     * ack_base: IRQ temizleme register başlangıcı
     * NULL ise status register'ı yazmak temizler
     */
    .ack_base       = MYDEV_REG_IRQ_ACK0,

    /* mask polarity: bazı cihazlarda 1=enable, 0=mask (ters) */
    .mask_invert    = false,

    /* init_ack_masked: başlangıçta masked IRQ'ları temizle */
    .init_ack_masked = true,
};

regmap_add_irq_chip Kullanımı

struct mydev_priv {
    struct regmap                *regmap;
    struct regmap_irq_chip_data  *irq_data;
    int                           irq;
};

static int mydev_probe(struct i2c_client *client)
{
    struct mydev_priv *priv;
    int ret;

    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->regmap = devm_regmap_init_i2c(client, &mydev_regmap_config);
    if (IS_ERR(priv->regmap))
        return PTR_ERR(priv->regmap);

    priv->irq = client->irq;
    if (!priv->irq)
        return -ENXIO;

    /*
     * regmap_add_irq_chip:
     *   @map:      regmap handle
     *   @irq:      parent donanım IRQ numarası
     *   @irq_flags: IRQF_ONESHOT | IRQF_TRIGGER_LOW (cihaza göre)
     *   @irq_base: sanal IRQ başlangıcı (0 = otomatik tahsis)
     *   @chip:     yukarıda tanımlanan regmap_irq_chip
     *   @data:     handle (alt sürücüler bunu kullanır)
     */
    ret = devm_regmap_add_irq_chip(&client->dev,
                                    priv->regmap,
                                    priv->irq,
                                    IRQF_ONESHOT | IRQF_TRIGGER_LOW,
                                    0,   /* irq_base: otomatik */
                                    &mydev_irq_chip,
                                    &priv->irq_data);
    if (ret) {
        dev_err(&client->dev, "IRQ chip eklenemedi: %d\n", ret);
        return ret;
    }

    /* Alt sürücüler için sanal IRQ'ları al */
    int oc_irq = regmap_irq_get_virq(priv->irq_data, MYDEV_IRQ_OC);
    int ot_irq = regmap_irq_get_virq(priv->irq_data, MYDEV_IRQ_OT);

    /* Bu virq'lar normal request_irq ile kullanılır */
    devm_request_threaded_irq(&client->dev, oc_irq,
                               NULL, mydev_oc_handler,
                               IRQF_ONESHOT, "mydev-oc", priv);

    i2c_set_clientdata(client, priv);
    return 0;
}

08 Hata Ayıklama

regmap'in otomatik debugfs entegrasyonu ve ham register erişim API'ları, donanım sorunlarını kullanıcı uzayından teşhis etmeyi kolaylaştırır.

debugfs Arayüzü

# regmap debugfs girdilerini listele
ls /sys/kernel/debug/regmap/
# 1-0048/   ← I2C bus 1, adres 0x48 (TMP112 örneği)
# 2-001a/   ← I2C bus 2, adres 0x1a

ls /sys/kernel/debug/regmap/1-0048/
# registers   ← tüm okunabilir register'ların dump'ı
# range       ← tanımlı register aralıkları
# access      ← erişim kısıtları (r/w/volatile)
# cache_bypass
# cache_only

# Tüm register'ları dump et
cat /sys/kernel/debug/regmap/1-0048/registers
# 00: 1a      ← REG 0x00 = 0x1a
# 01: 60      ← REG 0x01 = 0x60 (config)
# 02: 4b      ← REG 0x02 = 0x4b (tlow)
# 03: 50      ← REG 0x03 = 0x50 (thigh)

# Belirli register aralığını izle (watch ile)
watch -n 1 'cat /sys/kernel/debug/regmap/1-0048/registers'

regmap_raw_read / regmap_raw_write

/*
 * regmap_raw_read: cache ve format dönüşümü atlanarak ham bus okuma
 * Kullanım: donanım davranışı şüpheliyken, cache'siz ham değer gerektiğinde
 *
 * val: hedef buffer (val_bits/8 * count byte)
 */
u8 raw_buf[4];
ret = regmap_raw_read(priv->regmap,
                       REG_BASE,
                       raw_buf,
                       sizeof(raw_buf));
dev_dbg(dev, "Ham register değerleri: %02x %02x %02x %02x\n",
        raw_buf[0], raw_buf[1], raw_buf[2], raw_buf[3]);

/*
 * regmap_raw_write: ham bus yazma — format dönüşümü yok
 * Bazı özel protokoller için gerekli
 */
u8 raw_cmd[] = { 0x03, 0xFF };
regmap_raw_write(priv->regmap, CMD_REG, raw_cmd, sizeof(raw_cmd));

Cache Durumu Doğrulama

# Cache bypass modunu etkinleştir (debugfs)
echo 1 > /sys/kernel/debug/regmap/1-0048/cache_bypass

# Şimdi tüm okumalar donanımdan gelir — cache kontrolü
cat /sys/kernel/debug/regmap/1-0048/registers

# Normal moda dön
echo 0 > /sys/kernel/debug/regmap/1-0048/cache_bypass

# Cache only modu test
echo 1 > /sys/kernel/debug/regmap/1-0048/cache_only
# Bu modda tüm yazma sadece cache'e gider
# Donanım erişimi olmadığını doğrula
echo 0 > /sys/kernel/debug/regmap/1-0048/cache_only

Kernel Log ile Hata Takibi

/* CONFIG_REGMAP_DEBUG=y ile derlenmişse her erişim loglanır */
/* Çok ayrıntılı — sadece kısa test senaryolarında kullanın */

/* Kod içinde seçici debug */
static int mydev_probe(struct i2c_client *client)
{
    /* ... */

    /* regmap_read hata kontrolü — her zaman yapılmalı */
    ret = regmap_read(priv->regmap, REG_ID, &val);
    if (ret) {
        dev_err(&client->dev,
                "Cihaz ID okunamadı (I2C addr=0x%02x): %d\n",
                client->addr, ret);
        return ret;
    }

    if ((val & ID_MASK) != EXPECTED_ID) {
        dev_err(&client->dev,
                "Cihaz ID eşleşmiyor: beklenen=0x%02x, okunan=0x%02x\n",
                EXPECTED_ID, val & ID_MASK);
        return -ENODEV;
    }
    /* ... */
}

Yaygın Sorunlar ve Çözümler

regmap_read hep 0 dönüyorCache'de 0 olabilir — cache_bypass ile donanımdan oku. reg_defaults yanlış ayarlanmış olabilir. volatile_reg doğru tanımlı mı kontrol et.
regmap_write başarısızwriteable_reg() fonksiyonu false döndürüyordur. max_register değerini aşıyordur. I2C/SPI sinyali donanım katmanında başarısız oluyordur — osiloskop ile doğrula.
Cache senkronizasyon hatasıResume sonrası donanım tam hazır olmadan regcache_sync çağrıldı. Stabilizasyon gecikmesi ekle (usleep_range veya regmap_read_poll_timeout).
regmap_irq spurious tetiklemeIRQ_STATUS register volatile olarak işaretlenmemişse cache'den okunabilir — süresi geçmiş değer. IRQ status register'ı mutlaka volatile_reg'e ekle.
Endianness sorunu16/32-bit değerler tersine okunuyor. regmap_config'de val_format_endian ve reg_format_endian cihaz veri sayfasına göre ayarlanmalı.

regmap Varlığını Doğrulama — Sürücü Audit

/* Mevcut bir sürücünün regmap kullanıp kullanmadığını kontrol et */
/* kernel/drivers/ altında: */

/* regmap kullanan: */
grep -l "devm_regmap_init" drivers/iio/adc/*.c

/* regmap kullanması gereken ama kullanmayan (manual I2C): */
grep -l "i2c_smbus_write_byte_data\|i2c_transfer" \
    drivers/hwmon/*.c | head -20
/* Bu dosyalar regmap'e geçiş adayıdır */