【IMX6ULL笔记】--内核底层驱动初步探究

本章简单介绍,imx6ull (cortex-A7)和 imxrt1062(cortex-m7)驱动开发模式对比

前期准备:

1.imx6ull 准备内核文件,这里笔者使用的内核版本是 4.9.88,当然也可以下载其他版本,只要支持就行

The Linux Kernel Archives

Search | NXP Semiconductors

2.imxrt1062 官方sdk (实际imx6ull官方给了一套sdk,可基于IAR开发,感兴趣的可以试试,但是一个A核芯片肯定基于linux开发才会发挥其作用)

Select Board | MCUXpresso SDK Builder (nxp.com)

3.两款芯片数据手册

imx6ull 驱动开发模式(linux内核驱动开发):芯片厂商会从 The Linux Kernel Archives下载某版本的linux内核,然后将其移植到自己的CPU上,测试成功后就会将其开放到自己网站上,提供给开发者使用。我们去研读,提供的内核文件,肯定能找到,底层驱动操作(涉及到该芯片寄存器读写)

imxrt1062 驱动开发模式(常规单片机开发,如:stm32这种),官方驱动包sdk上直接调用,基本不用啥驱动框架(近些年,小型嵌入式系统逐渐有了驱动框架形式,如:rt-thread 这类物联网操作系统等等)

驱动开发中如何操作

驱动开发共同点,不论是ARM哪种核,最终操作的肯定是内存读写,对某个驱动的寄存器读写。另外对于一些有IO的外设,都是需要设置IO的复用和电气属性。接下来我们就拿一个SAI(全称:Synchronous Audio Interface )外设简单举例说明

1.imx6ull和rt1062寄存器地址描述

  • imx6ull 寄存器

sai1寄存器存在位置,起始地址:0x0202_8000 大小:0x4000(16KB)

【IMX6ULL笔记】--内核底层驱动初步探究_第1张图片

【IMX6ULL笔记】--内核底层驱动初步探究_第2张图片

寄存器地址描述位置,存在于imx6ull.dtsi文件中,路径: ./arch/arm/boot/dts/,通过该dtsi文件我们可以定制属于自己的dts设备树文件(截图下方:imx6ull-14x14-evk.dts 为官方开发板设备树文件,可以通过官方文件照猫画虎深入学习)

【IMX6ULL笔记】--内核底层驱动初步探究_第3张图片

  • rt1062 寄存器

sai1 寄存器存在位置

【IMX6ULL笔记】--内核底层驱动初步探究_第4张图片

【IMX6ULL笔记】--内核底层驱动初步探究_第5张图片

寄存器地址描述位置,存在于MIMXRT1062.h头文件中

【IMX6ULL笔记】--内核底层驱动初步探究_第6张图片

2.imx6ull和rt1062驱动函数文件所属位置

  • imx6ull 驱动文件

首先我们需要通过imx6ull.dtsi文件找到sai1,其次找到compatible描述位置,我们可以通过:“fsl,imx6ul-sai”,“fsl,imx6sx-sai”,全局搜索,可以找到fsl_sai.c文件,可以看出该文件还支持 imx7 imx8等等

【IMX6ULL笔记】--内核底层驱动初步探究_第7张图片

【IMX6ULL笔记】--内核底层驱动初步探究_第8张图片

研读fsl_sai.c fsl_sai.h文件,我们可以找到很多描述该外设寄存器和读写该外设寄存器的信息,从源码中会找到一些regmap字眼,regmap是很重要的子系统(regmap 用于提供一套方便的 API 函数去操作底层硬件寄存器,以提高代码的可重用性),具体细节可以网上搜索理解。另外还有描述寄存器偏移的信息,用datasheet对比是不是一样。

【IMX6ULL笔记】--内核底层驱动初步探究_第9张图片

补:根据如上方法,我们可以试试查阅 adc ,wdog, i2c等外设的信息

/*省略......*/
wdog1: wdog@020bc000 {
    compatible = "fsl,imx6ul-wdt", "fsl,imx21-wdt";
    reg = <0x020bc000 0x4000>;
    interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_WDOG1>;
};
/*省略......*/
adc1: adc@02198000 {
    compatible = "fsl,imx6ul-adc", "fsl,vf610-adc";
    reg = <0x02198000 0x4000>;
    interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_ADC1>;
    num-channels = <2>;
    clock-names = "adc";
    status = "disabled";
};
/*省略......*/
i2c1: i2c@021a0000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
    reg = <0x021a0000 0x4000>;
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_I2C1>;
    status = "disabled";
};
/*省略......*/

在文件底部,将会注册该驱动:module_platform_driver(内核文件中大量使用该函数)

module_platform_driver(fsl_sai_driver);
/*查询......*/
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)
/*查询......*/
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
/*省略......*/
  • rt1062 驱动文件

驱动文件可以直接从sdk drivers文件夹中找到

【IMX6ULL笔记】--内核底层驱动初步探究_第10张图片

3.imx6ull和rt1062 IO复用配置

注:各家厂商有区别,这里只是描述nxp的

  • imx6ull io配置

根据linux内核路劲:./arch/arm/boot/dts/ 可以找到:imx6ul-pinfunc.h imx6ull-pinfunc.himx6ull-pinfunc-snvs.h,根据宏后面的信息可知其相关配置信息

【IMX6ULL笔记】--内核底层驱动初步探究_第11张图片

另外在imx6ull.dtsi文件中有个iomux节点的重用信息,同样我们可以通过"fsl,imx6ul-iomuxc"查找到pinctrl-imx6ull.c源码

iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};

【IMX6ULL笔记】--内核底层驱动初步探究_第12张图片

除此之外配置信息,设备树dts文件中的需要做io配置,这里拿sai2举例

&iomuxc {
	pinctrl-names = "default";
/*省略......*/
		pinctrl_sai2: sai2grp {
			fsl,pins = <
				MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK	0x17088
				MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC	0x17088
				MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA	0x11088
				MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA	0x11088
				MX6UL_PAD_JTAG_TMS__SAI2_MCLK		0x17088
			>;
		};    
 /*省略......*/
}
 /*省略......*/
&sai2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sai2>;

	assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
			  <&clks IMX6UL_CLK_SAI2>;
	assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <0>, <12288000>;

	status = "okay";
};

rt1062 io配置

io配置文件可以直接从sdk drivers文件夹中找到 fsl_iomuxc.h

【IMX6ULL笔记】--内核底层驱动初步探究_第13张图片

然后调用两个函数,实现最终配置

/*!
 * @brief Sets the IOMUXC pin mux mode.
 * @note The first five parameters can be filled with the pin function ID macros.
 *
 * This is an example to set the PTA6 as the lpuart0_tx:
 * @code
 * IOMUXC_SetPinMux(IOMUXC_PTA6_LPUART0_TX, 0);
 * @endcode
 *
 * This is an example to set the PTA0 as GPIOA0:
 * @code
 * IOMUXC_SetPinMux(IOMUXC_PTA0_GPIOA0, 0);
 * @endcode
 *
 * @param muxRegister  The pin mux register.
 * @param muxMode      The pin mux mode.
 * @param inputRegister The select input register.
 * @param inputDaisy   The input daisy.
 * @param configRegister  The config register.
 * @param inputOnfield   Software input on field.
 */
static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
                                    uint32_t muxMode,
                                    uint32_t inputRegister,
                                    uint32_t inputDaisy,
                                    uint32_t configRegister,
                                    uint32_t inputOnfield)
{
    *((volatile uint32_t *)muxRegister) =
        IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) | IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);

    if (inputRegister != 0UL)
    {
        *((volatile uint32_t *)inputRegister) = inputDaisy;
    }
}

/*!
 * @brief Sets the IOMUXC pin configuration.
 * @note The previous five parameters can be filled with the pin function ID macros.
 *
 * This is an example to set pin configuration for IOMUXC_PTA3_LPI2C0_SCLS:
 * @code
 * IOMUXC_SetPinConfig(IOMUXC_PTA3_LPI2C0_SCLS,IOMUXC_SW_PAD_CTL_PAD_PUS_MASK|IOMUXC_SW_PAD_CTL_PAD_PUS(2U))
 * @endcode
 *
 * @param muxRegister  The pin mux register.
 * @param muxMode      The pin mux mode.
 * @param inputRegister The select input register.
 * @param inputDaisy   The input daisy.
 * @param configRegister  The config register.
 * @param configValue   The pin config value.
 */
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
                                       uint32_t muxMode,
                                       uint32_t inputRegister,
                                       uint32_t inputDaisy,
                                       uint32_t configRegister,
                                       uint32_t configValue)
{
    if (configRegister != 0UL)
    {
        *((volatile uint32_t *)configRegister) = configValue;
    }
}

补:除次之外还有时钟配置,外设时钟源选择,时钟分频等等,这里就不展开说说了,大家可以试试。

你可能感兴趣的:(imx6ull,Linux,arm,嵌入式硬件)