IMX6之PAD配置(IOMUX)

注:本文基于yocto编译出的u-boot.2016.03

参考:Document Number: IMX6DQRM
Rev. C, 1/2012

1、 IOMUX

Chapter 4
External Signals and Pin Multiplexing

The i.MX 6Dual/6Quad contains a limited number of pins, most of which have multiple signal options. These signal to pin and pin to signal options are selected by the inputoutput multiplexer called IOMUX. The IOMUX is also used to configure other pin characteristics, such as voltage level, drive strength, and hysteresis.

i.MX 6Dual/6Quad 的芯片引脚有限,大部分的引脚可以配置成不同的信号功能,引脚功能由IOMUX来决定,IOMUX也用来配置引脚的其它特性,如:电压电平,驱动强度和迟滞。

2、以UART4 为例

2.1 涉及的函数

mx6qsabreauto.c

这个board_early_init_f函数在初化序列里是在比较靠前的位置,这样可以来调试u-boot,用串口来打印。
int board_early_init_f(void)
{
	setup_iomux_uart();

	return 0;
}

imx_iomux_v3_setup_multiple_pads函数来初始化UART4的引脚。

static void setup_iomux_uart(void)
{
	imx_iomux_v3_setup_multiple_pads(uart4_pads, ARRAY_SIZE(uart4_pads));
}

2.2 UART4有两个引脚需要配置

static iomux_v3_cfg_t const uart4_pads[] = {
	MX6_PAD_KEY_COL0__UART4_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL),
	MX6_PAD_KEY_ROW0__UART4_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL),
};

2.3 函数调用关系

如果搜素这个“MX6_PAD_KEY_COL0__UART4_TX_DATA”找不着。

对应关系是怎么样的呢?见下图
在这里插入图片描述

通过 “MX6_PAD_DECL”这个宏定义了一个 64位的无符号长整型

typedef u64 iomux_v3_cfg_t;

2.4 pad定义

对于KEY_COL0这个PAD,需要配置以下的三个寄存器
在这里插入图片描述

使用一个64位的整型把偏移量与配置值都放在一起,传给函数:

在这里插入图片描述

2.4.1 iomux_v3_cfg_t的内存布局:

sel_input pad_ctrl mux_mode sel_input_ofs pad_ctrl_ofs mux_ctrl_ofs
菊花链 控制 复用 菊花链偏移 控制偏移 多路复用偏移
4 19 7 12 12 12
0 X 4 0x938 0x05C8 0x01F8

A 菊花链:

在这里插入图片描述

B X的定义为:

#define UART_PAD_CTRL  (PAD_CTL_PUS_100K_UP |			\
	PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm |			\
	PAD_CTL_SRE_FAST  | PAD_CTL_HYS)

UART_PAD_CTRL 宏定义把以下的这些位填充了。
在这里插入图片描述

C 多路复用:

在这里插入图片描述

2.5 、回写pad的相关寄存器

下面这个函数把组的一个64位的pad解析出来再写到相关的寄存器里

void imx_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
{
	u32 mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT;
	u32 mux_mode = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT;
	u32 sel_input_ofs =
		(pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT;
	u32 sel_input =
		(pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT;
	u32 pad_ctrl_ofs =
		(pad & MUX_PAD_CTRL_OFS_MASK) >> MUX_PAD_CTRL_OFS_SHIFT;
	u32 pad_ctrl = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT;

#if defined(CONFIG_MX6SL) || defined(CONFIG_MX6SLL)
	/* Check whether LVE bit needs to be set */
	if (pad_ctrl & PAD_CTL_LVE) {
		pad_ctrl &= ~PAD_CTL_LVE;
		pad_ctrl |= PAD_CTL_LVE_BIT;
	}
#endif

#ifdef CONFIG_IOMUX_LPSR
	u32 lpsr = (pad & MUX_MODE_LPSR) >> MUX_MODE_SHIFT;

#ifdef CONFIG_MX7
	if (lpsr == IOMUX_CONFIG_LPSR) {
		base = (void *)IOMUXC_LPSR_BASE_ADDR;
		mux_mode &= ~IOMUX_CONFIG_LPSR;
		/* set daisy chain sel_input */
		if (sel_input_ofs)
			sel_input_ofs += IOMUX_LPSR_SEL_INPUT_OFS;
	}
#else
	if (is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL)) {
		if (lpsr == IOMUX_CONFIG_LPSR) {
			base = (void *)IOMUXC_SNVS_BASE_ADDR;
			mux_mode &= ~IOMUX_CONFIG_LPSR;
		}
	}
#endif
#endif

	if (is_soc_type(MXC_SOC_MX7) || is_cpu_type(MXC_CPU_MX6ULL) ||
	    is_cpu_type(MXC_CPU_MX6SLL) || mux_ctrl_ofs)
		__raw_writel(mux_mode, base + mux_ctrl_ofs);

	if (sel_input_ofs)
		__raw_writel(sel_input, base + sel_input_ofs);

#ifdef CONFIG_IOMUX_SHARE_CONF_REG
	if (!(pad_ctrl & NO_PAD_CTRL))
		__raw_writel((mux_mode << PAD_MUX_MODE_SHIFT) | pad_ctrl,
			base + pad_ctrl_ofs);
#else
	if (!(pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs)
		__raw_writel(pad_ctrl, base + pad_ctrl_ofs);
#if defined(CONFIG_MX6SLL)
	else if ((pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs)
		clrbits_le32(base + pad_ctrl_ofs, PAD_CTL_IPD_BIT);
#endif
#endif

#ifdef CONFIG_IOMUX_LPSR
	if (lpsr == IOMUX_CONFIG_LPSR)
		base = (void *)IOMUXC_BASE_ADDR;
#endif

}

有用的参考

https://blog.csdn.net/u013554213/article/details/79309235
https://www.jianshu.com/p/3c2053508342

猜你喜欢

转载自blog.csdn.net/amwha/article/details/86630494