gpio和pinctrl的内核使用文档
-
gpio子系统
\Documentation\devicetree\bindings\gpio\gpio.txt
-
pinctrl子系统
\Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
pinctrl子系统
Linux中将芯片引脚的复用、配置抽出来,汇总了所有引脚的配置信息,做成pinctrl子系统给其他外设使用,而不用使用一个外设就要去查找寄存器一个一个配置。
imx6ull与pinctrl子系统相关节点
imx6ull.dtsi:
iomuxc: iomuxc@20e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x20e0000 0x4000>;
};
- compatible - 与pinctrl子系统的平台驱动匹配
- reg - 引脚配置寄存器基地址
imx6ull-seeed-npi.dts:
&iomuxc {
pinctrl-names = "default","init","sleep";
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl-1 =<&xxx>;
pinctrl-2 =<&yyy>;
...
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
};
...
}
节点配置方式:
- pinctrl-names :定义引脚状态
- pinctrl-0 : 定义第0中状态(也就是pinctrl-names的default状态)使用到的引脚,可引用其他节点,这里引用了pinctrl_hog_1节点
- pinctrl-1 : 定义第1中状态(也就是pinctrl-names的init状态)使用到的引脚…
- pinctrl-x : 以此类推
每家厂商的芯片的pinctrl节点格式都不一样,imx6ull以fsl,pins作为标识结合imx6ull的pinctrl子系统使用。
对于pinctrl的引用驱动基本不用管,当设备切换状态时对应的pinctrl会被自动调用。
fsl,pins属性值:
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
// 宏 + 十六进制数
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
宏的路径:
imx6ull.dtsi ->#include "imx6ull-pinfunc.h"-> #include "imx6ul-pinfunc.h"
#ifndef __DTS_IMX6UL_PINFUNC_H
#define __DTS_IMX6UL_PINFUNC_H
/*
* The pin function ID is a tuple of
* <mux_reg conf_reg input_reg mux_mode input_val>
*/
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10 0x0014 0x02a0 0x0000 5 0
#define MX6UL_PAD_BOOT_MODE1__GPIO5_IO11 0x0018 0x02a4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x001c 0x02a8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x0020 0x02ac 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x0024 0x02b0 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x0028 0x02b4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x002c 0x02b8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x0030 0x02bc 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x0034 0x02c0 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x0038 0x02c4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x003c 0x02c8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x0040 0x02cc 0x0000 5 0
...
...
#endif /* __DTS_IMX6UL_PINFUNC_H */
宏5个数值对应的含义:
比如MX6UL_PAD_UART1_TX_DATA__GPIO1_IO16:
#define MX6UL_PAD_UART1_TX_DATA__GPIO1_IO16 0x0084 0x0310 0x0000 5 0
<mux_reg conf_reg input_reg mux_mode input_val>
0x0084 0x0310 0x0000 5 0
-
mux_reg : 引脚复用设置寄存器地址相对及地址的偏移值
-
conf_reg: 引脚属性设置寄存器相对基地址的偏移值
-
input_reg:引脚输入设置寄存器。引脚需要输入功能时设置
-
mux_mode: 复用寄存器设置值,设置引脚复用,如i2c,spi
-
input_val : 输入寄存器的设置值
十六进制数:
属性寄存器设置值。因为pin作为不同复用为不同功能属性值就不一样,易变化,故需要独立设置。
使用pinctrl子系统初始化LED灯引脚
修改设备树:
1、在iomuxc节点添加LED控制引脚配置信息的子节点
pinctrl_rgb_led:rgb_led{
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
2、rgb_led节点添加引脚状态:
/*添加led节点*/
rgb_led {
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default"; //在rgb_led节点添加的pinctrl信息
pinctrl-0 = <&pinctrl_rgb_led>; //在rgb_led节点添加的pinctrl信息
compatible = "fire,rgb_led";
/*红灯节点*/
ranges;
rgb_led_red@0x020C406C{
compatible = "fire,rgb_led_red";
reg = <0x020C406C 0x00000004
0x020E006C 0x00000004
0x020E02F8 0x00000004
0x0209C000 0x00000004
0x0209C004 0x00000004>;
status = "okay";
};
/*绿灯节点*/
rgb_led_green@0x020C4074{
compatible = "fire,rgb_led_green";
reg = <0x020C4074 0x00000004
0x020E01E0 0x00000004
0x020E046C 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
/*蓝灯节点*/
rgb_led_blue@0x020C4074{
compatible = "fire,rgb_led_blue";
reg = <0x020C4074 0x00000004
0x020E01DC 0x00000004
0x020E0468 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
};
};
imx6ull的pinctrl子系统在内核源码 drivers/pinctrl/freescale/pinctrl-imx6ul.c中实现,也是基于平台总线驱动
gpio子系统
如果pinctrl子系统将芯片的pin复用成最基本的功能gpio功能,就要用到gpio子系统。linux将pin的输入输出功能抽象成了gpio子系统,而不在像操作寄存器那样麻烦。
drivers/gpio/gpio-mxc.c 为imx6ull的gpio子系统文件
static struct platform_driver mxc_gpio_driver = {
.driver = {
.name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids,
},
.probe = mxc_gpio_probe,
.id_table = mxc_gpio_devtype,
};
可以看出gpio子系统也是个平台总线设备驱动。
设备树总的gpio节点:
在imx6ull.dtsi设备文件中
gpio1: gpio@209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_GPIO1>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
gpio-ranges = <&iomuxc 0 23 10>, <&iomuxc 10 17 6>,
<&iomuxc 16 33 16>;
};
-
interrupts:GPIO中断相关
-
clocks:GPIO外设时钟相关
-
gpio-controller:表示 gpio1 节点是个 GPIO 控制器
-
#gpio-cells:表示要用2个cell描述一个 GPIO引脚
-
interrupt-controller:表示 gpio1 节点是个中断控制器
-
#interrupt-cells:表示要用2个cell描述一个中断
-
gpio-ranges:gpio编号转换成pin编号
以此类似的还有gpio2~gpio5
在imx6ull-seeed-npi.dts设备树文件新增rgb_led节点,使用gpio子系统
gpio子系统相关api
1、申请gpio
int gpio_request(unsigned gpio, const char *label)
//gpio - gpio编号
//label - 给gpio设置标签名字
2、释放gpio
void gpio_free(unsigned gpio) //不使用就释放
3、设置gpio为输入
int gpio_direction_input(unsigned gpio)
4、设置gpio为输出
int gpio_direction_output(unsigned gpio, int value)
5、读取gpio的输入值
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
6、设置gpio的值
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
7、获取gpio编号,也就是上面函数的形参gpio
int of_get_named_gpio(struct device_node *np,
const char *propname,
int index)
pinctrl子系统和gpio子系统的使用
1、在iomux节点中添加跟pinctrl相关的节点
/* 添加 pinctrl节点 pinctrl子系统通过fsl,pins来找到pinctrl子系统要初始化的pin */
pinctrl_red_led: fire_red_led {
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
>;
};
2、在根节点添加led的节点
red_led { /* 我加的设备树红色led的设备树子节点 */
#address-cells = <1>;
#size-cells = <1>;
compatible = "red_led";
pinctrl-names = "default"; /* 加上跟pinctrl子系统相关的属性 */
pinctrl-0 = <&pinctrl_red_led>; /* 加上跟pinctrl子系统相关的属性*/
red_led_gpio = <&gpio1 4 GPIO_ACTIVE_LOW>; /* 添加跟gpio子系统相关的 */
status = "okay";
// reg = < 0x20C406C 0x04 /* CCM_CCGR1 */ /* 去掉相关的寄存器 */
// 0x20E006C 0x04 /* MUX_PAD_GPIO1_IO04 */
// 0x209C000 0x04 /* GPIO1_DR */
// 0x209C004 0x04 >; /* GPIO1_GDIR */
};
2、在驱动程序中的的使用
#define RED_LED_DTS_NODE "/red_led" //red_led设备树节点路径
#define RED_LED_GPIO_NAME "red_led_gpio" //red led 节点中 gpio子系统相关属性名
//设备树的匹配条件
static struct of_device_id dts_match_table[] = {
{.compatible = "red_led", }, //通过设备树来匹配
};
static int led_red_driver_probe(struct platform_device *dev)
{
int err;
int ret;
struct device *tmpdev;
led_dev.dev_node = of_find_node_by_path(RED_LED_DTS_NODE); //找到red_led的设备树节点
if (!led_dev.dev_node) {
printk("red led device node can not found!\r\n");
return -EINVAL;
}
led_dev.red_led_gpio = of_get_named_gpio(led_dev.dev_node, RED_LED_GPIO_NAME, 0); //找到led的gpio的编号
if ( led_dev.red_led_gpio < 0) {
printk("red led gpio can not found!\r\n");
return -EINVAL;
}
// ret = gpio_request(led_dev.red_led_gpio, RED_LED_GPIO_NAME); //请求gpio
// if (ret < 0) {
// printk("rcan not request gpio!\r\n");
// return -EINVAL;
// }
ret = alloc_chrdev_region(&led_dev.dev_no, 0, 1, "red_led");
if (ret < 0) {
pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
return ret;
}
cdev_init(&led_dev.chrdev, &red_led_drv);
cdev_add(&led_dev.chrdev, led_dev.dev_no, 1);
led_dev.led_class = class_create(THIS_MODULE, "red_led_class");
err = PTR_ERR(led_dev.led_class);
if (IS_ERR(led_dev.led_class)) {
goto failed1;
}
//在 /dev目录下创建red_led设备节点
tmpdev = device_create(led_dev.led_class , NULL, led_dev.dev_no, NULL, "red_led");
if (IS_ERR(tmpdev)) {
ret = -EINVAL;
goto failed2;
}
printk(KERN_INFO"%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
failed2:
device_destroy(led_dev.led_class, led_dev.dev_no);
class_destroy(led_dev.led_class);
failed1:
cdev_del(&led_dev.chrdev);
unregister_chrdev_region(led_dev.dev_no, 1);
return ret;
}
int led_red_driver_remove(struct platform_device *dev)
{
printk(KERN_INFO"driver remove %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// gpio_free(led_dev.red_led_gpio); //释放gpio
device_destroy(led_dev.led_class, led_dev.dev_no);
class_destroy(led_dev.led_class);
unregister_chrdev_region(led_dev.dev_no, 1);
cdev_del(&led_dev.chrdev);
return 0;
}
static struct platform_driver red_led_platform_driver = {
.probe = led_red_driver_probe,
.remove = led_red_driver_remove,
.driver = {
.name = "fire-rgb-led",
.owner = THIS_MODULE,
.of_match_table = dts_match_table, //通过设备树匹配
},
};
static int __init red_led_driver_init(void)
{
int ret;
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
ret = platform_driver_register(&red_led_platform_driver); //注册platform驱动
return ret;
}
static void __exit red_led_driver_exit(void)
{
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&red_led_platform_driver);
}
使用gpio子系统操作led:
static int red_led_drv_open (struct inode *node, struct file *file)
{
int ret;
printk(KERN_INFO"open red led dev\n set gpio is ouput\n");
ret = gpio_direction_output(led_dev.red_led_gpio, 1); //设置gpio为输出,默认输出高电平
return ret;
}
static ssize_t red_led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int err;
char status;
printk("write %s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);
if (status)
{ /* 点灯 */
gpio_set_value(led_dev.red_led_gpio, 0); //输出低电平
}
else
{ /* 熄灯 */
gpio_set_value(led_dev.red_led_gpio, 1); //输出高电平
}
return 1;
}
static struct file_operations red_led_drv = {
.owner = THIS_MODULE,
.open = red_led_drv_open,
.write = red_led_drv_write,
};
测试应用程序:
debian@npi:~/nfs_rootfs$ sudo ./led_test /dev/red_led on
debian@npi:~/nfs_rootfs$ sudo ./led_test /dev/red_led off