[Beijing Xunwei] i.MX6ULL Terminator Linux I2C driver experiment IMX6ULL I2C bus driver analysis

In the previous section, we learned that the I2C framework is divided into three parts: I2C core, I2C bus driver and I2C device driver. Among them, the I2C bus driver is the SOC I2C controller driver, which is generally implemented by SOC manufacturers. The I2C device driver is actually implemented by users according to their different devices.
Under the imx6ull platform, NXP has officially implemented the I2C bus driver. Let’s briefly analyze it below.
First, find the device node of the I2C controller in the device tree. Open the imx6ull.dtsi file, it has the following content:

1 i2c1: i2c@021a0000 {
    
     
2      #address-cells = <1>; 
3      #size-cells = <0>; 
4      compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; 
5      reg = <0x021a0000 0x4000>; 
6      interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; 
7      clocks = <&clks IMX6UL_CLK_I2C1>; 
8      status = "disabled";
9 }; 

According to the compatible attribute values ​​"fsl,imx6ul-i2c" and "fsl,imx21-i2c" in the I2C1 device node, we can find the corresponding driver files in the kernel, and search for these two strings in the Linux kernel source code. Find the driver file as drivers/i2c/busses/i2c-imx.c, with the following contents:

244 static struct platform_device_id imx_i2c_devtype[] = {
    
     
245        {
    
     
246            .name = "imx1-i2c", 
247            .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata, 
248        }, {
    
     
249            .name = "imx21-i2c", 
250            .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata, 
251        }, {
    
     
252        /* sentinel */ 
253        } 
254 }; 
255 MODULE_DEVICE_TABLE(platform, imx_i2c_devtype); 
256 
257 static const struct of_device_id i2c_imx_dt_ids[] = {
    
     
258        {
    
     .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, }, 
259        {
    
     .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, }, 
260        {
    
     .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, }, 
261        {
    
     /* sentinel */ } 
262 }; 
263 MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids); 
...... 
1119 static struct platform_driver i2c_imx_driver = {
    
     
1120       .probe = i2c_imx_probe, 
1121       .remove = i2c_imx_remove, 
1122       .driver = {
    
     
1123               .name = DRIVER_NAME, 
1124               .owner = THIS_MODULE, 
1125               .of_match_table = i2c_imx_dt_ids, 
1126               .pm = IMX_I2C_PM, 
1127         }, 
1128       .id_table = imx_i2c_devtype, 
1129 }; 
1130 
1131 static int __init i2c_adap_imx_init(void) 
1132 {
    
     
1133         return platform_driver_register(&i2c_imx_driver); 
1134 }
1135       subsys_initcall(i2c_adap_imx_init); 
1136 
1137 static void __exit i2c_adap_imx_exit(void) 
1138 {
    
     
1139       platform_driver_unregister(&i2c_imx_driver); 
1140 } 
1141 module_exit(i2c_adap_imx_exit); 

It can be seen from the above code that the essence of the I2C bus driver is also a standard platform driver framework, so the I2C bus driver is implemented based on the platform framework, which is equivalent to encapsulating another layer.
In line 259, the compatible attribute value is "fsl,imx21-i2c". The compatible attribute value of the i2c1 node in the device tree matches this. Therefore, the i2c-imx.c file is the I2C adapter driver file of I.MX6U.
In line 1120, the i2c_imx_probe function will be executed when the device and the driver are successfully matched, and the i2c_imx_probe function will complete the initialization of the I2C adapter.
Part of the i2c_imx_probe function is as follows:

971 static int i2c_imx_probe(struct platform_device *pdev) 
972 {
    
     
973        const struct of_device_id *of_id = 
974            of_match_device(i2c_imx_dt_ids, &pdev->dev); 
975        struct imx_i2c_struct *i2c_imx; 
976        struct resource *res; 
977        struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); 
978        void __iomem *base; 
979      int irq, ret; 
980      dma_addr_t phy_addr; 
981 
982        dev_dbg(&pdev->dev, "<%s>\n", __func__); 
983 
984        irq = platform_get_irq(pdev, 0); 
...... 
990        res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
991        base = devm_ioremap_resource(&pdev->dev, res); 
992        if (IS_ERR(base)) 
993            return PTR_ERR(base); 
994 
995        phy_addr = (dma_addr_t)res->start; 
996        i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL); 
997        if (!i2c_imx) 
998          return -ENOMEM;
999 
1000   if (of_id) 
1001       i2c_imx->hwdata = of_id->data; 
1002   else 
1003       i2c_imx->hwdata = (struct imx_i2c_hwdata *) 
1004   platform_get_device_id(pdev)->driver_data; 
1005 
1006   /* Setup i2c_imx driver structure */ 
1007   strlcpy(i2c_imx->adapter.name, pdev->name, 
sizeof(i2c_imx->adapter.name)); 
1008   i2c_imx->adapter.owner = THIS_MODULE; 
1009   i2c_imx->adapter.algo = &i2c_imx_algo; 
1010   i2c_imx->adapter.dev.parent = &pdev->dev; 
1011   i2c_imx->adapter.nr = pdev->id; 
1012   i2c_imx->adapter.dev.of_node = pdev->dev.of_node; 
1013   i2c_imx->base = base; 
1014 
1015   /* Get I2C clock */ 
1016   i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); 
...... 
1022   ret = clk_prepare_enable(i2c_imx->clk); 
...... 
1027   /* Request IRQ */ 
1028   ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 
1029   IRQF_NO_SUSPEND, pdev->name, i2c_imx); 
...... 
1035   /* Init queue */ 
1036   init_waitqueue_head(&i2c_imx->queue); 
1037 
1038   /* Set up adapter data */ 
1039   i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); 
1040 
1041   /* Set up clock divider */ 
1042   i2c_imx->bitrate = IMX_I2C_BIT_RATE; 
1043   ret = of_property_read_u32(pdev->dev.of_node, 
1044       "clock-frequency", &i2c_imx->bitrate); 
1045   if (ret < 0 && pdata && pdata->bitrate) 
1046   i2c_imx->bitrate = pdata->bitrate; 
1047 
1048     /* Set up chip registers to defaults */ 
1049   imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, 
1050                   i2c_imx, IMX_I2C_I2CR); 
1051   imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
IMX_I2C_I2SR); 
1052 
1053   /* Add I2C adapter */ 
1054   ret = i2c_add_numbered_adapter(&i2c_imx->adapter); 
1055   if (ret < 0) {
    
     
1056       dev_err(&pdev->dev, "registration failed\n"); 
1057       goto clk_disable; 
1058     } 
1059 
1060   /* Set up platform driver data */ 
1061   platform_set_drvdata(pdev, i2c_imx); 
1062   clk_disable_unprepare(i2c_imx->clk); 
...... 
1070   /* Init DMA config if supported */ 
1071   i2c_imx_dma_request(i2c_imx, phy_addr); 
1072 
1073   return 0; /* Return OK */ 
1074 
1075 clk_disable: 
1076   clk_disable_unprepare(i2c_imx->clk); 
1077   return ret; 
1078 } 

On line 984, call platform_get_irq function to get the interrupt number.
On lines 990~991, call the platform_get_resource function to obtain the physical base address of the I2C1 controller register from the device tree, which is 0X021A0000. After obtaining the register base address, use the devm_ioremap_resource function to perform memory mapping on it to obtain a virtual address that can be used in the Linux kernel.
In line 996, NXP uses the imx_i2c_struct structure to represent the I2C controller of the I.MX series SOC. Here, the devm_kzalloc function is used to apply for memory.
Lines 1008~1013, the imx_i2c_struct structure must have a member variable called adapter. The adapter is i2c_adapter, and i2c_adapter is initialized here. Line 1009 sets the algo member variable of i2c_adapter to i2c_imx_algo, which is to set i2c_algorithm.
Lines 1028~1029, register the I2C controller interrupt, the interrupt service function is i2c_imx_isr.
Lines 1042~1044, set the I2C frequency to IMX_I2C_BIT_RATE=100KHz by default. If the device tree node has the "clock-frequency" attribute set, the I2C frequency will use the clock-frequency attribute value.
Lines 1049~1051 set the I2CR and I2SR registers controlled by I2C1.
On line 1054, call the i2c_add_numbered_adapter function to register the i2c_adapter with the Linux kernel.
Line 1071, apply for DMA. It seems that the I2C adapter driver of I.MX uses DMA.
The main work of i2c_imx_probe function is the following two points:
① Initialize i2c_adapter, set i2c_algorithm to i2c_imx_algo, and finally register i2c_adapter with Linux kernel.
② Initialize the relevant registers of the I2C1 controller.
i2c_imx_algo contains the communication function master_xfer between the I2C1 adapter and the I2C device. The i2c_imx_algo structure is defined as follows:

966 static struct i2c_algorithm i2c_imx_algo = {
    
     
967		.master_xfer = i2c_imx_xfer, 
968 		.functionality = i2c_imx_func, 
969 };

Let's take a look first. functionality, functionality is used to return what kind of communication protocol this I2C adapter supports, here functionality is the i2c_imx_func function, the content of the i2c_imx_func function is as follows:

static u32 i2c_imx_func(struct i2c_adapter *adapter) 
{
    
     
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL 
| I2C_FUNC_SMBUS_READ_BLOCK_DATA; 
} 

Focus on the i2c_imx_xfer function, because it is through this function to complete the communication with the I2C device. Part of the content of this function is as follows:

888 static int i2c_imx_xfer(struct i2c_adapter *adapter, 
889                    struct i2c_msg *msgs, int num) 
890 {
    
     
891        unsigned int i, temp; 
892        int result; 
893        bool is_lastmsg = false; 
894        struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); 
895 
896        dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); 
897 
898        /* Start I2C transfer */ 
899        result = i2c_imx_start(i2c_imx); 
900        if (result) 
901            goto fail0; 
902 
903        /* read/write data */ 
904        for (i = 0; i < num; i++) {
    
     
905            if (i == num - 1) 
906                is_lastmsg = true; 
907 
908          if (i) {
    
     
909                dev_dbg(&i2c_imx->adapter.dev, 
910                        "<%s> repeated start\n", __func__); 
911                temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); 
912                temp |= I2CR_RSTA; 
913              imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); 
914                result = i2c_imx_bus_busy(i2c_imx, 1);
915                if (result) 
916                    goto fail0; 
917            } 
918            dev_dbg(&i2c_imx->adapter.dev, 
919                    "<%s> transfer message: %d\n", __func__, i); 
920            /* write/read data */ 
...... 
938            if (msgs[i].flags & I2C_M_RD) 
939              result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); 
940            else {
    
     
941              if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) 
942            result = i2c_imx_dma_write(i2c_imx, &msgs[i]); 
943            else 
944                result = i2c_imx_write(i2c_imx, &msgs[i]); 
945        } 
946            if (result) 
947                goto fail0; 
948      } 
949 
950 fail0: 
951        /* Stop I2C transfer */ 
952        i2c_imx_stop(i2c_imx); 
953 
954        dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__, 
955                (result < 0) ? "error" : "success msg", 
956                (result < 0) ? result : num); 
957        return (result < 0) ? result : num; 
958 }

On line 899, call the i2c_imx_start function to start I2C communication.
In line 939, if you are reading data from an I2C device, call the i2c_imx_read function.
Lines 941~945, write data to the I2C device. If you want to use DMA, use the i2c_imx_dma_write function to complete the data writing. If you don't use DMA, use the i2c_imx_write function to finish writing data.
In line 952, call i2c_imx_stop function to stop I2C communication after I2C communication is completed.
The functions i2c_imx_start, i2c_imx_read, i2c_imx_write and i2c_imx_stop are the specific operation functions of the I2C register.

Insert picture description here

Guess you like

Origin blog.csdn.net/BeiJingXunWei/article/details/112506068