Openwrt MT7628/MT7620/MT7621——GPIO及LED/KEY的功能实现

GPIO及LED/KEY的功能实现

流程介绍

GPIO功能注册和控制

MT7628支持引脚功能的复用,通过配置GPIO1_MODE和GPIO2_MODE寄存器可指定引脚的功能,通常在dts文件中对引脚功能进行注册。

MT7628引脚的GPIO注册位于/target/linux/ramips/dts/mt7628an.dtsi文件中,GPIO总共被分为3组,gpio0(GPIO#0——GPIO#31)、gpio1(GPIO#32——GPIO#63)和gpio2(GPIO#64——GPIO#95), @指明了gpio寄存器的起始地址为0x600,节点中compatible属性值指定了设备使用驱动为 mtk,mt7621-gpio。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    gpio@600 {
        #address-cells = <1>;
        #size-cells = <0>;

        compatible = "mtk,mt7628-gpio", "mtk,mt7621-gpio";
        reg = <0x600 0x100>;

        interrupt-parent = <&intc>;
        interrupts = <6>;

        gpio0: bank@0 {
            reg = <0>;
            compatible = "mtk,mt7621-gpio-bank";
            gpio-controller;
            #gpio-cells = <2>;
        };

        gpio1: bank@1 {
            reg = <1>;
            compatible = "mtk,mt7621-gpio-bank";
            gpio-controller;
            #gpio-cells = <2>;
        };

        gpio2: bank@2 {
            reg = <2>;
            compatible = "mtk,mt7621-gpio-bank";
            gpio-controller;
            #gpio-cells = <2>;
        };
    };

使用GPIO引脚除了注册GPIO外,还需要释放GPIO引脚复用功能,将功能名称添加至 ralink group 属性中:

1
2
3
4
5
6
7
&pinctrl {
    state_default: pinctrl0 {
        gpio {
            ralink,group = "i2c", "gpio", "jtag", "i2s", "spi cs1", "uart2", "pwm0", "pwm1", "sdmode", "spis";
            ralink,function = "gpio";
        };
    };

与dts文件对应的引脚复用定义在source-17.01.4/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7628/linux-4.4.92/arch/mips/ralink/mt7620.c中。

如以下代码中i2c复用的gpio引脚定义为gpio#1和gpio#2(从gpio#1开始占用2个gpio), spi refclk复用的gpio引脚定义为gpio#37、gpio#38、gpio#39。同理,其他复用引脚的定义一样。

1
2
3
4
5
static struct rt2880_pmx_func i2c_grp[] =  { FUNC("i2c", 0, 1, 2) };
static struct rt2880_pmx_func spi_grp[] = { FUNC("spi", 0, 3, 4) };
static struct rt2880_pmx_func uartlite_grp[] = { FUNC("uartlite", 0, 15, 2) };
static struct rt2880_pmx_func rgmii1_grp[] = { FUNC("rgmii1", 0, 24, 12) };
static struct rt2880_pmx_func refclk_grp[] = { FUNC("spi refclk", 0, 37, 3) };

如以下代码中与WRTNODE2P.dts中ralink group属性的GPIO复用功能相对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static struct rt2880_pmx_group mt7628an_pinmux_data[] = {
……
    GRP_G("i2c", i2c_grp_mt7628, MT7628_GPIO_MODE_MASK,
                1, MT7628_GPIO_MODE_I2C),
    GRP("refclk", refclk_grp_mt7628, 1, MT7628_GPIO_MODE_REFCLK),
    GRP("perst", perst_grp_mt7628, 1, MT7628_GPIO_MODE_PERST),
    GRP("wdt", wdt_grp_mt7628, 1, MT7628_GPIO_MODE_WDT),
    GRP("spi", spi_grp_mt7628, 1, MT7628_GPIO_MODE_SPI),
    GRP_G("sdmode", sd_mode_grp_mt7628, MT7628_GPIO_MODE_MASK,
                1, MT7628_GPIO_MODE_SDMODE),
    GRP_G("uart0", uart0_grp_mt7628, MT7628_GPIO_MODE_MASK,
                1, MT7628_GPIO_MODE_UART0),
……
    { 0 }
};

用户可以通过sysfs接口方式访问GPIO(目录位于/sys/class/gpio),gpio目录中包括 export文件、 unexport文件、gpiochipx目录、gpiox目录。

/sys/class/export文件用于通过GPIO引脚编号导出控制的gpio文件,/sys/class/unexport文件用于通过GPIO引脚编号去除控制的gpio文件。export导出成功后会在目录中生成/sys/class/gpio/gpiox目录,如果未出现相应的目录,说明该引脚不可导出。

/sys/class/gpiochipx/目录用于管理和控制一组GPIO的控制器, 目录保存该组GPIO寄存器信息,如寄存器控制引脚的起始编号base、寄存器名称label、引脚总数ngpio。

/sys/class/gpiox/目录用于具体的GPIO引脚控制,目录下有 direction、 value、edge等属性文件。其中direction文件定义为GPIO引脚方向(out为输出、in为输入)。value文件定义为GPIO引脚的电平(0为低电平,1为高电平), 当GPIO引脚方向配置为输出时,value文件值可写(任何非零的值都将输出高电平),当GPIO引脚方向配置为输入时,value文件值可读(cat value)。edge定义为中断的触发方式。

GPIO-LED功能注册和控制

在将GPIO注册为LED设备之前需要在配置界面(make menuconfig命令进入配置界面)选择添加Led驱动(默认编译时已经选上)
Led驱动: > Kernel modules > LED modules > <*> kmod-leds-gpio

WRTNODE2P.dts文件中将GPIO注册为LED设备,compatible属性值与LED驱动文件leds-gpio.c中的compatible属性值相对应。label值最终对应为文件系统/sys/class/leds/目录下LED设备的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    gpio-leds {
        compatible = "gpio-leds";

        indicator {
            label = "wrtnode:blue:indicator";
            gpios = <&gpio1 9 1>;
        };
        gpio35 {
            label = "wrtnode:red:sys_status";
            gpios = <&gpio1 12 1>;
        };
        gpio3 {
            label = "wrtnode:yellow:3g_wwan1";
            gpios = <&gpio0 3 1>;
        };
    };

烧录固件后进入 /sys/class/leds 可查看注册成功的led设备,可通过对brightness文件写入1和0的方式控制LED亮灭。

GPIO-KEY功能注册和控制

将GPIO注册为KEY设备与LED的方法类似,需要在配置界面(make menuconfig命令进入配置界面)选择添加Button的驱动(默认编译时已经选上)
Button 驱动: > Kernel modules > Other modules > kmod-gpio-button-hotplug

同理WRTNODE2P.dts文件中将GPIO#5注册为名为"reset"的KEY,通过调试状态查看KEY值,hi 表示高电平,lo 表示低电平。

1
2
3
4
5
6
7
8
9
10
11
12
    gpio-keys-polled {
        compatible = "gpio-keys-polled";
        #address-cells = <1>;
        #size-cells = <0>;
        poll-interval = <20>;

        reset {
            label = "reset";
            gpios = <&gpio0 5 1>;
            linux,code = <KEY_RESTART>;
        };
    };

按键驱动文件 package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c中对按键的键值和名称进行了映射,比如"reset"按键,键值为 KEY_RESTART,当按下或释放该按键时驱动会向用户层发送事件执行/etc/rc.button/目录下所对应的脚本 reset.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static struct bh_map button_map[] = {
    BH_MAP(BTN_0,           "BTN_0"),
    BH_MAP(BTN_1,           "BTN_1"),
    BH_MAP(BTN_2,           "BTN_2"),
    BH_MAP(BTN_3,           "BTN_3"),
    BH_MAP(BTN_4,           "BTN_4"),
    BH_MAP(BTN_5,           "BTN_5"),
    BH_MAP(BTN_6,           "BTN_6"),
    BH_MAP(BTN_7,           "BTN_7"),
    BH_MAP(BTN_8,           "BTN_8"),
    BH_MAP(BTN_9,           "BTN_9"),
    BH_MAP(KEY_BRIGHTNESS_ZERO, "brightness_zero"),
    BH_MAP(KEY_CONFIG,      "config"),
    BH_MAP(KEY_COPY,        "copy"),
    BH_MAP(KEY_EJECTCD,     "eject"),
    BH_MAP(KEY_HELP,        "help"),
    BH_MAP(KEY_LIGHTS_TOGGLE,   "lights_toggle"),
    BH_MAP(KEY_PHONE,       "phone"),
    BH_MAP(KEY_POWER,       "power"),
    BH_MAP(KEY_RESTART,     "reset"),
    BH_MAP(KEY_RFKILL,      "rfkill"),
    BH_MAP(KEY_VIDEO,       "video"),
    BH_MAP(KEY_WIMAX,       "wwan"),
    BH_MAP(KEY_WLAN,        "wlan"),
    BH_MAP(KEY_WPS_BUTTON,      "wps"),
};

例子代码

1.dts文件需要引入头文件

#include #include 2.dts文件配置reset功能 32+6=38 对应GPIO38

1
2
3
4
5
6
7
8
9
10
 gpio-keys {
         compatible = "gpio-keys-polled";
         poll-interval = <20>;
         reset {
              label = "reset";
              gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_RESTART>;
        };

};

3. 修改/etc/rc.button/reset脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
. /lib/functions.sh

OVERLAY="$( grep ' /overlay ' /proc/mounts )"
case "$ACTION" in
pressed)
        [ -z "$OVERLAY" ] && return 0

        return 5
;;
timeout)
        . /etc/diag.sh
        set_state failsafe
;;
released)
        if [ "$SEEN" -ge 3 -a "$SEEN" -lt 8 ]
        then
                echo "REBOOT" > /dev/console
                sync
                reboot
        elif [ "$SEEN" -ge 10 -a -n "$OVERLAY" ]
        then
                echo "FACTORY RESET" > /dev/console
                jffs2reset -y && reboot &
        fi
;;
esac

参考链接

https://www.imooc.com/article/43412

https://blog.csdn.net/zhjmyx/article/details/103593528