Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题

时间:2022-09-04 20:02:33

Android系统采用4.3版本,内核版本为3.0.35,设备配置时没有高版本中高大上的设备树管理,遇到了个xx的问题,记录一下,其实Android的内核基本和Linux的一样,所以就把Android这层外衣脱掉,直接搞了Linux。

环境说明:芯片是i.MX6DL,copy的AI类型的板子(操蛋的采用了SD版本的配置文件)。开发环境是Ubuntu14.04,64bit。



先配置spidev,再说问题与解决方法。


1.管脚选择,SPI2用到的管脚如下图所示:

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题

查看芯片手册发现片选信号的模式可以用作内部片选信号ECSPI2_SS0和GPIO信号GPIO5_IO29(可人工采用GPIO进行片选),如下图:

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题


(1)在管脚配置文件中添加如下代码,其中包括SPI2和SPI3的管脚配置。由于功能需要,SPI3的MISO管脚另作他用,这里不初始化。

(2)片选信号ECSPI2_SS0可以采用GPIO配置,也可以采用内部片选信号配置,即:

        内部片选信号声明:MX6DL_PAD_CSI0_DAT11__ECSPI2_SS0

        GPIO用法声明:MX6DL_PAD_CSI0_DAT11__GPIO_5_29

在kernel_imx/arch/arm/plat-mxc/include/mach/spi.h文件中有spi_imx_master结构体的声明和关于片选信号的介绍,文件内容如下:

#ifndef __MACH_SPI_H_
#define __MACH_SPI_H_

/*
* struct spi_imx_master - device.platform_data for SPI controller devices.
* @chipselect: Array of chipselects for this master. <span style="color:#FF0000;">Numbers >= 0 mean gpio
* pins, numbers < 0 mean internal CSPI chipselects according
* to MXC_SPI_CS(). </span>Normally you want to use gpio based chip
* selects as the CSPI module tries to be intelligent about
* when to assert the chipselect: The CSPI module deasserts the
* chipselect once it runs out of input data. The other problem
* is that it is not possible to mix between high active and low
* active chipselects on one single bus using the internal
* chipselects. Unfortunately Freescale decided to put some
* chipselects on dedicated pins which are not usable as gpios,
* so we have to support the internal chipselects.
* @num_chipselect: ARRAY_SIZE(chipselect)
*/
struct spi_imx_master {
int*chipselect;
intnum_chipselect;
};

#define MXC_SPI_CS(no)((no) - 32)

#endif /* __MACH_SPI_H_*/

如上可知,如果采用内部的片选信号只需将值减32,即采用MXC_SPI_CS(no)宏定义即可(其实只要是小于0就表示内部片选,片选管脚书写的顺序表示片选值,因此猜测用-1代替应该也行,但是没验证)。


下面分别记录两种配置过程。

1)添加管脚声明代码如下:

<span style="font-size:18px;">static iomux_v3_cfg_t mx6dl_sabresd_ecspi_pads[] = {
/* SPI2 */
MX6DL_PAD_CSI0_DAT8__ECSPI2_SCLK,
MX6DL_PAD_CSI0_DAT9__ECSPI2_MOSI,
MX6DL_PAD_CSI0_DAT10__ECSPI2_MISO,
//MX6DL_PAD_CSI0_DAT11__ECSPI2_SS0,// 如果采用内部片选信号,屏蔽下一行GPIO的声明,打开本处声明
MX6DL_PAD_CSI0_DAT11__GPIO_5_29,


/* SPI3 */
MX6DL_PAD_DISP0_DAT0__ECSPI3_SCLK,
MX6DL_PAD_DISP0_DAT1__ECSPI3_MOSI,
//MX6DL_PAD_DISP0_DAT2__ECSPI3_MISO,
MX6DL_PAD_DISP0_DAT3__ECSPI3_SS0,
};</span>


2)添加GPIO声明:

    说明:如果采用内部片选信号,则不添加GPIO的声明。

#define SABRESD_ECSPI2_CS0 IMX_GPIO_NR(5, 29) // spi2 cs0
#define SABRESD_ECSPI3_CS0 IMX_GPIO_NR(4, 24) // spi3 cs0

3)片选结构体初始化:

/* SPI2 cs0 */
static int mx6q_sabresd_spi_cs2[] = {
//MXC_SPI_CS(2),// 如果采用内部片选信号,屏蔽下一行GPIO的声明,打开本处声明
SABRESD_ECSPI2_CS0,
};

static const struct spi_imx_master mx6q_sabresd_spi_data2 __initconst = {
.chipselect = mx6q_sabresd_spi_cs2,
.num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi_cs2),
};

/* SPI3 cs0 */
static int mx6q_sabresd_spi_cs3[] = {
//MXC_SPI_CS(3),// 如果采用内部片选信号,屏蔽下一行GPIO的声明,打开本处声明
SABRESD_ECSPI3_CS0,
};

static const struct spi_imx_master mx6q_sabresd_spi_data3 __initconst = {
.chipselect = mx6q_sabresd_spi_cs3,
.num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi_cs3),
};



4)设备参数配置:

    说明:这里根据需要配置为CS高片选,CLK类型选择MODE_1(POL=0, PHA=1)

static struct spi_board_info imx6_sabresd_spi_device[] __initdata = {
/* SPI2 */
{
.modalias = "spidev",
.max_speed_hz = 10000000, /* max spi clock (SCK) speed in HZ */
.bus_num = 1,
.chip_select = 0,
.mode = SPI_MODE_1 | SPI_CS_HIGH,
},
/* SPI3 */
{
.modalias = "spidev",
.max_speed_hz = 24000000, /* max spi clock (SCK) speed in HZ */
.bus_num = 2,
.chip_select = 0,
.mode = SPI_MODE_1 | SPI_CS_HIGH,
},
};

5)修改SPI初始化函数如下,并将该函数的调用添加到static void __init mx6_sabresd_board_init(void)函数中即可:

static void spi_device_init(void)
{
// 先初始化管脚
mxc_iomux_v3_setup_multiple_pads(mx6dl_sabresd_ecspi_pads,
ARRAY_SIZE(mx6dl_sabresd_ecspi_pads));

// 添加片选信号
imx6q_add_ecspi(1, &mx6q_sabresd_spi_data2);
imx6q_add_ecspi(2, &mx6q_sabresd_spi_data3);

// 注册设备
spi_register_board_info(imx6_sabresd_spi_device, ARRAY_SIZE(imx6_sabresd_spi_device));
}


以上配置完成后,编译源码并烧写,启动系统后会发现在/dev/目录下多了两个设:spidev1.0和spidev2.0。但是通过测试发现无论如何spidev1.0都不能收发数据。于是示波器抓取相应信号进行分析。


2.分析不能收发数据的原因

在收发过程中抓取CLK波形如下所示:

    分析:根据经验调档位为1V,结果没有CLK信号,将档位缩小到100mv,果然存在。。。如下图,可以看出其峰值大概为300mv,不满足硬件的需要,所以猜测是管脚ctrl_pad设置出现问题。

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题


3.查看并更改管脚的ctrl_pad值如下:

#define MX6DL_PAD_CSI0_DAT8__ECSPI2_SCLK                                       \
IOMUX_PAD(0x0398, 0x0084, 2, 0x07F4, 0, NO_PAD_CTRL)
#define MX6DL_PAD_CSI0_DAT9__ECSPI2_MOSI \
IOMUX_PAD(0x039C, 0x0088, 2, 0x07FC, 0, NO_PAD_CTRL)
#define MX6DL_PAD_CSI0_DAT10__ECSPI2_MISO \
IOMUX_PAD(0x0360, 0x004C, 2, 0x07F8, 0, NO_PAD_CTRL)
#define MX6DL_PAD_CSI0_DAT11__ECSPI2_SS0 \
IOMUX_PAD(0x0364, 0x0050, 2, 0x0800, 0, NO_PAD_CTRL)

可根据IOMUX_PAD宏定义判断最后一个参数为ctrl_pad值IOMUX_PAD和NO_PAD_CTRL声明在 kernel_imx/arch/arm/plat-mxc/include/mach/iomuxv3.h文件中,如下:

#define IOMUX_PAD(_pad_ctrl_ofs, _mux_ctrl_ofs, _mux_mode, _sel_input_ofs, \
_sel_input, _pad_ctrl)\
(((iomux_v3_cfg_t)(_mux_ctrl_ofs) << MUX_CTRL_OFS_SHIFT) |\
((iomux_v3_cfg_t)(_mux_mode) << MUX_MODE_SHIFT) |\
((iomux_v3_cfg_t)(_pad_ctrl_ofs) << MUX_PAD_CTRL_OFS_SHIFT) | \
((iomux_v3_cfg_t)(_pad_ctrl) << MUX_PAD_CTRL_SHIFT) |\
((iomux_v3_cfg_t)(_sel_input_ofs) << MUX_SEL_INPUT_OFS_SHIFT) | \
((iomux_v3_cfg_t)(_sel_input) << MUX_SEL_INPUT_SHIFT))

#define NO_PAD_CTRL(1 << 17)

可知,NO_PAD_CTRL=0x80000000,根据之前的介绍( http://blog.csdn.net/shengzhadon/article/details/49910311),表示该管脚的配置config( pad_ctrl)无效,或者说不需要配置,这显然不行。


参考之前做过的Linux3.18.22的移植与设备树配置:

pinctrl_ecspi2: ecspi2grp {
fsl,pins = <
MX6QDL_PAD_CSI0_DAT10__ECSPI2_MISO0x100b1
MX6QDL_PAD_CSI0_DAT9__ECSPI2_MOSI0x100b1
MX6QDL_PAD_CSI0_DAT8__ECSPI2_SCLK 0x100b1
>;
};

pinctrl_ecspi2_cs: ecspi2cs {
fsl,pins = <
MX6QDL_PAD_CSI0_DAT11__ECSPI2_SS00x80000000
>;
};

将pad值修改为0x100b1,如下

#define MX6DL_PAD_CSI0_DAT8__ECSPI2_SCLK                                       \
IOMUX_PAD(0x0398, 0x0084, 2, 0x07F4, 0, 0x100b1)
#define MX6DL_PAD_CSI0_DAT9__ECSPI2_MOSI \
IOMUX_PAD(0x039C, 0x0088, 2, 0x07FC, 0, 0x100b1)
#define MX6DL_PAD_CSI0_DAT10__ECSPI2_MISO \
IOMUX_PAD(0x0360, 0x004C, 2, 0x07F8, 0, 0x100b1)
#define MX6DL_PAD_CSI0_DAT11__ECSPI2_SS0 \
IOMUX_PAD(0x0364, 0x0050, 2, 0x0800, 0, NO_PAD_CTRL)

重新编译烧写,测试,抓取的CLK波形如下:

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题

从上图看出峰值大约为3V(上图是示波器探头带宽不足或者地离得比较远导致波形失真),测试中写入与读取的数据正确,说明问题解决。


===========================================================================

附图:另外换个高带宽的探头、采用近点的GND看修正后的抓图

    示波器三个通道分别是
        CH1(黄)——SCLK
        CH2(绿)——MOSI
        CH3(红)——MISO
        CH4(蓝)——SS


(1)发送数据0xB900

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题


(2)读取数据正确,为0xB9EA

Android系统驱动开发琐碎——解决spidev读写过程没有时钟信号的问题