• 硬件环境 开发板:nanopi2 (cpu:A9 s5p4418 ) ...内核版本: linux3.4.39  ...交叉编译器:arm-Linux-gcc version 4.9.3 (ctng-...Linux3.4内核GPIO驱动说明 Kernel 2.6.32版本以上提供了gpio口管理的库文件/

    硬件环境

    开发板:nanopi2 (cpu:A9 s5p4418 )

    软件环境

    内核版本: linux3.4.39 
    交叉编译器:arm-Linux-gcc version 4.9.3 (ctng-1.21.0-229g-FA) 64位系统版本

    Linux3.4内核GPIO驱动说明

    Kernel 2.6.32版本以上提供了gpio口管理的库文件/kernel/drivers/gpio/gpiolib.c。

    相关的接口:
    1.int gpio_request(unsigned gpio, const char *label)
    申请一个pin脚作为gpio口,命名为 * label,如果经过判断空闲的 申请成功了做一些初始的bit位设置。
    2.void gpio_free(unsigned gpio)
    释放这个gpio口
    3.int gpio_direction_input(unsigned gpio)
    设置gpio口为输入模式
    4.int gpio_direction_output(unsigned gpio, int value)
    设置gpio口为输出模式 value为初始值 0为高电平/1为低电平
    5.void __gpio_set_value(unsigned gpio, int value)
    设置gpio口的值
    6.int __gpio_get_value(unsigned gpio)
    获取gpio口的值
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    底层芯片具体实现

    在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的用于描述底层GPIO控制器的gpio_chip结构体,并要求具体的SoC实现gpio_chip结构体的成员函数,最后透过gpiochip_add()注册gpio_chip。
    • 1
    • 1

    驱动程序源码

    #include <linux/module.h>
    #include <linux/gpio.h>
    #include <linux/delay.h>
    #include <linux/kernel.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/hrtimer.h>
    #include <linux/ktime.h>
    #include <linux/device.h>
    #include <linux/kdev_t.h>
    #include <linux/interrupt.h> 
    #include <linux/sched.h>
    #include <linux/miscdevice.h>
    #include <mach/platform.h>
    #include <mach/devices.h>
    
    #define DEVICE_NAME "4418_relay"
    
    //nanopi2 4418
    unsigned int J1_GPIO = PAD_GPIO_C + 11;//模块GPIO脚
    unsigned int J2_GPIO = PAD_GPIO_C + 12;//模块GPIO脚
    
    
    #define J1_OFF 0x00
    #define J1_ON  0x01
    #define J2_OFF 0x10
    #define J2_ON  0x11
    
    
    char drv_buf[2];
    
    static int update_relay(void)
    {
    
        switch(drv_buf[0]) {
        case J1_ON:
            gpio_set_value(J1_GPIO, 0);  //输出低电平
            return 0;
        case J1_OFF:
            gpio_set_value(J1_GPIO, 1);  //输出高电平
            return 0;
        case J2_ON:
            gpio_set_value(J2_GPIO, 0);  //输出低电平
            return 0;
        case J2_OFF:
            gpio_set_value(J2_GPIO, 1);  //输出高电平
            return 0;
        default:
            return -EINVAL;
        }
    }
    
    
    static int relay_write(struct file *file, const char * buffer, size_t count, loff_t * ppos)
    {
        unsigned long err;          
        err = copy_from_user(drv_buf, buffer, 1);
        update_relay();
        return 1;
    }
    
    static struct file_operations dev_fops={
        write:relay_write,
    };
    
    static struct miscdevice misc = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEVICE_NAME,
        .fops = &dev_fops,
    };
    
    
    static int __init my_relay_init(void)
    {
        int ret;
    
        gpio_direction_output(J1_GPIO, 1);//设置输出
        gpio_direction_output(J2_GPIO, 1);//设置输出
    
    
        ret = misc_register(&misc);
        printk (DEVICE_NAME"\t#NanoPi2 J1 J2 initialized\n"); 
        return ret; 
    }
    
    static void __exit my_relay_exit(void)
    {
        misc_deregister(&misc);
    }
    
    module_init(my_relay_init);
    module_exit(my_relay_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("TONY");
    MODULE_DESCRIPTION("91arm.com Relay Driver");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    修改内核配置菜单,增加当前驱动配置。

    内核配置参考文档

    内核模块编译

    make CROSS_COMPILE=arm-linux- modules
    • 1
    • 1

    内核模块不能加载问题

    insmod 加载出现如下问题

    root@nanopi2:/home/fa# insmod 4418_relay.ko 
    insmod: ERROR: could not insert module 4418_relay.ko: Invalid module format
    
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    查看错误信息,version magic驱动程序同开发板内核不匹配。

    root@nanopi2:/home/fa# dmesg |tail
    
    [ 2589.164000] 4418_relay: version magic '3.4.39-s5p4418 SMP preempt mod_unload ARMv7 p2v8 ' should be '3.4.39-FriendlyARM SMP preempt mod_unload ARMv7 p2v8 '
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    修改内核版本信息,-s5p4418改成内核的FriendlyARM

    这里写图片描述

    测试程序源码

    #include     <stdio.h>    
    #include     <stdlib.h>     
    #include     <unistd.h>     
    #include     <sys/types.h>  
    #include     <sys/stat.h>   
    #include     <fcntl.h>      
    #include     <errno.h> 
    
    #define DEV_FILE "/dev/4418_relay"
    
    #define J1_OFF  0x00
    #define J1_ON   0x01
    #define J2_OFF  0x10
    #define J2_ON   0x11
    
    int main()
    {
        int fd_dev=-1;
        char dat[2];
        int cmd;
        printf("nanoPi driver Test\n");
    
        fd_dev = open(DEV_FILE,O_RDWR);
        if(fd_dev<0){
            printf("open device err\n");
            return 0;
        }
    
        while(1){
            printf("1:J1 OFF 2:J1 ON 3:J2 OFF 4:J2 ON\n");
            printf("Please input:"); 
            scanf("%d",&cmd);
            switch(cmd){
                case 1:     
                    dat[0] = J1_OFF;
                    break;
                case 2:
                    dat[0] = J1_ON;
                    break;
                case 3:
                    dat[0] = J2_OFF;
                    break;
                case 4:
                    dat[0] = J2_ON;
                    break;
                default:
                    break;
            }
            write(fd_dev,dat,1);
        }
    
        return 0;
    }
    展开全文
  • 了解Linux3.4内核的特性及新增功能,掌握Linux内核的编译过程及Linux内核配置选项的内容。 【实验环境】  ●主机:Ubuntu 10.10 (64bit);  ●目标机:FS_S5PC100平台;  &amp;nbsnbsp; ●交叉编译工具链...

    了解Linux3.4内核的特性及新增功能,掌握Linux内核的编译过程及Linux内核配置选项的内容。

    【实验环境】

            ●主机:Ubuntu 10.10 (64bit);
                    ●目标机:FS_S5PC100平台;
                  &nbsnbsp; ●交叉编译工具链:arm-eabi-4.4.0(Android4.0.4自带交叉工具链);

    【实验步骤】

    解压内核

        将linux3.4.0_android_goldfish.tar.bz2拷贝到Android源码下并解压;

    $ tar xvf linux3.4.0_android_goldfish.tar.bz2
            $ cd kernel/goldfish

    修改Makefile

        打开内核源码顶层的Makefile,修改CPU架构和交叉工具链;

    # vim Makefile 195行:

            195
    ARCH         ?= $(SUBARCH)
            196CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

    为:

    # vim Makefile 195行:

            195
    ARCH         ?= arm
            196CROSS_COMPILE?= ../../prebuilt/linux-x86/toolchain/ \
                                                          arm-eabi-4.4.0/bin/arm-eabi-
            #交叉工具链路径相对于Android源码的路径,具体以实际交叉工具链路径为准

    添加配置文件

        拷贝Samsung S5PC100标准板配置文件作为FS_S5PC100配置文件,make menuconfig所做的修改也会保存到.config文件中;

    $ cp arch/arm/configs/s5pc100_defconfig .config

    配置内核

        该命令执行时会弹出一个菜单,我们可以对内核进行详细的配置。

    $ make menuconfig

            ●添加NEON驱动

        NEON 技术是 ARM Cortex-A 系列处理器的128位SIMD(单指令多数据)体系结构扩展,旨在为消费性多媒体应用提供灵活强大的加速功能,从而明显改善用户体验。它具有 32个寄存器,64位宽(是16个寄存器,128位宽的双倍视图。)

    Floating point emulation --->
                [*]VFP-format floating point maths
                    [*] Advanced SIMD (NEON) Extension support

            ●添加Android所需要的驱动

    Device Drivers --->
                [*] Staging drivers --->
                    Android --->
                        [*]   Android Drivers
                        [*]   Android Binder IPC Driver
                        [*]   Enable the Anonymous Shared Memory Subsystem
                      <*>   Android log driver
                        [*]   Android RAM buffer console
                        [*]   Persistent function tracer
                        [*]   Timed output class driver (NEW)
            <*>   Android timed gpio driver
                        [*]   Android Low Memory Killer
                        <*>   Android Switch class support --->

    修改文件

        注释编译器不支持的调试语句。

    #  vimmm/page-writeback.c  +1772
            1772     //BUG_ON(PageWriteback(page))
    ;

    编译内核

        执行编译命令编译内核,如果主机配置多核CPU可以使用多线程编译;

    $ make zImage–jn     # n 代表编译线程的数量

    通过上述操作我们能够在arch/arm/boot目录下生成一个zImage文件,这就是经过压缩后的内核镜像。

    测试

    拷贝内核镜像到TFTP目录。连接串口,开发板上电,测试内核,可以看到内核启动的打印信息。

    展开全文
  • 首先,看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入 Device Drivers-> GPIO Support ->/sys/class/gpio/… (sysfs interface)。   /sys/class/gpio 的使用说明: ...

    首先,看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入   Device Drivers-> GPIO Support ->/sys/class/gpio/… (sysfs interface)

     

    /sys/class/gpio 的使用说明:

    gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射

      控制GPIO的目录位于/sys/class/gpio

      /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号

      /sys/class/gpio/unexport 用于通知系统取消导出

      /sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数 导出一个引脚的操作步骤

      首先计算此引脚编号,引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数

      /sys/class/gpio/export写入此编号,比如12号引脚,在shell中可以通过以下命令实现,命令成功后生成/sys/class/gpio/gpio12目录,如果没有出现相应的目录,说明此引脚不可导出

      direction文件,定义输入输入方向,可以通过下面命令定义为输出。direction接受的参数:in, out, high, lowhigh/low同时设置方向为输出,并将value设置为相应的1/0

      value文件是端口的数值,为10

     

    几个例子:

    1. 导出

    /sys/class/gpio# echo 44 > export

    2. 设置方向

    /sys/class/gpio/gpio44# echo out > direction

    3. 查看方向

    /sys/class/gpio/gpio44# cat direction

    4. 设置输出

    /sys/class/gpio/gpio44# echo 1 > value

    5. 查看输出值

    /sys/class/gpio/gpio44# cat value

    6. 取消导出

    /sys/class/gpio# echo 44 > unexport


    一、 以echo的形式调用system函数进行操作,这种形式编程比较简单,结构比较清晰,如下:

    void set_gpio64_low(void)  
    {     
        system("echo 64 > /sys/class/gpio/export");  
        system("echo out > /sys/class/gpio/gpio64/direction");  
        system("echo 0 > /sys/class/gpio/gpio64/value");  
    }  

    二、 通过文件的形式来调用

    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>   //define O_WRONLY and O_RDONLY
    
    void initGpio(int n)
    {
        FILE * fp =fdopen("/sys/class/gpio/export","w");
        if (fp == NULL)
            perror("export open filed");
        else
            fprintf(fp,"%d",n);
        fclose(fp);
    }   //create gpio file
    
    void setGpioDirection(int n,char *direction)
    {
        char path[100] = {0};
        sprintf(path,"/sys/class/gpio/gpio%d/direction",n);
        FILE * fp =fdopen(path,"w");
        if (fp == NULL)
            perror("direction open filed");
        else
            fprintf(fp,"%s",direction);
        fclose(fp);
    }   //set gpio "in" or "out"
    
    int getGpioValue(int n)
    {
        char path[64];
        char value_str[3];
        int fd;
    
        snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", n);
        fd = open(path, O_RDONLY);
        if (fd < 0) {
            perror("Failed to open gpio value for reading!");
            return -1;
        }
    
        if (read(fd, value_str, 3) < 0) {
            perror("Failed to read value!");
            return -1;
        }
    
        close(fd);
        return (atoi(value_str));
    }   //get gpio(n)'s value
    
    
    int main()
    
    {
        initGpio(18);
        setGpioDirection(18,"in");
        while(1)
        {
            printf("%d\n",getGpioValue(18));<span style="white-space:pre">	</span>//每隔1s输出一次gpio18的值
            sleep(1);
        }
    
        return 0;
    
    }
    


    展开全文
  • (最新内核3.4Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)

    BLSP(BAM Low-Speed Peripheral) , 每一个BLSP含有两个QUP, 每一个QUP可以被配置为I2C, SPI, UART, UIM接口, BLSP是高通对于低速接口的一种管理方式。

        i2c@f9923000 { /* BLSP-1 QUP-1 */
            cell-index = <1>;
            compatible = "qcom,i2c-qup";
            #address-cells = <1>;
            #size-cells = <0>;
            reg-names = "qup_phys_addr";
            reg = <0xf9923000 0x1000>;
            interrupt-names = "qup_err_intr";
            interrupts = <0 95 0>;
            qcom,i2c-bus-freq = <100000>;
            qcom,i2c-src-freq = <19200000>;
            qcom,sda-gpio = <&msmgpio 2 0>;
            qcom,scl-gpio = <&msmgpio 3 0>;
                    qcom,master-id = <86>;
        };
    
        i2c_cdc: i2c@f9927000 { /* BLSP1 QUP5 */
                    cell-index = <5>;
                    compatible = "qcom,i2c-qup";
                    #address-cells = <1>;
                    #size-cells = <0>;
                    reg-names = "qup_phys_addr";
                    reg = <0xf9927000 0x1000>;
                    interrupt-names = "qup_err_intr";
                    interrupts = <0 99 0>;
                    qcom,i2c-bus-freq = <100000>;
                    qcom,i2c-src-freq = <19200000>;
                    qcom,master-id = <86>;
            };
    
        i2c: i2c@f9928000 { /* BLSP1 QUP6 */
            cell-index = <6>;
            compatible = "qcom,i2c-qup";
            #address-cells = <1>;
            #size-cells = <0>;
            reg-names = "qup_phys_addr";
            reg = <0xf9928000 0x1000>;
            interrupt-names = "qup_err_intr";
            interrupts = <0 100 0>;
            qcom,i2c-bus-freq = <100000>;
            qcom,i2c-src-freq = <19200000>;
            qcom,sda-gpio = <&msmgpio 16 0>;
            qcom,scl-gpio = <&msmgpio 17 0>;
                    qcom,master-id = <86>;
        };
    
    
        i2c@f9924000 { /* BLSP-1 QUP-3 */
            cell-index = <2>;
            compatible = "qcom,i2c-qup";
            #address-cells = <1>;
            #size-cells = <0>;
            reg-names = "qup_phys_addr";
            reg = <0xf9924000 0x1000>;
            interrupt-names = "qup_err_intr";
            interrupts = <0 96 0>;
            qcom,i2c-bus-freq = <100000>;
            qcom,i2c-src-freq = <19200000>;
            qcom,sda-gpio = <&msmgpio 8 0>;
            qcom,scl-gpio = <&msmgpio 9 0>;
            qcom,master-id = <86>;
        };
    
        i2c@f9925000 { /* BLSP-1 QUP-3 */
            cell-index = <0>;
            compatible = "qcom,i2c-qup";
            #address-cells = <1>;
            #size-cells = <0>;
            reg-names = "qup_phys_addr";
            reg = <0xf9925000 0x1000>;
            interrupt-names = "qup_err_intr";
            interrupts = <0 97 0>;
            qcom,i2c-bus-freq = <100000>;
                    qcom,i2c-src-freq = <19200000>;
                    qcom,sda-gpio = <&msmgpio 10 0>;
                    qcom,scl-gpio = <&msmgpio 11 0>;
            qcom,clk-ctl-xfer;
                    qcom,master-id = <86>;
        };

    以上是I2C的硬件接口描述。

     i2c@f9923000{    //I2C设备挂的两种设备 focaltech  goodix
            focaltech@38{
                compatible = "focaltech,5x06";
                reg = <0x38>;
                interrupt-parent = <&msmgpio>;
                interrupts = <1 0x2008>;
                vdd-supply = <&pm8110_l19>;
                vcc_i2c-supply = <&pm8110_l14>;
                focaltech,name = "ft5x06";
                focaltech,family-id = <0x54>;
                focaltech,reset-gpio = <&msmgpio 0 0x00>;
                focaltech,irq-gpio = <&msmgpio 1 0x00>;
                focaltech,display-coords = <0 0 480 854>;
                focaltech,button-map= <139 172 158>;
                focaltech,no-force-update;
                focaltech,i2c-pull-up;
                focaltech,group-id = <1>;
                focaltech,hard-reset-delay-ms = <20>;
                focaltech,soft-reset-delay-ms = <150>;
                focaltech,num-max-touches = <10>;
                focaltech,fw-name = "5436_Ref_Asus89118_V12_D01_20160712_app.i";
                focaltech,fw-delay-aa-ms = <50>;
                focaltech,fw-delay-55-ms = <30>;
                focaltech,fw-upgrade-id1 = <0x79>;
                focaltech,fw-upgrade-id2 = <0x03>;
                focaltech,fw-delay-readid-ms = <10>;
                focaltech,fw-delay-era-flsh-ms = <2000>;
            };
            goodix@5d {
                compatible = "goodix,gt915l";    //用于.of_match_table = goodix_match_table,
                reg = <0x5d>;           //地址
                interrupt-parent = <&msmgpio>;  //中断块
                interrupts = <1 0x2008>;    //中断地址
                vdd_ana-supply = <&pm8110_l19>; 
                vcc_i2c-supply = <&pm8110_l14>;
                goodix,rst-gpio = <&msmgpio 0 0x00>;    //复位引脚
                goodix,irq-gpio = <&msmgpio 1 0x00>;    //中断引脚
                //goodix,panel-coords = <0 0 540 980>;
                //goodix,display-coords = <0 0 540 960>;
                //goodix,button-map= <139 172 158>;
                //goodix,product-id = "915L";
                //goodix,enable-power-off;
                goodix,cfg-group0 = [
                    47 E0 01 56 03 05 34 01 01 05 
                    28 08 50 32 03 05 00 00 FF 7F 
                    00 11 05 17 18 23 14 8C 2E 0E 
                    35 33 0F 0A 00 00 01 BA 33 1D 
                    00 01 00 00 00 00 00 0A 10 00 
                    2B 2E 55 94 C5 02 00 00 00 04 
                    83 31 00 79 37 00 70 3E 00 68 
                    46 00 61 50 00 61 18 38 60 00 
                    F0 4A 3A EE EE 27 00 00 00 00 
                    00 00 00 00 00 00 00 00 00 00 
                    00 00 00 00 00 00 0F 19 00 00 
                    4B 37 0E 10 12 14 16 18 1A 1C 
                    02 04 06 08 0A 0C 00 00 00 00 
                    00 00 00 00 00 00 00 00 00 00 
                    00 00 2A 29 28 26 24 22 21 20 
                    1F 1E 1D 1C 16 18 00 02 04 06 
                    08 0A 0C 0F 10 12 13 14 00 00 
                    00 00 00 00 00 00 00 00 00 00 
                    00 00 00 00 4C 01];
                goodix,cfg-group2 = [
                    48 D0 02 00 05 05 75 01 01 0F 24 
                    0F 64 3C 03 05 00 00 00 02 00 00 
                    00 16 19 1C 14 8C 0E 0E 24 00 31 
                    0D 00 00 00 83 33 1D 00 41 00 00 
                    3C 0A 14 08 0A 00 2B 1C 3C 94 D5 
                    03 08 00 00 04 93 1E 00 82 23 00 
                    74 29 00 69 2F 00 5F 37 00 5F 20 
                    40 60 00 F0 40 30 55 50 27 00 00 
                    00 00 00 00 00 00 00 00 00 00 00 
                    00 00 00 00 00 00 00 14 19 00 00 
                    50 50 02 04 06 08 0A 0C 0E 10 12 
                    14 16 18 1A 1C 00 00 00 00 00 00 
                    00 00 00 00 00 00 00 00 00 00 1D 
                    1E 1F 20 21 22 24 26 28 29 2A 1C 
                    18 16 14 13 12 10 0F 0C 0A 08 06 
                    04 02 00 00 00 00 00 00 00 00 00 
                    00 00 00 00 00 00 00 00 3C 01];
            };
        };

    I2C bus 总线的构建。
    在kernel/include/linux/init.h中

    #define arch_initcall(fn) 
        #define __define_initcall(level,fn,id) \  
            static initcall_t __initcall_##fn##id __used \  
            __attribute__((__section__(".initcall" level ".init"))) = fn  

    将i2c-bus的driver init函数放到.initcall3.init 代码段中。
    之所以,i2c-bus的driver和i2c 设备的驱动init函数使用的不同的函数放到.initcall代码段,主要原因是后面调用.initcall中的函数执行顺序是按.initcall段的函数顺序进行的(按照initcall的level从0到7依次存放的)。

        #define INIT_CALLS_LEVEL(level)                        \  
                VMLINUX_SYMBOL(__initcall##level##_start) = .;        \  
                *(.initcall##level##.init)                \  
                *(.initcall##level##s.init)      
    
        #define INIT_CALLS                          \  
                VMLINUX_SYMBOL(__initcall_start) = .;           \  
                *(.initcallearly.init)                  \  
                INIT_CALLS_LEVEL(0)                 \  
                INIT_CALLS_LEVEL(1)                 \  
                INIT_CALLS_LEVEL(2)                 \  
                INIT_CALLS_LEVEL(3)                 \  
                INIT_CALLS_LEVEL(4)                 \  
                INIT_CALLS_LEVEL(5)                 \  
                INIT_CALLS_LEVEL(rootfs)                \  
                INIT_CALLS_LEVEL(6)                 \  
                INIT_CALLS_LEVEL(7)                 \  
                VMLINUX_SYMBOL(__initcall_end) = .;  

    如module_init()的驱动初始化是等级66

    (kernel/include/linux/init.h)
    
    #define module_init(x)        __initcall(x);
    
    #define __initcall(fn)                               device_initcall(fn)
    #define device_initcall(fn)                __define_initcall("6",fn,6)
    #define __define_initcall(level,fn,id) /
            static initcall_t __initcall_##fn##id __used /
            __attribute__((__section__(".initcall" level ".init"))) = fn
    
    initcall_ttypedef int (*initcall_t)(void))

    这就保证了I2C总线的会提前的注册好,在注册挂载的驱动和设备。

    在linux  /init/main.c 中,
    static void __init do_initcalls(void)
    {
      initcall_t *call;
    
      call = &__initcall_start;
      do {
       (*call)();
       call++;
      } while (call < &__initcall_end);
      /* Make sure there is no pending stuff from the initcall sequence */
      flush_scheduled_tasks();
    } 

    通过do_initcalls就可以把__initcall section段的函数调用起來。
    kernel/drivers/i2c/busses/i2c-qup.c

    /* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 and
     * only version 2 as published by the Free Software Foundation.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     */
    /*
     * QUP driver for Qualcomm MSM platforms
     *
     */
    
    /* #define DEBUG */
    
    #include <linux/module.h>
    #include <linux/clk.h>
    #include <linux/err.h>
    #include <linux/init.h>
    #include <linux/i2c.h>
    #include <linux/i2c/i2c-qup.h>
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    #include <linux/delay.h>
    #include <linux/io.h>
    #include <linux/mutex.h>
    #include <linux/timer.h>
    #include <linux/slab.h>
    #include <linux/slab.h>
    #include <linux/pm_runtime.h>
    #include <linux/gpio.h>
    #include <linux/of.h>
    #include <linux/of_i2c.h>
    #include <linux/of_gpio.h>
    #include <mach/board.h>
    #include <mach/gpiomux.h>
    #include <mach/msm_bus_board.h>
    
    MODULE_LICENSE("GPL v2");
    MODULE_VERSION("0.2");
    MODULE_ALIAS("platform:i2c_qup");
    
    /* QUP Registers */
    enum {
        QUP_CONFIG              = 0x0,
        QUP_STATE               = 0x4,
        QUP_IO_MODE             = 0x8,
        QUP_SW_RESET            = 0xC,
        QUP_OPERATIONAL         = 0x18,
        QUP_ERROR_FLAGS         = 0x1C,
        QUP_ERROR_FLAGS_EN      = 0x20,
        QUP_MX_READ_CNT         = 0x208,
        QUP_MX_INPUT_CNT        = 0x200,
        QUP_MX_WR_CNT           = 0x100,
        QUP_OUT_DEBUG           = 0x108,
        QUP_OUT_FIFO_CNT        = 0x10C,
        QUP_OUT_FIFO_BASE       = 0x110,
        QUP_IN_READ_CUR         = 0x20C,
        QUP_IN_DEBUG            = 0x210,
        QUP_IN_FIFO_CNT         = 0x214,
        QUP_IN_FIFO_BASE        = 0x218,
        QUP_I2C_CLK_CTL         = 0x400,
        QUP_I2C_STATUS          = 0x404,
    };
    
    /* QUP States and reset values */
    enum {
        QUP_RESET_STATE         = 0,
        QUP_RUN_STATE           = 1U,
        QUP_STATE_MASK          = 3U,
        QUP_PAUSE_STATE         = 3U,
        QUP_STATE_VALID         = 1U << 2,
        QUP_I2C_MAST_GEN        = 1U << 4,
        QUP_OPERATIONAL_RESET   = 0xFF0,
        QUP_I2C_STATUS_RESET    = 0xFFFFFC,
    };
    
    /* QUP OPERATIONAL FLAGS */
    enum {
        QUP_OUT_SVC_FLAG        = 1U << 8,
        QUP_IN_SVC_FLAG         = 1U << 9,
        QUP_MX_INPUT_DONE       = 1U << 11,
    };
    
    /* QUP_CONFIG values and flags */
    enum {
        I2C_MINI_CORE           = 2U << 8,
        I2C_N_VAL               = 0xF,
        I2C_CORE_CLK_ON_EN      = BIT(13),
    
    };
    
    /* Packing Unpacking words in FIFOs , and IO modes*/
    enum {
        QUP_WR_BLK_MODE  = 1U << 10,
        QUP_RD_BLK_MODE  = 1U << 12,
        QUP_UNPACK_EN = 1U << 14,
        QUP_PACK_EN = 1U << 15,
    };
    
    /* QUP tags */
    enum {
        QUP_OUT_NOP   = 0,
        QUP_OUT_START = 1U << 8,
        QUP_OUT_DATA  = 2U << 8,
        QUP_OUT_STOP  = 3U << 8,
        QUP_OUT_REC   = 4U << 8,
        QUP_IN_DATA   = 5U << 8,
        QUP_IN_STOP   = 6U << 8,
        QUP_IN_NACK   = 7U << 8,
    };
    
    /* Status, Error flags */
    enum {
        I2C_STATUS_WR_BUFFER_FULL  = 1U << 0,
        I2C_STATUS_BUS_ACTIVE      = 1U << 8,
        I2C_STATUS_BUS_MASTER      = 1U << 9,
        I2C_STATUS_ERROR_MASK      = 0x38000FC,
        QUP_I2C_NACK_FLAG          = 1U << 3,
        QUP_IN_NOT_EMPTY           = 1U << 5,
        QUP_STATUS_ERROR_FLAGS     = 0x7C,
    };
    
    /* Master status clock states */
    enum {
        I2C_CLK_RESET_BUSIDLE_STATE = 0,
        I2C_CLK_FORCED_LOW_STATE    = 5,
    };
    
    enum msm_i2c_state {
        MSM_I2C_PM_ACTIVE,
        MSM_I2C_PM_SUSPENDED,
        MSM_I2C_SYS_SUSPENDING,
        MSM_I2C_SYS_SUSPENDED,
    };
    #define QUP_MAX_CLK_STATE_RETRIES   300
    #define DEFAULT_CLK_RATE        (19200000)
    #define I2C_STATUS_CLK_STATE        13
    #define QUP_OUT_FIFO_NOT_EMPTY      0x10
    #define I2C_GPIOS_DT_CNT        (2)     /* sda and scl */
    
    static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
    
    static struct gpiomux_setting recovery_config = {
        .func = GPIOMUX_FUNC_GPIO,
        .drv = GPIOMUX_DRV_8MA,
        .pull = GPIOMUX_PULL_NONE,
    };
    
    /**
     * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote
     *
     * @client_hdl when zero, client is not registered with the bus scaling driver,
     *      and bus scaling functionality should not be used. When non zero, it
     *      is a bus scaling client id and may be used to vote for clock path.
     * @reg_err when true, registration error was detected and an error message was
     *      logged. i2c will attempt to re-register but will log error only once.
     *      once registration succeed, the flag is set to false.
     */
    struct qup_i2c_clk_path_vote {
        u32                         client_hdl;
        struct msm_bus_scale_pdata *pdata;
        bool                        reg_err;
    };
    
    struct qup_i2c_dev {
        struct device                *dev;
        void __iomem                 *base;     /* virtual */
        void __iomem                 *gsbi;     /* virtual */
        int                          in_irq;
        int                          out_irq;
        int                          err_irq;
        int                          num_irqs;
        struct clk                   *clk;
        struct clk                   *pclk;
        struct i2c_adapter           adapter;
    
        struct i2c_msg               *msg;
        int                          pos;
        int                          cnt;
        int                          err;
        int                          mode;
        int                          clk_ctl;
        int                          one_bit_t;
        int                          out_fifo_sz;
        int                          in_fifo_sz;
        int                          out_blk_sz;
        int                          in_blk_sz;
        int                          wr_sz;
        struct msm_i2c_platform_data *pdata;
        enum msm_i2c_state           pwr_state;
        atomic_t             xfer_progress;
        struct mutex                 mlock;
        void                         *complete;
        int                          i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
        struct qup_i2c_clk_path_vote clk_path_vote;
    };
    
    #ifdef CONFIG_PM
    static int i2c_qup_pm_resume_runtime(struct device *device);
    #endif
    
    #ifdef DEBUG
    static void
    qup_print_status(struct qup_i2c_dev *dev)
    {
        uint32_t val;
        val = readl_relaxed(dev->base+QUP_CONFIG);
        dev_dbg(dev->dev, "Qup config is :0x%x\n", val);
        val = readl_relaxed(dev->base+QUP_STATE);
        dev_dbg(dev->dev, "Qup state is :0x%x\n", val);
        val = readl_relaxed(dev->base+QUP_IO_MODE);
        dev_dbg(dev->dev, "Qup mode is :0x%x\n", val);
    }
    #else
    static inline void qup_print_status(struct qup_i2c_dev *dev)
    {
    }
    #endif
    
    static irqreturn_t
    qup_i2c_interrupt(int irq, void *devid)
    {
        struct qup_i2c_dev *dev = devid;
        uint32_t status = 0;
        uint32_t status1 = 0;
        uint32_t op_flgs = 0;
        int err = 0;
    
        if (atomic_read(&dev->xfer_progress) != 1) {
            dev_err(dev->dev, "irq:%d when PM suspended\n", irq);
            return IRQ_NONE;
        }
    
        status = readl_relaxed(dev->base + QUP_I2C_STATUS);
        status1 = readl_relaxed(dev->base + QUP_ERROR_FLAGS);
        op_flgs = readl_relaxed(dev->base + QUP_OPERATIONAL);
    
        if (!dev->msg || !dev->complete) {
            /* Clear Error interrupt if it's a level triggered interrupt*/
            if (dev->num_irqs == 1) {
                writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
                /* Ensure that state is written before ISR exits */
                mb();
            }
            return IRQ_HANDLED;
        }
    
        if (status & I2C_STATUS_ERROR_MASK) {
            dev_err(dev->dev, "QUP: I2C status flags :0x%x, irq:%d\n",
                status, irq);
            err = status;
            /* Clear Error interrupt if it's a level triggered interrupt*/
            if (dev->num_irqs == 1) {
                writel_relaxed(QUP_RESET_STATE, dev->base+QUP_STATE);
                /* Ensure that state is written before ISR exits */
                mb();
            }
            goto intr_done;
        }
    
        if (status1 & 0x7F) {
            dev_err(dev->dev, "QUP: QUP status flags :0x%x\n", status1);
            err = -status1;
            /* Clear Error interrupt if it's a level triggered interrupt*/
            if (dev->num_irqs == 1) {
                writel_relaxed((status1 & QUP_STATUS_ERROR_FLAGS),
                    dev->base + QUP_ERROR_FLAGS);
                /* Ensure that error flags are cleared before ISR
                 * exits
                 */
                mb();
            }
            goto intr_done;
        }
    
        if ((dev->num_irqs == 3) && (dev->msg->flags == I2C_M_RD)
            && (irq == dev->out_irq))
            return IRQ_HANDLED;
        if (op_flgs & QUP_OUT_SVC_FLAG) {
            writel_relaxed(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
            /* Ensure that service flag is acknowledged before ISR exits */
            mb();
        }
        if (dev->msg->flags == I2C_M_RD) {
            if ((op_flgs & QUP_MX_INPUT_DONE) ||
                (op_flgs & QUP_IN_SVC_FLAG)) {
                writel_relaxed(QUP_IN_SVC_FLAG, dev->base
                        + QUP_OPERATIONAL);
                /* Ensure that service flag is acknowledged before ISR
                 * exits
                 */
                mb();
            } else
                return IRQ_HANDLED;
        }
    
    intr_done:
        dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n",
                irq, status, status1);
        qup_print_status(dev);
        dev->err = err;
        complete(dev->complete);
        return IRQ_HANDLED;
    }
    
    static int
    qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t req_state, bool only_valid)
    {
        uint32_t retries = 0;
    
        dev_dbg(dev->dev, "Polling for state:0x%x, or valid-only:%d\n",
                    req_state, only_valid);
    
        while (retries != 2000) {
            uint32_t status = readl_relaxed(dev->base + QUP_STATE);
    
            /*
             * If only valid bit needs to be checked, requested state is
             * 'don't care'
             */
            if (status & QUP_STATE_VALID) {
                if (only_valid)
                    return 0;
                else if ((req_state & QUP_I2C_MAST_GEN) &&
                        (status & QUP_I2C_MAST_GEN))
                    return 0;
                else if ((status & QUP_STATE_MASK) == req_state)
                    return 0;
            }
            if (retries++ == 1000)
                udelay(100);
        }
        return -ETIMEDOUT;
    }
    
    static int
    qup_update_state(struct qup_i2c_dev *dev, uint32_t state)
    {
        if (qup_i2c_poll_state(dev, 0, true) != 0)
            return -EIO;
        writel_relaxed(state, dev->base + QUP_STATE);
        if (qup_i2c_poll_state(dev, state, false) != 0)
            return -EIO;
        return 0;
    }
    
    #define MSM_I2C_CLK_PATH_SUSPEND (0)
    #define MSM_I2C_CLK_PATH_RESUME  (1)
    #define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000)
    
    static int i2c_qup_clk_path_init(struct platform_device *pdev,
                                struct qup_i2c_dev *dev)
    {
        struct msm_bus_vectors *paths    = NULL;
        struct msm_bus_paths   *usecases = NULL;
    
        if (!dev->pdata->master_id)
            return 0;
    
        dev_dbg(&pdev->dev, "initialises bus-scaling clock voting");
    
        paths = devm_kzalloc(&pdev->dev, sizeof(*paths) * 2, GFP_KERNEL);
        if (!paths) {
            dev_err(&pdev->dev,
            "msm_bus_paths.paths memory allocation failed");
            return -ENOMEM;
        }
    
        usecases = devm_kzalloc(&pdev->dev, sizeof(*usecases) * 2, GFP_KERNEL);
        if (!usecases) {
            dev_err(&pdev->dev,
            "msm_bus_scale_pdata.usecases memory allocation failed");
            goto path_init_err;
        }
    
        dev->clk_path_vote.pdata = devm_kzalloc(&pdev->dev,
                            sizeof(*dev->clk_path_vote.pdata),
                            GFP_KERNEL);
        if (!dev->clk_path_vote.pdata) {
            dev_err(&pdev->dev,
            "msm_bus_scale_pdata memory allocation failed");
            goto path_init_err;
        }
    
        paths[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_vectors) {
            dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0, 0
        };
    
        paths[MSM_I2C_CLK_PATH_RESUME]  = (struct msm_bus_vectors) {
            dev->pdata->master_id, MSM_BUS_SLAVE_EBI_CH0, 0,
                            MSM_I2C_CLK_PATH_MAX_BW(dev)
        };
    
        usecases[MSM_I2C_CLK_PATH_SUSPEND] = (struct msm_bus_paths) {
            .num_paths = 1,
            .vectors   = &paths[MSM_I2C_CLK_PATH_SUSPEND],
        };
    
        usecases[MSM_I2C_CLK_PATH_RESUME] = (struct msm_bus_paths) {
            .num_paths = 1,
            .vectors   = &paths[MSM_I2C_CLK_PATH_RESUME],
        };
    
        *dev->clk_path_vote.pdata = (struct msm_bus_scale_pdata) {
            .active_only  = dev->pdata->active_only,
            .name         = pdev->name,
            .num_usecases = 2,
            .usecase      = usecases,
        };
    
        return 0;
    
    path_init_err:
        devm_kfree(&pdev->dev, paths);
        devm_kfree(&pdev->dev, usecases);
        devm_kfree(&pdev->dev, dev->clk_path_vote.pdata);
        dev->clk_path_vote.pdata = NULL;
        return -ENOMEM;
    }
    
    static void i2c_qup_clk_path_teardown(struct qup_i2c_dev *dev)
    {
        if (dev->clk_path_vote.client_hdl) {
            msm_bus_scale_unregister_client(dev->clk_path_vote.client_hdl);
            dev->clk_path_vote.client_hdl = 0;
        }
    }
    
    static void i2c_qup_clk_path_vote(struct qup_i2c_dev *dev)
    {
        if (dev->clk_path_vote.client_hdl)
            msm_bus_scale_client_update_request(
                            dev->clk_path_vote.client_hdl,
                            MSM_I2C_CLK_PATH_RESUME);
    }
    
    static void i2c_qup_clk_path_unvote(struct qup_i2c_dev *dev)
    {
        if (dev->clk_path_vote.client_hdl)
            msm_bus_scale_client_update_request(
                            dev->clk_path_vote.client_hdl,
                            MSM_I2C_CLK_PATH_SUSPEND);
    }
    
    /**
     * i2c_qup_clk_path_postponed_register: reg with bus-scaling after it is probed
     *
     * Workaround: i2c driver may be probed before the bus scaling driver. Thus,
     * this function should be called not from probe but from a later context.
     * This function may be called more then once before register succeed. At
     * this case only one error message will be logged. At boot time all clocks
     * are on, so earlier i2c transactions should succeed.
     */
    static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev)
    {
        /*
         * bail out if path voting is diabled (master_id == 0) or if it is
         * already registered (client_hdl != 0)
         */
        if (!dev->pdata->master_id || dev->clk_path_vote.client_hdl)
            return;
    
        dev->clk_path_vote.client_hdl = msm_bus_scale_register_client(
                            dev->clk_path_vote.pdata);
    
        if (dev->clk_path_vote.client_hdl) {
            if (dev->clk_path_vote.reg_err) {
                /* log a success message if an error msg was logged */
                dev->clk_path_vote.reg_err = false;
                dev_info(dev->dev,
                    "msm_bus_scale_register_client(mstr-id:%d "
                    "actv-only:%d):0x%x",
                    dev->pdata->master_id, dev->pdata->active_only,
                    dev->clk_path_vote.client_hdl);
            }
    
            if (dev->pdata->active_only)
                i2c_qup_clk_path_vote(dev);
        } else {
            /* guard to log only one error on multiple failure */
            if (!dev->clk_path_vote.reg_err) {
                dev->clk_path_vote.reg_err = true;
    
                dev_info(dev->dev,
                    "msm_bus_scale_register_client(mstr-id:%d "
                    "actv-only:%d):0",
                    dev->pdata->master_id, dev->pdata->active_only);
            }
        }
    }
    
    static int i2c_qup_gpio_request(struct qup_i2c_dev *dev)
    {
        int i;
        int result = 0;
    
        for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
            if (dev->i2c_gpios[i] >= 0) {
                result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]);
                if (result) {
                    dev_err(dev->dev,
                        "gpio_request for pin %d failed with error %d\n",
                        dev->i2c_gpios[i], result);
                    goto error;
                }
            }
        }
        return 0;
    
    error:
        for (; --i >= 0;) {
            if (dev->i2c_gpios[i] >= 0)
                gpio_free(dev->i2c_gpios[i]);
        }
        return result;
    }
    
    static void i2c_qup_gpio_free(struct qup_i2c_dev *dev)
    {
        int i;
    
        for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
            if (dev->i2c_gpios[i] >= 0)
                gpio_free(dev->i2c_gpios[i]);
        }
    }
    
    static void i2c_qup_pm_suspend_clk(struct qup_i2c_dev *dev)
    {
        uint32_t status;
    
        /* reset core and enable conditional dynamic clock gating */
        qup_update_state(dev, QUP_RESET_STATE);
        status = readl_relaxed(dev->base + QUP_CONFIG);
        status |= I2C_CORE_CLK_ON_EN;
        writel_relaxed(status, dev->base + QUP_CONFIG);
        /* ensure that write has really gone through */
        mb();
    
        clk_disable_unprepare(dev->clk);
        if (!dev->pdata->keep_ahb_clk_on)
            clk_disable_unprepare(dev->pclk);
    }
    
    static void i2c_qup_pm_resume_clk(struct qup_i2c_dev *dev)
    {
        clk_prepare_enable(dev->clk);
        if (!dev->pdata->keep_ahb_clk_on)
            clk_prepare_enable(dev->pclk);
    }
    
    static void i2c_qup_pm_suspend(struct qup_i2c_dev *dev)
    {
        if (dev->pwr_state == MSM_I2C_PM_SUSPENDED) {
            dev_err(dev->dev, "attempt to suspend when suspended\n");
            return;
        }
    
        if (!dev->pdata->clk_ctl_xfer)
            i2c_qup_pm_suspend_clk(dev);
    
        if (!dev->pdata->active_only)
            i2c_qup_clk_path_unvote(dev);
    
        i2c_qup_gpio_free(dev);
        dev->pwr_state = MSM_I2C_PM_SUSPENDED;
    }
    
    static void i2c_qup_pm_resume(struct qup_i2c_dev *dev)
    {
        if (dev->pwr_state == MSM_I2C_PM_ACTIVE)
            return;
    
        i2c_qup_gpio_request(dev);
    
        i2c_qup_clk_path_postponed_register(dev);
        if (!dev->pdata->active_only)
            i2c_qup_clk_path_vote(dev);
    
        if (!dev->pdata->clk_ctl_xfer)
            i2c_qup_pm_resume_clk(dev);
        dev->pwr_state = MSM_I2C_PM_ACTIVE;
    }
    
    static int
    qup_i2c_poll_writeready(struct qup_i2c_dev *dev, int rem)
    {
        uint32_t retries = 0;
    
        while (retries != 2000) {
            uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
    
            if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
                if (((dev->msg->flags & I2C_M_RD) || (rem == 0)) &&
                    !(status & I2C_STATUS_BUS_ACTIVE))
                    return 0;
                else if ((dev->msg->flags == 0) && (rem > 0))
                    return 0;
                else /* 1-bit delay before we check for bus busy */
                    udelay(dev->one_bit_t);
            }
            if (retries++ == 1000) {
                /*
                 * Wait for FIFO number of bytes to be absolutely sure
                 * that I2C write state machine is not idle. Each byte
                 * takes 9 clock cycles. (8 bits + 1 ack)
                 */
                usleep_range((dev->one_bit_t * (dev->out_fifo_sz * 9)),
                    (dev->one_bit_t * (dev->out_fifo_sz * 9)));
            }
        }
        qup_print_status(dev);
        return -ETIMEDOUT;
    }
    
    static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev)
    {
        uint32_t retries = 0;
        uint32_t op_flgs = -1, clk_state = -1;
    
        /*
         * Wait for the clock state to transition to either IDLE or FORCED
         * LOW.  This will usually happen within one cycle of the i2c clock.
         */
    
        while (retries++ < QUP_MAX_CLK_STATE_RETRIES) {
            uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
            clk_state = (status >> I2C_STATUS_CLK_STATE) & 0x7;
            /* Read the operational register */
            op_flgs = readl_relaxed(dev->base +
                QUP_OPERATIONAL) & QUP_OUT_FIFO_NOT_EMPTY;
    
            /*
             * In very corner case when slave do clock stretching and
             * output fifo will have 1 block of data space empty at
             * the same time.  So i2c qup will get output service
             * interrupt and as it doesn't have more data to be written.
             * This can lead to issue where output fifo is not empty.
            */
            if (op_flgs == 0 &&
                (clk_state == I2C_CLK_RESET_BUSIDLE_STATE ||
                clk_state == I2C_CLK_FORCED_LOW_STATE)){
                dev_dbg(dev->dev, "clk_state 0x%x op_flgs [%x]\n",
                    clk_state, op_flgs);
                return 0;
            }
    
            /* 1-bit delay before we check again */
            udelay(dev->one_bit_t);
        }
    
        dev_err(dev->dev, "Error waiting for clk ready clk_state: 0x%x op_flgs: 0x%x\n",
            clk_state, op_flgs);
        return -ETIMEDOUT;
    }
    
    #ifdef DEBUG
    static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
                    uint32_t addr, int rdwr)
    {
        if (rdwr)
            dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
        else
            dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
    }
    #else
    static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val,
                    uint32_t addr, int rdwr)
    {
    }
    #endif
    
    static void
    qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
            uint32_t carry_over)
    {
        uint16_t addr = (msg->addr << 1) | 1;
        /* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field
         * is treated as 256 byte read.
         */
        uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);
    
        if (*idx % 4) {
            writel_relaxed(carry_over | ((QUP_OUT_START | addr) << 16),
            dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */
    
            qup_verify_fifo(dev, carry_over |
                ((QUP_OUT_START | addr) << 16), (uint32_t)dev->base
                + QUP_OUT_FIFO_BASE + (*idx - 2), 1);
            writel_relaxed((QUP_OUT_REC | rd_len),
                dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */
    
            qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
            (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1);
        } else {
            writel_relaxed(((QUP_OUT_REC | rd_len) << 16)
                | QUP_OUT_START | addr,
                dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */
    
            qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
            QUP_OUT_START | addr,
            (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
        }
        *idx += 4;
    }
    
    static void
    qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
                int *idx, uint32_t *carry_over)
    {
        int entries = dev->cnt;
        int empty_sl = dev->wr_sz - ((*idx) >> 1);
        int i = 0;
        uint32_t val = 0;
        uint32_t last_entry = 0;
        uint16_t addr = msg->addr << 1;
    
        if (dev->pos == 0) {
            if (*idx % 4) {
                writel_relaxed(*carry_over | ((QUP_OUT_START |
                                addr) << 16),
                        dev->base + QUP_OUT_FIFO_BASE);
    
                qup_verify_fifo(dev, *carry_over | QUP_OUT_START << 16 |
                    addr << 16, (uint32_t)dev->base +
                    QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
            } else
                val = QUP_OUT_START | addr;
            *idx += 2;
            i++;
            entries++;
        } else {
            /* Avoid setp time issue by adding 1 NOP when number of bytes
             * are more than FIFO/BLOCK size. setup time issue can't appear
             * otherwise since next byte to be written will always be ready
             */
            val = (QUP_OUT_NOP | 1);
            *idx += 2;
            i++;
            entries++;
        }
        if (entries > empty_sl)
            entries = empty_sl;
    
        for (; i < (entries - 1); i++) {
            if (*idx % 4) {
                writel_relaxed(val | ((QUP_OUT_DATA |
                    msg->buf[dev->pos]) << 16),
                    dev->base + QUP_OUT_FIFO_BASE);
    
                qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
                    msg->buf[dev->pos] << 16, (uint32_t)dev->base +
                    QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
            } else
                val = QUP_OUT_DATA | msg->buf[dev->pos];
            (*idx) += 2;
            dev->pos++;
        }
        if (dev->pos < (msg->len - 1))
            last_entry = QUP_OUT_DATA;
        else if (rem > 1) /* not last array entry */
            last_entry = QUP_OUT_DATA;
        else
            last_entry = QUP_OUT_STOP;
        if ((*idx % 4) == 0) {
            /*
             * If read-start and read-command end up in different fifos, it
             * may result in extra-byte being read due to extra-read cycle.
             * Avoid that by inserting NOP as the last entry of fifo only
             * if write command(s) leave 1 space in fifo.
             */
            if (rem > 1) {
                struct i2c_msg *next = msg + 1;
                if (next->addr == msg->addr && (next->flags & I2C_M_RD)
                    && *idx == ((dev->wr_sz*2) - 4)) {
                    writel_relaxed(((last_entry |
                        msg->buf[dev->pos]) |
                        ((1 | QUP_OUT_NOP) << 16)), dev->base +
                        QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
    
                    qup_verify_fifo(dev,
                        ((last_entry | msg->buf[dev->pos]) |
                        ((1 | QUP_OUT_NOP) << 16)),
                        (uint32_t)dev->base +
                        QUP_OUT_FIFO_BASE + (*idx), 0);
                    *idx += 2;
                } else if ((dev->pos == msg->len - 1)
                        && *idx < (dev->wr_sz*2) &&
                        (next->addr != msg->addr)) {
                    /* Last byte of an intermittent write */
                    writel_relaxed((QUP_OUT_STOP |
                            msg->buf[dev->pos]),
                        dev->base + QUP_OUT_FIFO_BASE);
    
                    qup_verify_fifo(dev,
                        QUP_OUT_STOP | msg->buf[dev->pos],
                        (uint32_t)dev->base +
                        QUP_OUT_FIFO_BASE + (*idx), 0);
                    *idx += 2;
                } else
                    *carry_over = (last_entry | msg->buf[dev->pos]);
            } else {
                writel_relaxed((last_entry | msg->buf[dev->pos]),
                dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
    
                qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
                (uint32_t)dev->base + QUP_OUT_FIFO_BASE +
                (*idx), 0);
            }
        } else {
            writel_relaxed(val | ((last_entry | msg->buf[dev->pos]) << 16),
            dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */
    
            qup_verify_fifo(dev, val | (last_entry << 16) |
            (msg->buf[dev->pos] << 16), (uint32_t)dev->base +
            QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
        }
    
        *idx += 2;
        dev->pos++;
        dev->cnt = msg->len - dev->pos;
    }
    
    static void
    qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
    {
        uint32_t wr_mode = (dev->wr_sz < dev->out_fifo_sz) ?
                    QUP_WR_BLK_MODE : 0;
        if (rd_len > 256) {
            dev_dbg(dev->dev, "HW limit: Breaking reads in chunk of 256\n");
            rd_len = 256;
        }
        if (rd_len <= dev->in_fifo_sz) {
            writel_relaxed(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN,
                dev->base + QUP_IO_MODE);
            writel_relaxed(rd_len, dev->base + QUP_MX_READ_CNT);
        } else {
            writel_relaxed(wr_mode | QUP_RD_BLK_MODE |
                QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
            writel_relaxed(rd_len, dev->base + QUP_MX_INPUT_CNT);
        }
    }
    
    static int
    qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
    {
        int total_len = 0;
        int ret = 0;
        int len = dev->msg->len;
        struct i2c_msg *next = NULL;
        if (rem > 1)
            next = dev->msg + 1;
        while (rem > 1 && next->flags == 0 && (next->addr == dev->msg->addr)) {
            len += next->len + 1;
            next = next + 1;
            rem--;
        }
        if (len >= (dev->out_fifo_sz - 1)) {
            total_len = len + 1 + (len/(dev->out_blk_sz-1));
    
            writel_relaxed(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
                dev->base + QUP_IO_MODE);
            dev->wr_sz = dev->out_blk_sz;
        } else
            writel_relaxed(QUP_PACK_EN | QUP_UNPACK_EN,
                dev->base + QUP_IO_MODE);
    
        if (rem > 1) {
            if (next->addr == dev->msg->addr &&
                next->flags == I2C_M_RD) {
                qup_set_read_mode(dev, next->len);
                /* make sure read start & read command are in 1 blk */
                if ((total_len % dev->out_blk_sz) ==
                    (dev->out_blk_sz - 1))
                    total_len += 3;
                else
                    total_len += 2;
            }
        }
        /* WRITE COUNT register valid/used only in block mode */
        if (dev->wr_sz == dev->out_blk_sz)
            writel_relaxed(total_len, dev->base + QUP_MX_WR_CNT);
        return ret;
    }
    
    
    static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
    {
        int i;
        int gpio_clk;
        int gpio_dat;
        bool gpio_clk_status = false;
        uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
        struct gpiomux_setting old_gpio_setting[ARRAY_SIZE(i2c_rsrcs)];
    
        if (dev->pdata->msm_i2c_config_gpio)
            return;
    
        if (!(status & (I2C_STATUS_BUS_ACTIVE)) ||
            (status & (I2C_STATUS_BUS_MASTER)))
            return;
    
        gpio_clk = dev->i2c_gpios[0];
        gpio_dat = dev->i2c_gpios[1];
    
        if ((gpio_clk == -1) && (gpio_dat == -1)) {
            dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n");
            return;
        }
    
        disable_irq(dev->err_irq);
        for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
            if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
                    &recovery_config, &old_gpio_setting[i])) {
                dev_err(dev->dev, "GPIO pins have no active setting\n");
                goto recovery_end;
            }
        }
    
        dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
             gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
    
        for (i = 0; i < 9; i++) {
            if (gpio_get_value(gpio_dat) && gpio_clk_status)
                break;
            gpio_direction_output(gpio_clk, 0);
            udelay(5);
            gpio_direction_output(gpio_dat, 0);
            udelay(5);
            gpio_direction_input(gpio_clk);
            udelay(5);
            if (!gpio_get_value(gpio_clk))
                udelay(20);
            if (!gpio_get_value(gpio_clk))
                usleep_range(10000, 10000);
            gpio_clk_status = gpio_get_value(gpio_clk);
            gpio_direction_input(gpio_dat);
            udelay(5);
        }
    
        /* Configure ALT funciton to QUP I2C*/
        for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
            msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
                    &old_gpio_setting[i], NULL);
        }
    
        udelay(10);
    
        status = readl_relaxed(dev->base + QUP_I2C_STATUS);
        if (!(status & I2C_STATUS_BUS_ACTIVE)) {
            dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
                 "status %x\n",
                 i, status);
            goto recovery_end;
        }
    
        dev_warn(dev->dev, "Bus still busy, status %x\n", status);
    
    recovery_end:
        enable_irq(dev->err_irq);
    }
    
    static int
    qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
    {
        DECLARE_COMPLETION_ONSTACK(complete);
        struct qup_i2c_dev *dev = i2c_get_adapdata(adap);
        int ret;
        int rem = num;
        long timeout;
        int err;
    
        /*
         * If all slaves of this controller behave as expected, they will
         * implement suspend and won't call any transaction if they are
         * suspended. Since controller is its parent, controller's suspend
         * will be called only AFTER alls slaves are suspended.
         * However reality is differe and some slave don't implement suspend
         * If a slave tries to initiate transfer when we are suspended,
         * pm_runtime_enabled is set to false by system-pm.
         * Make sure we return error when transaction is initiated while
         * we are in suspended state
         */
        mutex_lock(&dev->mlock);
        if (dev->pwr_state >= MSM_I2C_SYS_SUSPENDING) {
            dev_err(dev->dev,
                "xfer not allowed when ctrl is suspended addr:0x%x\n",
                msgs->addr);
            mutex_unlock(&dev->mlock);
            return -EIO;
        }
        if (!pm_runtime_enabled(dev->dev)) {
            dev_dbg(dev->dev, "Runtime PM FEATURE is disabled\n");
            i2c_qup_pm_resume(dev);
        } else {
            pm_runtime_get_sync(dev->dev);
        }
    
    
        if (dev->pdata->clk_ctl_xfer)
            i2c_qup_pm_resume_clk(dev);
    
        atomic_set(&dev->xfer_progress, 1);
        /* Initialize QUP registers during first transfer */
        if (dev->clk_ctl == 0) {
            int fs_div;
            int hs_div;
            uint32_t fifo_reg;
    
            if (dev->gsbi) {
                writel_relaxed(0x2 << 4, dev->gsbi);
                /* GSBI memory is not in the same 1K region as other
                 * QUP registers. mb() here ensures that the GSBI
                 * register is updated in correct order and that the
                 * write has gone through before programming QUP core
                 * registers
                 */
                mb();
            }
    
            fs_div = ((dev->pdata->src_clk_rate
                    / dev->pdata->clk_freq) / 2) - 3;
            hs_div = 3;
            dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
            fifo_reg = readl_relaxed(dev->base + QUP_IO_MODE);
            if (fifo_reg & 0x3)
                dev->out_blk_sz = (fifo_reg & 0x3) * 16;
            else
                dev->out_blk_sz = 16;
            if (fifo_reg & 0x60)
                dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
            else
                dev->in_blk_sz = 16;
            /*
             * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
             * associated with each byte written/received
             */
            dev->out_blk_sz /= 2;
            dev->in_blk_sz /= 2;
            dev->out_fifo_sz = dev->out_blk_sz *
                        (2 << ((fifo_reg & 0x1C) >> 2));
            dev->in_fifo_sz = dev->in_blk_sz *
                        (2 << ((fifo_reg & 0x380) >> 7));
            dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
                    dev->in_blk_sz, dev->in_fifo_sz,
                    dev->out_blk_sz, dev->out_fifo_sz);
        }
    
        writel_relaxed(1, dev->base + QUP_SW_RESET);
        ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false);
        if (ret) {
            dev_err(dev->dev, "QUP Busy:Trying to recover\n");
            goto out_err;
        }
    
        if (dev->num_irqs == 3) {
            enable_irq(dev->in_irq);
            enable_irq(dev->out_irq);
        }
        enable_irq(dev->err_irq);
    
        /* Initialize QUP registers */
        writel_relaxed(0, dev->base + QUP_CONFIG);
        writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
        writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
    
        writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
    
        /* Initialize I2C mini core registers */
        writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL);
        writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
    
        while (rem) {
            bool filled = false;
    
            dev->cnt = msgs->len - dev->pos;
            dev->msg = msgs;
    
            dev->wr_sz = dev->out_fifo_sz;
            dev->err = 0;
            dev->complete = &complete;
    
            if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN, false) != 0) {
                ret = -EIO;
                goto out_err;
            }
    
            qup_print_status(dev);
            /* HW limits Read upto 256 bytes in 1 read without stop */
            if (dev->msg->flags & I2C_M_RD) {
                qup_set_read_mode(dev, dev->cnt);
                if (dev->cnt > 256)
                    dev->cnt = 256;
            } else {
                ret = qup_set_wr_mode(dev, rem);
                if (ret != 0)
                    goto out_err;
                /* Don't fill block till we get interrupt */
                if (dev->wr_sz == dev->out_blk_sz)
                    filled = true;
            }
    
            err = qup_update_state(dev, QUP_RUN_STATE);
            if (err < 0) {
                ret = err;
                goto out_err;
            }
    
            qup_print_status(dev);
            writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
            /* CLK_CTL register is not in the same 1K region as other QUP
             * registers. Ensure that clock control is written before
             * programming other QUP registers
             */
            mb();
    
            do {
                int idx = 0;
                uint32_t carry_over = 0;
    
                /* Transition to PAUSE state only possible from RUN */
                err = qup_update_state(dev, QUP_PAUSE_STATE);
                if (err < 0) {
                    ret = err;
                    goto out_err;
                }
    
                qup_print_status(dev);
                /* This operation is Write, check the next operation
                 * and decide mode
                 */
                while (filled == false) {
                    if ((msgs->flags & I2C_M_RD))
                        qup_issue_read(dev, msgs, &idx,
                                carry_over);
                    else if (!(msgs->flags & I2C_M_RD))
                        qup_issue_write(dev, msgs, rem, &idx,
                                &carry_over);
                    if (idx >= (dev->wr_sz << 1))
                        filled = true;
                    /* Start new message */
                    if (filled == false) {
                        if (msgs->flags & I2C_M_RD)
                                filled = true;
                        else if (rem > 1) {
                            /* Only combine operations with
                             * same address
                             */
                            struct i2c_msg *next = msgs + 1;
                            if (next->addr != msgs->addr)
                                filled = true;
                            else {
                                rem--;
                                msgs++;
                                dev->msg = msgs;
                                dev->pos = 0;
                                dev->cnt = msgs->len;
                                if (msgs->len > 256)
                                    dev->cnt = 256;
                            }
                        } else
                            filled = true;
                    }
                }
                err = qup_update_state(dev, QUP_RUN_STATE);
                if (err < 0) {
                    ret = err;
                    goto out_err;
                }
                dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n",
                    idx, rem, num, dev->mode);
    
                qup_print_status(dev);
                timeout = wait_for_completion_timeout(&complete,
                        msecs_to_jiffies(dev->out_fifo_sz));
                if (!timeout) {
                    uint32_t istatus = readl_relaxed(dev->base +
                                QUP_I2C_STATUS);
                    uint32_t qstatus = readl_relaxed(dev->base +
                                QUP_ERROR_FLAGS);
                    uint32_t op_flgs = readl_relaxed(dev->base +
                                QUP_OPERATIONAL);
    
                    /*
                     * Dont wait for 1 sec if i2c sees the bus
                     * active and controller is not master.
                     * A slave has pulled line low. Try to recover
                     */
                    if (!(istatus & I2C_STATUS_BUS_ACTIVE) ||
                        (istatus & I2C_STATUS_BUS_MASTER)) {
                        timeout =
                        wait_for_completion_timeout(&complete,
                                        HZ);
                        if (timeout)
                            goto timeout_err;
                    }
                    qup_i2c_recover_bus_busy(dev);
                    dev_err(dev->dev,
                        "Transaction timed out, SL-AD = 0x%x\n",
                        dev->msg->addr);
    
                    dev_err(dev->dev, "I2C Status: %x\n", istatus);
                    dev_err(dev->dev, "QUP Status: %x\n", qstatus);
                    dev_err(dev->dev, "OP Flags: %x\n", op_flgs);
                    writel_relaxed(1, dev->base + QUP_SW_RESET);
                    /* Make sure that the write has gone through
                     * before returning from the function
                     */
                    mb();
                    ret = -ETIMEDOUT;
                    goto out_err;
                }
    timeout_err:
                if (dev->err) {
                    if (dev->err > 0 &&
                        dev->err & QUP_I2C_NACK_FLAG) {
                        dev_err(dev->dev,
                        "I2C slave addr:0x%x not connected\n",
                        dev->msg->addr);
                        dev->err = ENOTCONN;
                    } else if (dev->err < 0) {
                        dev_err(dev->dev,
                        "QUP data xfer error %d\n", dev->err);
                        ret = dev->err;
                        goto out_err;
                    } else if (dev->err > 0) {
                        /*
                         * ISR returns +ve error if error code
                         * is I2C related, e.g. unexpected start
                         * So you may call recover-bus-busy when
                         * this error happens
                         */
                        qup_i2c_recover_bus_busy(dev);
                    }
                    ret = -dev->err;
                    goto out_err;
                }
                if (dev->msg->flags & I2C_M_RD) {
                    int i;
                    uint32_t dval = 0;
                    for (i = 0; dev->pos < dev->msg->len; i++,
                            dev->pos++) {
                        uint32_t rd_status =
                            readl_relaxed(dev->base
                                + QUP_OPERATIONAL);
                        if (i % 2 == 0) {
                            if ((rd_status &
                                QUP_IN_NOT_EMPTY) == 0)
                                break;
                            dval = readl_relaxed(dev->base +
                                QUP_IN_FIFO_BASE);
                            dev->msg->buf[dev->pos] =
                                dval & 0xFF;
                        } else
                            dev->msg->buf[dev->pos] =
                                ((dval & 0xFF0000) >>
                                 16);
                    }
                    dev->cnt -= i;
                } else
                    filled = false; /* refill output FIFO */
                dev_dbg(dev->dev, "pos:%d, len:%d, cnt:%d\n",
                        dev->pos, msgs->len, dev->cnt);
            } while (dev->cnt > 0);
            if (dev->cnt == 0) {
                if (msgs->len == dev->pos) {
                    rem--;
                    msgs++;
                    dev->pos = 0;
                }
                if (rem) {
                    err = qup_i2c_poll_clock_ready(dev);
                    if (err < 0) {
                        ret = err;
                        goto out_err;
                    }
                    err = qup_update_state(dev, QUP_RESET_STATE);
                    if (err < 0) {
                        ret = err;
                        goto out_err;
                    }
                }
            }
            /* Wait for I2C bus to be idle */
            ret = qup_i2c_poll_writeready(dev, rem);
            if (ret) {
                dev_err(dev->dev,
                    "Error waiting for write ready\n");
                goto out_err;
            }
        }
    
        ret = num;
     out_err:
        disable_irq(dev->err_irq);
        if (dev->num_irqs == 3) {
            disable_irq(dev->in_irq);
            disable_irq(dev->out_irq);
        }
        dev->complete = NULL;
        dev->msg = NULL;
        dev->pos = 0;
        dev->err = 0;
        dev->cnt = 0;
        if (dev->pdata->clk_ctl_xfer)
            i2c_qup_pm_suspend_clk(dev);
        atomic_set(&dev->xfer_progress, 0);
        mutex_unlock(&dev->mlock);
        pm_runtime_mark_last_busy(dev->dev);
        pm_runtime_put_autosuspend(dev->dev);
        return ret;
    }
    
    enum msm_i2c_dt_entry_status {
        DT_REQUIRED,
        DT_SUGGESTED,
        DT_OPTIONAL,
    };
    
    enum msm_i2c_dt_entry_type {
        DT_U32,
        DT_GPIO,
        DT_BOOL,
    };
    
    struct msm_i2c_dt_to_pdata_map {
        const char                  *dt_name;
        void                        *ptr_data;
        enum msm_i2c_dt_entry_status status;
        enum msm_i2c_dt_entry_type   type;
        int                          default_val;
    };
    
    int __devinit msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev,
                    struct msm_i2c_platform_data *pdata, int *gpios)
    {
        int  ret, err = 0;
        struct device_node *node = pdev->dev.of_node;
        struct msm_i2c_dt_to_pdata_map *itr;
        struct msm_i2c_dt_to_pdata_map  map[] = {
        {"qcom,i2c-bus-freq", &pdata->clk_freq,     DT_REQUIRED,  DT_U32,   0},
        {"cell-index",        &pdev->id,            DT_REQUIRED,  DT_U32,  -1},
        {"qcom,i2c-src-freq", &pdata->src_clk_rate, DT_SUGGESTED, DT_U32,   0},
        {"qcom,master-id",    &pdata->master_id,    DT_SUGGESTED, DT_U32,   0},
        {"qcom,scl-gpio",      gpios,               DT_OPTIONAL,  DT_GPIO, -1},
        {"qcom,sda-gpio",      gpios + 1,           DT_OPTIONAL,  DT_GPIO, -1},
        {"qcom,clk-ctl-xfer", &pdata->clk_ctl_xfer, DT_OPTIONAL,  DT_BOOL, -1},
        {"qcom,active-only",  &pdata->active_only,  DT_OPTIONAL,  DT_BOOL,  0},
        {NULL,                 NULL,                0,            0,        0},
        };
    
        for (itr = map; itr->dt_name ; ++itr) {
            switch (itr->type) {
            case DT_GPIO:
                ret = of_get_named_gpio(node, itr->dt_name, 0);
                if (ret >= 0) {
                    *((int *) itr->ptr_data) = ret;
                    ret = 0;
                }
                break;
            case DT_U32:
                ret = of_property_read_u32(node, itr->dt_name,
                                 (u32 *) itr->ptr_data);
                break;
            case DT_BOOL:
                *((bool *) itr->ptr_data) =
                    of_property_read_bool(node, itr->dt_name);
                ret = 0;
                break;
            default:
                dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
                                    itr->type);
                ret = -EBADE;
            }
    
            dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
                    ret, itr->dt_name, *((int *)itr->ptr_data));
    
            if (ret) {
                *((int *)itr->ptr_data) = itr->default_val;
    
                if (itr->status < DT_OPTIONAL) {
                    dev_err(&pdev->dev, "Missing '%s' DT entry\n",
                                    itr->dt_name);
    
                    /* cont on err to dump all missing entries */
                    if (itr->status == DT_REQUIRED && !err)
                        err = ret;
                }
            }
        }
    
        return err;
    }
    
    static u32
    qup_i2c_func(struct i2c_adapter *adap)
    {
        return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
    }
    
    static const struct i2c_algorithm qup_i2c_algo = {
        .master_xfer    = qup_i2c_xfer,
        .functionality  = qup_i2c_func,
    };
    
    static int __devinit
    qup_i2c_probe(struct platform_device *pdev)
    {
        struct qup_i2c_dev  *dev;
        struct resource         *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
        struct resource     *in_irq, *out_irq, *err_irq;
        struct clk         *clk, *pclk;
        int  ret = 0;
        int  i;
        int  dt_gpios[I2C_GPIOS_DT_CNT];
        bool use_device_tree = pdev->dev.of_node;
        struct msm_i2c_platform_data *pdata;
    
        gsbi_mem = NULL;
        dev_dbg(&pdev->dev, "qup_i2c_probe\n");
    
        if (use_device_tree) {
            pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
            if (!pdata)
                return -ENOMEM;
    
            ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
            if (ret)
                goto get_res_failed;
        } else
            pdata = pdev->dev.platform_data;
    
        if (!pdata) {
            dev_err(&pdev->dev, "platform data not initialized\n");
            return -ENOSYS;
        }
        qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                            "qup_phys_addr");
        if (!qup_mem) {
            dev_err(&pdev->dev,
                "platform_get_resource_byname(qup_phys_addr) failed\n");
            ret = -ENODEV;
            goto get_res_failed;
        }
    
        /*
         * We only have 1 interrupt for new hardware targets and in_irq,
         * out_irq will be NULL for those platforms
         */
        in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                            "qup_in_intr");
    
        out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                            "qup_out_intr");
    
        err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                            "qup_err_intr");
        if (!err_irq) {
            dev_err(&pdev->dev, "no error irq resource?\n");
            ret = -ENODEV;
            goto get_res_failed;
        }
    
        qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
                        pdev->name);
        if (!qup_io) {
            dev_err(&pdev->dev, "QUP region already claimed\n");
            ret = -EBUSY;
            goto get_res_failed;
        }
        if (!pdata->use_gsbi_shared_mode) {
            gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                "gsbi_qup_i2c_addr");
            if (!gsbi_mem) {
                dev_dbg(&pdev->dev, "Assume BLSP\n");
                /*
                 * BLSP core does not need protocol programming so this
                 * resource is not expected
                 */
                goto blsp_core_init;
            }
            gsbi_io = request_mem_region(gsbi_mem->start,
                            resource_size(gsbi_mem),
                            pdev->name);
            if (!gsbi_io) {
                dev_err(&pdev->dev, "GSBI region already claimed\n");
                ret = -EBUSY;
                goto err_res_failed;
            }
        }
    
    blsp_core_init:
        clk = clk_get(&pdev->dev, "core_clk");
        if (IS_ERR(clk)) {
            dev_err(&pdev->dev, "Could not get core_clk\n");
            ret = PTR_ERR(clk);
            goto err_clk_get_failed;
        }
    
        pclk = clk_get(&pdev->dev, "iface_clk");
        if (IS_ERR(pclk)) {
            dev_err(&pdev->dev, "Could not get iface_clk\n");
            ret = PTR_ERR(pclk);
            clk_put(clk);
            goto err_clk_get_failed;
        }
    
        /* We support frequencies upto FAST Mode(400KHz) */
        if (pdata->clk_freq <= 0 ||
                pdata->clk_freq > 400000) {
            dev_err(&pdev->dev, "clock frequency not supported\n");
            ret = -EIO;
            goto err_config_failed;
        }
    
        dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
        if (!dev) {
            ret = -ENOMEM;
            goto err_alloc_dev_failed;
        }
    
        dev->dev = &pdev->dev;
        if (in_irq)
            dev->in_irq = in_irq->start;
        if (out_irq)
            dev->out_irq = out_irq->start;
        dev->err_irq = err_irq->start;
        if (in_irq && out_irq)
            dev->num_irqs = 3;
        else
            dev->num_irqs = 1;
        dev->clk = clk;
        dev->pclk = pclk;
        dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
        if (!dev->base) {
            ret = -ENOMEM;
            goto err_ioremap_failed;
        }
    
        /* Configure GSBI block to use I2C functionality */
        if (gsbi_mem) {
            dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
            if (!dev->gsbi) {
                ret = -ENOMEM;
                goto err_gsbi_failed;
            }
        }
    
        for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
            if (use_device_tree && i < I2C_GPIOS_DT_CNT) {
                dev->i2c_gpios[i] = dt_gpios[i];
            } else {
                res = platform_get_resource_byname(pdev, IORESOURCE_IO,
                                   i2c_rsrcs[i]);
                dev->i2c_gpios[i] = res ? res->start : -1;
            }
        }
    
        platform_set_drvdata(pdev, dev);
    
        dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1;
        dev->pdata = pdata;
        dev->clk_ctl = 0;
        dev->pos = 0;
    
        ret = i2c_qup_clk_path_init(pdev, dev);
        if (ret) {
            dev_err(&pdev->dev,
            "Failed to init clock path-voting data structs. err:%d", ret);
            /* disable i2c_qup_clk_path_xxx() functionality */
            dev->pdata->master_id = 0;
        }
    
        if (dev->pdata->src_clk_rate <= 0) {
            dev_info(&pdev->dev,
                "No src_clk_rate specified in platfrom data\n");
            dev_info(&pdev->dev, "Using default clock rate %dHz\n",
                                DEFAULT_CLK_RATE);
            dev->pdata->src_clk_rate = DEFAULT_CLK_RATE;
        }
    
        ret = clk_set_rate(dev->clk, dev->pdata->src_clk_rate);
        if (ret)
            dev_info(&pdev->dev, "clk_set_rate(core_clk, %dHz):%d\n",
                        dev->pdata->src_clk_rate, ret);
    
        clk_prepare_enable(dev->clk);
        clk_prepare_enable(dev->pclk);
        /*
         * If bootloaders leave a pending interrupt on certain GSBI's,
         * then we reset the core before registering for interrupts.
         */
        writel_relaxed(1, dev->base + QUP_SW_RESET);
        if (qup_i2c_poll_state(dev, 0, true) != 0)
            goto err_reset_failed;
        clk_disable_unprepare(dev->clk);
        clk_disable_unprepare(dev->pclk);
    
        /*
         * We use num_irqs to also indicate if we got 3 interrupts or just 1.
         * If we have just 1, we use err_irq as the general purpose irq
         * and handle the changes in ISR accordingly
         * Per Hardware guidelines, if we have 3 interrupts, they are always
         * edge triggering, and if we have 1, it's always level-triggering
         */
        if (dev->num_irqs == 3) {
            ret = request_irq(dev->in_irq, qup_i2c_interrupt,
                    IRQF_TRIGGER_RISING, "qup_in_intr", dev);
            if (ret) {
                dev_err(&pdev->dev, "request_in_irq failed\n");
                goto err_request_irq_failed;
            }
            /*
             * We assume out_irq exists if in_irq does since platform
             * configuration either has 3 interrupts assigned to QUP or 1
             */
            ret = request_irq(dev->out_irq, qup_i2c_interrupt,
                    IRQF_TRIGGER_RISING, "qup_out_intr", dev);
            if (ret) {
                dev_err(&pdev->dev, "request_out_irq failed\n");
                free_irq(dev->in_irq, dev);
                goto err_request_irq_failed;
            }
            ret = request_irq(dev->err_irq, qup_i2c_interrupt,
                    IRQF_TRIGGER_RISING, "qup_err_intr", dev);
            if (ret) {
                dev_err(&pdev->dev, "request_err_irq failed\n");
                free_irq(dev->out_irq, dev);
                free_irq(dev->in_irq, dev);
                goto err_request_irq_failed;
            }
        } else {
            ret = request_irq(dev->err_irq, qup_i2c_interrupt,
                    IRQF_TRIGGER_HIGH, "qup_err_intr", dev);
            if (ret) {
                dev_err(&pdev->dev, "request_err_irq failed\n");
                goto err_request_irq_failed;
            }
        }
        disable_irq(dev->err_irq);
        if (dev->num_irqs == 3) {
            disable_irq(dev->in_irq);
            disable_irq(dev->out_irq);
        }
        i2c_set_adapdata(&dev->adapter, dev);
        dev->adapter.algo = &qup_i2c_algo;
        strlcpy(dev->adapter.name,
            "QUP I2C adapter",
            sizeof(dev->adapter.name));
        dev->adapter.nr = pdev->id;
        dev->adapter.dev.parent = &pdev->dev;
        if (pdata->msm_i2c_config_gpio)
            pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
    
        mutex_init(&dev->mlock);
        dev->pwr_state = MSM_I2C_PM_SUSPENDED;
        atomic_set(&dev->xfer_progress, 0);
        /* If the same AHB clock is used on Modem side
         * switch it on here itself and don't switch it
         * on and off during suspend and resume.
         */
        if (dev->pdata->keep_ahb_clk_on)
            clk_prepare_enable(dev->pclk);
    
        ret = i2c_add_numbered_adapter(&dev->adapter);
        if (ret) {
            dev_err(&pdev->dev, "i2c_add_adapter failed\n");
            if (dev->num_irqs == 3) {
                free_irq(dev->out_irq, dev);
                free_irq(dev->in_irq, dev);
            }
            free_irq(dev->err_irq, dev);
        } else {
            if (dev->dev->of_node) {
                dev->adapter.dev.of_node = pdev->dev.of_node;
                of_i2c_register_devices(&dev->adapter);
            }
    
            pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
            pm_runtime_use_autosuspend(&pdev->dev);
            pm_runtime_enable(&pdev->dev);
            return 0;
        }
    
    
    err_request_irq_failed:
        if (dev->gsbi)
            iounmap(dev->gsbi);
    err_reset_failed:
        clk_disable_unprepare(dev->clk);
        clk_disable_unprepare(dev->pclk);
        i2c_qup_clk_path_teardown(dev);
    err_gsbi_failed:
        iounmap(dev->base);
    err_ioremap_failed:
        kfree(dev);
    err_alloc_dev_failed:
    err_config_failed:
        clk_put(clk);
        clk_put(pclk);
    err_clk_get_failed:
        if (gsbi_mem)
            release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
    err_res_failed:
        release_mem_region(qup_mem->start, resource_size(qup_mem));
    get_res_failed:
        if (pdev->dev.of_node)
            kfree(pdata);
        return ret;
    }
    
    static void qup_i2c_mem_release(struct platform_device *pdev, const char *name)
    {
        struct resource *res =
            platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
    
        if (res)
            release_mem_region(res->start, resource_size(res));
        else
            dev_dbg(&pdev->dev,
                "platform_get_resource_byname(%s) failed\n", name);
    }
    
    static int __devexit
    qup_i2c_remove(struct platform_device *pdev)
    {
        struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
    
        /* Grab mutex to ensure ongoing transaction is over */
        mutex_lock(&dev->mlock);
        dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
        mutex_unlock(&dev->mlock);
        i2c_qup_pm_suspend(dev);
        dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
        mutex_destroy(&dev->mlock);
        platform_set_drvdata(pdev, NULL);
        if (dev->num_irqs == 3) {
            free_irq(dev->out_irq, dev);
            free_irq(dev->in_irq, dev);
        }
        free_irq(dev->err_irq, dev);
        i2c_del_adapter(&dev->adapter);
        if (!dev->pdata->keep_ahb_clk_on) {
            clk_put(dev->pclk);
        }
        clk_put(dev->clk);
    
        if (dev->pdata->active_only)
            i2c_qup_clk_path_unvote(dev);
        i2c_qup_clk_path_teardown(dev);
    
        if (dev->gsbi)
            iounmap(dev->gsbi);
        iounmap(dev->base);
    
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
    
        if (!(dev->pdata->use_gsbi_shared_mode))
            qup_i2c_mem_release(pdev, "gsbi_qup_i2c_addr");
    
        qup_i2c_mem_release(pdev, "qup_phys_addr");
    
        if (dev->dev->of_node)
            kfree(dev->pdata);
        kfree(dev);
        return 0;
    }
    
    #ifdef CONFIG_PM
    static int i2c_qup_pm_suspend_runtime(struct device *device)
    {
        struct platform_device *pdev = to_platform_device(device);
        struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
        dev_dbg(device, "pm_runtime: suspending...\n");
        i2c_qup_pm_suspend(dev);
        return 0;
    }
    
    static int i2c_qup_pm_resume_runtime(struct device *device)
    {
        struct platform_device *pdev = to_platform_device(device);
        struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
        dev_dbg(device, "pm_runtime: resuming...\n");
        i2c_qup_pm_resume(dev);
        return 0;
    }
    
    static int i2c_qup_pm_suspend_sys(struct device *device)
    {
        struct platform_device *pdev = to_platform_device(device);
        struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
        /* Acquire mutex to ensure current transaction is over */
        mutex_lock(&dev->mlock);
        dev->pwr_state = MSM_I2C_SYS_SUSPENDING;
        mutex_unlock(&dev->mlock);
        if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
            dev_dbg(device, "system suspend\n");
            i2c_qup_pm_suspend(dev);
            /*
             * set the device's runtime PM status to 'suspended'
             */
            pm_runtime_disable(device);
            pm_runtime_set_suspended(device);
            pm_runtime_enable(device);
        }
        dev->pwr_state = MSM_I2C_SYS_SUSPENDED;
        return 0;
    }
    
    static int i2c_qup_pm_resume_sys(struct device *device)
    {
        struct platform_device *pdev = to_platform_device(device);
        struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
        /*
         * Rely on runtime-PM to call resume in case it is enabled
         * Even if it's not enabled, rely on 1st client transaction to do
         * clock ON and gpio configuration
         */
        dev_dbg(device, "system resume\n");
        dev->pwr_state = MSM_I2C_PM_SUSPENDED;
        return 0;
    }
    #endif /* CONFIG_PM */
    
    static const struct dev_pm_ops i2c_qup_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(
            i2c_qup_pm_suspend_sys,
            i2c_qup_pm_resume_sys
        )
        SET_RUNTIME_PM_OPS(
            i2c_qup_pm_suspend_runtime,
            i2c_qup_pm_resume_runtime,
            NULL
        )
    };
    
    static struct of_device_id i2c_qup_dt_match[] = {
        {
            .compatible = "qcom,i2c-qup",
        },
        {}
    };
    
    static struct platform_driver qup_i2c_driver = {
        .probe      = qup_i2c_probe,
        .remove     = __devexit_p(qup_i2c_remove),
        .driver     = {
            .name   = "qup_i2c",
            .owner  = THIS_MODULE,
            .pm = &i2c_qup_dev_pm_ops,
            .of_match_table = i2c_qup_dt_match,
        },
    };
    
    /* QUP may be needed to bring up other drivers */
    int __init qup_i2c_init_driver(void)
    {
        static bool initialized;
    
        if (initialized)
            return 0;
        else
            initialized = true;
    
        return platform_driver_register(&qup_i2c_driver);
    }
    EXPORT_SYMBOL(qup_i2c_init_driver);
    arch_initcall(qup_i2c_init_driver);
    
    static void __exit qup_i2c_exit_driver(void)
    {
        platform_driver_unregister(&qup_i2c_driver);
    }
    module_exit(qup_i2c_exit_driver);

    使用中注意设备树中配成相应的

    static struct of_device_id i2c_qup_dt_match[] = {
        {
            .compatible = "qcom,i2c-qup",
        },
        {}
    };

    第一:
    arch_initcall(qup_i2c_init_driver);
    第二:
    platform_driver_register(&qup_i2c_driver);

    static struct platform_driver qup_i2c_driver = {
        .probe      = qup_i2c_probe,
        .remove     = __devexit_p(qup_i2c_remove),
        .driver     = {
            .name   = "qup_i2c",
            .owner  = THIS_MODULE,
            .pm = &i2c_qup_dev_pm_ops,
            .of_match_table = i2c_qup_dt_match,
        },
    };

    在probe中,先提取platform_device 的设备信息填充i2c平台数据(struct msm_i2c_platform_data) *pdata;
    struct device_node *node = pdev->dev.of_node;

    compatible = “qcom,i2c-qup”; 用于与驱动match的
    reg-names = “qup_phys_addr”;
    reg = <0xf9923000 0x1000>;
    qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
    “qup_phys_addr”);
    这个用于一块的IORESOURCE_MEM (resource)的数据结构(struct resource *qup_mem)
    interrupt-names = “qup_err_intr”;
    interrupts = <0 95 0>;
    err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
    “qup_err_intr”);
    用于读取IORESOURCE_IRQ资源,

    ret = of_get_named_gpio(node, itr->dt_name, 0);
    static inline int of_get_named_gpio(struct device_node *np,const char *propname, int index)
    {
    return of_get_named_gpio_flags(np, propname, index, NULL);*返回的是GPIO的有效值,使用Linux通用GPIO的API,或者是一个错误

    *值在错误条件。如果“标记”不为空,则函数也填充

    对于GPIO标志*。
    }
    ret = of_property_read_u32(node, itr->dt_name,(u32 *) itr->ptr_data);
    static inline int of_property_read_u32(const struct device_node *np,const char *propname, u32 *out_value)
    {
    return of_property_read_u32_array(np, propname, out_value, 1);*在设备节点中搜索一个属性,并从中读取32位值到out_value(S)

    *。成功执行将返回0
    }
    ((bool ) itr->ptr_data) =of_property_read_bool(node, itr->dt_name);等
    还有将

        qup_io = request_mem_region(qup_mem->start,resource_size(qup_mem),pdev->name);

    request_mem_region 是申请 I/O 内存用的 . 申请了之后 , 还需要使用 ioremap 或者 ioremap_nocache 函数来映射 .对于 request_region, 三个参数 start,n,name 表示你想使用从 start 开始的 size 为 n 的 I/O port 资源 ,name 自然就是你的名字。
    这样i2c的一个接口设备驱动就完成了,基础就有了。
    重要的平台驱动注册。platform_driver_register
    platform_driver_register()的注册过程:

    1 platform_driver_register(&qup_i2c_driver)
    
    2 driver_register(&drv->driver)
    
    3 bus_add_driver(drv)
    
    4 driver_attach(drv)
    
    5 bus_for_each_dev(drv->bus, NULL, drv,__driver_attach)
    
    6 __driver_attach(struct device * dev, void * data)
    
    7 driver_probe_device(drv, dev)
    
    8 really_probe(dev, drv)
    
    在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)
    

    这是完成平台驱动的注册和设备的probe。

    sys/bus/platfrom的模型

    //platform设备声明
    struct device platform_bus = {
        .bus_id        = "platform",
    };
    EXPORT_SYMBOL_GPL(platform_bus);
    
    //platform总线设备声明
    struct bus_type platform_bus_type = {
        .name        = "platform",
        .dev_attrs    = platform_dev_attrs,
        .match        = platform_match,
        .uevent        = platform_uevent,
        .suspend    = platform_suspend,
        .suspend_late    = platform_suspend_late,
        .resume_early    = platform_resume_early,
        .resume        = platform_resume,
    };
    EXPORT_SYMBOL_GPL(platform_bus_type);

    这里定义了两个数据结构分别是
    /sys/devices/platfrom/
    /sys/bus/platfrom/

    
    int __init platform_bus_init(void)
    {
        int error;
    
        error = device_register(&platform_bus);//注册了"platform"的设备
        if (error)
            return error;
        error = bus_register(&platform_bus_type);//注册了叫"platform"的总线
        if (error)
            device_unregister(&platform_bus);
        return error;
    }

    有了总线就可以挂设备了。

    //常用的platform_device_register,内部调用了platform_device_add,将设备挂在了platform总线上
    /**
    * platform_device_register - add a platform-level device
    * @pdev: platform device we're adding
    */
    int platform_device_register(struct platform_device *pdev)
    {
        device_initialize(&pdev->dev);
        return platform_device_add(pdev);
    }
    EXPORT_SYMBOL_GPL(platform_device_register);
    
    //这里在platform总线上挂设备
    int platform_device_add(struct platform_device *pdev)
    {
        int i, ret = 0;
    
        if (!pdev)
            return -EINVAL;
    
        if (!pdev->dev.parent)
            pdev->dev.parent = &platform_bus;//父设备设置为platform_bus
    
        pdev->dev.bus = &platform_bus_type;//设置挂在platform总线上
    
        if (pdev->id != -1)
            snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
                 pdev->id);
        else
            strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
    
        for (i = 0; i < pdev->num_resources; i++) {
            struct resource *p, *r = &pdev->resource[i];
    
            if (r->name == NULL)
                r->name = pdev->dev.bus_id;
    
            p = r->parent;
            if (!p) {
                if (r->flags & IORESOURCE_MEM)
                    p = &iomem_resource;
                else if (r->flags & IORESOURCE_IO)
                    p = &ioport_resource;
            }
    
            if (p && insert_resource(p, r)) {
                printk(KERN_ERR
                       "%s: failed to claim resource %d\n",
                       pdev->dev.bus_id, i);
                ret = -EBUSY;
                goto failed;
            }
        }
    
        pr_debug("Registering platform device '%s'. Parent at %s\n",
             pdev->dev.bus_id, pdev->dev.parent->bus_id);
    
        ret = device_add(&pdev->dev);
        if (ret == 0)
            return ret;
    
    failed:
        while (--i >= 0)
            if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
                release_resource(&pdev->resource[i]);
        return ret;
    }
    EXPORT_SYMBOL_GPL(platform_device_add);

    blsp_core_init(blsp的初始化)
    if (!pdata->use_gsbi_shared_mode)
    判断是否使用gsbi
    gsbi是物理上实际存在的部件,它可以模拟成i2c、gpio、spi、uart、sdio等,此i2c即是gsbi模拟的。这里顺便提下i2c adapter和i2c总线,两者都是物理上必须存在的,哪怕是其他部件模拟的,例如GSBI模拟I2C,那么GSBI这个部件是实际存在的。GSBI的走线会与所有i2c设备互连,GSBI的接口即是i2c adapter,与其他i2c设备互连的走线即是i2c总线。如果GSBI模拟成uart,那么与uart的rx/tx pin连接的走线即是uart串口线。这是高通平台的设计,换作其他平台,也一定会有一个i2c adapter或者说可以模拟成i2c adapter的物理部件存在。猜测i2c adapter位于片上系统中,类似I/O接口的东西,没有固件,驱动它的代码即是各平台相关的i2c adapter driver。GSBI的地址以及模拟成的qup i2c/uart的地址都是编址好的,在arch/arm/mach-msm/devices-msm8x60.c最开头可以看到这些物理地址。我们知道ARM是统一编址的,在代码中要用ioremap把它映射到虚拟内存,这样就可以访问了。

            gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                "gsbi_qup_i2c_addr");
            if (!gsbi_mem) {
                dev_dbg(&pdev->dev, "Assume BLSP\n");
                /*
                 * BLSP core does not need protocol programming so this
                 * resource is not expected
                 */
                goto blsp_core_init;
            }

    也就是不是Gsbi时goto blsp_core_init;

    1. 获取clk;
    2. 获取pclk;
    3. 实例化一个dev(struct qup_i2c_dev)
    4. dev->dev = &pdev->dev;
    5. dev->err_irq = err_irq->start;
    6. dev->clk = clk;
    7. dev->pclk = pclk;
    8. dev->base = ioremap(qup_mem->start, resource_size(qup_mem));//映射到虚拟地址
    9. dev->i2c_gpios[i] = dt_gpios[i];//获取sda scl;
    10. platform_set_drvdata(pdev, dev);
      pdev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将pdev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。
    11. pdata = pdev->dev.platform_data;dev->pdata = pdata;
    12. ret = i2c_qup_clk_path_init(pdev, dev);
    13. ret = request_irq(dev->err_irq, qup_i2c_interrupt,IRQF_TRIGGER_HIGH, “qup_err_intr”, dev);
    14. disable_irq(dev->err_irq);
    15. i2c_set_adapdata(&dev->adapter, dev);
    16. dev->adapter.algo = &qup_i2c_algo;//等等
    17. ret = i2c_add_numbered_adapter(&dev->adapter); //i2c-core.c添加适配器是core层做的事。主要i2c_register_adapter(adap);
      关于i2c_register_adapter(adap)详解一下;
      适配器的存在就是一个设备的存在,按照希望的过程是在/sys/bus/下构建一个i2c的驱动模型框架。设备模型是希望一个功能可以通过某个设备和某个设备驱动probe成功,让驱动完成与应用层的交互。devices这个数据结构就是一个抽象(可以说是个类的概念)
      它就有着device_register(struct device *),它与sysfs的体现就是构建了devices的管理路径.
      INIT_LIST_HEAD(&adap->userspace_clients);// 初始化一个clients的双向链表;
      dev_set_name(&adap->dev, “i2c-%d”, adap->nr);//设置一个具体的适配器设备文件名。
      adap->dev.bus = &i2c_bus_type;
    struct bus_type i2c_bus_type = {
        .name       = "i2c",// /sys/bus/i2c  设备模型的框
        .match      = i2c_device_match,//这个设备match函数
        .probe      = i2c_device_probe,//这个设备的probe函数
        .remove     = i2c_device_remove,//驱动与设备的移除
        .shutdown   = i2c_device_shutdown,//设备关闭
        .pm     = &i2c_device_pm_ops,//电源管理
    };

    adap->dev.type = &i2c_adapter_type;//这个设备(i2c适配器)的sysfs的接口应用

    static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
    static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
    static struct attribute *i2c_adapter_attrs[] = {
        &dev_attr_name.attr,
        &dev_attr_new_device.attr,
        &dev_attr_delete_device.attr,
        NULL
    };
    static struct attribute_group i2c_adapter_attr_group = {
        .attrs      = i2c_adapter_attrs,
    };
    static const struct attribute_group *i2c_adapter_attr_groups[] = {
        &i2c_adapter_attr_group,
        NULL
    };
    struct device_type i2c_adapter_type = {
        .groups     = i2c_adapter_attr_groups,
        .release    = i2c_adapter_dev_release,
    };

    res = device_register(&adap->dev); //设备的注册
    (难点)

        /* 相应的代码位于i2c-core.c:create pre-declared device nodes */
        if (adap->nr < __i2c_first_dynamic_bus_num)
            i2c_scan_static_board_info(adap);.......
    struct i2c_devinfo {
        struct list_head    list;
        int         busnum;
        struct i2c_board_info   board_info;
    };
    struct i2c_board_info {
        char        type[I2C_NAME_SIZE];
        unsigned short  flags;
        unsigned short  addr;
        void        *platform_data;
        struct dev_archdata *archdata;
        struct device_node *of_node;
        int     irq;
    };      
    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
        struct i2c_devinfo  *devinfo;
        down_read(&__i2c_board_lock);
        list_for_each_entry(devinfo, &__i2c_board_list, list) {
            if (devinfo->busnum == adapter->nr
                    && !i2c_new_device(adapter,
                            &devinfo->board_info))
                dev_err(&adapter->dev,
                    "Can't create device at 0x%02x\n",
                    devinfo->board_info.addr);
        }
        up_read(&__i2c_board_lock);
    }

    在i2c_scan_static_board_info()函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter(nr)的总线号相等,
    回顾下在设备树中获取总线号的方法,当然也是配置的方法:
    {“cell-index”, &pdev->id, DT_REQUIRED, DT_U32, -1},
    adapter->nr = pdev->id;
    dev_set_name(&adap->dev, “i2c-%d”, adap->nr);
    至于I2C设备链表的创建和填充是在

    int of_platform_populate(struct device_node *root,
                const struct of_device_id *matches,
                const struct of_dev_auxdata *lookup,
                struct device *parent)
    {
        struct device_node *child;
        int rc = 0;
    
        root = root ? of_node_get(root) : of_find_node_by_path("/");
        if (!root)
            return -EINVAL;
        for_each_child_of_node(root, child) {   //遍历设备树
            rc = of_platform_bus_create(child, matches, lookup, parent, true);
            if (rc)
                break;
        }
        of_node_put(root);
        return rc;
    }
    *创建一个platform_device为device_node
    *递归为所有子节点创建设备。
    static int of_platform_bus_create(struct device_node *bus,
                      const struct of_device_id *matches,
                      const struct of_dev_auxdata *lookup,
                      struct device *parent, bool strict)
    {
        const struct of_dev_auxdata *auxdata;
        struct device_node *child;
        struct platform_device *dev;
        const char *bus_id = NULL;
        void *platform_data = NULL;
        int rc = 0;
    
        /* Make sure it has a compatible property */
        if (strict && (!of_get_property(bus, "compatible", NULL))) {
            pr_debug("%s() - skipping %s, no compatible prop\n",
                 __func__, bus->full_name);
            return 0;
        }
    
        auxdata = of_dev_lookup(lookup, bus);
        if (auxdata) {
            bus_id = auxdata->name;
            platform_data = auxdata->platform_data;
        }
    
        if (of_device_is_compatible(bus, "arm,primecell")) {
            of_amba_device_create(bus, bus_id, platform_data, parent);
            return 0;
        }
    
        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        if (!dev || !of_match_node(matches, bus))
            return 0;
    
        for_each_child_of_node(bus, child) {
            pr_debug("   create child: %s\n", child->full_name);
            rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
            if (rc) {
                of_node_put(child);
                break;
            }
        }
        return rc;
    }
    在适配器probe时如果是设备树的方式注册。
    if (dev->dev->of_node) {
                dev->adapter.dev.of_node = pdev->dev.of_node;
                of_i2c_register_devices(&dev->adapter);
            }
    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_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;
            }
    
            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;
            }
    
            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;
            }
            info.irq = irq_of_parse_and_map(node, 0);
            info.of_node = of_node_get(node);
            info.archdata = &dev_ad;
            request_module("%s%s", I2C_MODULE_PREFIX, info.type);
            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;
            }
        }
    }

    如果使用board_info 的方式则是遍历devices链表,当设备的busnum等于这个i2c的总线号就确认这个设备是挂在这i2c的总线上的。
    最后都是使用函数i2c_new_device()创建该设备。

    struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
        struct i2c_client   *client;
        int         status;
    
        client = kzalloc(sizeof *client, GFP_KERNEL);
        if (!client)
            return NULL;
    
        client->adapter = adap;
    
        client->dev.platform_data = info->platform_data;
    
        if (info->archdata)
            client->dev.archdata = *info->archdata;
    
        client->flags = info->flags;
        client->addr = info->addr;
        client->irq = info->irq;
    
        strlcpy(client->name, info->type, sizeof(client->name));
    
        /* Check for address validity */
        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 */
        status = i2c_check_addr_busy(adap, client->addr);
        if (status)
            goto out_err;
    
        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;
    
        /* For 10-bit clients, add an arbitrary offset to avoid collisions */
        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
                 client->addr | ((client->flags & I2C_CLIENT_TEN)
                         ? 0xa000 : 0));
        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;
    }

    通过struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
    传入的适配器和i2c_board_info构建成返回一个i2c_client,(如果返回NULL 一般就是地址的有误或冲突)
    client->dev.parent = &client->adapter->dev;//将一个设备的父容器dev设置为其适配器的设备容器
    client->dev.bus = &i2c_bus_type;//设备总线描述(在bus/i2c/{下的设备}devices/)
    client->dev.type = &i2c_client_type;//设备类型描述(att『属性』,uevent,release等)
    client->dev.of_node = info->of_node;//设备树的节点,类似pdate,
    dev_set_name(&client->dev, “%d-%04x”, i2c_adapter_id(adap),
    client->addr | ((client->flags & I2C_CLIENT_TEN)? 0xa000 : 0));//设备的命名 如:
    /sys/bus/i2c/devices/1-0038/
    通过设备容器的嵌套:1-0038 -> ../../../devices/f9923000.i2c/i2c-1/1-0038
    也就是实际上sys/bus/i2c/devices/* 是/sys/devices/的链接文件。
    status = device_register(&client->dev);//完成上面的设置。
    这就完成了一个client。而这个client构建的时候也是具有了适配器,算法,等。

    注意:
    *bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    新的适配器加入内核时调用函数 bus_for_each_drv时调用的函数。
    完成当一个新的适配器加入内核时,会在总线类型为i2c_bus_type的驱动中找与之匹配的,
    并将驱动支持的设备插入新加入的这个适配器。
    函数bus_for_each_dev() 和 bus_for_each_drv()分别用于遍历bus上devices和drivers链表中的所有元素。*
    这部分在已经废弃了,不在建议使用
    / *通知BUS的机制,一个新的公共bus已经出现,它即将
    *删除。你应该避免使用这个,不久的将来它将被删除
    * /
    int (attach_adapter)(struct i2c_adapter ) __deprecated;
    int (detach_adapter)(struct i2c_adapter ) __deprecated;

    struct i2c_client {
        unsigned short flags;       /* div., see below      */
        unsigned short addr;        /* chip address - NOTE: 7bit    */
                        /* addresses are stored in the  */
                        /* _LOWER_ 7 bits       */
        char name[I2C_NAME_SIZE];
        struct i2c_adapter *adapter;    /* the adapter we sit on    */
        struct i2c_driver *driver;  /* and our access routines  */
        struct device dev;      /* the device structure     */
        int irq;            /* irq issued by device     */
        struct list_head detected;
    };
    struct i2c_driver {
        unsigned int class;
    
        /* Notifies the driver that a new bus has appeared or is about to be
         * removed. You should avoid using this, it will be removed in a
         * near future.
         */
        int (*attach_adapter)(struct i2c_adapter *) __deprecated;
        int (*detach_adapter)(struct i2c_adapter *) __deprecated;
    
        /* Standard driver model interfaces */
        int (*probe)(struct i2c_client *, const struct i2c_device_id *);
        int (*remove)(struct i2c_client *);
    
        /* driver model interfaces that don't relate to enumeration  */
        void (*shutdown)(struct i2c_client *);
        int (*suspend)(struct i2c_client *, pm_message_t mesg);
        int (*resume)(struct i2c_client *);
    
        /* Alert callback, for example for the SMBus alert protocol.
         * The format and meaning of the data value depends on the protocol.
         * For the SMBus alert protocol, there is a single bit of data passed
         * as the alert response's low bit ("event flag").
         */
        void (*alert)(struct i2c_client *, unsigned int data);
    
        /* a ioctl like command that can be used to perform specific functions
         * with the device.
         */
        int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    
        struct device_driver driver;
        const struct i2c_device_id *id_table;
    
        /* Device detection callback for automatic device creation */
        int (*detect)(struct i2c_client *, struct i2c_board_info *);
        const unsigned short *address_list;
        struct list_head clients;
    };
    struct i2c_adapter {
        struct module *owner;
        unsigned int class;       /* classes to allow probing for */
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */
        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_device_id {
        char name[I2C_NAME_SIZE];    //它由于对比具体的client的 name于 设备的驱动probe
        kernel_ulong_t driver_data  /* Data private to the driver */
                __attribute__((aligned(sizeof(kernel_ulong_t))));
    };
    

    一、关于client与设备驱动的匹配
    到内核,这些设备client的name在该驱动的id_table中。
    老的内核还是通过函数driver->detect便是填充结构体i2c_board_info,用i2c_board_info去初始化
    client,驱动id_table中的name就是i2c_board_info->type带入client的。
    该驱动与设备就是根据这个name相匹配的,匹配后调用函数probe执行一些初始化或是设备探测。
    当然该驱动也是可以不携带地址信息的,而是注册后与内核中已存在的client匹配执行probe()。
    “legacy”driver是不携带地址信息的,它的主要任务是调用函数driver->detach_adapter为新加入
    的适配器,或是与之匹配的适配器创建一个可依附的设备节点,或者是做其他初始化工作。
    新的内核就通过设备树的方式,将一个适配器和它附属的设备顺序的完成clinet的创建。
    驱动实际只需要要匹配设备name就可以probe,和使用设备依赖的适配器的算法和设备数据。
    编写一个设备驱动就简单的很多。
    如:
    module_init(goodix_ts_init); //调用等级为7.

    static struct i2c_driver goodix_ts_driver = {
        .probe      = goodix_ts_probe,
        .remove     = goodix_ts_remove,
        .id_table   = goodix_ts_id,   //I2C设备支持的驱动程序列表 /sys/bus/i2c/drivers/XXXX
        .driver = {
            .name     = GTP_I2C_NAME,
            .owner    = THIS_MODULE,
    #ifdef CONFIG_OF
            .of_match_table = goodix_match_table,
    #endif
    #if !defined(CONFIG_FB) && defined(CONFIG_PM)
            .pm       = &gtp_pm_ops,
    #endif
        },
    };
    

    定义一个i2c_driver的数据结构;

    static const struct of_device_id goodix_match_table[] = {
            {.compatible = "goodix,gt915l",}, 
            { },
    };

    of_device_id为了实现于client的probe。(client->name)///sys/bus/i2c/devices/1-005d/name
    驱动可以驱动多个设备,但是设备被一个驱动服务。
    ret = i2c_add_driver(&goodix_ts_driver);//调用一个add函数,它具体完成i2c设备的内核注册。

    #define i2c_add_driver(driver) \
        i2c_register_driver(THIS_MODULE, driver)//他是core层代码。

    driver->driver.owner = owner; //THIS_MODULE
    driver->driver.bus = &i2c_bus_type; //sys/bus/i2c/driver/XXXX
    res = driver_register(&driver->driver); //类似device_register();
    INIT_LIST_HEAD(&driver->clients); //初始化client的链表头。
    i2c_for_each_dev(driver, __process_new_driver); //find已经存在的适配器 //新的内核已经废弃。
    static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
    client :传递的适配器。
    id :传递驱动(name ,data)。
    probe成功后只需要保存这个client的数据结构,就可以操作具体的设备了。

    展开全文
  • linuxGPIO操作

    2019-01-27 16:58:06
    GPIO 用户空间操作

    注:本文基于linux-3.0.35
    #1、 内核配置

    可以通过make menuconfig的图形界面来配置

    Device Drivers --->
           GPIO Support --->
                  【*】 /sys/class/gpio/...(sysfs interface)
    

    或者:修改配置文件arch/arm/configs/imx6_defconfig

    CONFIG_GENERIC_GPIO=y
    

    #2 引脚配置

    在内核里配置引脚,以GPIO3 23为例:arch/arm/mach-mx6/board-mx6q_sabreauto.c

    static iomux_v3_cfg_t mx6q_sabreauto_pads[] = {
    
    .....
    MX6Q_PAD_EIM_D23__GPIO_3_23, // brank 3 gpio23
    .....
    
    }
    

    #3 用户层控制
    通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等

    查看目录:

    root@freescale /App$ ls /sys/class/gpio/
    export       gpiochip0    gpiochip160  gpiochip32   gpiochip96
    gpio87       gpiochip128  gpiochip192  gpiochip64   unexport
    

    3.1 计算

    nr=(P -1) 32 + N*

    (3-1)*32 +23 = 87

    ###3.2 导出

    /sys/class/gpio# echo 87> export
    

    导出后可以看到这个目录下的文件如下:

    root@freescale /App$ ls /sys/class/gpio/gpio87/
    active_low  direction   edge        power       subsystem   uevent      value
    

    如果不能导出 有可能是GPIO脚被占用,如被别的驱动占用了

    ###3.3 设置方向

    /sys/class/gpio/# echo out > gpio87/direction
    

    ###3.4 查看方向

    root@freescale /App$ cat /sys/class/gpio/gpio87/direction 
    out
    

    ###3.5 设置输出

    root@freescale /App$ echo 1 /sys/class/gpio/gpio87/value 
    1 /sys/class/gpio/gpio87/value
    root@freescale /App$ echo 0 /sys/class/gpio/gpio87/value 
    0 /sys/class/gpio/gpio87/value
    

    ###3.6 查看输出值

    root@freescale /App$ cat /sys/class/gpio/gpio87/value 
    0
    

    3.7 取消导出

    root@freescale /App$ echo 87 >/sys/class/gpio/unexport  
    root@freescale /App$ ls /sys/class/gpio/               
    export       gpiochip128  gpiochip192  gpiochip64   unexport
    gpiochip0    gpiochip160  gpiochip32   gpiochip96
    

    参考

    https://developer.ridgerun.com/wiki/index.php?title=How_to_use_GPIO_signals#References
    https://blog.csdn.net/u014213012/article/details/53140781
    linux-3.0.35/documentation/gpio.txt

    展开全文
  • 开发环境Ubuntu:14.04开发板:A33-Vstar开发板系统:linux-3.4.39-----------------------------------------------------1. 启用GPIO sysfs interface首先看系统中有没有“/sys/class/gpio”这个文件夹。如果没有...
  • linux3.4.2 之LCD驱动

    2019-04-09 22:03:45
    驱动源码 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fb.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h>...linux/gpio.h&...
  • Contents [hide] 1 Interfaces explained2 gpio-dev3 gpio-sysfs 3.1 File ... structure3.2 GPIO ... access to a GPIO3.4 Getting and Setting Direction3.5 Getting and Setting S
  • linux3.4.1 按键驱动程序分析(pandaboard omap4460) 在内核中,按键的驱动程序已经设计好了,要使自己板上的按键工作起来,需要做的是在相应的文件中添加硬件信息,然后对内核进行正确的配置。 以下先使按键...
  • 文章目录1. gpio使用实例1.1 dts定义... gpio 子系统原理3.1 gpio_to_desc()3.2 gpiochip_add()3.3 gpiod_direction_output3.4 of_get_named_gpio()4. pinctrl原理 1. gpio使用实例 1.1 dts定义 在dts中: 定义一个d
  • 一、关于i2c协议概述 I2C总线协议只需要2根信号线即可完成数据的传输,这两根线分别是时钟线SCL和信号线SDA。I2C线上有且只有1个主设备Master和若干个从设备Slave,区别Master和Slave的标准是SCL,即谁是SCL的提供...
  • Documentation:Linux/GPIO

    2014-03-09 15:17:13
    Documentation:Linux/GPIO Contents [hide] 1 Interfaces explained2 gpio-dev3 gpio-sysfs 3.1 File structure3.2 GPIO Numbers3.3 Getting access to a GPIO3.4 Getting and Setting Dir
  • Nanopi2 GPIO学习心得

    2016-08-10 21:48:52
    最近入手Nanopi2,打算做点东西,但需要驱动一些模块,并且4418平台上并没有合适的驱动可以直接使用,所以只能自己另行开发。...4418的GPIO并不是跟其他的一样都是储存在regs-gpio.h中,/linux-3.4.y/arch/arm
  • 本文部分案例和文字英文... 更多精华文章请扫描下方二维码关注Linux阅码场 1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pain in the a...
  • 本文对intel e3800的GPIO驱动源码进行分析。
1 2 3 4 5 ... 20
收藏数 1,515
精华内容 606
热门标签