Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> MSM8909+Android5.1.1 SPI驅動開發(PSAM部分)

MSM8909+Android5.1.1 SPI驅動開發(PSAM部分)

編輯:關於Android編程

MSM8909+Android5.1.1SPI驅動開發(PSAM部分)

 

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控制器設備節點

 

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控制器設備節點信息

 

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

 

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從設備節點信息

 

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電源控制的結構體

 

struct psam_control_data {
       /*system */
       structspi_device *client;
      
       /*dtsi */
       intpsam_en_gpio;
};

 

在spidev_probe()函數中默認初始化PSAM_EN為低電平,控制代碼如下:

 

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

 

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定義如下:

 

 

#define SPI_IOC_SPI_IOC_ENABLE _IOW(SPI_IOC_MAGIC, 5, __u32)
psam_power_control()函數的源代碼如下:

 

 

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部分

 

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()相關代碼如下:

 

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文件,內容如下:

 

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注釋掉就可以看到了

 

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下修改

 

static const struct of_device_idspidev_dt_ids[] = {
       //{.compatible = rohm,dh2228fv },
       {.compatible = nxp,lpc1114 },
       {},
};

 

(2) kernelarcharmootdtsqcommsm8909-cb03.dtsi的spi_2: spi@78b7000節點下增加從設備節點

 

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增加下面的內容來修改權限

 

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就可以了。

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved