I2C Kernel architecture analysis

This chapter uses linux3.14.0 For reference , Discuss Linux Medium i2c How the controller driver is implemented .

Drive entry

Samsung i2c The controller driver is based on platform Bus implementation ,struct platform_driver The definition is as follows :
 Insert picture description here  Insert picture description here

When the node information of the device tree compatible Information and registration platform_driver.driver. of_match_table The string will pass through platform Bus macth Methods pairing , After successful matching, it will call probe function s3c24xx_i2c_probe().

Drive the core structure

To understand i2c First of all, we must understand these institutions :

s3c24xx_i2c

The structure is Samsung i2c Controller specific structure , Describes all the resources of the controller , Includes a waiting queue for waiting for an interrupt to wake up 、 transmission i2c_msg Temporary pointer to 、 Record the status of communication with hardware 、 Interrupt number 、 Controller base address 、 The clock 、i2c_adapter、 Device tree information pdata etc. .i2c The structure variable is created for the controller when the controller is initialized , And initialize it .

i2c_adapter

Object implements a set of functions through a i2c The controller sends all the information in the message , Including timing , Address, etc. , That is encapsulation. i2c Controller " Control information ". It has been i2c Host driver creation , adopt clien Domain and i2c_client and i2c_driver Connected to a , In this way, the device driver can use the methods and i2c The physical controller comes with a i2c The physical devices of the bus interact with each other .

i2c_algorithm

Describe a i2c Information about the transmission timing of the host , Objects of this class algo yes i2c_adapter A domain of , Where the registered function master_xfer() Finally, it is driven by i2c_transfer() Callback .

i2c_msg

Describe a data flow between the device side and the host side , Package in device driver and pass i2c_transfer() send out . amount to skbuf For network devices ,urb To USB equipment .

The relationship between these structures :
 Insert picture description here

i2c_client

Describe a hook in hardware i2c Device information for devices on the bus , namely i2c The device object of the device , And i2c_driver After the object is successfully matched, it passes detected and i2c_driver as well as i2c_adapter Connected to a , After the controller driver successfully matches with the controller device, it is driven by the controller i2c_new_device() establish . Attached from the device i2c The controller is saved to the member during initialization adapter.

i2c_driver

Describe a hook in hardware i2c How to drive the device on the bus , namely i2c Device driver object , adopt i2c_bus_type And device information i2c_client matching , After the match is successful clients and i2c_client Objects and i2c_adapter Objects are connected .
 Insert picture description here

As shown in the figure above :Linux Kernel maintenance i2c bus Bus , be-all i2c From device information to i2c_client, And register to the i2c Bus , If there is no equipment, it is usually filled in the following documents :

linux-3.14-fs4412\arch\arm\mach-s5pc100\ Mach-smdkc100.c
 Insert picture description here

Kernel boot will i2c_board_info The structure is transformed into i2c_client.

With a device tree , Kernel startup will automatically convert the device tree node to i2c_client.

i2c_adapter

 Let me start with i2c_adapter,  It's not about writing a i2c Device drivers need it ,  Usually when we configure the kernel, we have i2c The device information and drivers of the controller have been compiled into the kernel ,  This is this. adapter The object has been created ,  But understanding the members of it is important to understand i2c The driver framework is very important ,  All device drivers have to go through this object to communicate with physical devices 

//include/linux/i2c.h

 Insert picture description here

  • 428–> This i2c The control algorithm that the controller needs , The most important members are master_xfer() Interface , This interface is hardware related , The operations inside are all based on concrete SoC i2c The register of , It will complete sending data to the physical i2c Controller " Last mile "
  • 436–> This is a device, Will be linked to the linked list in the kernel to manage , Among them
  • 443–> This node will be a i2c_adapter Object and what it belongs to i2c_client Objects and corresponding i2c_driver Objects are connected together

Here is 2 individual i2c-core.c Provided i2c_adapter Directly related operations API, It is not usually used in device driver development .

Adapter initialization

i2c The node information of the device tree of the controller passes through platform The bus passes down , The parameter pdev.probe The main function of initialization is adapter, apply i2c All kinds of resources needed by the controller , At the same time, all the slave devices under the controller are initialized through the device tree node , establish i2c_client Structure .

ps3c24xx_i2c_probe

static int   s3c24xx_i2c_probe(struct platform_device *pdev){   struct s3c24xx_i2c *i2c;// The most important structure    // Save device tree information    struct s3c2410_platform_i2c *pdata =   NULL;   struct resource *res;   int ret;   if (!pdev->dev.of_node) {  pdata =   dev_get_platdata(&pdev->dev);  if (!pdata) { dev_err(&pdev->dev,   "no platform data\n"); return -EINVAL;  }   }   /* Is the structure variable i2c Allocate memory */   i2c = devm_kzalloc(&pdev->dev,   sizeof(struct s3c24xx_i2c), GFP_KERNEL);   if (!i2c) {  dev_err(&pdev->dev,   "no memory for state\n");  return -ENOMEM;   }   i2c->pdata =   devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);   if (!i2c->pdata) {  dev_err(&pdev->dev,   "no memory for platform data\n");  return -ENOMEM;   }   /*i2c Some special behaviors of controller
              #define QUIRK_S3C2440              (1 << 0)
              #define QUIRK_HDMIPHY            (1 << 1)
              #define QUIRK_NO_GPIO             (1 << 2)
              #define QUIRK_POLL            (1 << 3)
               among bite:3 If the rotation training mode is adopted to communicate with the underlying hardware, the value is 1, The interrupt mode value is 0*/   i2c->quirks =   s3c24xx_get_device_quirks(pdev);   if (pdata)  memcpy(i2c->pdata, pdata,   sizeof(*pdata));   else  s3c24xx_i2c_parse_dt(pdev->dev.of_node,   i2c);   strlcpy(i2c->adap.name,   "s3c2410-i2c", sizeof(i2c->adap.name));   i2c->adap.owner   = THIS_MODULE;   /* by i2c_msg Transfer method assignment ,*/   i2c->adap.algo    = &s3c24xx_i2c_algorithm;   i2c->adap.retries = 2;   i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;   i2c->tx_setup     = 50;   // Initialize wait queue , The waiting queue is used to wake up the process of reading and writing data    init_waitqueue_head(&i2c->wait);   /* find the clock and enable it */   i2c->dev = &pdev->dev;   // Get clock    i2c->clk =   devm_clk_get(&pdev->dev, "i2c");   if (IS_ERR(i2c->clk)) {  dev_err(&pdev->dev,   "cannot get clock\n");  return -ENOENT;   }   dev_dbg(&pdev->dev, "clock   source %p\n", i2c->clk);   /* map the registers */   // adopt pdev obtain i2c Register address resource of the controller    res = platform_get_resource(pdev,   IORESOURCE_MEM, 0);   // mapping i2c The physical base address of the controller is the virtual base address    i2c->regs =   devm_ioremap_resource(&pdev->dev, res);   if (IS_ERR(i2c->regs))  return PTR_ERR(i2c->regs);   dev_dbg(&pdev->dev,   "registers %p (%p)\n",  i2c->regs, res);   /* setup info block for the i2c core */   /* Change the structure variable i2c Save to i2c_adapter Private variable pointer to algo_data,
        To write i2c Device drivers can be driven by adapter The pointer finds the structure i2c*/   i2c->adap.algo_data = i2c;   i2c->adap.dev.parent =   &pdev->dev;   i2c->pctrl =   devm_pinctrl_get_select_default(i2c->dev);   /* inititalise the i2c gpio lines */   // obtain i2c Reuse of gpio Pin and initialize    if (i2c->pdata->cfg_gpio) {  i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));   } else if (IS_ERR(i2c->pctrl)   && s3c24xx_i2c_parse_dt_gpio(i2c)) {  return -EINVAL;   }   /* initialise the i2c controller */   clk_prepare_enable(i2c->clk);   /* Write from device address to register S3C2410_IICADD, Also initialize the clock frequency */   ret = s3c24xx_i2c_init(i2c);   clk_disable_unprepare(i2c->clk);   if (ret != 0) {  dev_err(&pdev->dev,   "I2C controller init failed\n");  return ret;   }   /* find the IRQ for this unit (note,   this relies on the init call to
        * ensure no current IRQs pending
        */   if (!(i2c->quirks & QUIRK_POLL))   {  /* from plat_device Get the interrupt number in */ i2c->irq = ret =   platform_get_irq(pdev, 0);  if (ret <= 0) { dev_err(&pdev->dev,   "cannot find IRQ\n"); return ret;  }   /* Register interrupt handler s3c24xx_i2c_irq()*/   ret =   devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0,dev_name(&pdev->dev),   i2c);
              if (ret != 0) { dev_err(&pdev->dev,   "cannot claim IRQ %d\n", i2c->irq); return ret;  }   }   ret =   s3c24xx_i2c_register_cpufreq(i2c);   if (ret < 0) {  dev_err(&pdev->dev,   "failed to register cpufreq notifier\n");  return   ret;   }   /* Note, previous versions of the   driver used i2c_add_adapter()
        * to add the bus at any number. We now pass   the bus number via
        * the platform data, so if unset it will now   default to always
        * being bus 0.
        */   /* preservation i2c The channel number of the controller , This example is bus 5*/   i2c->adap.nr =   i2c->pdata->bus_num;   i2c->adap.dev.of_node =   pdev->dev.of_node;   // register adapter   ret =   i2c_add_numbered_adapter(&i2c->adap);   if (ret < 0) {  dev_err(&pdev->dev,   "failed to add bus to i2c core\n");  s3c24xx_i2c_deregister_cpufreq(i2c);  return ret;   }   /* Save private variables i2c To pdev->dev->p->driver_data*/   platform_set_drvdata(pdev, i2c);   pm_runtime_enable(&pdev->dev);   pm_runtime_enable(&i2c->adap.dev);   dev_info(&pdev->dev, "%s:   S3C I2C adapter\n", dev_name(&i2c->adap.dev));   return 0;}

i2c_add_numbered_adapter

The old registration function is i2c_add_adapter() The new version encapsulates the function , take i2c The control channel number has been registered , By default nr The value is 0.

i2c_add_numbered_adapter->__i2c_add_numbered_adapter-> i2c_register_adapter

int   i2c_add_numbered_adapter(struct i2c_adapter *adap){   if (adap->nr == -1) /* -1 means   dynamically assign bus id */  return i2c_add_adapter(adap);   return __i2c_add_numbered_adapter(adap);}

i2c_add_adapter

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;   // Set up adapter name , This example will generate the following nodes after registration /dev/i2c-5   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   */   /* initialization sda、scl, Usually these two pins are multiplexed gpio Pin */   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; /*sda、scl Resource assignment */ 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 */   /* Register all the slave devices under the controller through the device tree node */   of_i2c_register_devices(adap);   acpi_i2c_register_devices(adap);   /* Associated with dynamically assigned bus numbers , The dynamically assigned bus number should be based on the existing maximum bus number +1 Of ,
        This ensures that the dynamically assigned bus number will not conflict with the board level bus number
        Without a device tree , Will be based on the queue __i2c_board_list,  establish i2c_client
        Where nodes struct i2c_board_info Fill in by hand */   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;}

of_i2c_register_devices

This function is used to convert the slave device node to i2c_client, And register to the i2c On the bus .

static void   of_i2c_register_devices(struct i2c_adapter *adap){   void *result;   struct device_node *node;   /* Only register child devices if the   adapter has a node pointer set */   if (!adap->dev.of_node)  return;   dev_dbg(&adap->dev,   "of_i2c: walking child nodes\n");   for_each_available_child_of_node(adap->dev.of_node,   node) {  struct i2c_board_info info = {};  struct dev_archdata dev_ad = {};  const __be32 *addr;  int len;  dev_dbg(&adap->dev,   "of_i2c: register %s\n", node->full_name);  if (of_modalias_node(node,   info.type, sizeof(info.type)) < 0) { dev_err(&adap->dev,   "of_i2c: modalias failure on %s\n",node->full_name); continue;  }  /* Get the address of the slave device */  addr = of_get_property(node,   "reg", &len);  if (!addr || (len <   sizeof(int))) { dev_err(&adap->dev,   "of_i2c: invalid reg on %s\n",node->full_name); continue;  }  /* Store slave address */  info.addr = be32_to_cpup(addr);  if (info.addr > (1 <<   10) - 1) { dev_err(&adap->dev,   "of_i2c: invalid addr=%x on %s\n",info.addr,   node->full_name); continue;  }  /* Get interrupt number */  info.irq =   irq_of_parse_and_map(node, 0);  info.of_node =   of_node_get(node);  info.archdata = &dev_ad;  /* Get the device tree node wakeup-source Information */  if (of_get_property(node,   "wakeup-source", NULL)) info.flags |=   I2C_CLIENT_WAKE;  request_module("%s%s",   I2C_MODULE_PREFIX, info.type);  /* take i2c_board_info convert to i2c_client And register to the i2c Bus */  result = i2c_new_device(adap,   &info);  if (result == NULL) { dev_err(&adap->dev,   "of_i2c: Failure registering %s\n",node->full_name); of_node_put(node); irq_dispose_mapping(info.irq); continue;  }   }}

i2c_new_device ( )

take i2c_board_info convert to i2c_client And register to the Linux The core .

{   struct i2c_client      *client;   int                 status;   /* to i2c_client Allocate memory */   client = kzalloc(sizeof *client, GFP_KERNEL);   if (!client)  return NULL;   /* take adapter Save your address to i2c_client->adapter,
        In the driver function, you can use i2c_client find adapter*/   client->adapter = adap;   client->dev.platform_data =   info->platform_data;   if (info->archdata)  client->dev.archdata =   *info->archdata;   /* Save slave address type */   client->flags = info->flags;   /* Save slave device address */   client->addr = info->addr;   /* Save slave interrupt number */   client->irq = info->irq;  
       strlcpy(client->name, info->type,   sizeof(client->name));   /* Check for address validity */   /* Check whether the slave address is legal , The number of primary check points */   status =   i2c_check_client_addr_validity(client);   if (status) {  dev_err(&adap->dev,   "Invalid %d-bit I2C address 0x%02hx\n", client->flags &   I2C_CLIENT_TEN ? 10 : 7, client->addr);  goto out_err_silent;   }   /* Check for address business */   /* Detect whether the slave address is occupied , The same slave address under the same controller can only be registered once */   status = i2c_check_addr_busy(adap,   client->addr);   if (status)  goto out_err;   /* Establish a parent-child relationship between the slave device and the adapter */   client->dev.parent =   &client->adapter->dev;   client->dev.bus = &i2c_bus_type;   client->dev.type =   &i2c_client_type;   client->dev.of_node = info->of_node;   ACPI_COMPANION_SET(&client->dev,   info->acpi_node.companion);   i2c_dev_set_name(adap, client);   /* Sign up to Linux The core */   status =   device_register(&client->dev);   if (status)  goto out_err;   dev_dbg(&adap->dev, "client   [%s] registered with bus id %s\n",  client->name,   dev_name(&client->dev));   return client;out_err:   dev_err(&adap->dev, "Failed   to register i2c client %s at 0x%02x "  "(%d)\n",   client->name, client->addr, status);out_err_silent:   kfree(client);   return NULL;}

i2c_msg How to transfer ?

The core approach i2c_transfer

l i2c_transfer() yes i2c The core provides the sending method for the device driver , The data sent through it needs to be packaged into i2c_msg, This function will eventually call back the corresponding i2c_adapter->i2c_algorithm->master_xfer() The interface will i2c_msg Object sent to i2c Physical controller ,

i2c_adapte->algo In function s3c24xx_i2c_probe() Assignment in :
 Insert picture description here

The variable is defined as follows :
 Insert picture description here
i2c_transfer() The function will eventually be called s3c24xx_i2c_xfer();

i2c_msg Interrupt transmission

Here is one time i2c_msg General steps of interrupt mode of transmission :

  1. i2c_transfer() First, through the function i2c_trylock_adapter() Try to get adapter Control right . If adapter Busy, error message is returned ;

  2. __i2c_transfer() By calling a method adap->algo->master_xfer(adap, msgs, num) transmission i2c_msg, If it fails, it will try to retransmit , Most retransmissions adap->retries;

  3. adap->algo->master_xfer() That's the function. s3c24xx_i2c_xfer(), The function eventually calls s3c24xx_i2c_doxfer(i2c, msgs, num) Transmit information ;

  4. s3c24xx_i2c_doxfer() By function s3c24xx_i2c_message_start(i2c, msgs) produce S and AD+W The signal of , And then through the function wait_event_timeout( ) Blocking waiting queue i2c->wait On ;

  5. In the upper right corner mpu6050 The timing of writing and reading , Reply from device ACK and DATA Will send interrupt signal to CPU. Every interrupt calls s3c24xx_i2c_irq->i2c_s3c_irq_nextbyte,

  6. The last interruption , After all data is sent or read, it will call s3c24xx_i2c_stop->s3c24xx_i2c_master_complete, adopt wake_up Wake up blocked waiting queue i2c->wait Tasks on .

The detailed code flow is as follows :
 Insert picture description here

  1. i2c_transfer() First, through the function i2c_trylock_adapter() Try to get adapter Control right . If adapter Busy, error message is returned ;

  2. __i2c_transfer() By calling a method adap->algo->master_xfer(adap,msgs, num) transmission i2c_msg, If it fails, it will try to retransmit , Most retransmissions adap->retries;

  3. adap->algo->master_xfer() That's the function. s3c24xx_i2c_xfer(), The function eventually calls s3c24xx_i2c_doxfer(i2c, msgs, num) Transmit information ;

  4. s3c24xx_i2c_doxfer() By function s3c24xx_i2c_message_start(i2c, msgs) produce S and AD+W The signal of , And then through the function wait_event_timeout() Blocking waiting queue i2c->wait On ;

  5. In the upper right corner mpu6050 The timing of writing and reading , Reply from device ACK and DATA Will send interrupt signal to CPU. Every interrupt calls s3c24xx_i2c_irq->i2c_s3c_irq_nextbyte,

  6. The last interruption , After all data is sent or read, it will call s3c24xx_i2c_stop->s3c24xx_i2c_master_complete, adopt wake_up Wake up blocked waiting queue i2c->wait Tasks on .

The detailed code flow is as follows :
 Insert picture description here

You can follow the code step by step according to the code line number in the figure above , When it comes to register settings, please refer to the screenshot of register usage in Chapter 1 .