Openwrt的不同版本中關於MT7628,MT7688的GPIO配置,存在或多或少的一些小問題
下面將以MT7628AN爲基礎,介紹其全部的GPIO功能複用配置,以及需要注意的地方
MT76x8一個就47個可作爲GPIO引腳,如下
關於其全部的功能複用表可以參考MTK官方的開發demo版Linkit
在這全部的引腳中,需要特別注意的是:
1. 網口模式,因爲MTK的設計,一共5個網口,要麼全部作爲普通網口使用,要麼只有一個網口(Port0),沒有中間選項;
因此,如果需要用到UART2,SDCARD等功能時,系統只剩一個P0網口可用,這也是MTK所宣傳的IOT模式
2. SPI接口,datasheet介紹可以作爲GPIO使用,但是因爲大多系統都接了SPI NOR Flash,因此其不能設置爲GPIO模式;而SPI_CS1另一個SPI片選腳,倒是不受影響,可作爲GPIO
3. 關於GPIO/GPO的描述,比如I2C的兩個腳是GPO,但經實際測試,描述爲GPO的其實可以作爲輸入使用
關於DTS配置,其主要參考arch/mips/ralink/mt7620.c文件
static struct rt2880_pmx_group mt7628an_pinmux_data[] = {
GRP_G("pwm1", pwm1_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_PWM1),
GRP_G("pwm0", pwm0_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_PWM0),
GRP_G("uart2", uart2_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_UART2),
GRP_G("uart1", uart1_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_UART1),
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),
GRP_G("i2s", i2s_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_I2S),
GRP_G("spi cs1", spi_cs1_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_CS1),
GRP_G("spis", spis_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_SPIS),
GRP_G("gpio", gpio_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_GPIO),
GRP_G("wled_an", wled_an_grp_mt7628, MT7628_GPIO_MODE_MASK,
1, MT7628_GPIO_MODE_WLED_AN)
}
在DTS文件中,填寫對應的name和func名字就可以,如:
pinctrl {
state_default: pinctrl0 {
gpio {
ralink,group = "gpio","wled_an", "i2s", "refclk", "perst", "wdt", "spi cs1";
ralink,function = "gpio";
};
}
常用接口及功能配置如下:
UART1開啓:
uart1@d00 {
status = "okay";
};
SPI_CS1 開啓,使用系統spi控制器,注意pinctrl-0 = <&spi_pins>, <&spi_cs1_pins>,配置CS1爲SPI_CS1功能,同時應該去掉pinctrl0中"spi cs1"的GPIO複用
spi@b00 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi_pins>, <&spi_cs1_pins>;
spidev@1 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "spidev";
reg = <1 0>;
spi-max-frequency = <40000000>;
};
}
GPIO模擬SPI配置,如:使用I2S的複用腳0-3,注意先配置I2S爲GPIO模式
spi-gpio {
compatible = "spi-gpio";
#address-cells = <0x1>;
cs-gpios = <&gpio0 2 0>;
gpio-sck = <&gpio0 0 0>;
gpio-mosi = <&gpio0 3 0>;
gpio-miso = <&gpio0 1 0>;
num-chipselects = <1>;
spidev@0 {
#address-cells = <0x1>;
compatible = "spidev";
spi-max-frequency = <10000000>;
reg = <0>;
};
};
I2C的RTC時鐘配置,以PCF8563爲例,注意reg地址的問題,pcf8563地址是0x51,常用的ds1307是0x68
i2c@900 {
status = "okay";
rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
LED配置參考,注意MT76x8有兩組GPIO,GPIO0對應0-31,GPIO1對應32-46
gpio-leds {
compatible = "gpio-leds";
modpwr {
label = "modpwr";
gpios = <&gpio1 8 1>;
};
modrst {
label = "modrst";
gpios = <&gpio1 4 0>;
};
heart {
label = "heart";
gpios = <&gpio1 6 1>;
};
}
lable對應運行系統中/sys/class/leds/lable/brightness 的lable名字
gpios = <&gpio1 8 1>; 表示GPIO1組,8 表示 32+8 即GPIO40,1表示低電平有效,即默認輸出高電平
按鍵配置參考,
gpio-keys-polled {
compatible = "gpio-keys-polled";
#address-cells = <1>;
#size-cells = <0>;
poll-interval = <20>;
vccin {
label = "BTN_0";
gpios = <&gpio0 6 0>;
linux,code = <0x100>;
};
reset {
label = "reset";
gpios = <&gpio1 7 1>;
linux,code = <0x198>;
};
};
poll-interval 表示輸入檢測消抖時間,label對應於運行系統中/etc/rc.button/lable,即按鍵後對應的執行腳本文件,如上述配置,系統中應該存在reset和BTN_0兩個腳本, gpios同LED說明
linux,code = <0x198>; 對應input.h的宏定義值,配合驅動文件gpio-button-hotplug.c中的相關配置,如:
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_POWER, "power"),
BH_MAP(KEY_RESTART, "reset"),
BH_MAP(KEY_RFKILL, "rfkill"),
BH_MAP(KEY_WPS_BUTTON, "wps"),
BH_MAP(KEY_WIMAX, "wwan"),
};
其原理是gpio-button-hotplug驅動會檢測按鍵事件,然後通過netlionk方式廣播出來,proc會一直監測事件,觸發hotplug,進而執行到相關reset等腳本,熱拔插事件也是此類方式,具體不再詳細介紹,本文主要介紹引腳複用配置
另,還需注意的是,配置了相關引腳功能後,還需在kernel中開啓相關驅動模塊以及選擇相關的應用才能正常使用
到此,常用接口基本介紹完畢
下面大概提一下不同openwrt版本中的代碼錯誤,以下只貼出正確的code,各位看客有功能錯誤的各自根據自己的現有版本對比修正即可
1. 看門狗dts 地址錯誤,位置:target/linux/ramips/dts/mt7628an.dtsi
watchdog: watchdog@100 {
compatible = "ralink,mt7628an-wdt", "mediatek,mt7621-wdt";
reg = <0x100 0x30>;
resets = <&rstctrl 8>;
reset-names = "wdt";
interrupt-parent = <&intc>;
interrupts = <24>;
};
根據DataSheet,watchdog的偏移地址應該是100
同時,如果需要自行喂狗的話,注意先關閉proc進程中自動喂狗功能
2. REFCLK,PERST的配置錯誤,位置arch/mips/ralink/mt7620.c,原來的reclk和perst引腳配置反了
static struct rt2880_pmx_func refclk_grp_mt7628[] = { FUNC("reclk", 0, 37, 1) };
static struct rt2880_pmx_func perst_grp_mt7628[] = { FUNC("perst", 0, 36, 1) };
3. PHY 0-4的LED不能作爲GPIO的問題,參考補丁文件:
--- a/arch/mips/ralink/mt7620.c 2018-09-04 17:14:33.000000000 +0800
+++ b/arch/mips/ralink/mt7620.c 2018-04-09 08:28:51.000000000 +0800
@@ -138,8 +138,8 @@
FUNC("i2c", 0, 4, 2),
};
-static struct rt2880_pmx_func refclk_grp_mt7628[] = { FUNC("reclk", 0, 36, 1) };
-static struct rt2880_pmx_func perst_grp_mt7628[] = { FUNC("perst", 0, 37, 1) };
+static struct rt2880_pmx_func refclk_grp_mt7628[] = { FUNC("reclk", 0, 37, 1) };
+static struct rt2880_pmx_func perst_grp_mt7628[] = { FUNC("perst", 0, 36, 1) };
static struct rt2880_pmx_func wdt_grp_mt7628[] = { FUNC("wdt", 0, 38, 1) };
static struct rt2880_pmx_func spi_grp_mt7628[] = { FUNC("spi", 0, 7, 4) };
@@ -185,23 +185,50 @@
FUNC("gpio", 0, 11, 1),
};
-static struct rt2880_pmx_func wled_kn_grp_mt7628[] = {
- FUNC("rsvd", 3, 35, 1),
- FUNC("rsvd", 2, 35, 1),
- FUNC("gpio", 1, 35, 1),
- FUNC("wled_kn", 0, 35, 1),
+struct rt2880_pmx_func wled_an_grp_mt7628[] = {
+ FUNC("rsvd", 3, 44, 1),
+ FUNC("rsvd", 2, 44, 1),
+ FUNC("gpio", 1, 44, 1),
+ FUNC("wled_an", 0,44, 1),
+};
+
+static struct rt2880_pmx_func ephy_p0_grp_mt7628[] = {
+ FUNC("rsvd", 3, 43, 1),
+ FUNC("rsvd", 2, 43, 1),
+ FUNC("gpio", 1, 43, 1),
+ FUNC("ephy", 0, 43, 1),
+};
+
+static struct rt2880_pmx_func ephy_p1_grp_mt7628[] = {
+ FUNC("rsvd", 3, 42, 1),
+ FUNC("rsvd", 2, 42, 1),
+ FUNC("gpio", 1, 42, 1),
+ FUNC("ephy", 0, 42, 1),
+};
+static struct rt2880_pmx_func ephy_p2_grp_mt7628[] = {
+ FUNC("rsvd", 3, 41, 1),
+ FUNC("rsvd", 2, 41, 1),
+ FUNC("gpio", 1, 41, 1),
+ FUNC("ephy", 0, 41, 1),
+};
+static struct rt2880_pmx_func ephy_p3_grp_mt7628[] = {
+ FUNC("rsvd", 3, 40, 1),
+ FUNC("rsvd", 2, 40, 1),
+ FUNC("gpio", 1, 40, 1),
+ FUNC("ephy", 0, 40, 1),
+};
+static struct rt2880_pmx_func ephy_p4_grp_mt7628[] = {
+ FUNC("rsvd", 3, 39, 1),
+ FUNC("rsvd", 2, 39, 1),
+ FUNC("gpio", 1, 39, 1),
+ FUNC("ephy", 0, 39, 1),
};
-
-static struct rt2880_pmx_func wled_an_grp_mt7628[] = {
- FUNC("rsvd", 3, 44, 1),
- FUNC("rsvd", 2, 44, 1),
- FUNC("gpio", 1, 44, 1),
- FUNC("wled_an", 0, 44, 1),
-};
-
#define MT7628_GPIO_MODE_MASK 0x3
-
-#define MT7628_GPIO_MODE_WLED_KN 48
+#define MT7628_GPIO_MODE_EPHY_P4 42
+#define MT7628_GPIO_MODE_EPHY_P3 40
+#define MT7628_GPIO_MODE_EPHY_P2 38
+#define MT7628_GPIO_MODE_EPHY_P1 36
+#define MT7628_GPIO_MODE_EPHY_P0 34
#define MT7628_GPIO_MODE_WLED_AN 32
#define MT7628_GPIO_MODE_PWM1 30
#define MT7628_GPIO_MODE_PWM0 28
@@ -236,7 +263,12 @@
GRP_G("spis", spis_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_SPIS),
GRP_G("gpio", gpio_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_GPIO),
GRP_G("wled_an", wled_an_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_WLED_AN),
- GRP_G("wled_kn", wled_kn_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_WLED_KN),
+ GRP_G("ephy_p0", ephy_p0_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_EPHY_P0),
+ GRP_G("ephy_p1", ephy_p1_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_EPHY_P1),
+ GRP_G("ephy_p2", ephy_p2_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_EPHY_P2),
+ GRP_G("ephy_p3", ephy_p3_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_EPHY_P3),
+ GRP_G("ephy_p4", ephy_p4_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_EPHY_P4),
+
{ 0 }
};