1. 程式人生 > >《linux裝置驅動開發詳解》筆記——15 linux i2c驅動

《linux裝置驅動開發詳解》筆記——15 linux i2c驅動

  結合實際程式碼和書中描述,可能跟書上有一定出入。本文後續晶片相關程式碼參考ZYNQ。

15.1 總體結構

  如下圖,i2c驅動分為如下幾個重要模組

  • 核心層core,完成i2c匯流排、裝置、驅動模型,對使用者提供sys檔案系統訪問支援;為i2c內部adpter等提供註冊介面。
  •  adpter,介面卡,實際就是CPU整合的IIC控制器,有cpu控制,完成i2c物理匯流排操作,busses資料夾裡,有各個cpu adapter的具體實現;algorithm整合到這個模組裡,而且是adapter的一部分,主要實現總  線傳輸的標準化。
  • i2c-dev,把adapter裝置化,採用標準的file_operations字元裝置的形式,便於使用者層直接讀寫adapter裝置檔案,不是主流,一般也就是除錯時用用。
  • muxes,i2c切換晶片,不重點討論。

  

自己理解的結構圖,與書中有出入,簡化了。

i2c匯流排裝置驅動模型,與platform類似,軟體架構都一樣。通過這種模型,最終在sysfs中建立檔案,且建立檔案時要填充類似file_operations類似的結構體中的讀寫函式,從而完成使用者層到底層驅動的傳遞。

15.2 adapter和algorithm——最終對外提供硬體無關介面

   busses/i2c-cadence.c檔案是ZYNQ i2c控制器的驅動,實現i2c的adapter和algorithm。adapter採用platform機制。

   先看看關鍵資料結構:

15.2.1 關鍵資料接結構

/**
 * struct cdns_i2c - I2C device private data structure
 * @membase:        Base address of the I2C device
 * @adap:        I2C adapter instance
 * @p_msg:        Message pointer
 * @err_status:        Error status in Interrupt Status Register
 * @xfer_done:        Transfer complete status
 * @p_send_buf:        Pointer to transmit buffer
 * @p_recv_buf:        Pointer to receive buffer
 * @suspended:        Flag holding the device's PM status
 * @send_count:        Number of bytes still expected to send
 * @recv_count:        Number of bytes still expected to receive
 * @curr_recv_count:    Number of bytes to be received in current transfer
 * @irq:        IRQ number
 * @input_clk:        Input clock to I2C controller
 * @i2c_clk:        Maximum I2C clock speed
 * @bus_hold_flag:    Flag used in repeated start for clearing HOLD bit
 * @clk:        Pointer to struct clk
 * @clk_rate_change_nb:    Notifier block for clock rate changes
 
*/ struct cdns_i2c { void __iomem *membase; struct i2c_adapter adap; struct i2c_msg *p_msg; int err_status; struct completion xfer_done; unsigned char *p_send_buf; unsigned char *p_recv_buf; u8 suspended; unsigned int send_count; unsigned int recv_count; unsigned int curr_recv_count; int irq; unsigned long input_clk; unsigned int i2c_clk; unsigned int bus_hold_flag; struct clk *clk; struct notifier_block clk_rate_change_nb; }; /* * 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,實體也在此檔案中定義,並在probe函式裡指向此實體 */ 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; struct i2c_bus_recovery_info *bus_recovery_info; }; /** * 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. * * 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 occured 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 *); };

15.2.2 adapter的註冊與登出

  1. adapter被組織成platform驅動形式
  2. 順便理解platform的匹配機制:
  • dts裡有若干device的描述,linux在初始化時會把這些裝置展開,形成裝置列表
  • platform driver中有匹配欄位of_match_table,估計註冊platform驅動時(module_platform_driver),platform bus負責匹配此欄位和已有的dts裝置列表。
  • platform driver和dev列表匹配上以後,driver中的probe就會執行,同時dev列表中的資訊以probe形參struct platform_device *pdev的形式傳遞給probe()函式。

  一般linux的iic、spi、usb等外設都是這個思路,介面卡為別人提供匯流排,但是其本身是掛到platform總線上的。

    3. adapter通過iic-core.c核心層提供的介面,註冊或登出到iic總線上

  • i2c_add_adapter():新增adapter資料結構,核心層裡詳述
  • i2c_del_adapter():刪除adapter設資料結構
static int cdns_i2c_probe(struct platform_device *pdev)  // dts裡的裝置資訊傳遞進來了
{
    struct resource *r_mem;
    struct cdns_i2c *id;
    int ret;

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

  
xxx_adapter_hw_init();    //通常初始化iic介面卡使用的硬體資源,如申請IO地址、中斷號、時鐘等 id->adap.dev.of_node = pdev->dev.of_node; id->adap.algo = &cdns_i2c_algo;  // 把altorithm連進來 id->adap.timeout = CDNS_I2C_TIMEOUT; id->adap.retries = 3; /* Default retry value. */ id->adap.algo_data = id; id->adap.dev.parent = &pdev->dev; ret = i2c_add_adapter(&id->adap); ... } static int cdns_i2c_remove(struct platform_device *pdev) { struct cdns_i2c *id = platform_get_drvdata(pdev); i2c_del_adapter(&id->adap); xxx_adapter_hw_free(); // 硬體相關資源的free return 0; } static const struct of_device_id cdns_i2c_of_match[] = { { .compatible = "cdns,i2c-r1p10", },           { /* end of table */ } }; MODULE_DEVICE_TABLE(of, cdns_i2c_of_match); static struct platform_driver cdns_i2c_drv = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = cdns_i2c_of_match,    // dts匹配的依據 .pm = &cdns_i2c_dev_pm_ops, }, .probe = cdns_i2c_probe, .remove = cdns_i2c_remove, }; module_platform_driver(cdns_i2c_drv);

 iic相關的dts資訊如下

iic相關dts
        ps7_i2c_1: ps7-[email protected] {
            clock-frequency = <400000>;
            clocks = <&clkc 39>;
            compatible = "cdns,i2c-r1p10"; 
            interrupt-parent = <&ps7_scugic_0>;
            interrupts = <0 48 4>;
            reg = <0xe0005000 0x1000>;
            xlnx,has-interrupt = <0x0>;
            #address-cells = <1>;
            #size-cells = <0>;
            [email protected]52 {
                compatible = "at,24c512";
                reg = <0x52>;
            };
        } ;

15.2.3 i2c匯流排的通訊方法 —— algorithm

  主要需要實現i2c_algorithm結構體中的master_xfer()和functionality()函式,i2c_algorithm的作用是高度總結iic匯流排通訊機制,把具體的介面卡(不同型號)與其他通用驅動隔離開。

 functionality()函式比較簡單,返回支援的通訊協議。

  master_xfer()函式在介面卡上完成i2c_msg的資料傳輸。

static const struct i2c_algorithm cdns_i2c_algo = {
    .master_xfer    = cdns_i2c_master_xfer,
    .functionality    = cdns_i2c_func,
};


/**
 * cdns_i2c_func - Returns the supported features of the I2C driver
 * @adap:    pointer to the i2c adapter structure
 *
 * Return: 32 bit value, each bit corresponding to a feature
 */
static u32 cdns_i2c_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
        (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
        I2C_FUNC_SMBUS_BLOCK_DATA;
}


/**
 * cdns_i2c_master_xfer - The main i2c transfer function
 * @adap:    pointer to the i2c adapter driver instance
 * @msgs:    pointer to the i2c message structure
 * @num:    the number of messages to transfer
 *
 * Initiates the send/recv activity based on the transfer message received.
 *
 * Return: number of msgs processed on success, negative error otherwise
 */
static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
                int num)
{
    int ret, count;
    u32 reg;
    struct cdns_i2c *id = adap->algo_data;

    /* Check if the bus is free */
    if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
        return -EAGAIN;

    /*
     * Set the flag to one when multiple messages are to be
     * processed with a repeated start.
     */
    if (num > 1) {
        id->bus_hold_flag = 1;
        reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
        reg |= CDNS_I2C_CR_HOLD;
        cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
    } else {
        id->bus_hold_flag = 0;
    }

    /* Process the msg one by one */
    for (count = 0; count < num; count++, msgs++) {
        if (count == (num - 1))
            id->bus_hold_flag = 0;

        ret = cdns_i2c_process_msg(id, msgs, adap);
        if (ret)
            return ret;

        /* Report the other error interrupts to application */
        if (id->err_status) {
            cdns_i2c_master_reset(adap);

            if (id->err_status & CDNS_I2C_IXR_NACK)
                return -ENXIO;

            return -EIO;
        }
    }

    return num;
}

15.3 core

15.3.1 i2c匯流排、裝置、驅動模型建立和維護

建立i2c匯流排、裝置、驅動大框架。

static int __init i2c_init(void)  // linux初始化時執行
{
int retval; retval = bus_register(&i2c_bus_type);  // 在sys資料夾中建立i2c匯流排,完成將i2c匯流排註冊到系統,與platform匯流排平級,建立 /sys/i2c目錄 if (retval) return retval; #ifdef CONFIG_I2C_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter");  // 建立類,/sys/class/i2c-adapter if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; } #endif retval = i2c_add_driver(&dummy_driver);    // 註冊一個dummy驅動,不知道為了啥? 在 /sys/bus/i2c/drivers目錄下建立了dummy驅動 if (retval) goto class_err; return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif bus_unregister(&i2c_bus_type); return retval; } static void __exit i2c_exit(void) { i2c_del_driver(&dummy_driver); #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); #endif bus_unregister(&i2c_bus_type); } /* We must initialize early, because some subsystems register i2c drivers * in subsys_initcall() code, but are linked (and initialized) before i2c. */ postcore_initcall(i2c_init);    // linux初始化時呼叫 module_exit(i2c_exit);

引出知識:driver中的probe是如何執行的?!匯流排match以後,匯流排結構體中的probe會被執行(核心程式碼實現的),匯流排probe函式會呼叫driver中的probe。所有匯流排、驅動、模型,包括platform都是這種機制。

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
    .pm        = &i2c_device_pm_ops,
};

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 (!driver->probe || !driver->id_table)
        return -ENODEV;

    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");

    acpi_dev_pm_attach(&client->dev, true);
    status = driver->probe(client, i2c_match_id(driver->id_table, client));
    if (status)
        acpi_dev_pm_detach(&client->dev, true);

    return status;
}

15.3.2 新增/刪除adapter介面

// i2c/buses/i2c_cadence.c
static int cdns_i2c_probe(struct platform_device *pdev)
{
    ...
    ret = i2c_add_adapter(&id->adap);        // 新增adapter
    ...
}

/**
 * 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;                // 從dts中自動獲取i2c的adapter的個數
            return __i2c_add_numbered_adapter(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 (id < 0)
        return id;

    adapter->nr = id;

    return i2c_register_adapter(adapter);
}

/**
 * __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 (id < 0)
        return id == -ENOSPC ? -EBUSY : id;

    return i2c_register_adapter(adap);
}



static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0;

    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p))) {
        res = -EAGAIN;
        goto out_list;
    }

    /* Sanity checks */
    if (unlikely(adap->name[0] == '\0')) {
        pr_err("i2c-core: Attempt to register an adapter with "
               "no name!\n");
        return -EINVAL;
    }
    if (unlikely(!adap->algo)) {
        pr_err("i2c-core: Attempt to register adapter '%s' with "
               "no algo!\n", adap->name);
        return -EINVAL;
    }

    rt_mutex_init(&adap->bus_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;

    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)
        goto out_list;

    dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

#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

    /* bus recovery specific initialization */
    if (adap->bus_recovery_info) {
        struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;

        if (!bri->recover_bus) {
            dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
            adap->bus_recovery_info = NULL;
            goto exit_recovery;
        }

        /* Generic GPIO recovery */
        if (bri->recover_bus == i2c_generic_gpio_recovery) {
            if (!gpio_is_valid(bri->scl_gpio)) {
                dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
                adap->bus_recovery_info = NULL;
                goto exit_recovery;
            }

            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->set_scl || !bri->get_scl) {
            /* Generic SCL recovery */
            dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
            adap->bus_recovery_info = NULL;
        }
    }

exit_recovery:
    /* create pre-declared device nodes */
    of_i2c_register_devices(adap);
    acpi_i2c_register_devices(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;
}

15.3.3 新增/刪除i2c_driver介面

向i2c_bus註冊驅動,可用於匹配i2c總線上的裝置,即i2c_client,例如EEPROM等

/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)

/*
 * 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 (unlikely(WARN_ON(!i2c_bus_type.p)))
        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;

    /* 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;

    /* Drivers should switch to dev_pm_ops instead. */
    if (driver->suspend)
        pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
            driver->driver.name);
    if (driver->resume)
        pr_warn("i2c-core: driver [%s] using legacy resume method\n",
            driver->driver.name);

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    i2c_for_each_dev(driver, __process_new_driver);

    return 0;
}
EXPORT_SYMBOL(i2c_register_driver);


/**
 * i2c_del_driver - unregister I2C driver
 * @driver: the driver being unregistered
 * Context: can sleep
 */
void i2c_del_driver(struct i2c_driver *driver)
{
    i2c_for_each_dev(driver, __process_removed_driver);

    driver_unregister(&driver->driver);
    pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
}
EXPORT_SYMBOL(i2c_del_driver);

15.3.4 i2c傳輸介面

外部裝置使用這些標準傳輸介面編寫驅動。

/**
 * 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_adapter(adap);
            if (!ret)
                /* I2C activity is ongoing. */
                return -EAGAIN;
        } else {
            i2c_lock_adapter(adap);
        }

        ret = __i2c_transfer(adap, msgs, num);
        i2c_unlock_adapter(adap);

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

/**
 * i2c_master_send - issue a single I2C message in master transmit mode
 * @client: Handle to slave device
 * @buf: Data that will be written to the slave
 * @count: How many bytes to write, must be less than 64k since msg.len is u16
 *
 * Returns negative errno, or else the number of bytes written.
 */
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
    int ret;
    struct i2c_adapter *adap = client->adapter;
    struct i2c_msg msg;

    msg.addr = client->addr;
    msg.flags = client->flags & I2C_M_TEN;
    msg.len = count;
    msg.buf = (char *)buf;

    ret = i2c_transfer(adap, &msg, 1);

    /*
     * If everything went ok (i.e. 1 msg transmitted), return #bytes
     * transmitted, else error code.
     */
    return (ret == 1) ? count : ret;
}



/**
 * i2c_master_recv - issue a single I2C message in master receive mode
 * @client: Handle to slave device
 * @buf: Where to store data read from slave
 * @count: How many bytes to read, must be less than 64k since msg.len is u16
 *
 * Returns negative errno, or else the number of bytes read.
 */
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
    struct i2c_adapter *adap = client->adapter;
    struct i2c_msg msg;
    int ret;

    msg.addr = client->addr;
    msg.flags = client->flags & I2C_M_TEN;
    msg.flags |= I2C_M_RD;
    msg.len = count;
    msg.buf = buf;

    ret = i2c_transfer(adap, &msg, 1);

    /*
     * If everything went ok (i.e. 1 msg received), return #bytes received,
     * else error code.
     */
    return (ret == 1) ? count : ret;
}

15.4 裝置驅動i2c_driver和i2c_client

i2c_dirver就是i2c標準匯流排裝置驅動模型中的驅動部分,i2c_client可理解為i2c總線上掛的外設。

1. 驅動的註冊和登出

drivers/misc/eeprom/at24.c

// 初始化和驅動模型
static struct i2c_driver at24_driver = {
    .driver = {
        .name = "at24",
        .owner = THIS_MODULE,
    },
    .probe = at24_probe,
    .remove = at24_remove,
    .id_table = at24_ids,
};

static int __init at24_init(void)
{
    if (!io_limit) {
        pr_err("at24: io_limit must not be 0!\n");
        return -EINVAL;
    }

    io_limit = rounddown_pow_of_two(io_limit);
    return i2c_add_driver(&at24_driver);  // 匹配後,driver中的probe就能執行
}
module_init(at24_init);

static void __exit at24_exit(void)
{
    i2c_del_driver(&at24_driver);
}
module_exit(at24_exit);

// id列表
static const struct i2c_device_id at24_ids[] = {
    /* needs 8 addresses as A0-A2 are ignored */
    { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
    /* old variants can't be handled with this generic entry! */
    { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
    { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    /* spd is a 24c02 in memory DIMMs */
    { "spd", AT24_DEVICE_MAGIC(2048 / 8,
        AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
    { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    /* 24rf08 quirk is handled at i2c-core */
    { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
    { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
    { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
    { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
    { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
    { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
    { "at24", 0 },
    { /* END OF LIST */ }
};

//dts:
        ps7_i2c_1: ps7-[email protected] {
            clock-frequency = <1000000>; 
            clocks = <&clkc 39>;
            compatible = "cdns,i2c-r1p10"; 
            interrupt-parent = &l