Developing IIC under linux

Introduce:

Through the development of LED, button, interrupt and other drivers, it can be found that linux development is nothing more than the following:

  1. Find the structure
  2. Initialize structure
  3. Register with kernel
  4. Log off to the kernel when not in use

IIC is divided into controller part and device driver

The controller part is to complete the initialization of IIC controller, including the pin, clock, speed, read-write function.

The device driver includes the configuration of the device register used and the reading of the register, that is, how to write and get data from the device used.

Problem 1: IIC bus driver under Linux (IIC controller driver development)

Linux kernel abstracts I2C adapter (controller) of SOC into I2C adapter

struct i2c_adapter {
    struct module *owner;
    unsigned int class; /* classes to allow probing for */
    const struct i2c_algorithm *algo; /*  Bus access algorithm */
    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;
    const struct i2c_adapter_quirks *quirks;
};

Among them, I const struct I2C ﹣ algorithm * algo; / * bus access algorithm * / is more important

This structure is used to complete the API functions of IIC controller

struct i2c_algorithm {
......
    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 *);
......
};

This function can be used by master? To complete the communication with IIC devices

So according to the development experience, what we have to do is

  1. Initialize the I2C adapter structure variable
  2. Then set the master function in I2C algorithm
  3. Register the I2C adapter with the kernel

Question 2: registration function

Dynamic registration

int i2c_add_adapter(struct i2c_adapter *adapter)

Static registration
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

Delete:

void i2c_del_adapter(struct i2c_adapter * adap)

Adapter or adapter: the I2C adapter to add to the Linux kernel, that is, the I2C adapter.
Return value: 0, success; negative, failure.

Note: generally, I2C bus drivers of SOC are written by semiconductor manufacturers. For example, the I2C adapter driver NXP of I.MX6U has been written, which does not need to be written by users. So I2C bus driver is actually shielded for SoC users. We just need to focus on I2C device driver.

Question 3: IIC device driver

The device driver also needs to find the corresponding structure first

I2C client structure

struct i2c_client {
    unsigned short flags; /* sign */
    unsigned short addr; /* Chip address, 7 bits, low 7 bits present */
    ......
    char name[I2C_NAME_SIZE]; /* Name */
    struct i2c_adapter *adapter; /* Corresponding I2C adapter */
    struct device dev; /* Equipment structure */
    int irq; /* interrupt */
    struct list_head detected;
    ......
};

A device corresponds to an I2C client. Every time an I2C device is detected, an I2C client will be assigned to the I2C device.

I2C Φ driver structure

The I2C driver is similar to the platform driver, which is the focus of the I2C device driver

struct i2c_driver {
    unsigned int class;
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
    void (*shutdown)(struct i2c_client *);
    void (*alert)(struct i2c_client *, unsigned int data);
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    struct device_driver driver;
    const struct i2c_device_id *id_table;
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};

When the I2C device and driver match successfully, the probe function will execute, just like the platform driver.

The device driver drives the structure. If you use the device tree, you need to set the of match table member variable of the device driver, that is, the compatible attribute of the driver.

ID table is a traditional device matching ID table without using device tree.

For our I2C device driver writers, the key work is to build the I2C driver, which needs to be registered with the Linux kernel after the build

Question 4: registration and cancellation functions

register

The I2C driver registration function is int I2C register driver or I2C add driver

int i2c_register_driver(struct module *owner,struct i2c_driver *driver)

owner: it is generally the "module".

driver: I2C? driver to register.

I2C add driver is a macro definition, which is equivalent to encapsulating another layer of I2C register driver.

#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)

Return value: 0, success; negative, failure

Cancellation

Log out using the I2C del driver function

void i2c_del_driver(struct i2c_driver *driver)

The function parameters and return values are as follows:
driver: i2 to log off

Question 4: example code

// i2c driven probe function
static int xxx_probe(struct i2c_client *client,
{
/* Function specific program */
    return 0;
}


// i2c driven remove function
static int ap3216c_remove(struct i2c_client *client)
{
/* Function specific program */
    return 0;
}


 static const struct i2c_device_id iic_id[] = {
    {"100askIIC_id", 0},
    {}
};

static const struct of_device_id iic_of_match[] = {
    { .compatible = "100askIIC" },
    { /* Sentinel */ }
};


static struct i2c_driver iic_dev = {
    .probe = iic_probe,
    .remov = iic_remov,
    .driver = {
        .owner = THIS_MODULE,
        .name = "IIC_DEV",
        .of_match_table = iic_of_match,
    },
    .id_table = iic_id,

};

static int __init iic_init(void)
{
    int ret = 0;
    printk("cd %s\r\n",__FOUNCTION__);
    //Register the I2C driver structure
    ret = i2c_add_driver(iic_dev);
    if (ret) {
        printk("i2c add driver fail %d\r\n",ret);
    }
    return ret;
}

static void __exit iic_exit(void)
{
    printk("cd %s\r\n",__FOUNCTION__);
    //Unregister the I2C driver structure
    i2c_del_driver(iic_dev);
}

module_init(iic_init);
module_exit(iic_exit);

MODULE_LICENSE("GPL");
MODULE_AURHOR("liuxiang")

 

Published 35 original articles, won praise 6, visited 5020
Private letter follow

Tags: Linux Attribute

Posted on Sun, 15 Mar 2020 05:10:52 -0400 by vbmurray