Linux i2c subsystem architecture, linuxi2c Subsystem
Writing i2c Device Drivers (slave devices) can be either of the following methods:
1. You can write an independent Slave Device Driver and use it directly.
2. the Linux kernel has implemented a general device driver. The general device driver is used to write an application (User-mode driver) and a large number of interfaces provided by the device driver are used in the application, use applications to control slave devices.
Bus driver
4.1 Overview
The I2C bus driver is a software implementation of the I2C Adapter. It provides the ability to complete data communication between the I2C adapter and the slave device, such as the start, stop, response signal, and master_xfer implementation functions.
The I2C bus driver is described by i2c_adapter and i2c_algorithm.
4.2 Hardware Description of S3c2440I2C Controller
The S3c2440 processor is integrated with an I2C controller, which is controlled by four registers:
IICCON I2C control register
IICSTAT I2C Status Register
IICDS I2C data transmission and receiving shift register
IICADD I2C Address Register
Through the IICCON, IICDS, and IICADD registers, the start bit, stop bit, data, and address can be generated on the I2C bus, and the transmission status can be obtained through the IICSTAT register.
4.3 i2c-s3c2410 bus driver analysis (platform_driver)
I2C bus driver code in drivers/i2c/busses/i2c-s3c2410.c, this code also supports Samsung series such as s3c2410, s3c246410, s5pc110 chip.
Initialize and uninstall modules
[Cpp]View plaincopy
- Static int _ init i2c_adap_initi_init (void)
- {
- Returnplatform_driver_register (& s3c24xx_i2c_driver );
- }
- Static void _ exit i2c_adap_initi_exit (void)
- {
- Platform_driver_unregister (& s3c24xx_i2c_driver );
- }
Bus drivers are implemented based on platform and comply with the idea of the device driver model.
[Cpp]View plaincopy
- Static struct platform_drivers3c24xx_i2c_driver = {
- . Probe = s3c24xx_i2c_probe,
- . Remove = s3c24xx_i2c_remove,
- . Id_table = s3c24xx_driver_ids,
- . Driver = {
- . Owner = THIS_MODULE,
- . Name = "s3c-i2c ",
- . Pm = S3C24XX_DEV_PM_OPS,
- . Of_match_table = s3c24xx_i2c_match,
- },
- };
S3c24xx_i2c_probe Function
When the platform_driver_register function is called to register the platform_driver struct, if the platformdevice and platform driver match successfully, the probe function is called to initialize the adapter hardware.
[Cpp]View plaincopy
- Static int s3c24xx_i2c_probe (structplatform_device * pdev)
- {
- ......
- /* Initialize the adapter information */
- Strlcpy (i2c-> adap. name, "s3c2410-i2c", sizeof (i2c-> adap. name ));
- I2c-> adap. owner = THIS_MODULE;
- I2c-> adap. algo = & s3c24xx_i2c_algorithm;
- I2c-> adap. retries = 2;
- I2c-> adap. class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- I2c-> tx_setup = 50;
- /* Initialize the spin lock and wait queue header */
- Spin_lock_init (& i2c-> lock );
- Init_waitqueue_head (& i2c-> wait );
- /* Ing register */
- Res = platform_get_resource (pdev, IORESOURCE_MEM, 0 );
- I2c-> ioarea = request_mem_region (res-> start, resource_size (res ),
- Pdev-> name );
- I2c-> regs = ioremap (res-> start, resource_size (res ));
- /* Set the information required by the I2C core */
- I2c-> adap. algo_data = i2c;
- I2c-> adap. dev. parent = & pdev-> dev;
- /* Initialize the I2C controller */
- Ret = s3c24xx_i2c_init (i2c );
- /* Application interrupted */
- I2c-> irq = ret = platform_get_irq (pdev, 0 );
- Ret = request_irq (i2c-> irq, s3c24xx_i2c_irq, 0,
- Dev_name (& pdev-> dev), i2c );
- /* Register the I2C adapter */
- Ret = i2c_add_numbered_adapter (& i2c-> adap );
- ......
- }
Probe is mainly used to handle hardware and request the I/o address and interrupt number used by the I2C adapter, and then add the adapter to the I2C core. I2c_adapter registration process i2c_add_numbered_adapter-> i2c_register_adapter
I2C bus Communication Method
[Cpp]View plaincopy
- Static const struct i2c_algorithms3c24xx_i2c_algorithm = {
- . Master_xfer = s3c24xx_i2c_xfer,
- . Functionality = s3c24xx_i2c_func,
- };
The s3c24xx_i2c_xfer function is a specific implementation of the bus communication mode. It depends on the s3c24xx_i2c_doxfer and s3c24xx_i2c_message_start functions;
[Cpp]View plaincopy
- Static int s3c24xx_i2c_doxfer (structs3c24xx_i2c * i2c,
- Struct i2c_msg * msgs, int num)
- {
- Ret = s3c24xx_i2c_set_master (i2c );
- I2c-> msg = msgs;
- I2c-> msg_num = num;
- I2c-> msg_ptr = 0;
- I2c-> msg_idx = 0;
- I2c-> state = STATE_START;
- S3c24xx_i2c_message_start (i2c, msgs );
- }
First, set the s3c I2C device as the master device, and then call the s3c24xx_i2c_message_start function to start I2C message transmission.
The s3c24xx_i2c_func function returns the communication functions supported by the adapter.
4.4 device resource of the adapter (platform_device)
The I2C bus driver of S3c2440 is implemented based on platform. We have analyzed the platformdriver section before. Let's take a look at the platform device section.
The platform_device struct and I2C controller resource information are defined in the arch/arm/plat-samsung/dev-i2c0.c file:
[Cpp]View plaincopy
- Static struct resource initi_i2c_resource [] = {
- [0] = {
- . Start = maid,
- . End = maid + SZ_4K-1,
- . Flags = IORESOURCE_MEM,
- },
- [1] = {
- . Start = IRQ_IIC,
- . End = IRQ_IIC,
- . Flags = IORESOURCE_IRQ,
- },
- };
- Struct platform_device initi_device_i2c0 = {
- . Name = "s3c2410-i2c",/* device name */
- # Ifdef config_cloud_dev_i2c1
- . Id = 0,
- # Else
- . Id =-1,
- # Endif
- . Num_resources = ARRAY_SIZE (cloud_i2c_resource ),
- . Resource = maid resource,
- };
- Struct s3c2410_platform_i2cdefault_i2c_data _ initdata = {
- . Flags = 0,
- . Slave_addr = 0x10,/* I2C adapter address */
- . Frequency = 100*1000,/* bus frequency */
- . Sda_delay = 100,/* SDA edge Delay Time ns */
- };
- Void _ init jx_i2c0_set_platdata (structs3c2410_platform_i2c * pd)
- {
- Structs3c2410_platform_i2c * npd;
- If (! Pd)
- Pd = & default_i2c_data;
- Npd = maid (pd, sizeof (struct s3c2410_platform_i2c ),
- & Amp; cloud_device_i2c0 );
- If (! Npd-> cfg_gpio)
- Npd-> pai_gpio = maid;
- }
Register platform_device into the kernel in the Board file:
[Cpp]View plaincopy
- Static struct platform_device * mini2440_devices [] _ initdata = {
- ......
- & Amp; cloud_device_i2c0,
- ......
- };
Call the initial_i2c0_set_platdata function to assign the specific data of the adapter to dev. platform_data:
[Cpp]View plaincopy
- Static void _ init mini2440_init (void)
- {
- ......
- Initi_i2c0_set_platdata (NULL );
- }
The I2C bus driver is analyzed here.