The structure of the I2C subsystem within the kernel of the I2C subsystem

Source: Internet
Author: User

This article begins by analyzing the kernel's I2C subsystem.

Description: 1. The kernel version of the analysis is 2.6.37.1

2. Development Board for TQ2440, onboard ARM9 (s3c2440)

3.I2C equipment for AT24C02

4. The sequence of analysis is the registration order of the Kernel I2C subsystem (that is, the order in which this series of articles is published).


It is necessary to understand the sequence of initialization functions before entering the code analysis, and the whole framework of I2C should be known after this order.

1. Initialization function Order of execution

1.1 How the order of function execution is determined

The order in which the initialization functions are executed after the kernel compilation link is complete is determined. This is done by invoking the link script/arch/arm/kernel/vmlinux.lds on the link. Scripts specify the location of code snippets for different pieces of code, such as _init, text, data, and so on. Suppose that the same _init function a () and function B () are all placed on the _init address defined in the script, but is the first or B () function of the A () function in front? This is determined by the makefile file in the directory. The order in which the functions are stored in the makefile file is determined if Obj+y = A () is preceded by obj+y = B (), then the a function is executed before the B () function when the last connection is made. So the order in which the function is executed is determined by the 1.vmlinux.lds link Script 2. The driver directory under the makefile file is determined together


first, analyze the control of the order in the link script.

Vmlinux.lds:

#ifndef __armeb__
jiffies = jiffies_64;
#else
jiffies = jiffies_64 + 4;
#endif

SECTIONS
{
                ... 
                Init_call
               Con_initcall
		security_initcall
                ...
}
Where Init_calls is a macro, defined as follows:

#define Initcalls                                                       \
	* (. initcallearly.init)						\
	Vmlinux_symbol (__early_initcall_end) =.;			\
  	* (. initcall0.init) \ * (
  	. initcall0s.init)						\
  	* (. initcall1.init)						\
  	* (. Initcall1s.init)						\
  	* (. initcall2.init)						\
  	* (. initcall2s.init)						\
  	* (. initcall3.init)						\
  	* (. initcall3s.init) \ * (
  	. initcall4.init)						\
  	* (. initcall4s.init)						\
  	* ( . initcall5.init)						\
  	* (. initcall5s.init)						\
	* (. initcallrootfs.init)						\
  	* (. Initcall6.init)						\
  	* (. initcall6s.init)						\
  	* (. initcall7.init)						\
  	* (. initcall7s.init)

#define Init_calls							\
		vmlinux_symbol (__initcall_start) =.;			\
		initcalls						\
		vmlinux_symbol (__initcall_end) =.;
You can see that the order in which the links are stored are:. Initcall0.init, Initcall0s.init, Initcall1.init ...

This is just the syntax in the connection script, in which the function requires a macro to mark the function.

Specifically in/include/linux/init.h, the relevant code is as follows:

#define __define_initcall (level,fn,id) \ static initcall_t __initcall_# #fn # #id __used \ __attribute__ (__section__ (".
 Initcall "level". Init ")) = FN/* Early Initcalls run before initializing SMP.
 * Only for built-in code, not modules. * * #define EARLY_INITCALL (FN) __define_initcall ("early", fn,early) * * A "pure" Initcall has no dependencies on Anythin
 G else, and purely * Initializes variables that couldn ' t is statically initialized.
 * This is exists for built-in code and not for modules. * * #define PURE_INITCALL (FN) __define_initcall ("0", fn,0) #define Core_initcall (FN) __define_initcall ("1", fn,1) # Define Core_initcall_sync (FN) __define_initcall ("1s", fn,1s) #define Postcore_initcall (FN) __define_initcall ("2", FN, 2) #define POSTCORE_INITCALL_SYNC (FN) __define_initcall ("2s", fn,2s) #define Arch_initcall (FN) __define_initcall ("3", fn,3) #define ARCH_INITCALL_SYNC (FN) __define_initcall ("3s", fn,3s) #define Subsys_initcall (FN) __define_initcall ("4" , fn,4) #define SubsYs_initcall_sync (FN) __define_initcall ("4s", fn,4s) #define Fs_initcall (FN) __define_initcall ("5", fn,5) #define Fs_ Initcall_sync (FN) __define_initcall ("5s", fn,5s) #define Rootfs_initcall (FN) __define_initcall ("Rootfs", Fn,rootfs) # Define Device_initcall (FN) __define_initcall ("6", fn,6) #define DEVICE_INITCALL_SYNC (FN) __define_initcall ("6s", FN, 6s) #define Late_initcall (FN) __define_initcall ("7", fn,7) #define LATE_INITCALL_SYNC (FN) __define_initcall ("7s", FN,
 7s) #define __initcall (FN) Device_initcall (FN) ... #define MODULE_INIT (x)     __initcall (x); 
Here it can be found that in the actual drive, the initialization function has the following form: Module_init (Init_func)

Originally Module_init is a macro, you can find Module_init macro eventually expanded will be init_func function as a

The initcall6.init tag, and eventually this function is linked to the initcall6.init position, and the functions of those numbers that are large initcall6.init

Holds the position before the function that is module_init decorated.

Control of Makefile Files

The related source code of the I2C subsystem is concentrated in the/DRIVER/I2C directory

So here we only analyze/driver/i2c/makefile from the Makefile file in the/DRIVER/I2C directory:

obj-$ (config_i2c_boardinfo)     + = i2c-boardinfo.o
obj-$ (config_i2c)               + = I2C-CORE.O
obj-$ (config_i2c_ SMBUS         + + = i2c-smbus.o
obj-$ (config_i2c_chardev)       + + i2c-dev.o
obj-$ (config_i2c_mux)           = I2C-MUX.O
obj-y                           + + algos/busses/muxes/

ccflags-$ (config_i2c_debug_core): =-ddebug

The order of the visible links is i2c.-boardinfo, I2c-core, I2c-dev ... ...


Sequence of initialization functions for 1.2 I2C subsystems

Combined with Vmlinux.lds and makefile, you can determine the order in which I2C initialization functions are executed:

Functions in 1./dricer/i2c/i2c-core.c: I2c_init () Postcore_initcall level

Functions in 2./ARCH/ARM/MACH-S3C2440/MACH-SMDK2440.C: Smdk2440_machine_init () Arch_initcall level

Functions in 3.DRIVER/I2C/BUSES/I2C-S3C2410.C: I2c_adap_s3c_init () Subsys_initcall level

Functions in 4./DRIVER/I2C/I2C-DEV.C: I2c_dev_init () mo Dule_init level


model of I2C subsystem in 2 kernel

In order to facilitate explanation and understanding, the I2C model of the lower kernel is briefly introduced. I2C mainly from the structure, so divided into host and from machine.

The kernel uses adapter to represent the host, the client to represent the machine, and the device model (Bus,device,driver) that conforms to the kernel at the same time.

So the I2C subsystem in the kernel should include adapter device, adapter driver, client device, client driver, bus, etc.

In addition, the first thing to register is the Platform_device type of device and the corresponding driver Platform_driver (where Platform_device contains

Adapter), then the pairing succeeds in calling the probe function, and finally using the Platform_device in the probe function to contain the adapter information

Create adapter, then client, and so on.

1.adapter

The adapter here refers to the s3c2440 I2C controller. The kernel is described by Platform_device. Specifically as follows:

struct Platform_device s3c_device_i2c0 = {
. Name = "S3C2410-I2C",
#ifdef CONFIG_S3C_DEV_I2C1
. id = 0,
#else
. id =-1,
#endif
. num_resources = Array_size (S3c_i2c_resource),
. resource = S3c_i2c_resource,
};

Where name is finally modified to "S3C2440-I2C" in the S3c244x_map_io () function

static struct S3C2410_PLATFORM_I2C default_i2c_data0 __initdata = {
. Flags = 0,
. slave_addr = 0x50,
. frequency = 100*1000,
. Sda_delay = 100,
};

static struct resource s3c_i2c_resource[] = {
[0] = {
. Start = S3c_pa_iic,
. end = S3c_pa_iic + Sz_4k-1,
. Flags = Ioresource_mem,
},
[1] = {
. Start = Irq_iic,
. end = IRQ_IIC,
. Flags = IORESOURCE_IRQ,
},
};


2.client

The client here is AT24C02. The name after initialization is 0-0050, the first digit is the bus number, and the second four digits represent the address from the machine.

Specifically as follows:

static struct At24_platform_data At24c02 = {
. Byte_len = SZ_2K/8,
. page_size = 8,
};

static struct I2c_board_info tq2440_i2c_devs[] __initdata = {
{
I2c_board_info ("24c02", 0x50),
. Platform_data = &at24c02,
},
};

3.bus

In the I2C subsystem refers to the bus does not refer to which specific I2C bus, which is a virtual bus for management, peace is often said that the I2C communication with a different bus.

Usually said I2C bus refers to the adapter and client connected to the specific can be used to communicate the line, and here the so-called bus is used for management, hanging with

All adapter and client in the system.

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,
};

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.