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

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.     组件配置

kernel\arch\arm\configs\msm8909-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  kernel\arch\arm\boot\dts\qcom\msm8909-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: [email protected] { /* 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  kernel\arch\arm\boot\dts\qcom\msm8909-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  kernel\arch\arm\boot\dts\qcom\msm8909-qrd-skue-cb03.dtsi增加SPI2控制器挂载的SPI从设备节点信息

[email protected] {
              [email protected]{
                     compatible= "nxp,lpc1114";//挂载的是NXP厂家的lpc1114设备
                     reg= <0>;
                     spi-max-frequency= <1000000>;
                     qcom,psam_en_gpio= <&msm_gpio 23 0>;
              };
       };

这里说明下[email protected]后面的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测试代码

在kernel\Documentation\spi文件夹下就是SPI测试程序,其中spidev_test.c是用于测试自发自收的。我在system\extras下新建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\target\product\msm8909\symbols\system\bin目录下。然后我们可以通过adb push命令把此文件拷贝到设备某个目录下用./spidev_test执行,如果提示权限不够,就用chmod 777 spidev_test命令。

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

7.     遇到的问题及解决

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

把kernel\arch\arm\boot\dts\qcom\msm8909-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)  kernel\arch\arm\boot\dts\qcom\msm8909-cb03.dtsi的spi_2: [email protected]节点下增加从设备节点

[email protected] {
              [email protected]{
                     compatible= "nxp,lpc1114";
                     reg= <0>;
                     spi-max-frequency= <1000000>;
                     qcom,psam_en_gpio= <&msm_gpio 23 0>;
              };
       };

只要这两个地方的名字一样就可以。

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

在system\core\rootdir\ 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就可以了。

SPI数据传输参考:

http://blog.csdn.net/droidphone/article/details/24663659

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-11 07:37:09

MSM8909+Android5.1.1 SPI驱动开发(PSAM部分)的相关文章

Linux下spi驱动开发

转载至:http://www.embedu.org/Column/Column367.htm 作者:刘洪涛,华清远见嵌入式学院讲师. 一.概述 基于子系统去开发驱动程序已经是linux内核中普遍的做法了.前面写过基于I2C子系 统的驱动开发.本文介绍另外一种常用总线SPI的开发方法.SPI子系统的开发和I2C有很多的相似性,大家可以对比学习.本主题分为两个部分叙述,第一 部分介绍基于SPI子系统开发的理论框架:第二部分以华清远见教学平台FS_S5PC100上的M25P10芯片为例(内核版本2.6

MSM8909+Android5.1.1键盘驱动------概述

采用SN7326带智能指扫描的键盘扩展芯片,通过I2C接口来读取其状态寄存器的值就可知道是单按键还是多按键按下,可知道具体是哪个按键按下.然后键盘驱动调用input_event()上报linux的扫描码,比如KEY_RIGHT,然后传递给android框架层,流程如下图: 图1 下面介绍要实现键盘驱动所涉及的主要方方面面 1.     Input子系统 Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简

MSM8909+Android5.1.1之BSP开发---开关机充电三色灯控制

图1 1.     充电三色灯控制 1.1   关机充电三色灯控制 相关文件:system\core\healthd\healthd_mode_charger.cpp 当前电量不等于之前电量时,调用handle_power_supply_state()--->set_battery_soc_leds()来控制三色灯,先来看其中用到的全局数组 #define RED_LED_PATH "/sys/class/leds/red/brightness" #define GREEN_LE

MSM8909+Android5.1.1之BSP开发---电池管理2---BatteryInfo.java

先来借用MTK对电池管理的框架图 图1 通过电话测试指令:*#*#4636#*#*可以弹出Testing界面 图2 选择Battery information,进入: 图3 我们接入USB或是DC充电的时候,Power plug都显示的时Unknown 说明底层就判断为Unknown类型,此问题要查清楚 BatteryInfo对应的代码为packages\apps\Settings\src\com\android\settings\BatteryInfo.java 1.     \package

MSM8909+Android5.1.1键盘驱动---sn7326介绍

1.     Sn7326概述 SN7326是一款带智能自扫描的键盘扩展芯片,支持多达8*8个按键.按下/松开按键的动作被编码成一个字节的数据存入到按键事件寄存器(key event register)中,主控制器可通过I2C串行总线读取按键事件寄存器. SN7326具有去抖动功能,在任何按键按下时中断输出引脚会置低,为降低功耗,在没有按键动作时SN7326自动进入低功耗模式. SN7326的主要特性 (1)   2.4V到5.5V的工作电压 (2)   400kHz的I2C串行接口 (3)  

linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》

本文摘自本人拙著 <嵌入式Linux驱动模板精讲与项目实践> 初步看起来Linux设备驱动开发涉及内容非常多,而须要实现驱动的设备千差万别.事实上做一段时间驱动之后回首看来主要就是下面几点: (1)对驱动进行分类.先归纳为哪个类型的驱动.归类正确再利用内核提供的子系统进行开发,往往会发现事实上非常多通用的事情内核已经帮我们做了,一个优秀的驱动project师应该最大程度上利用内核的资源.内核已经实现的毕竟稳定性强.可移植性高. (2)找到内核的提供的子系统.接下来就是要制作该子系统对该类设备提

《Linux设备驱动开发详解(第3版)》海量更新总结

本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 2015.2.26 几乎完成初稿. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]添加关于QEMU模拟vexpress板的描述 第2章 <驱动设计的硬件基础> [N]增加关于SoC的介绍:[N]增加关于eFuse的内容:[D]删除ISA总线的内容了:[N]增加关于SP

《Linux设备驱动开发具体解释(第3版)》进展同步更新

本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]加入关于QEMU模拟vexpress板的描写叙述 第2章 <驱动设计的硬件基础> [

Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析

关于Exynos4412 IIC 裸机开发请看 :Exynos4412 裸机开发 -- IIC总线 ,下面回顾下 IIC 基础概念 一.IIC 基础概念 IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备.IIC总线产生于在80年代,最初为音频和视频设备开发,如今主要在服务器管理中使用,其中包括单个组件状态的通信.例如管理员可对各个组件进行查询,以管理系统的配置或掌握组件的功能状态,如电源和系统风扇.可随时监