IMX6 有很多系列版本,芯片的寄存器有一些是不一样的,我使用的CPU具体的partnumber 为 FREESCALE_MCIMX6U5EVM10AC
需求:配置 引脚 GPIO6_IO04 为输出高电平。
IMX6 GPIO 介绍,根据 TRM Chapter 2 Memory Maps 可知。IMX6 有7组GPIO,每组32个引脚。Base Address如下:
GPIO7_BASE 0x020B4000
GPIO6_BASE 0x020B0000
GPIO5_BASE 0x020AC000
GPIO4_BASE 0x020A8000
GPIO3_BASE 0x020A4000
GPIO2_BASE 0x020A0000
GPIO1_BASE 0x0209C000
1 GPIO 操作(memmory)
(1) 使能 gpio clocks
根据TRM,这款imx6 和imx6ull 不同,imx6ull 需要通过寄存器来配置,时能gpio的clocks ,如下
不过 IMX6U5 这款,默认都是使能的,我在TRM里并没有找到直接配置的寄存器。应该不用配置。
(2) 设置引脚复用
比如 GPIO6_IO04 这个引脚,在TRM里搜索 GPIO6_IO04 , 我们需要将此引脚配置成 ALT5 也就是GPIO的模式。
那就需要 配置 IOMUXC_SW_MUX_CTL_PAD_CSI0_DATA18 这个寄存器
注意,一定要将 SION这个bit 设置为1 ,否则 从寄存器读出来的值 将一直为0
(3) 设置上下拉参数
TRM 搜索 CSI0_DAT18 , 我们需要配置 IOMUXC_SW_PAD_CTL_PAD_CSI0_DATA18
正常GPIO, 可以设置为 0x1b0b0
(4)设置GPIO的方向 GPIOx_GDIR
设置引脚方向,每位对应一个引脚, 1-output, 0-input
(5)设置GPIO的电平值 GPIOx_GDIR
设置输出引脚的电平,每位对应一个引脚, 1-高电平, 0-低电平
2 设备树对GPIO的配置
基于 linux-imx_4.14.98_2.0.0_ga 。
关于 IMX6 DTS的解析可参考 drivers/pinctrl/freescale/pinctrl-imx.c
以配置引脚 GPIO6_IO04 为例。在 arch/arm/boot/dts/imx6dl-pinfunc.h 中搜索 GPIO6_IO04 ,可以看到这个引脚的6中复用功能,我们记住 其中GPIO的这一项,其他复用类似。
在设备树中添加,MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 其实已经代表了 复用关系, 0x1b0b0 是配置的上下拉参数。
如果要操作GPIO,可以使用GPIO子系统
在linux在驱动中使用gpio_request时,io 端口号为 IMX_GPIO_NR(6,4)=32×(6-1)+4=164;
在arch/arm/mach-imx/hardware.h,IMX_GPIO_NR定义为
#define IMX_GPIO_NR(bank, nr) (((bank) - 1) * 32 + (nr))
提供一个example
#include <linux/gpio.h>
#define IMX_GPIO_NR(bank, nr) (((bank) - 1) * 32 + (nr))
#define MINI_PCIE_POWER_RESET_DELAY 5000 // 5000 ms
static int gpio_set_val( int base, int num, int value)
{
int gpio = 32*base + num;
if (gpio_request_one( gpio , GPIOF_OUT_INIT_LOW, "gpio_out") < 0) {
pr_err("Failed to request GPIO%d for GPIO%d_%d\n", gpio, base,num);
return -1;
}
gpio_set_value( gpio, value);
//gpio_free( gpio );
return 0;
}
static int gpio_reset_one( int base, int num, int time)
{
int gpio = 32*base + num;
if (gpio_request_one( gpio, GPIOF_OUT_INIT_HIGH, "gpio_out") < 0) {
pr_err("Failed to request GPIO%d for GPIO%d_%d\n", gpio, base,num);
return -1;
}
gpio_set_value( gpio, 1);
mdelay( 100 );
gpio_set_value( gpio, 0);
mdelay( time );
gpio_set_value( gpio, 1);
mdelay( 100 );
//gpio_free( gpio );
return 0;
}
gpio_reset_one(6, 4, MINI_PCIE_POWER_RESET_DELAY);
注意,如果free掉gpio 之后,gpio的电平值将会发生变化,不受控制。
再分享一个通过shell 在应用层来读取或者配置GPIO的方法,前提是在dts里面已经配置好复用模式。
#!/bin/sh
if [ "$#" != "2" ] && [ "$#" != "3" ];then
echo "usage of read :$0 2 25"
echo "usage of write :$0 2 25 0"
echo "usage of reset :$0 2 25 reset"
exit -1
fi
base=$1
num=$2
case $base in
1)
PHY_ADDRESS=0x0209C000
;;
2)
PHY_ADDRESS=0x020A0000
;;
3)
PHY_ADDRESS=0x020A4000
;;
4)
PHY_ADDRESS=0x020A8000
;;
5)
PHY_ADDRESS=0x020AC000
;;
6)
PHY_ADDRESS=0x020B0000
;;
7)
PHY_ADDRESS=0x020B4000
;;
*)
echo "base error"
exit -1
esac
DIR_REG=$[$PHY_ADDRESS+0x4]
DATA_REG=$[$PHY_ADDRESS+0x0]
DIR_VAL=`devmem $DIR_REG`
DATA_VAL=`devmem $DATA_REG`
bit_set=$((1<<num))
bit_clear=$((~(1<<num) & 0xFFFFFFFF))
#printf "bit_set = 0x%x\n" $bit_set
#printf "bit_clear = 0x%x\n" $bit_clear
GPIO_READ()
{
if [ $(( DIR_VAL&(1<<num))) -eq $((1<<num)) ];then
DIR="out"
else
DIR="in"
fi
if [ $(( DATA_VAL&(1<<num))) -eq $((1<<num)) ];then
VAL=1
else
VAL=0
fi
printf "Get GPIO%d DIR_REG :0x%x[ 0x%x ] GPIO%d_%d = %s\n" $base $DIR_REG $DIR_VAL $base $num $DIR
printf "Get GPIO%d DATA_REG :0x%x[ 0x%x ] GPIO%d_%d = %d\n" $base $DATA_REG $DATA_VAL $base $num $VAL
}
GPIO_Set_High()
{
# set to out_put
DIR_VAL=$(( $DIR_VAL | $bit_set ))
devmem $DIR_REG 32 $DIR_VAL
DATA_VAL=$(( $DATA_VAL | $bit_set ))
devmem $DATA_REG 32 $DATA_VAL
}
GPIO_Set_Low()
{
# set to out_put
DIR_VAL=$(( $DIR_VAL | $bit_set ))
devmem $DIR_REG 32 $DIR_VAL
DATA_VAL=$(( $DATA_VAL & $bit_clear ))
devmem $DATA_REG 32 $DATA_VAL
}
#printf "=========Before===========\n"
#GPIO_READ
if [ "$#" == "2" ];then
GPIO_READ
exit -1
fi
if [ "$3" == "1" ];then
GPIO_Set_High
elif [ "$3" == "0" ];then
GPIO_Set_Low
elif [ "$3" == "reset" ];then
GPIO_Set_High
sleep 0.5
GPIO_Set_Low
sleep 0.5
GPIO_Set_High
sleep 0.5
fi
使用方法如下: