1. 程式人生 > >linux的I2C驅動——讀寫操作

linux的I2C驅動——讀寫操作

一、體系結構

 接下來開始整體的介紹I2C,主要參考《Linux裝置驅動開發詳解》。

1、I2C核心
  I2C核心提供了I2C匯流排驅動和裝置驅動的註冊、登出方法,I2C通訊方法。
2、I2C匯流排驅動
  I2C匯流排驅動是對I2C硬體體系結構中介面卡的實現。
  I2C匯流排驅動主要包含I2C介面卡資料結構i2c_adapter、I2C介面卡的algorithm資料結構i2c_algorithm和控制I2C介面卡產生通訊訊號的函式。
  經由I2C匯流排驅動的程式碼,我們可以控制I2C介面卡以主控方式產生開始位、停止位、讀寫週期,以及以從裝置方式被讀寫、產生ACK等。
3、I2C裝置驅動
  I2C裝置驅動主要包含資料結構i2c_driver和i2c_client。

二、I2C匯流排

  程式碼路徑:drivers/I2C/busses/i2c_tegra.c
每個程式碼架構都有他對應的匯流排程式碼,下面介紹i2c_adapter、i2c_algorithm、i2c_msg三個結構體
  定義匯流排介面卡

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 */ struct rt_mutex bus_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; };

資料傳輸結構體,決定I2C的通訊方式

struct i2c_algorithm {
    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 *);
};

資料格式

struct i2c_msg {
    __u16 addr; /* 從地址          */
    __u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
    __u16 len;      /* 資料長度             */
    __u8 *buf;      /* 資料指標         */
};

I2C匯流排的初始化

static int __init tegra_i2c_init_driver(void)
{
    return platform_driver_register(&tegra_i2c_driver);
}
subsys_initcall(tegra_i2c_init_driver);
int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(platform_driver_register);

匯流排會像普通裝置那個去註冊驅動,並呼叫probe函式。
tegra_i2c_probe函式用於初始化i2c_adapter和i2c_algorithm結構體

static int tegra_i2c_probe(struct platform_device *pdev)
{
    struct tegra_i2c_dev *i2c_dev;
    struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
    struct resource *res;
    struct resource *iomem;
    struct clk *clk;
    struct clk *i2c_clk;
    void *base;
    int irq;
    int ret = 0;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ……
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    ……
    i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
    if (!i2c_dev) {
        ret = -ENOMEM;
        goto err_i2c_clk_put;
    }

    i2c_dev->base = base;
    i2c_dev->clk = clk;
    i2c_dev->i2c_clk = i2c_clk;
    i2c_dev->iomem = iomem;
    i2c_dev->adapter.algo = &tegra_i2c_algo;
    i2c_dev->irq = irq;
    i2c_dev->cont_id = pdev->id;
    i2c_dev->dev = &pdev->dev;
    i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000;

    if (pdev->id == 3)
        i2c_dev->is_dvc = 1;
    init_completion(&i2c_dev->msg_complete);

    platform_set_drvdata(pdev, i2c_dev);

    ret = tegra_i2c_init(i2c_dev);
    ……
    i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
    i2c_dev->adapter.owner = THIS_MODULE;
    i2c_dev->adapter.class = I2C_CLASS_HWMON;
    strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
        sizeof(i2c_dev->adapter.name));
    i2c_dev->adapter.algo = &tegra_i2c_algo;
    i2c_dev->adapter.dev.parent = &pdev->dev;
    i2c_dev->adapter.nr = pdev->id;

    ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
    if (ret) {
        dev_err(&pdev->dev, "Failed to add I2C adapter\n");
        goto err_free_irq;
    }

    return 0;
    ……
}

通過i2c_set_adapdata函式設定i2c_adapter;
通過i2c_dev->adapter.algo = &tegra_i2c_algo去指定i2c_algorithm結構體

/*drivers/I2C/busses/i2c-tegra.c*/
static const struct i2c_algorithm tegra_i2c_algo = {
    .master_xfer    = tegra_i2c_xfer,
    .functionality  = tegra_i2c_func,
};

master_xfer定義了資料傳輸函式。

三、上層呼叫

上層通過read、write介面函式去呼叫驅動中對應的函式,在驅動中會定義file_operations 
struct file_operations at24cxx_fops = 
{
    .owner = THIS_MODULE,
    .read = at24cxx_read,
    .write = at24cxx_write,
};

在at24cxx_read函式中會呼叫i2c_transfer函式,進行資料的讀取。

/*drivers/I2C/i2c-core.c*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    unsigned long orig_jiffies;
    int ret, try;

    if (adap->algo->master_xfer) {
        if (in_atomic() || irqs_disabled()) {
            ret = i2c_trylock_adapter(adap);
            if (!ret)
                /* I2C activity is ongoing. */
                return -EAGAIN;
        } else {
            i2c_lock_adapter(adap);
        }

        /* 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;
        }
        i2c_unlock_adapter(adap);

        return ret;
    } else {
        dev_dbg(&adap->dev, "I2C level transfers not supported\n");
        return -EOPNOTSUPP;
    }
}
EXPORT_SYMBOL(i2c_transfer);

在i2c_transfer函式中呼叫adap->algo->master_xfer所指定的函式進行資料的傳輸。即呼叫匯流排去傳輸資料。

I2C裝置的整體執行過程如下:
  上層通過read、write函式去呼叫驅動中對應的介面,然後由介面函式再去呼叫匯流排的相關函式,然後實現與裝置的通訊。