日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

《linux设备驱动开发详解》笔记——15 linux i2c驱动

發布時間:2023/12/29 linux 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《linux设备驱动开发详解》笔记——15 linux i2c驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

《linux設備驅動開發詳解》筆記——15 linux i2c驅動

?

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_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, 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的注冊與注銷

  • adapter被組織成platform驅動形式
  • 順便理解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(); // 硬件相關資源的freereturn 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相關dtsps7_i2c_1: ps7-i2c@e0005000 {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>;eeprom@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_COMPATi2c_adapter_compat_class = class_compat_register("i2c-adapter");  // 創建類,/sys/class/i2c-adapterif (!i2c_adapter_compat_class) {retval = -ENOMEM;goto bus_err;} #endifretval = i2c_add_driver(&dummy_driver);    // 注冊一個dummy驅動,不知道為了啥? 在 /sys/bus/i2c/drivers目錄下創建了dummy驅動if (retval)goto class_err;return 0;class_err: #ifdef CONFIG_I2C_COMPATclass_compat_unregister(i2c_adapter_compat_class); bus_err: #endifbus_unregister(&i2c_bus_type);return retval; }static void __exit i2c_exit(void) {i2c_del_driver(&dummy_driver); #ifdef CONFIG_I2C_COMPATclass_compat_unregister(i2c_adapter_compat_class); #endifbus_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_COMPATres = 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;elsebri->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 DEBUGfor (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) ? "+" : "");} #endifif (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-i2c@e0005000 {clock-frequency = <1000000>; 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>;eeprom@50 {compatible = "at,24c512";reg = <0x50>;};rtc@68 {compatible = "ds,ds1338";reg = <0x68>;};} ;

    ?

    ?

    2. driver中的probe和remove分析

    static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) {struct at24_platform_data chip;bool writable;int use_smbus = 0;struct at24_data *at24;int err;unsigned i, num_addresses;kernel_ulong_t magic;if (client->dev.platform_data) {chip = *(struct at24_platform_data *)client->dev.platform_data;} else {if (!id->driver_data)return -ENODEV;magic = id->driver_data;chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));magic >>= AT24_SIZE_BYTELEN;chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);/** This is slow, but we can't know all eeproms, so we better* play safe. Specifying custom eeprom-types via platform_data* is recommended anyhow.*/chip.page_size = 1;/* update chipdata if OF is present */at24_get_ofdata(client, &chip);chip.setup = NULL;chip.context = NULL;}if (!is_power_of_2(chip.byte_len))dev_warn(&client->dev,"byte_len looks suspicious (no power of 2)!\n");if (!chip.page_size) {dev_err(&client->dev, "page_size must not be 0!\n");return -EINVAL;}if (!is_power_of_2(chip.page_size))dev_warn(&client->dev,"page_size looks suspicious (no power of 2)!\n");/* Use I2C operations unless we're stuck with SMBus extensions. */if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {if (chip.flags & AT24_FLAG_ADDR16)return -EPFNOSUPPORT;if (i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;} else if (i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_WORD_DATA)) {use_smbus = I2C_SMBUS_WORD_DATA;} else if (i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_BYTE_DATA)) {use_smbus = I2C_SMBUS_BYTE_DATA;} else {return -EPFNOSUPPORT;}}if (chip.flags & AT24_FLAG_TAKE8ADDR)num_addresses = 8;elsenum_addresses = DIV_ROUND_UP(chip.byte_len,(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);if (!at24)return -ENOMEM;mutex_init(&at24->lock);at24->use_smbus = use_smbus;at24->chip = chip;at24->num_addresses = num_addresses;/** Export the EEPROM bytes through sysfs, since that's convenient.* By default, only root should see the data (maybe passwords etc)*/sysfs_bin_attr_init(&at24->bin);at24->bin.attr.name = "eeprom";at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;at24->bin.read = at24_bin_read;at24->bin.size = chip.byte_len;    // 讀寫函數使用i2c-core.c提供的標準transfer接口at24->macc.read = at24_macc_read;writable = !(chip.flags & AT24_FLAG_READONLY);if (writable) {if (!use_smbus || i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {unsigned write_max = chip.page_size;at24->macc.write = at24_macc_write;at24->bin.write = at24_bin_write;at24->bin.attr.mode |= S_IWUSR;if (write_max > io_limit)write_max = io_limit;if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)write_max = I2C_SMBUS_BLOCK_MAX;at24->write_max = write_max;/* buffer (data + address at the beginning) */at24->writebuf = devm_kzalloc(&client->dev,write_max + 2, GFP_KERNEL);if (!at24->writebuf)return -ENOMEM;} else {dev_warn(&client->dev,"cannot write due to controller restrictions.");}}at24->client[0] = client;/* use dummy devices for multiple-address chips */for (i = 1; i < num_addresses; i++) {at24->client[i] = i2c_new_dummy(client->adapter,client->addr + i);if (!at24->client[i]) {dev_err(&client->dev, "address 0x%02x unavailable\n",client->addr + i);err = -EADDRINUSE;goto err_clients;}}err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);  // !!! 在sysfs中創建文件,讀此文件的讀寫,就是對EEPROM的讀寫,at24-bin的讀寫函數與i2c底層操作掛鉤if (err)goto err_clients;i2c_set_clientdata(client, at24);dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",at24->bin.size, client->name,writable ? "writable" : "read-only", at24->write_max);if (use_smbus == I2C_SMBUS_WORD_DATA ||use_smbus == I2C_SMBUS_BYTE_DATA) {dev_notice(&client->dev, "Falling back to %s reads, ""performance will suffer\n", use_smbus ==I2C_SMBUS_WORD_DATA ? "word" : "byte");}/* export data to kernel code */if (chip.setup)chip.setup(&at24->macc, chip.context);return 0;err_clients:for (i = 1; i < num_addresses; i++)if (at24->client[i])i2c_unregister_device(at24->client[i]);return err; }static int at24_remove(struct i2c_client *client) {struct at24_data *at24;int i;at24 = i2c_get_clientdata(client);sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);for (i = 1; i < at24->num_addresses; i++)i2c_unregister_device(at24->client[i]);return 0; }

    ?

    15.5 i2c-dev.c文件分析

    采用file_oprations方式,組織標準字符設備驅動,對adapter進行設備化,應用層可以通過read、write函數對adapter進行直接操作。

    15.6 總結

    研究過i2c驅動以后,再看spi、usb等驅動框架,幾乎是一樣的,如下表。

    ?

    總結

    以上是生活随笔為你收集整理的《linux设备驱动开发详解》笔记——15 linux i2c驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。