MSM8909+Android5.1.1 SPI驱动开发(PSAM部分)

    xiaoxiao2021-03-25  79

    1. PSAM部分的硬件设计

     

    图1

    CS 片选信号

    SCK 时钟信号

    MISO 主设备的数据输入、从设备的数据输出脚

    MOSI 主设备的数据输出、从设备的数据输入脚

     

    2. PSAM部分软件设计

     

    图2

     

    (1) PSAM应用

    这部分只需要调用API函数即可。

     

    (2) PSAM API

    API的主要工作就是给M0上电、设置SPI读写模式、设置SPI读写的每字节的位数、SPI的工作频率等。

     

    (3) SPI驱动

    接收到API设置SPI的参数后做相应的工作。

     

    (4) M0固件

    和TDA8007的主要工作由M0固件完成,包括协议的等等。

     

    3. 组件配置

    kernelarcharmconfigsmsm8909-1gb-CB03-perf_defconfig配置文件,确保下面选项设置如下:

    CONFIG_SPI=y

    CONFIG_SPI_QUP=y

    CONFIG_SPI_SPIDEV=m,后来给改为y

     

    如果是user版本就采用此文件,如果是eng文件就改msm8909-1gb-CB03_defconfig文件。

     

    4. 设备树配置

    PSAM部分设备树节点的设置层次

     

    图3

    其中绿色矩形框部分是PSAM部分需要修改。

     

    4.1 kernelarcharmootdtsqcommsm8909-cb03.dtsi增加SPI2控制器设备节点

     

    ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 aliases {                /* smdtty devices */                smd1= &smdtty_apps_fm;                smd2= &smdtty_apps_riva_bt_acl;                smd3= &smdtty_apps_riva_bt_cmd;                smd5= &smdtty_apps_riva_ant_cmd;                smd6= &smdtty_apps_riva_ant_data;                smd7= &smdtty_data1;                smd8= &smdtty_data4;                smd11= &smdtty_data11;                smd21= &smdtty_data21;                smd36= &smdtty_loopback;                   sdhc1= &sdhc_1; /* SDC1 eMMC slot */                sdhc2= &sdhc_2; /* SDC2 SD card slot */                //spi0= &spi_0; /* SPI0 controller device */                spi2 = &spi_2; /* SPI2 controller device */                 i2c5 = &i2c_5; /* I2c5 cntroller device */                //i2c3= &i2c_3; /* I2C3 controller */                i2c1= &i2c_1; /* I2C1 controller */                i2c2= &i2c_2; /* I2C2 NFC qup2 device */                i2c4= &i2c_4; /* I2C4 controller device */                i2c6= &i2c_6; /* I2c6 cntroller device */         }; 增加spi2 = &spi_2; /*SPI2 controller device */,同时注释掉i2c3 =&i2c_3;,且去掉msm8909-qrd-skue-cb03.dtsi文件下i2c_3相关的信息

     

     

    增加SPI2控制器设备节点信息

     

    ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 spi_2: spi @78b7000 { /* BLSP1 QUP3 */                  compatible =qcom,spi-qup-v2;                  #address-cells = < 1 >;                 #size-cells = < 0 >;                  reg-names =spi_physical, spi_bam_physical;                  reg = < 0x78b7000 0x600 >,                        <0x78840000x23000>;                  interrupt-names =spi_irq, spi_bam_irq;                  interrupts = < 0 97 0 >,< 0 238 0 >;                  spi-max-frequency =< 19200000 >;                  pinctrl-names =spi_default, spi_sleep;                  pinctrl- 0 =<&spi2_default &spi2_cs0_active>;                  pinctrl- 1 = <&spi2_sleep&spi2_cs0_sleep>;                  clocks = <&clock_gccclk_gcc_blsp1_ahb_clk>,                           <&clock_gccclk_gcc_blsp1_qup3_spi_apps_clk>;                  clock-names =iface_clk, core_clk;                  qcom,infinite-mode = < 0 >;                  qcom,use-bam;                  qcom,use-pinctrl;                  qcom,ver-reg-exists;                  qcom,bam-consumer-pipe-index =< 8 >;                  qcom,bam-producer-pipe-index =< 9 >;                  qcom,master-id = < 86 >;         }; For latest detail please follow /kernel/Documentation/devicetree/bindings/spi/spi_qsd.txt

     

     

    这里说明下SPI2的2指SPI控制器对应的总线号,对应spi_maste结构体的成员bus_num。

     

     

    4.2 kernelarcharmootdtsqcommsm8909-pinctrl-cb03.dtsi增加SP2控制器引脚控制设置。

    Pin控制的文档可参考/kernel/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt

     

    ? 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 spi2_active {                          /* MOSI, MISO, CLK */                          qcom,pins = <&gp0>, <&gp 1 >, <&gp 3 >;                          qcom,num-grp-pins =< 3 >;                          qcom,pin-func =< 1 >;                          label =spi2-active;                          /* active state */                          spi2_default:spi2_default {                                  drive-strength= < 12 >; /* 12 MA */                                  bias-disable =< 0 >; /* No PULL */                         };                  };                     spi2_suspend {                          /* MOSI, MISO, CLK */                          qcom,pins = <&gp0>, <&gp 1 >, <&gp 3 >;                          qcom,num-grp-pins =< 3 >;                          qcom,pin-func =< 0 >;                          label =spi2-suspend;                          /* suspended state */                          spi2_sleep: spi2_sleep{                                  drive-strength= < 2 >; /* 2 MA */                                  bias-pull-down; /* pull down */                          };                  };                  spi2_cs0_active {                          /* CS */                          qcom,pins = <&gp2>;                          qcom,num-grp-pins =< 1 >;                         qcom,pin-func =< 1 >;                          label =spi2-cs0-active;                          spi2_cs0_active:cs0_active {                                  drive-strength= < 2 >;                                  bias-disable =< 0 >;                         };                  };                        spi2_cs0_suspend {                          /* CS */                          qcom,pins = <&gp2>;                          qcom,num-grp-pins =< 1 >;                          qcom,pin-func =< 0 >;                         label =spi2-cs0-suspend;                          spi2_cs0_sleep:cs0_sleep {                                  drive-strength= < 2 >;                                  bias-disable =< 0 >;                          };                  };

     

    4.3 kernelarcharmootdtsqcommsm8909-qrd-skue-cb03.dtsi增加SPI2控制器挂载的SPI从设备节点信息

     

    ? 1 2 3 4 5 6 7 8 spi @78b7000 {                  spidev @0 {                       compatible= nxp,lpc1114; //挂载的是NXP厂家的lpc1114设备                       reg= < 0 >;                       spi-max-frequency= < 1000000 >;                       qcom,psam_en_gpio= <&msm_gpio 23 0 >;                };         }; 这里说明下spidev@0后面的0是指SPI采用哪个CS引脚选择的SPI从设备,对应struct spi_master结构体成员num_chipselect。那为什么我们是用cs0呢。先来看我们用的SPI控制器对应的引脚

     

     

    图4

    这里CS_N的N为什么是0呢?再来看下图:

     

    图5

    我们可知BSP1~3都可以通过扩展的CS1、CS2和CS3来片选SPI从设备,当然还有CS0,也就是说BSP1~3对应的SPI0~SPI2控制器,每个都可以支持多达4个SPI从设备,BSP4~6只能挂接1个SPI从设备,只能通过CS0来片选。

     

    5. 驱动代码控制

    5.1 PSAM_EN控制

    新建PSAM电源控制的结构体

     

    ? 1 2 3 4 5 6 7 struct psam_control_data {         /*system */         structspi_device *client;                 /*dtsi */         intpsam_en_gpio; };

     

    在spidev_probe()函数中默认初始化PSAM_EN为低电平,控制代码如下:

     

    ? 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 static bool parse_psam_control_dtsi(structdevice *dev, struct psam_control_data *psam_data) {         //enumof_gpio_flags dummy;         structdevice_node *node = dev->of_node;         intret;                 psam_data->psam_en_gpio= of_get_named_gpio_flags(node,                                                   qcom,psam_en_gpio, 0 , NULL);                if (psam_data->psam_en_gpio < 0 )                {                                             returnERR_PTR(psam_data->psam_en_gpio);                }                else {                       ret= gpio_request(psam_data->psam_en_gpio, psam_en_gpio);                       if (ret < 0 ){                              pr_err(Failedto request psam_en_gpio, ERRNO:%d, ret);                              gotofree_gpio;                       }                       gpio_direction_output(psam_data->psam_en_gpio, 0 );                }    free_gpio:         gpio_free(psam_data->psam_en_gpio);         returntrue; }

     

    在PSAM应用程序打开的时候,应用层通过ioctl(fd, SPI_IOC_SPI_IOC_ENABLE, &sam_enable)来控制上电,对应调用spidev.c下的spidev_ioctl(),增加case

     

    ? 1 2 3 4 5 6 7 case SPI_IOC_SPI_IOC_ENABLE:                retval= __get_user(tmp, (u8 __user *)arg);                   if (retval == 0 ) {                       psam_power_control(tmp);                }                break ; 在spidev.h增加SPI_IOC_SPI_IOC_ENABLE定义如下:

     

     

    ? 1 #define SPI_IOC_SPI_IOC_ENABLE _IOW(SPI_IOC_MAGIC, 5 , __u32) psam_power_control()函数的源代码如下:

     

     

    ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static bool psam_power_control( bool on) {         intret;            psam_control->psam_en_gpio= of_get_named_gpio_flags(psam_control->client->dev.of_node,                                                   qcom,psam_en_gpio, 0 , NULL);                if (psam_control->psam_en_gpio < 0 )                       returnERR_PTR(psam_control->psam_en_gpio);                else {                       ret= gpio_request(psam_control->psam_en_gpio, psam_en_gpio);                       if (ret < 0 ){                              pr_err(Failedto request psam_en_gpio, ERRNO:%d, ret);                              gotofree_gpio;                       }                       gpio_direction_output(psam_control->psam_en_gpio,on);                }    free_gpio:         gpio_free(psam_control->psam_en_gpio);            returnret; } 5.2 SPI从设备节点属性compatible要和spidev.c下的spidev_spi_driver保持一致

     

    同时参考2.3部分

     

    ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static const struct of_device_idspidev_dt_ids[] = {         //{.compatible = rohm,dh2228fv },         {.compatible = nxp,lpc1114 },         {}, };    MODULE_DEVICE_TABLE(of, spidev_dt_ids);    static struct spi_driver spidev_spi_driver= {         .driver= {                .name=        spidev,                .owner=       THIS_MODULE,                .of_match_table= of_match_ptr(spidev_dt_ids),         },         .probe= spidev_probe,         .remove=     spidev_remove,            /*NOTE:  suspend/resume methods are notnecessary here.          * We don't do anything except pass therequests to/from          * the underlying controller.  The refrigerator handles          * most issues; the controller driver handlesthe rest.          */ };

     

    5.3 CPOL和CPHA极性设置

    我们用CPOL表示时钟信号的初始电平的状态,CPOL为0表示时钟信号初始状态为低电平,为1表示时钟信号的初始电平是高电平。另外,我们用CPHA来表示在那个时钟沿采样数据,CPHA为0表示在首个时钟变化沿采样数据,而CPHA为1则表示要在第二个时钟变化沿来采样数据。内核用CPOL和CPHA的组合来表示当前SPI需要的工作模式:

    CPOL=0,CPHA=1 模式0

    CPOL=0,CPHA=1 模式1

    CPOL=1,CPHA=0 模式2

    CPOL=1,CPHA=1 模式3

     

    我们这里SPI从设备CPOL和CPHA采用的是模式1,所以我们SPI控制器也采用模式1。

     

    module_init(spidev_init);

    spidev_init()相关代码如下:

     

    ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (busnum != - 1 && chipselect !=- 1 ) {                structspi_board_info chip = {                                     .modalias     = spidev,                                     .mode           = spimode,                                     .bus_num     = busnum,                                     .chip_select  = chipselect,                                     .max_speed_hz   = maxspeed,                };                   structspi_master *master;                   master= spi_busnum_to_master(busnum);                if (!master) {                       status= -ENODEV;                       gotoerror_busnum;                }                   /*We create a virtual device that will sit on the bus */                spi= spi_new_device(master, &chip);

     

     

    5.4

    6. SPI测试代码

    在kernelDocumentationspi文件夹下就是SPI测试程序,其中spidev_test.c是用于测试自发自收的。我在systemextras下新建spi文件夹,并把spidev_test.c拷贝到spi文件夹下,并创建一个Android.mk文件,内容如下:

     

    ? 1 2 3 4 5 6 7 LOCAL_PATH := $(call my-dir)    include $(CLEAR_VARS) LOCAL_MODULE := spidev_test LOCAL_SRC_FILES := spidev_test.c LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE)

     

    然后进入此spi目录下用mm命令编译生成的可执行文件spidev_test在out argetproductmsm8909symbolssystemin目录下。然后我们可以通过adb push命令把此文件拷贝到设备某个目录下用./spidev_test执行,如果提示权限不够,就用chmod 777 spidev_test命令。

     

    用此程序可以测试spi设备驱动是否正常,但是否能够正常驱动SPI从设备还需要根据具体的从设备来增加对应的控制。

     

    7. 遇到的问题及解决

    7.1 /sys/class/spi_master下找不到spi2

     

    把kernelarcharmootdtsqcommsm8909-cb03.dtsi的i2c3 = &i2c_3注释掉就可以看到了

     

    ? 1 2 3 4 5 6 7 8 9 10 11 aliases {         …                //spi0 = &spi_0; /* SPI0 controllerdevice */                spi2= &spi_2; /* SPI0 controller device */                 i2c5 = &i2c_5; /* I2c5 cntroller device */                //i2c3 = &i2c_3; /* I2C3 controller */                i2c1= &i2c_1; /* I2C1 controller */                i2c2= &i2c_2; /* I2C2 NFC qup2 device */                i2c4= &i2c_4; /* I2C4 controller device */                i2c6= &i2c_6; /* I2c6 cntroller device */         };

     

    7.2 /sys/class/spidev下看不到SPI2控制器下挂载的SPI从设备

    (1) Spidev.c下修改

     

    ? 1 2 3 4 5 static const struct of_device_idspidev_dt_ids[] = {         //{.compatible = rohm,dh2228fv },         {.compatible = nxp,lpc1114 },         {}, };

     

    (2) kernelarcharmootdtsqcommsm8909-cb03.dtsi的spi_2: spi@78b7000节点下增加从设备节点

     

    ? 1 2 3 4 5 6 7 8 spi @78b7000 {                  spidev @0 {                       compatible= nxp,lpc1114;                       reg= < 0 >;                       spi-max-frequency= < 1000000 >;                       qcom,psam_en_gpio= <&msm_gpio 23 0 >;                };         }; 只要这两个地方的名字一样就可以。

     

     

    7.3 应用层open()设备/dev/spidev2.0失败

    在systemcore ootdir init_CB03.rc增加下面的内容来修改权限

     

    ? 1 chmod 0666 /dev/spidev2. 0

     

    7.4 应用层用write()和read()测试自发自收失败

    用系统自带的spidev_test采用的ioctl方式测试自发自收可以,但用write()写返回值却是0,正确的应该是返回我们实际写入的字节数,目前原因不知道,相关的帖子:http://bbs.csdn.net/topics/391858635?page=1#post-400571674。

     

    没办法我们PSAM的API层只能改用ioctl的方式就可以了。

     

    7.5 SPI工作频率过低

    通过PSAM的API我设置SPI控制器工作的频率为200kHZ,SPI控制器的spi_qsd提示频率过低,改为1MHZ就可以了。

     

    7.6 TDA8007给PSAM卡上电失败

    用我们实际的PSAM程序测试,根据调试信息可知选卡槽命令正常,但是给PSAM卡供5V电的时候一直没有数据回来,后来查明是

     

    图6

    把SW1的下拉改为上拉到3.3V就可以了。

    转载请注明原文地址: https://ju.6miu.com/read-13632.html

    最新回复(0)