pinctrl與gpio子系統(tǒng)下的字符設(shè)備驅(qū)動(dòng)框架
點(diǎn)亮Linux驅(qū)動(dòng)開(kāi)發(fā)路上的第一個(gè)燈一文中將與外設(shè)有關(guān)的寄存器信息,定義到驅(qū)動(dòng)代碼中,直接操作寄存器來(lái)控制外設(shè)。缺點(diǎn)是當(dāng)芯片的寄存器發(fā)了變動(dòng),就要對(duì)底層的驅(qū)動(dòng)進(jìn)行重寫(xiě)。
設(shè)備樹(shù)下的字符設(shè)備驅(qū)動(dòng)框架一文中將與外設(shè)有關(guān)的寄存器信息,寫(xiě)到了設(shè)備樹(shù)文件中,通過(guò)設(shè)備樹(shù)API函數(shù)獲取外設(shè)信息。當(dāng)外設(shè)的信息有變化時(shí),只需要修改設(shè)備樹(shù)文件即可,無(wú)需修改底層驅(qū)動(dòng),提高了驅(qū)動(dòng)代碼的復(fù)用能力,但仍需要直接操作寄存器來(lái)控制外設(shè)。
本文介紹的pinctrl和gpio子系統(tǒng)實(shí)現(xiàn)了對(duì)寄存器的操作,我們只需要使用子系統(tǒng)提供的API函數(shù)即可,而無(wú)需再直接操作寄存器了。
1. pinctrl與gpio子系統(tǒng)介紹
1.1 pinctrl子系統(tǒng)
pinctrl子系統(tǒng)就是管理PIN引腳的一個(gè)系統(tǒng),在設(shè)備樹(shù)里設(shè)置PIN的配置信息,pinctrl會(huì)根據(jù)提供的信息來(lái)配置PIN功能。其主要工作內(nèi)容如下:
- 獲取設(shè)備樹(shù)中的PIN信息
- 設(shè)置獲取到的PIN的復(fù)用功能
- 設(shè)置獲取到的PIN的電氣特性
打開(kāi)設(shè)備樹(shù)文件imx6ull-andyxi-emmc.dts,其中iomuxc節(jié)點(diǎn)就是I.MX6ULL的外設(shè)節(jié)點(diǎn)。代碼中的pinctrl_hog_1子節(jié)點(diǎn)就是和熱插拔有關(guān)的PIN集合
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
};
......
};
};
下面以UART1_RTS_B這個(gè)PIN為例,介紹如何添加PIN的配置信息:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
前半部分:設(shè)置UART1_RTS_B引腳的復(fù)用功能
//MX6UL_PAD_UART1_RTS_B__GPIO1_IO19宏定義在文件arch/arm/boot/dts/imx6ul-pinfunc.h中
#define MX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS 0x0090 0x031C 0x0620 0x0 0x3
#define MX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS 0x0090 0x031C 0x0000 0x0 0x0
#define MX6UL_PAD_UART1_RTS_B__ENET1_TX_ER 0x0090 0x031C 0x0000 0x1 0x0
#define MX6UL_PAD_UART1_RTS_B__USDHC1_CD_B 0x0090 0x031C 0x0668 0x2 0x1
#define MX6UL_PAD_UART1_RTS_B__CSI_DATA05 0x0090 0x031C 0x04CC 0x3 0x1
#define MX6UL_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT 0x0090 0x031C 0x0000 0x4 0x0
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
#define MX6UL_PAD_UART1_RTS_B__USDHC2_CD_B 0x0090 0x031C 0x0674 0x8 0x2
上面代碼中共有8個(gè)UART1_RTS_B引腳的宏定義,分別對(duì)應(yīng)該引腳的8個(gè)復(fù)用IO。宏定義的值,被分為了5段,每段的值都有具體的含義:
- mux_reg:mux_reg寄存器偏移地址 (見(jiàn)下圖1)
- conf_reg:conf_reg寄存器偏移地址 (見(jiàn)下圖2)
- input_reg:input_reg寄存器偏移地址 (此處無(wú)效)
- mux_mode:mux_reg寄存器的值,用于設(shè)置復(fù)用功能 (見(jiàn)下圖3)
- input_val:input_reg寄存器值 (此處無(wú)效)
后半部分:此值就是conf_reg寄存器的值,用于設(shè)置UART1_RTS_B引腳的電氣特性
1.2 gpio子系統(tǒng)
gpio子系統(tǒng)就是管理gpio功能的一個(gè)系統(tǒng),其作用是初始化gpio,并提供對(duì)外的API接口。使用gpio子系統(tǒng)后,就無(wú)需自己操作寄存器,通過(guò)調(diào)用相關(guān)API函數(shù)即可實(shí)現(xiàn)對(duì)gpio的控制。
仍以熱插拔節(jié)點(diǎn)為例,pinctrl子系統(tǒng)已經(jīng)將UART1_RTS_B復(fù)用為GPIO1_IO19,并設(shè)置好了電氣屬性。驅(qū)動(dòng)程序通過(guò)讀取其高低電平來(lái)判斷SD卡有沒(méi)有插入。
那驅(qū)動(dòng)程序怎么知道CD引腳連接的是GPIO1_IO19呢 ?這就需要設(shè)備樹(shù)來(lái)告訴驅(qū)動(dòng)了,在設(shè)備樹(shù)的SD卡節(jié)點(diǎn)下添加一個(gè)描述CD引腳的屬性:
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
pinctrl-3 = <&pinctrl_hog_1>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};
上面代碼中pinctrl-3指定了CD引腳的pinctrl信息,cd-gpios屬性描述了CD引腳使用哪個(gè)IO,屬性值共有三個(gè),具體含義如下:
- &gpio1 表示CD引腳所使用的IO屬于GPIO1組
- 19 表示GPIO1組的第19號(hào)IO
- GPIO_ACTIVE_LOW 表示低電平有效
設(shè)置好設(shè)備樹(shù)后就可使用gpio子系統(tǒng)提供的API函數(shù)來(lái)操作指定的GPIO,gpio子系統(tǒng)向開(kāi)發(fā)人員屏蔽了具體的讀寫(xiě)寄存器過(guò)程。下圖中有常用的API函數(shù)介,此外還有pinctrl和gpio子系統(tǒng)的使用模板:
**2. **pinctrl與gpio子系統(tǒng)字符設(shè)備驅(qū)動(dòng)框架
下圖為pinctrl與gpio子系統(tǒng)下的字符設(shè)備驅(qū)動(dòng)框架:
接下來(lái)根據(jù)上面的框架圖,以驅(qū)動(dòng)LED (GPIO1_IO03) 為例,分步介紹具體的代碼編寫(xiě)流程
2.1 修改設(shè)備樹(shù)文件
添加pinctrl節(jié)點(diǎn):在iomuxc節(jié)點(diǎn)的imx6ul-evk子節(jié)點(diǎn)下創(chuàng)建pinctrl_led節(jié)點(diǎn),復(fù)用GPIO1_IO03
pinctrl_led: ledgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
>;
};
//MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 復(fù)用GPIO1_IO03
//0x10B0 設(shè)置pin的電氣特性
添加LED設(shè)備節(jié)點(diǎn):在根節(jié)點(diǎn)下創(chuàng)建LED設(shè)備節(jié)點(diǎn),指定對(duì)應(yīng)的pinctrl節(jié)點(diǎn),指定所使用的GPIO
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "alpha-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
檢查PIN是否沖突:檢查pinctrl和設(shè)備節(jié)點(diǎn)中指定的引腳有沒(méi)有被別的外設(shè)占用
//檢查GPIO_IO03這個(gè)PIN有沒(méi)有被其他的pinctrl節(jié)點(diǎn)使用
pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0
//GPIO_IO03被pinctrl_tsc節(jié)點(diǎn)占用,因此需要屏蔽掉
/* MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 */
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0
>;
};
//檢查"gpio1 3"有沒(méi)有被其他設(shè)備節(jié)點(diǎn)占用
&tsc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc>;
//"gpio1 3"被tsc設(shè)備節(jié)點(diǎn)占用,因此需要屏蔽掉
/* xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; */
measure-delay-time = <0xffff>;
pre-charge-time = <0xfff>;
status = "okay";
};
編譯設(shè)備樹(shù):使用make dtbs命令編譯設(shè)備樹(shù),并使用該設(shè)備樹(shù)啟動(dòng)Linux系統(tǒng)
#在內(nèi)核根目錄下
make dtbs #編譯設(shè)備樹(shù)
#啟動(dòng)Linux系統(tǒng)后
cd /proc/device-tree #查看"gpioled"節(jié)點(diǎn)是否存在
2.2 編寫(xiě)驅(qū)動(dòng)程序
創(chuàng)建驅(qū)動(dòng)程序文件gpioled.c,添加如下代碼
宏定義及設(shè)備結(jié)構(gòu)體定義
struct gpioled_dev{
dev_t devid; //設(shè)備號(hào)
struct cdev cdev; //cdev字符設(shè)備
struct class *class; //類(lèi)
struct device *device; //設(shè)備
int major; //主設(shè)備號(hào)
int minor; //次設(shè)備號(hào)
struct device_node *nd; //設(shè)備節(jié)點(diǎn)
int led_gpio; //所使用的gpio編號(hào)
};
struct gpioled_dev gpioled; //定義led設(shè)備
編寫(xiě)設(shè)備操作函數(shù):write函數(shù)中,直接使用gpio子系統(tǒng)提供的API函數(shù)操作GPIO
static ssize_t led_write(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0){
printk("kernel write failed!\\r\\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON){
gpio_set_value(dev->led_gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
驅(qū)動(dòng)入口函數(shù)中:獲取GPIO編號(hào)后初始化
/* 設(shè)置 LED 所使用的 GPIO */
/* 1、獲取設(shè)備節(jié)點(diǎn):gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
printk("gpioled node cant not found!\\r\\n");
return -EINVAL;
} else {
printk("gpioled node has been found!\\r\\n");
}
/* 2、 獲取設(shè)備樹(shù)中的 gpio 屬性,得到 LED 所使用的 LED 編號(hào) */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\\r\\n", gpioled.led_gpio);
/* 3、設(shè)置 GPIO1_IO03 為輸出,并且輸出高電平,默認(rèn)關(guān)閉 LED 燈 */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\\r\\n");
}
驅(qū)動(dòng)入口函數(shù)中:注冊(cè)字符設(shè)備驅(qū)動(dòng),代碼與Linux點(diǎn)燈中一樣
驅(qū)動(dòng)出口函數(shù)中:注銷(xiāo)設(shè)備驅(qū)動(dòng),刪除類(lèi)和設(shè)備,代碼可參考Linux點(diǎn)燈一文
2.3 編寫(xiě)測(cè)試程序
實(shí)現(xiàn)操作驅(qū)動(dòng)文件對(duì)外設(shè)進(jìn)行控制的功能。創(chuàng)建測(cè)試程序文件gpioledApp.c,代碼內(nèi)容與Linux點(diǎn)燈一文中的測(cè)試程序代碼一致,此處不再贅述
2.4 編譯測(cè)試
編譯驅(qū)動(dòng)程序:當(dāng)前目錄下創(chuàng)建Makefile文件,并make編譯
KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := gpioled.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
編譯測(cè)試程序:無(wú)需內(nèi)核參與,直接編譯即可
arm-linux-gnueabihf-gcc gpioledApp.c -o gpioledApp
運(yùn)行測(cè)試:?jiǎn)?dòng)開(kāi)發(fā)板后,加載驅(qū)動(dòng)模塊,之后使用應(yīng)用程序測(cè)試驅(qū)動(dòng)是否正常工作
depmod #第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令
modprobe gpioled.ko #加載驅(qū)動(dòng)
./gpioledApp /dev/gpioled 1 #打開(kāi)LED燈
./gpioledApp /dev/gpioled 0 #關(guān)閉LED燈
rmmod gpioled.ko #卸載驅(qū)動(dòng)模塊
-
寄存器
+關(guān)注
關(guān)注
31文章
5301瀏覽量
119861 -
Linux
+關(guān)注
關(guān)注
87文章
11213瀏覽量
208736 -
子系統(tǒng)
+關(guān)注
關(guān)注
0文章
109瀏覽量
12375 -
字符
+關(guān)注
關(guān)注
0文章
232瀏覽量
25155 -
GPIO
+關(guān)注
關(guān)注
16文章
1189瀏覽量
51846
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論