标签:
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: 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>;
};
这里说明下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从设备节点信息
spi@78b7000 {
spidev@0{
compatible= "nxp,lpc1114";//挂载的是NXP厂家的lpc1114设备
reg= <0>;
spi-max-frequency= <1000000>;
qcom,psam_en_gpio= <&msm_gpio 23 0>;
};
};
图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;#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;
}同时参考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: 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失败
在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
版权声明:本文为博主原创文章,未经博主允许不得转载。
MSM8909+Android5.1.1 SPI驱动开发(PSAM部分)
标签:
原文地址:http://blog.csdn.net/loongembedded/article/details/49922723