Complete Linux I2C Driver Analysis (2)

Source: Internet
Author: User

Blogger Press: hot day, just loaded from the fifth crossing to the stone room, about 4 kilometers. Finally let me find a coffee house to take a break and continue to write this driver analysis. Single life is boring. Don't complain. Let's show it to yourself! It's difficult to step on at your feet! Continue the whole process !~

 

First, I have a question in this article, but I have never understood it. I am very grateful to you for writing it here!

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

What does i2c_func_protocol_mangling mean? Why are these things defined? I am not quite familiar with the annotations. Please explain!

 

3. I2C bus driver code analysis

The bus driver code of S3C2440 is in i2c-s3c2410.c. As shown in the following example, start with init.

Static int _ init i2c_adap_initi_init (void) <br/>{< br/> return platform_driver_register (& s3c24xx_i2c_driver); <br/>}

In init, only the platform driver registration function is called to register an I2C platform driver s3c24xx_i2c_driver. This driver is a structure variable of platform_driver. Note that this is not the i2c_driver structure, because i2c_driver is the driver of the device, and the driver of the controller must use platform_driver.

Static struct platform_driver s3c24xx_i2c_driver ={< br/>. probe = s3c24xx_i2c_probe, <br/>. remove = s3c24xx_i2c_remove, <br/>. suspend_late = s3c24xx_i2c_suspend_late, <br/>. resume = s3c24xx_i2c_resume, <br/>. id_table = s3c24xx_driver_ids, <br/>. driver ={< br/>. owner = this_module, <br/>. name = "s3c-i2c", <br/>}, <br/> };

Similarly, the important functions are probe, remove, suspend_late, and resume. Add an id_table and device_driver struct variable.

The following analysis is performed one by one:

* Probe function

When the platform_driver_register function is called to register the platform_driver struct, The s3c24xx_i2c_probe function pointed to by the probe pointer will be called. For a detailed explanation of this part, refer to another article in this blog, "Analysis of the S3C2410 dog driver". Careful friends may find that in the s3c24xx_i2c_driver, the driver name is "s3c-i2c", and in the Board file can be seen that the device name is "s3c2410-i2c", these two names are not the same, how does the driver match the device? The answer is id_table. This id_table contains the device ID table supported by the driver. During the match operation, check whether the table name is the same as the device name. If the table name is the same, the match is successful. This is why a driver can match multiple devices at the same time. If the driver is matched by the name in platform_driver --> driver, the driver and the device can only be one-to-one.

Static struct platform_device_id s3c24xx_driver_ids [] ={< br/>{< br/>. name = "s3c2410-i2c", <br/>. driver_data = type_s3c2410, <br/>},{ <br/>. name = "s3c2440-i2c", <br/>. driver_data = type_s3c2440, <br/>},{}, <br/> };

Let's take a look at the probe code ~

Static int s3c24xx_i2c_probe (struct platform_device * pdev) <br/>{< br/> struct s3c24xx_i2c * I2C; <br/> struct s3c2410_platform_i2c * pdata; <br/> struct resource * res; <br/> int ret; </P> <p> pdata = pdev-> Dev. platform_data; <br/> If (! Pdata) {<br/> dev_err (& pdev-> Dev, "no platform data/N"); <br/> return-einval; <br/>}< br/> // apply for space for the s3c24xx_i2c struct <br/> I2C = kzarloc (sizeof (struct s3c24xx_i2c), gfp_kernel); <br/> If (! I2C) {<br/> dev_err (& pdev-> Dev, "no memory for State/N"); <br/> return-enomem; <br/>}< br/> // fill in items in the s3c24xx_i2c struct, including name, owner, algorithm, and Class. <br/> strlcpy (I2C-> ADAP. name, "s3c2410-i2c", sizeof (I2C-> ADAP. name); <br/> I2C-> ADAP. owner = this_module; <br/> I2C-> ADAP. algo = & s3c24xx_i2c_algorithm; // This section focuses on <br/> I2C-> ADAP. retries = 2; <br/> I2C-> ADAP. class = i2c_class_hwmon | i2c_class_spd; <br/> I2C-> tx_setup = 50; </P> <p> spin_lock_init (& I2C-> lock); <br/> init_waitqueue_head (& I2C-> wait ); </P> <p>/* Find the clock and enable it */<br/> // find the I2C always and enable it <br/> I2C-> Dev = & pdev-> dev; <br/> I2C-> CLK = clk_get (& pdev-> Dev, "I2C"); <br/> If (is_err (I2C-> CLK )) {<br/> dev_err (& pdev-> Dev, "cannot get clock/N"); <br/> ret =-enoent; <br/> goto err_noclk; <br/>}</P> <p> dev_dbg (& pdev-> Dev, "clock source % P/N", I2C-> CLK); </P> <P> clk_enable (I2C-> CLK ); </P> <p>/* map the registers */<br/>/* ing register */<br/> res = platform_get_resource (pdev, ioresource_mem, 0 ); <br/> If (RES = NULL) {<br/> dev_err (& pdev-> Dev, "cannot find IO resource/N "); <br/> ret =-enoent; <br/> goto err_clk; <br/>}</P> <p> I2C-> ioarea = request_mem_region (res-> start, resource_size (RES), <br/> pdev-> name); </P> <p> If (I2C-> ioarea = NULL) {<br/> dev_err (& pdev-> de V, "cannot request IO/N"); <br/> ret =-enxio; <br/> goto err_clk; <br/>}</P> <p> I2C-> regs = ioremap (res-> Start, resource_size (RES )); </P> <p> If (I2C-> regs = NULL) {<br/> dev_err (& pdev-> Dev, "cannot map IO/N "); <br/> ret =-enxio; <br/> goto err_ioarea; <br/>}</P> <p> dev_dbg (& pdev-> Dev, "registers % P (% P, % P)/n", <br/> I2C-> regs, I2C-> ioarea, Res ); </P> <p>/* setup info block for the I2C core */</P> <p> I2C-> ADAP. algo_data = I2C; <br/> I2C-> ADAP. dev. parent = & pdev-> dev; </P> <p>/* initialise the I2C controller */<br/>/* after filling in the necessary information of the s3c24xx_i2c struct variable I2C, start initialization */<br/> ret = s3c24xx_i2c_init (I2C); <br/> If (Ret! = 0) <br/> goto err_iomap; </P> <p>/* Find the IRQ for this unit (note, this relies on the init call to <br/> * ensure no current irqs pending <br/> */<br/> // apply for interruption next <br/> I2C-> IRQ = ret = platform_get_irq (pdev, 0); <br/> If (Ret <= 0) {<br/> dev_err (& pdev-> Dev, "cannot find IRQ/N "); <br/> goto err_iomap; <br/>}</P> <p> ret = request_irq (I2C-> IRQ, s3c24xx_i2c_irq, ir1__disabled, <br/> dev_name (& pdev-> Dev ), I2C); </P> <p> If (Ret! = 0) {<br/> dev_err (& pdev-> Dev, "cannot claim IRQ % d/N", I2C-> IRQ); <br/> goto err_iomap; <br/>}</P> <p> ret = s3c24xx_i2c_register_cpufreq (I2C); <br/> If (Ret <0) {<br/> dev_err (& pdev-> Dev, "failed to register cpufreq notifier/N"); <br/> goto err_irq; <br/>}</P> <p>/* Note, previous versions of the driver used i2c_add_adapter () <br/> * to add the bus at any number. we now pass the bus number via <br/> * The Platform data, so if unset it will now default to always <br/> * being bus 0. <br/> */</P> <p> I2C-> ADAP. nr = pdata-> bus_num; </P> <p> // you can see it? The i2c_add_adapter function in the i2c-core is called below to add an I2C controller <br/> // The difference between i2c_add_numbered_adapter and i2c_add_adapter is that the former is used to add an integrated adapter, the latter is used to add an external CPU adapter. Obviously, the former should be used here. <Br/> ret = i2c_add_numbered_adapter (& I2C-> ADAP); <br/> If (Ret <0) {<br/> dev_err (& pdev-> Dev, "failed to add bus to I2C Core/N"); <br/> goto err_cpufreq; <br/>}</P> <p> platform_set_drvdata (pdev, I2C ); </P> <p> dev_info (& pdev-> Dev, "% s: s3c I2C adapter/N", dev_name (& I2C-> ADAP. dev); <br/> return 0; </P> <p> err_cpufreq: <br/> s3c24xx_i2c_deregister_cpufreq (I2C); </P> <p> err_irq: <br/> free_irq (I2C-> IRQ, I2C); </P> <p> err_iomap: <br/> iounmap (I2C-> regs ); </P> <p> err_ioarea: <br/> release_resource (I2C-> ioarea); <br/> kfree (I2C-> ioarea ); </P> <p> err_clk: <br/> clk_disable (I2C-> CLK); <br/> clk_put (I2C-> CLK ); </P> <p> err_noclk: <br/> kfree (I2C); <br/> return ret; <br/>}

 

* Remove Function

This is a function opposite to probe, called at i2c_adap_initi_exit. The main function is to unregister the adapter, release the interrupt, release the memory area, and disable the persistence. I can see all the parts of err _ in the above Code? Remove is a summary of them.

 

* Suspend and resume Functions

Let's put the two together. The suspension and restoration functions. When suspended, save the status and set the flag bit. When restored, reinitialize the I2C adapter and set the flag bit.

 

Algorithm

Oh, I am here. It makes me uncomfortable. Here we will focus on the introduction, not only to know its own, but also to know its own, so that we will be confident in writing our own drivers.

Static const struct i2c_algorithm s3c24xx_i2c_algorithm ={< br/>. master_xfer = s3c24xx_i2c_xfer, <br/>. Functionality = s3c24xx_i2c_func, <br/> };

The implementation here is the s3c24xx_i2c_xfer. This is the key to whether the controller can be operated. If this is missing, the controller is the scrap copper.

Static int s3c24xx_i2c_xfer (struct i2c_adapter * ADAP, <br/> struct i2c_msg * msgs, int num) <br/>{< br/> struct s3c24xx_i2c * I2C = (struct s3c24xx_i2c *) ADAP-> algo_data; <br/> int retry; <br/> int ret; </P> <p> for (retry = 0; retry <ADAP-> retries; retry ++) {</P> <p> ret = s3c24xx_i2c_doxfer (I2C, msgs, num); </P> <p> If (Ret! =-Eagain) <br/> return ret; </P> <p> dev_dbg (I2C-> Dev, "retrying transmission (% d)/n", retry ); </P> <p> udelay (100); <br/>}</P> <p> return-eremoteio; <br/>}

 

The function used to complete the task is s3c24xx_i2c_doxfer (). The source code is as follows,Static int s3c24xx_i2c_doxfer (struct s3c24xx_i2c * I2C, <br/> struct i2c_msg * msgs, int num) <br/>{< br/> unsigned long timeout; <br/> int ret; </P> <p> If (I2C-> suincluded) <br/> return-EIO; </P> <p> ret = s3c24xx_i2c_set_master (I2C); <br/> If (Ret! = 0) {<br/> dev_err (I2C-> Dev, "cannot get bus (error % d)/n", RET); <br/> ret =-eagain; <br/> goto out; <br/>}</P> <p> spin_lock_irq (& I2C-> lock ); </P> <p> I2C-> MSG = msgs; <br/> I2C-> msg_num = num; <br/> I2C-> msg_ptr = 0; <br/> I2C-> msg_idx = 0; <br/> I2C-> state = state_start; </P> <p> s3c24xx_i2c_enable_irq (I2C ); <br/> s3c24xx_i2c_message_start (I2C, MSGs); <br/> spin_unlock_irq (& I2C-> lock); </P> <p> timeout = wait_e Vent_timeout (I2C-> wait, I2C-> msg_num = 0, Hz * 5); </P> <p> ret = I2C-> msg_idx; </P> <p>/* having these next two as dev_err () makes life very <br/> * noisy when doing an i2cdetect */</P> <p> If (timeout = 0) <br/> dev_dbg (I2C-> Dev, "timeout/N"); <br/> else if (Ret! = Num) <br/> dev_dbg (I2C-> Dev, "incomplete xfer (% d)/n", RET ); </P> <p>/* Ensure the stop has been through the bus */</P> <p> msleep (1); </P> <p> out: <br/> return ret; <br/>}

The code above can be divided into several parts:

* The function s3c24xx_i2c_set_master () checks the I2C bus status every 1 ms. The timeout value is 400 ms. If the bus status is not busy during this period, zero is returned. Otherwise,-etimedout is returned.

* The messages and other information to be sent are sent to I2C-> MSG and other variables, and the status is set to state_start.

* S3c24xx_i2c_enable_irq () Enable interruption

* S3c24xx_i2c_message_start () is a top priority. Before reading the code, let's take a look at what is said on datasheet 2440.

The code list is as follows:Static void s3c24xx_i2c_message_start (struct s3c24xx_i2c * I2C, <br/> struct i2c_msg * MSG) <br/>{< br/> unsigned int ADDR = (MSG-> ADDR & 0x7f) <1; <br/> unsigned Long STAT; <br/> unsigned long iiccon; </P> <p> stat = 0; <br/> stat | = s3c2410_iicstat_txrxen; </P> <p> If (MSG-> flags & i2c_m_rd) {// if it is read data, from slave to masterstat | = s3c2410_iicstat_master_rx; <br/> ADDR | = 1; <br/>} else <br/> stat | = s3c2410_iicstat_master_tx; </P> <p> If (MSG-> flags & i2c_m_rev_dir_addr) <br/> ADDR ^ = 1; </P> <p>/* todo-check for wether ack wanted or not */<br/> s3c24xx_i2c_enable_ack (I2C ); </P> <p> iiccon = readl (I2C-> regs + s3c2410_iiccon); <br/> writel (stat, I2C-> regs + s3c2410_iicstat ); </P> <p> dev_dbg (I2C-> Dev, "Start: % 08lx to iicstat, % 02x to DS/N", stat, ADDR ); <br/> writeb (ADDR, I2C-> regs + s3c2410_iicds ); </P> <p>/* delay here to ensure the Data byte has gotten onto the bus <br/> * before the transaction is started */</P> <p> ndelay (I2C-> tx_setup ); </P> <p> dev_dbg (I2C-> Dev, "iiccon, % 08lx/N", iiccon); <br/> writel (iiccon, i2C-> regs + s3c2410_iiccon); </P> <p> stat | = s3c2410_iicstat_start; <br/> writel (stat, I2C-> regs + s3c2410_iicstat ); <br/>}

 

(Not finished today. Continue tomorrow ~)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.