Imx53 gpio操作

一、mx53/mx51 GPIO 操作原理

（一）GPIO 寄存器

每组 GPIO 有 8 个寄存器：

1. DR： Data Register

当 GDIR 设置为输出时, 写 DR 的内容用来驱动 GPIO 的 pins，读 DR 的内容则返

回存储在 DR 中的值

当 GDIR 设置为输入时，读 DR 返回给定 IO pin 的状态(PSR data)，而不是 DR

data

2. GDIR: Data Direction Register

控制 GPIO pins 的方向， 1 作为输出，0 作为输入，寄存器中的每一位标识一

个特定 pad 的方向. 仅当相应的 pins 被设置为 GPIO，GDIR 才起作用

3. PSR：Pad Sample Register

32-bit 的只读寄存器. 寄存器中的每一位都存储相应 pad 的值

4. ICR1， ICR2： 中断控制寄存器

两个 32-bit 寄存器， 寄存器中每两位控制一条中断线，ICR1 控制中断 0~15,

ICR2 控制中断 16～31

00 中断是 low-level 触发

01 中断是 high-level 触发

10 中断是 rise-edge 触发

11 中断时 fall-edge 触发

5. IMR： Interrupt Mask Register

32bit register. 每一位是相应中断线的屏蔽位， 0 中断被屏蔽，1 中断被使

能

6. ISR： Interrupt Status Register

32bit register, 每一位用于指定对应的中断线是否有中断发生，当一个中断

发生，这个寄存器中的相应位被设置

7. EDGE_SEL：Edge Select Register

32bit 寄存器，覆盖 ICR 寄存器的配置，选择 edge 作为中断触发的条件

（二）GPIO 模式选择 --- 即 IO 引脚复用设置

一个 IC 上的引脚，通过一个 n 选 1 的多路开关，把需要的外设连接到该引脚上.

具体的配置通过 IOMUXC_SW_MUX_CTL_PAD 寄存器来实现.

上图是 IOMUXC_SW_MUX_CTL_PAD_AUD3_UART3_RXD 的 IO 复用配置，主要分 2 部

分，SION 和 IO 复用配置

SION：是软件强行配置模式，配置了该模式后，该 IO 的具体电平不由所连接

模块的所决定，而是可以由软件写入决定，该功能估计是在调试硬件设备时使

用；

IO 复用：写入不同的配置，IO 引脚连接到对应的模块

还有一个配套的寄存器 IOMUXC_SW_PAD_CTL_PAD，主要用于配置管脚上拉、开

漏，驱动强度，信号的变化率等功能.

最后一个寄存器是 imx51/53 特有的 Daisy-chain 架构

Daisy chain 的模型如下图所示

如上图所示的所有管脚的模式中，都具有串口通讯的功能，那么 MCU 内部就需

要通过 daisy chain 寄存器来决定，使用哪个管脚传进来的信号作为串口通讯

的信号.

比如

AUD3_BB_RXD 管脚的 ALT1 模式具有 RXD_MUX 的功能

UART3_RXD 管脚的 ALT1 模式也具有 RXD_MUX 的功能

如果这两个管脚的工作模式都配置了 RXD_MUX 功能，那么 MCU 究竟要处理哪个

管脚的 RXD 信号呢，正是通过配置 MUX_SELECT_INPUT 来选择.

（三）GPIO 功能

从 pad 读取数据

1.  配置 IOMUX 选择 GPIO 模式

2.  配置 GPIO 的 GPDR 为输入

3.  从 PSR 寄存器读取数据(也可以从 DR 读取，此时会返回 PSR 内的值)

写数据到 pad

1. 配置 IOMUX 选择 GPIO 模式

2. 配置 GPIO 的 GPDR 为输出

3. 写值到 Data Register(DR)

中断控制

除了通用的 input/output 功能外， GPIO 内部的 edge-detect 逻辑反映一个被

配置为 input 的 pad 是否发生了状态转换

二、GPIO  操作方法

（一）配置 IOMUX 选择 GPIO 模式

在板级初始化文件中 mx51_babbage.c (arch\arm\mach-mx5)

在以下数组添加以下宏定义

static iomux_v3_cfg_t mx51babbage_pads[] = {

..........

MX51_PAD_EIM_A20__GPIO2_14,

..........

}

MX51_PAD_EIM_A20 为管脚的名字，GPIO2_14 为管脚的模式.

--该宏在文件 iomux-mx51.h (arch\arm\plat-mxc\include\mach)中有定义


 * 1) define MX51_PAD_EIM_A20__GPIO2_14

(_MX51_PAD_EIM_A20__GPIO2_14 |MUX_PAD_CTRL(MX51_GPIO_PAD_CTRL))


 * 1) define _MX51_PAD_EIM_A20__GPIO2_14

IOMUX_PAD(0x440, 0xac,1, 0x0000, 0, 0)


 * 1) define MX51_GPIO_PAD_CTRL (PAD_CTL_DSE_HIGH |PAD_CTL_PKE | \

PAD_CTL_SRE_FAST)

-iomux-v3.h (arch\arm\plat-mxc\include\mach)


 * 1) define MUX_PAD_CTRL(x) ((iomux_v3_cfg_t)(x) <<MUX_PAD_CTRL_SHIFT)


 * 1) define PAD_CTL_DSE_HIGH (2 <<1)


 * 1) define PAD_CTL_PKE (1 <<7)


 * 1) define PAD_CTL_SRE_FAST (1 <<0)


 * 1) define MUX_CTRL_OFS_SHIFT 0


 * 1) define MUX_MODE_SHIFT 36


 * 1) define MUX_PAD_CTRL_OFS_SHIFT 12


 * 1) define MUX_PAD_CTRL_SHIFT 41


 * 1) define MUX_SEL_INPUT_OFS_SHIFT 24


 * 1) define MUX_SEL_INPUT_SHIFT 58


 * 1) 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))

60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41

_sel_inpu

t

_pad_ctrl

40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21

_mux_mode _sel_input_ofs

20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 1 0

_pad_ctrl_ofs _mux_ctrl_ofs


 * 1) define _MX51_PAD_EIM_A20__GPIO2_14

IOMUX_PAD(0x440, 0xac,1, 0x0000, 0, 0)

60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41

0 0

40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21

1 0x0000

20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 1 0

0x440 0xac

该数组会在 mxc_iomux_v3_setup_multiple_pads 函数中初始化所有 GPIO.

static void __init mx51_babbage_io_init(void)

{

mxc_iomux_v3_setup_multiple_pads(mx51babbage_pads,

ARRAY_SIZE(mx51babbage_pads));

..........

｝

--iomux-v3.c (arch\arm\plat-mxc)

int mxc_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list, unsigned count)

{

iomux_v3_cfg_t *p =pad_list;

int i;

int ret;

for (i =0;i >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 (mux_ctrl_ofs)

__raw_writel(mux_mode, base + mux_ctrl_ofs);

if (sel_input_ofs)

__raw_writel(sel_input, base + sel_input_ofs);

if (!(pad_ctrl &NO_PAD_CTRL) && pad_ctrl_ofs)

__raw_writel(pad_ctrl, base + pad_ctrl_ofs);

return 0;

}

mx51_babbage_io_init 会在系统启动的时候，驱动加载之前被调用.

（二）

1.GPIO 资源索引号定义


 * 1) define BABBAGE_FEC_PHY_RESET (1*32 +14) /*GPIO_2_14 */

2.向系统查询管脚使用情况,"sdhc1-detect"只是个字符标记，填什么都可以

gpio_request(BABBAGE_FEC_PHY_RESET, "fec-phy-reset");

3.设置 GPIO 方向

gpio_direction_output(BABBAGE_FEC_PHY_RESET, 0);

4.设置 GPIO 值

gpio_set_value(BABBAGE_FEC_PHY_RESET, 1);//设为高

三、LINUX GPIO API 操作如何与实际寄存器关联

Gpiolib.c (drivers\gpio)

struct gpio_desc {

struct gpio_chip *chip;

unsigned long flags;

/*flag symbols are bit numbers */


 * 1) define FLAG_REQUESTED 0


 * 1) define FLAG_IS_OUT 1


 * 1) define FLAG_RESERVED 2


 * 1) define FLAG_EXPORT 3 /*protected by sysfs_lock */


 * 1) define FLAG_SYSFS 4 /*exported via /sys/class/gpio/control */


 * 1) define FLAG_TRIG_FALL 5 /*trigger on falling edge */


 * 1) define FLAG_TRIG_RISE 6 /*trigger on rising edge */


 * 1) define FLAG_ACTIVE_LOW 7 /*sysfs value has active low */


 * 1) define PDESC_ID_SHIFT 16 /*add new flags before this one */


 * 1) define GPIO_FLAGS_MASK ((1 <<PDESC_ID_SHIFT) - 1)


 * 1) define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) |BIT(FLAG_TRIG_RISE))


 * 1) ifdef CONFIG_DEBUG_FS

const char *label;


 * 1) endif

};

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

/*

struct gpio_desc gpio_desc[ARCH_NR_GPIOS]是一个全局的数组变量，这个数组描述的是

这个平台所有的 GPIO 的属性或者说是资源，每一个 struct gpio_desc 结构代表了一个

GPIO 资源.



intgpio_request(unsigned gpio, const char *label)

{

struct gpio_desc *desc;

struct gpio_chip *chip;

int status =-EINVAL;

unsigned long flags;

spin_lock_irqsave(&gpio_lock, flags);

/*判断参数的取值范围是否有效*/

if (!gpio_is_valid(gpio))

goto done;

/*

根据函数传参提供的索引号，在全局数组中找到对应的 GPIO 资源.

这里有一个问题：这个涵盖了整个平台 GPIO 资源的数组是什么时候初始化的呢？



desc = &gpio_desc[gpio];

if (chip ==NULL)

goto done;

/*

该函数用于增加模块使用计数；若返回为 0，表示调用失败，希望使用的模块没有被加载或

正在被卸载中



if (!try_module_get(chip->owner))

goto done;

/*这里是整个函数的重点之所在，如果&desc->flags 的第 FLAG_REQUESTED 位原值为 0,则把

该位置 1，同时把原值返回. 如果原值是 1，则直接返回 1，表示这个 GPIO 的资源已经有人

在使用了,那就 goto done 了*/

if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) ==0) {

desc_set_label(desc, label ?: "?");

status =0;

}else {

status =-EBUSY;

module_put(chip->owner);

goto done;

}

if (chip->request) {

/*chip->request may sleep */

spin_unlock_irqrestore(&gpio_lock, flags);

status =chip->request(chip, gpio - chip->base);

spin_lock_irqsave(&gpio_lock, flags);

if (status <0) {

desc_set_label(desc, NULL);

module_put(chip->owner);

clear_bit(FLAG_REQUESTED, &desc->flags);

}

}

done:

if (status)

pr_debug("gpio_request: gpio-%d (%s) status %d\n",

gpio, label ?: "?", status);

spin_unlock_irqrestore(&gpio_lock, flags);

return status;

}

EXPORT_SYMBOL_GPL(gpio_request);

下面我们追踪一下，struct gpio_desc gpio_desc[ARCH_NR_GPIOS]这个全局数组是什么时

候初始化的.

在系统启动的时候，板级初始化时会调用以下函数注册所有的 GPIO 资源

devices.c (arch\arm\mach-mx5)

int __init mxc_register_gpios(void)

{

if (cpu_is_mx51)

return mxc_gpio_init(mxc_gpio_ports, 4);

else if (cpu_is_mx50)

return mxc_gpio_init(mxc_gpio_ports, 6);

return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));

}

gpio.c (arch\arm\plat-mxc)

int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)

{

int i, j;

int ret =0;

/*save for local usage */

mxc_gpio_ports =port;

gpio_table_size =cnt;

printk(KERN_INFO "MXC GPIO hardware\n");

for (i =0;i <cnt;i++) {

/*disable the interrupt and clear the status */

__raw_writel(0, port[i].base +GPIO_IMR);

__raw_writel(~0, port[i].base +GPIO_ISR);

for (j =port[i].virtual_irq_start;

j <port[i].virtual_irq_start +32; j++) {

set_irq_chip(j, &gpio_irq_chip);

set_irq_handler(j, handle_level_irq);

set_irq_flags(j, IRQF_VALID);

}

/*register gpio chip */

port[i].chip.direction_input =mxc_gpio_direction_input;

port[i].chip.direction_output =mxc_gpio_direction_output;

port[i].chip.get =mxc_gpio_get;

port[i].chip.set =mxc_gpio_set;

port[i].chip.base =i *32;

port[i].chip.ngpio =32;

spin_lock_init(&port[i].lock);

// 在 gpio_desc[]中分配空间，并链接 chip 结构

BUG_ON( gpiochip_add(&port[i].chip) <0 );

if (!cpu_is_mx2 ||cpu_is_mx25) {

/*setup one handler for each entry */

set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);

set_irq_data(port[i].irq, &port[i]);

if (port[i].irq_high) {

set_irq_chained_handler(port[i].irq_high, mx3_gpio_irq_handler);

set_irq_data(port[i].irq_high, &port[i]);

}

}

}

if (cpu_is_mx2) {

/*setup one handler for all GPIO interrupts */

set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler);

set_irq_data(port[0].irq, port);

}

return ret;

}

gpiolib.c (drivers\gpio)

int gpiochip_add(struct gpio_chip *chip)

{

unsigned long flags;

int status =0;

unsigned id;

int base =chip->base;

if ((!gpio_is_valid(base) || !gpio_is_valid(base +chip->ngpio - 1))

&&base >=0) {

status =-EINVAL;

goto fail;

}

spin_lock_irqsave(&gpio_lock, flags);

if (base <0) {

/*

以下这个函数在 gpiolib.c 中，在 gpio_desc[]中分配 chip->ngpio 个空间(从最后往前分

配)，返回第一个 index



base = gpiochip_find_base(chip->ngpio);

if (base <0) {

status =base;

goto unlock;

}

chip->base =base;

}

for (id =base;id ngpio;id++) {

if (gpio_desc[id].chip =NULL) {

status =-EBUSY;

break;

}

}

if (status ==0) {

for (id =base;id ngpio;id++) {

gpio_desc[id].chip =chip;

gpio_desc[id].flags = !chip->direction_input

?(1 << FLAG_IS_OUT)


 * 0;

}

}

unlock:

spin_unlock_irqrestore(&gpio_lock, flags);

if (status ==0)

status =gpiochip_export(chip);

fail:

if (status)

pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",

chip->base, chip->base +chip->ngpio - 1,

chip->label ?: "generic");

return status;

}

以上函数执行完后，基本结果如下图：

也就是说比如我们要调用

gpio_direction_output(MX53_SMD_FEC_RST, 0);

其实最终会调用

mxc_gpio_direction_output(MX53_SMD_FEC_RST, 0);

static int mxc_gpio_direction_output(struct gpio_chip *chip,

unsigned offset, int value)

{

mxc_gpio_set(chip, offset, value);

_set_gpio_direction(chip, offset, 1);

return 0;

}

现在能看到最低层的寄存器操作了吧

static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value)

{

struct mxc_gpio_port *port =

container_of(chip, struct mxc_gpio_port, chip);

void __iomem *reg =port->base +GPIO_DR;

u32 l;

unsigned long flags;

spin_lock_irqsave(&port->lock, flags);

l =(__raw_readl(reg) &(~(1 <lock, flags);

}