Linux gpio、pinctl 驱动解析

文章目录

  • 1. gpio使用实例
    • 1.1 dts定义
    • 1.2 platform driver
    • 1.3 driver probe
    • 1.4 常用gpio操作函数
    • 1.5 pinctrl配置
    • 1.6 寄存器调试
  • 2. gpio sys文件系统
  • 3. gpio 子系统原理
    • 3.1 gpio_to_desc()
    • 3.2 gpiochip_add()
    • 3.3 gpiod_direction_output
    • 3.4 of_get_named_gpio()
  • 4. pinctrl原理

1. gpio使用实例

1.1 dts定义

在dts中:

  • 定义一个device node “serial_parallel”,系统会为其创建一个platform device;
  • 一个compatible property;
  • 以及6个gpio property,每个property对应一个gpio;
serial_parallel {
    
    compatible = "robot,74HC595D,sn74hc165";
    
    hc595_DATA_OUT  = <&gpio3 4 GPIO_ACTIVE_HIGH>;
    hc595_SHCP  = <&gpio3 6 GPIO_ACTIVE_HIGH>;
    hc595_STCP  = <&gpio3 7 GPIO_ACTIVE_HIGH>;

    hc165_DATA_IN  = <&gpio3 9 GPIO_ACTIVE_HIGH>;
    hc165_SH  = <&gpio3 13 GPIO_ACTIVE_HIGH>;
    hc165_CLK  = <&gpio3 14 GPIO_ACTIVE_HIGH>;
};

1.2 platform driver

在dts中创建了platform device “serial_parallel”,我们也需要注册对应的platform driver:

static const struct of_device_id serial_parallel_match[] = {
	{ .compatible = "robot,74HC595D,sn74hc165", },
	{}
};

static struct platform_driver serial_parallel_driver = {
	.probe = serial_parallel_probe,
    .remove = serial_parallel_remove,
	.driver	= {
		.name = "robot_serial_parallel_",
        .owner = THIS_MODULE,
		.of_match_table	= serial_parallel_match,
	},
};


static __init int serial_parallel_init(void)
{

	ret = platform_driver_register(&serial_parallel_driver);
	if (ret == 0)

}

module_init(serial_parallel_init);

1.3 driver probe

在platform driver的probe函数中,我们解析出gpio property对应的gpio id:


struct sp_drv_data
{
    int gpio_595_data_out;
    int gpio_595_shcp;
    int gpio_595_stcp;
    int gpio_165_data_in;
    int gpio_165_sh;
    int gpio_165_clk;
};

static int serial_parallel_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
    struct sp_drv_data *pdata = NULL;

    /* 分配驱动的私有结构数据 */
    pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    f (!pdata) {
        dev_err(dev, "failed to allocate sp_drv_data\n");
        return -ENOMEM;
    }
    
    
    /* 获取 GP3_04的 ID号 ,然后申请 一个GPIO 操作对象。 */
    pdata->gpio_595_data_out = of_get_named_gpio(np,"hc595_DATA_OUT", 0);

    pdata->gpio_595_shcp = of_get_named_gpio(np,"hc595_SHCP", 0);
    pdata->gpio_595_stcp = of_get_named_gpio(np,"hc595_STCP", 0);
    
    pdata->gpio_165_data_in = of_get_named_gpio(np,"hc165_DATA_IN", 0);
    pdata->gpio_165_sh = of_get_named_gpio(np,"hc165_SH", 0);
    pdata->gpio_165_clk = of_get_named_gpio(np,"hc165_CLK", 0);

    /* 设置驱动私有数据到pdev->dev->driver_data指针当中 */
    platform_set_drvdata(pdev, pdata);

    /* 使用的时候,通过相反方式取出 */
    pdata = (struct sp_drv_data *)platform_get_drvdata(pdev);
}

gpio实例操作:

    /* 在 driver 代码中 ,  需要包含 #include 使用 : */ 

    /* 获取 GP3_04的 ID号 ,然后申请 一个GPIO 操作对象。 */
    gpio_id = of_get_named_gpio(your_driver->dev->of_node,"hc595_DATA_OUT", 0);

    if (gpio_is_valid(gpio_id))  //判断一个IO是否合法
        devm_gpio_request_one(&platform_device->dev, gpio_id,GPIOF_OUT_INIT_LOW, name);

    /* 设置GPIO的方向,如果是输出同时设置电平: */
    /* set as input or output, returning 0 or negative errno */
    int gpio_direction_input(unsigned gpio);
    int gpio_direction_output(unsigned gpio, int value);

    /* 获取输入引脚的电平: */
    /* GPIO INPUT: return zero or nonzero */
    int gpio_get_value(unsigned gpio);

    /* 设置输出: */ 
    void gpio_set_value(unsigned gpio, int value);

    /* 释放申请的GPIO对象 */ 
    void gpio_free(unsigned gpio);

    /* 将GPIO映射为IRQ中断: */
    /* map GPIO numbers to IRQ numbers */
    int gpio_to_irq(unsigned gpio);

    /* map IRQ numbers to GPIO numbers (avoid using this) */
    int irq_to_gpio(unsigned irq);

    /* 设置GPIO IRQ中断类型: */
    set_irq_type(gpio_to_irq( gpio), IRQ_TYPE_EDGE_FALLING);

1.4 常用gpio操作函数

include/linux/gpio.h:

static inline bool gpio_is_valid(int number)

static inline int gpio_request(unsigned gpio, const char *label)


static inline int gpio_request_one(unsigned gpio,
					unsigned long flags, const char *label)


static inline int gpio_request_array(const struct gpio *array, size_t num)


static inline void gpio_free(unsigned gpio)


static inline void gpio_free_array(const struct gpio *array, size_t num)

static inline int gpio_direction_input(unsigned gpio)


static inline int gpio_direction_output(unsigned gpio, int value)


static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)


static inline int gpio_get_value(unsigned gpio)


static inline void gpio_set_value(unsigned gpio, int value)


static inline int devm_gpio_request(struct device *dev, unsigned gpio,
				    const char *label)


static inline int devm_gpio_request_one(struct device *dev, unsigned gpio,
					unsigned long flags, const char *label)


static inline void devm_gpio_free(struct device *dev, unsigned int gpio)


...

1.5 pinctrl配置

在dts中增加以下配置:

&dra7_pmx_core {

	serial_parallel_pins_default: serial_parallel_pins_default {
		pinctrl-single,pins = <
			DRA7XX_CORE_IOPAD(0x34F4, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D0.gpio3_4 */
            DRA7XX_CORE_IOPAD(0x34FC, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D2.gpio3_6 */
            DRA7XX_CORE_IOPAD(0x3500, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D3.gpio3_7 */
            DRA7XX_CORE_IOPAD(0x3504, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D4.gpio3_8 */
			DRA7XX_CORE_IOPAD(0x3508, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D5.gpio3_9 */
            DRA7XX_CORE_IOPAD(0x3518, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D9.gpio3_13 */
            DRA7XX_CORE_IOPAD(0x351C, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D10.gpio3_14 */
		>;
	};
};

serial_parallel {
    
    compatible = "robot,74HC595D,sn74hc165";
    
    hc595_DATA_OUT  = <&gpio3 4 GPIO_ACTIVE_HIGH>;
    hc595_SHCP  = <&gpio3 6 GPIO_ACTIVE_HIGH>;
    hc595_STCP  = <&gpio3 7 GPIO_ACTIVE_HIGH>;

    hc165_DATA_IN  = <&gpio3 9 GPIO_ACTIVE_HIGH>;
    hc165_SH  = <&gpio3 13 GPIO_ACTIVE_HIGH>;
    hc165_CLK  = <&gpio3 14 GPIO_ACTIVE_HIGH>;

    pinctrl-names = "default";
	pinctrl-0 = <&serial_parallel_pins_default>;
};

在驱动probe时,会自动配置pin ctrl:

driver_probe_device() -> really_probe() -> pinctrl_bind_pins():

int pinctrl_bind_pins(struct device *dev)
{
	int ret;

	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
	if (!dev->pins)
		return -ENOMEM;

    /* (1) 获取到device的pinctrl配置
        根据dts中的配置创建 
     */
	dev->pins->p = devm_pinctrl_get(dev);
	if (IS_ERR(dev->pins->p)) {
		dev_dbg(dev, "no pinctrl handle\n");
		ret = PTR_ERR(dev->pins->p);
		goto cleanup_alloc;
	}

    /* (2) 获取"default"配置 */
	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_DEFAULT);
	if (IS_ERR(dev->pins->default_state)) {
		dev_dbg(dev, "no default pinctrl state\n");
		ret = 0;
		goto cleanup_get;
	}

    /* (3) 获取"init"配置 */
	dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_INIT);
	if (IS_ERR(dev->pins->init_state)) {
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no init pinctrl state\n");

        /* (4.1) 配置"default"配置 */
		ret = pinctrl_select_state(dev->pins->p,
					   dev->pins->default_state);
	} else {
        /* (4.2) 配置"init"配置 */
		ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
	}

	if (ret) {
		dev_dbg(dev, "failed to activate initial pinctrl state\n");
		goto cleanup_get;
	}

#ifdef CONFIG_PM
	/*
	 * If power management is enabled, we also look for the optional
	 * sleep and idle pin states, with semantics as defined in
	 * 
	 */
	dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_SLEEP);
	if (IS_ERR(dev->pins->sleep_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no sleep pinctrl state\n");

	dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_IDLE);
	if (IS_ERR(dev->pins->idle_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no idle pinctrl state\n");
#endif

	return 0;

	/*
	 * If no pinctrl handle or default state was found for this device,
	 * let's explicitly free the pin container in the device, there is
	 * no point in keeping it around.
	 */
cleanup_get:
	devm_pinctrl_put(dev->pins->p);
cleanup_alloc:
	devm_kfree(dev, dev->pins);
	dev->pins = NULL;

	/* Only return deferrals */
	if (ret != -EPROBE_DEFER)
		ret = 0;

	return ret;
}

也可以在驱动的代码中自定义修改这些配置:

#include 

struct pinctrl *gp_pinctrl;
struct pinctrl_state *gp_gpio;//gpio

static int serial_parallel_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
    struct sp_drv_data *pdata = NULL;

    /* 获取当前device的pinctrl操作句柄 */
    gp_pinctrl = devm_pinctrl_get(&pdata->dev);
    if (IS_ERR_OR_NULL(gp_pinctrl)) {
        printk("Failed to get pin ctrl\n");
        return PTR_ERR(gp_pinctrl);
    }

    /* 获取pinctrl的"default"配置 */
    gp_gpio = pinctrl_lookup_state(gp_pinctrl, "default");
    if (IS_ERR_OR_NULL(gp_gpio)) {
        printk("Failed to lookup pinctrl gpio state\n");
        return PTR_ERR(gp_gpio);
    }
    
    /* 把pinctrl配置成"default"模式 */
    result = pinctrl_select_state(gp_pinctrl, gp_gpio);
    if (result) {
        printk("%s: Can not set %s pins\n", __func__, "default");
    }


}

1.6 寄存器调试

把寄存器映射出来,打印出具体的值来调试:

    void *base = NULL;
    void *base1 = NULL;

    /* (1) 打印出control module对应寄存器的值 */
    base = devm_ioremap(&pdev->dev, 0x4a003400, 0x500);

    printk(" VIN1A_D0.gpio3_4 reg 0x%x: 0x%x  \n", 0x34F4, *(unsigned int *)((0x34F4-0x3400)+base));
    printk(" VIN1A_D2.gpio3_6 reg 0x%x: 0x%x  \n", 0x34FC, *(unsigned int *)((0x34FC-0x3400)+base));
    printk(" VIN1A_D3.gpio3_7 reg 0x%x: 0x%x  \n", 0x3500, *(unsigned int *)((0x3500-0x3400)+base));
    printk(" VIN1A_D5.gpio3_9 reg 0x%x: 0x%x  \n", 0x3508, *(unsigned int *)((0x3508-0x3400)+base));
    printk(" VIN1A_D9.gpio3_13 reg 0x%x: 0x%x  \n", 0x3518, *(unsigned int *)((0x3518-0x3400)+base));
    printk(" VIN1A_D10.gpio3_14 reg 0x%x: 0x%x  \n", 0x351C, *(unsigned int *)((0x351C-0x3400)+base));

    /* (2) 打印出gpio3对应寄存器的值 */
    base1 = devm_ioremap(&pdev->dev, 0x48057000, 0x200);

    printk(" GPIO3 GPIO_OE reg 0x%x: 0x%x  \n", 0x00000134, *(unsigned int *)(0x00000134+base1));

2. gpio sys文件系统

在/sys/class/gpio目录下存在三种文件:
export/unexport文件
gpioN指代具体的gpio引脚
gpio_chipN指代gpio控制器

1、export/unexport文件接口

/sys/class/gpio/export,该接口只能写不能读
用户程序通过写入gpio的编号来向内核申请将某个gpio的控制权导出到用户空间当然前提是没有内核代码申请这个gpio端口
比如 echo 19 > export
上述操作会为19号gpio创建一个节点gpio19,此时/sys/class/gpio目录下边生成一个gpio19的目录

/sys/class/gpio/unexport和导出的效果相反。
比如 echo 19 > unexport
上述操作将会移除gpio19这个节点。

2、/sys/class/gpio/gpioN

指代某个具体的gpio端口,里边有如下属性文件:

  • direction:表示gpio端口的方向,读取结果是in或out。该文件也可以写,写入out 时该gpio设为输出同时电平默认为低。写入low或high则不仅可以设置为输出 还可以设置输出的电平;
  • value:表示gpio引脚的电平,0(低电平)1(高电平),如果gpio被配置为输出,这个值是可写的,记住任何非零的值都将输出高电平, 如果某个引脚能并且已经被配置为中断,则可以调用poll(2)函数监听该中断,中断触发后poll(2)函数就会返回;

3、/sys/class/gpio/gpiochipN

gpiochipN表示的就是一个gpio_chip,用来管理和控制一组gpio端口的控制器,该目录下存在一下属性文件:

  • base:和N相同,表示控制器管理的最小的端口编号;
  • lable:诊断使用的标志(并不总是唯一的);
  • ngpio:表示控制器管理的gpio端口数量(端口范围是:N ~ N+ngpio-1);

4、具体实例:

root@am57xx-evm:~# ls /sys/class/gpio/
export       gpiochip128/ gpiochip192/ gpiochip32/  gpiochip96/  
gpiochip0/   gpiochip160/ gpiochip224/ gpiochip64/  unexport     
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/base   
0
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/     
base       device/    label      ngpio      power/     subsystem/ uevent
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/ngpio 
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip32/base   
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip64/base   
64
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip96/base   
96
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip32/ngpio 
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip64/ngpio 
32
root@am57xx-evm:~#
root@am335x-evm:~# ls /sys/class/gpio/
export      gpiochip0/  gpiochip32/ gpiochip64/ gpiochip96/ unexport
root@am335x-evm:~# ls /sys/class/gpio/gpiochip0/
base       label      ngpio      power/     subsystem/ uevent     
root@am335x-evm:~# cat /sys/class/gpio/gpiochip0/base   
0
root@am335x-evm:~# cat /sys/class/gpio/gpiochip32/base 
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip64/base   
64
root@am335x-evm:~# cat /sys/class/gpio/gpiochip96/base   
96
root@am335x-evm:~# cat /sys/class/gpio/gpiochip0/ngpio   
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip32/ngpio  
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip64/ngpio   
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip96/ngpio   
32

3. gpio 子系统原理

3.1 gpio_to_desc()

根据gpio编号,找到对应gpio描述符。

struct gpio_desc *gpio_to_desc(unsigned gpio)
{
	struct gpio_device *gdev;
	unsigned long flags;

	spin_lock_irqsave(&gpio_lock, flags);

    /* (1) 遍历gdev链表,查找符合 gdev->base <= gpio < (gdev->base + gdev->ngpio) 的gdev
        每个gdev对应一个gpio chip,在gpiochip_add时创建
        注意gdev->base的值,不一定是从0开始的
    */
	list_for_each_entry(gdev, &gpio_devices, list) {
		if (gdev->base <= gpio &&
		    gdev->base + gdev->ngpio > gpio) {
			spin_unlock_irqrestore(&gpio_lock, flags);

            /* (2) 根据gpio在chip中的偏移,返回gdev中的对应描述符descs[offset] */
			return &gdev->descs[gpio - gdev->base];
		}
	}

	spin_unlock_irqrestore(&gpio_lock, flags);

	if (!gpio_is_valid(gpio))
		WARN(1, "invalid GPIO %d\n", gpio);

	return NULL;
}

3.2 gpiochip_add()

gpio controller的驱动。

dts中gpio controller的定义实例:

        gpio0: gpio@****addr {
            compatible = "**********";
            reg = <0 0x****addr 0 0x50>;
            interrupts = ;
            #gpio-cells = <2>;
            gpio-controller;
            gpio-ranges = <&pfc 0 0 16>;
            #interrupt-cells = <2>;
            interrupt-controller;
            clocks = <&cpg CPG_MOD 912>;
            power-domains = <*****>;
        };

gpio驱动的核心是调用gpiochip_add()函数:

static inline int gpiochip_add(struct gpio_chip *chip)
{
	return gpiochip_add_data(chip, NULL);
}

↓

int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
	unsigned long	flags;
	int		status = 0;
	unsigned	i;
	int		base = chip->base;
	struct gpio_device *gdev;

	/*
	 * First: allocate and populate the internal stat container, and
	 * set up the struct device.
	 */
    /* (1) 分配gpio chip对应的gdev结构 */
	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
	if (!gdev)
		return -ENOMEM;
	gdev->dev.bus = &gpio_bus_type;
	gdev->chip = chip;
	chip->gpiodev = gdev;
	if (chip->parent) {
		gdev->dev.parent = chip->parent;
		gdev->dev.of_node = chip->parent->of_node;
	}

#ifdef CONFIG_OF_GPIO
	/* If the gpiochip has an assigned OF node this takes precedence */
	if (chip->of_node)
		gdev->dev.of_node = chip->of_node;
#endif

	gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
	if (gdev->id < 0) {
		status = gdev->id;
		goto err_free_gdev;
	}
	dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
	device_initialize(&gdev->dev);
	dev_set_drvdata(&gdev->dev, gdev);
	if (chip->parent && chip->parent->driver)
		gdev->owner = chip->parent->driver->owner;
	else if (chip->owner)
		/* TODO: remove chip->owner */
		gdev->owner = chip->owner;
	else
		gdev->owner = THIS_MODULE;

    /* (2) 根据gpio chip中gpio的数量,分配gpio描述符空间
        每个gpio pin对应一个独立的描述符gdev->descs[offset] 
    */
	gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
	if (!gdev->descs) {
		status = -ENOMEM;
		goto err_free_ida;
	}

	if (chip->ngpio == 0) {
		chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
		status = -EINVAL;
		goto err_free_descs;
	}

	if (chip->label)
		gdev->label = kstrdup(chip->label, GFP_KERNEL);
	else
		gdev->label = kstrdup("unknown", GFP_KERNEL);
	if (!gdev->label) {
		status = -ENOMEM;
		goto err_free_descs;
	}

	gdev->ngpio = chip->ngpio;
	gdev->data = data;

	spin_lock_irqsave(&gpio_lock, flags);

	/*
	 * TODO: this allocates a Linux GPIO number base in the global
	 * GPIO numberspace for this chip. In the long run we want to
	 * get *rid* of this numberspace and use only descriptors, but
	 * it may be a pipe dream. It will not happen before we get rid
	 * of the sysfs interface anyways.
	 */
    /* (3) 根据chip->base给gdev->base赋值 */
	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
		if (base < 0) {
			status = base;
			spin_unlock_irqrestore(&gpio_lock, flags);
			goto err_free_label;
		}
		/*
		 * TODO: it should not be necessary to reflect the assigned
		 * base outside of the GPIO subsystem. Go over drivers and
		 * see if anyone makes use of this, else drop this and assign
		 * a poison instead.
		 */
		chip->base = base;
	}
	gdev->base = base;

    /* (4) 将gdev加入到全局链表中 */
	status = gpiodev_add_to_list(gdev);
	if (status) {
		spin_unlock_irqrestore(&gpio_lock, flags);
		goto err_free_label;
	}

	spin_unlock_irqrestore(&gpio_lock, flags);

    /* (5) 初始化gpio描述符gdev->descs[i] */
	for (i = 0; i < chip->ngpio; i++) {
		struct gpio_desc *desc = &gdev->descs[i];

		desc->gdev = gdev;
		/*
		 * REVISIT: most hardware initializes GPIOs as inputs
		 * (often with pullups enabled) so power usage is
		 * minimized. Linux code should set the gpio direction
		 * first thing; but until it does, and in case
		 * chip->get_direction is not set, we may expose the
		 * wrong direction in sysfs.
		 */

		if (chip->get_direction) {
			/*
			 * If we have .get_direction, set up the initial
			 * direction flag from the hardware.
			 */
			int dir = chip->get_direction(chip, i);

			if (!dir)
				set_bit(FLAG_IS_OUT, &desc->flags);
		} else if (!chip->direction_input) {
			/*
			 * If the chip lacks the .direction_input callback
			 * we logically assume all lines are outputs.
			 */
			set_bit(FLAG_IS_OUT, &desc->flags);
		}
	}

#ifdef CONFIG_PINCTRL
	INIT_LIST_HEAD(&gdev->pin_ranges);
#endif

	status = gpiochip_set_desc_names(chip);
	if (status)
		goto err_remove_from_list;

	status = gpiochip_irqchip_init_valid_mask(chip);
	if (status)
		goto err_remove_from_list;

    /* (6.1) chip注册 */
	status = of_gpiochip_add(chip);
	if (status)
		goto err_remove_chip;

    /* (6.2) chip注册 */
	acpi_gpiochip_add(chip);

	/*
	 * By first adding the chardev, and then adding the device,
	 * we get a device node entry in sysfs under
	 * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
	 * coldplug of device nodes and other udev business.
	 * We can do this only if gpiolib has been initialized.
	 * Otherwise, defer until later.
	 */
	if (gpiolib_initialized) {
		status = gpiochip_setup_dev(gdev);
		if (status)
			goto err_remove_chip;
	}
	return 0;

...

}
EXPORT_SYMBOL_GPL(gpiochip_add_data);

3.3 gpiod_direction_output

根据gpio描述符,设置gpio为ouput和初始化值

int gpiod_direction_output(struct gpio_desc *desc, int value)
{
	VALIDATE_DESC(desc);
	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
		value = !value;
	else
		value = !!value;
	return _gpiod_direction_output_raw(desc, value);
}

↓

static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
    /* (1) 根据gpio描述符,找到对应的gpio chip结构 */
	struct gpio_chip *gc = desc->gdev->chip;
	int val = !!value;
	int ret;

	/* GPIOs used for IRQs shall not be set as output */
	if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
		gpiod_err(desc,
			  "%s: tried to set a GPIO tied to an IRQ as output\n",
			  __func__);
		return -EIO;
	}

    /* (2) gpio_chip_hwgpio(desc)的含义是根据描述符的偏移,计算gpio在chip中的偏移 */

    /* (3) 调用chip的实际执行函数,设置gpio的其他模式 */
	if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
		/* First see if we can enable open drain in hardware */
		ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
						  PIN_CONFIG_DRIVE_OPEN_DRAIN);
		if (!ret)
			goto set_output_value;
		/* Emulate open drain by not actively driving the line high */
		if (val)
			return gpiod_direction_input(desc);
	}
	else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
		ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
						  PIN_CONFIG_DRIVE_OPEN_SOURCE);
		if (!ret)
			goto set_output_value;
		/* Emulate open source by not actively driving the line low */
		if (!val)
			return gpiod_direction_input(desc);
	} else {
		gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
					    PIN_CONFIG_DRIVE_PUSH_PULL);
	}

set_output_value:
	if (!gc->set || !gc->direction_output) {
		gpiod_warn(desc,
		       "%s: missing set() or direction_output() operations\n",
		       __func__);
		return -EIO;
	}

    /* (4) 调用chip的实际执行函数,设置gpio的output模式及其初始值 */
	ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
	if (!ret)
		set_bit(FLAG_IS_OUT, &desc->flags);
	trace_gpio_value(desc_to_gpio(desc), 0, val);
	trace_gpio_direction(desc_to_gpio(desc), 0, ret);
	return ret;
}

3.4 of_get_named_gpio()

在dts中使用如下格式定义一个gpio:

serial_parallel {
    
    pwd-gpios  = <&gpio3 4 GPIO_ACTIVE_HIGH>;

};

在代码中使用of_get_named_gpio()函数来获取到dts中关于gpio的定义:

/* 获取 GP0_07的 ID号 ,然后申请 一个GPIO 操作对象。 */
gpio_id = of_get_named_gpio(your_driver->dev->of_node,"pwd-gpios", 0);

of_get_named_gpio()函数的具体实现:

static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
{
    /* (1) 各个参数含义为:
        np: "pwd-gpios" property的父node,例如"serial_parallel"对应的device node
        propname: property的name,例如"pwd-gpios"
        index: 在pwd-gpios属性中的index,一般为0。
               如果pwd-gpios属性包含多个<&gpio3 4 GPIO_ACTIVE_HIGH &gpio3 6 GPIO_ACTIVE_HIGH>,index指定具体哪一个。
        
     */
	return of_get_named_gpio_flags(np, propname, index, NULL);
}

↓

int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
			    int index, enum of_gpio_flags *flags)
{
	struct gpio_desc *desc;

	desc = of_get_named_gpiod_flags(np, list_name, index, flags);

	if (IS_ERR(desc))
		return PTR_ERR(desc);
	else
		return desc_to_gpio(desc);
}

↓

struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
		     const char *propname, int index, enum of_gpio_flags *flags)
{
	struct of_phandle_args gpiospec;
	struct gpio_chip *chip;
	struct gpio_desc *desc;
	int ret;

    /* (1.1) 根据父device node、gpio的property、index找到对应的 gpiospec结构,
        对<&gpio3 4 GPIO_ACTIVE_HIGH>的解析的话,首先根据'&gpio3'找到对应的gpio controller,然后'gpio3' controller中的"#gpio-cells"属性会指定gpio参数的个数,一般是2,然后根据长度2来解析'4 GPIO_ACTIVE_HIGH'
        最终得到的gpiospec各子成员如下:
        struct of_phandle_args {
        	struct device_node *np;            // '&gpio3'对应的device node
        	int args_count;                    // "#gpio-cells"对应的值,一般情况 = 2
        	uint32_t args[MAX_PHANDLE_ARGS];   // 参数值,例如'4 GPIO_ACTIVE_HIGH'
        };
     */
	ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
					 &gpiospec);
	if (ret) {
		pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n",
			__func__, propname, np, index);
		return ERR_PTR(ret);
	}

    /* (1.2) 根据gpio controller的device node,遍历gdev链表,找到对应的chip结构 */
	chip = of_find_gpiochip_by_xlate(&gpiospec);
	if (!chip) {
		desc = ERR_PTR(-EPROBE_DEFER);
		goto out;
	}

    /* (1.3) 使用chip提供的of_xlate()函数,和参数'4 GPIO_ACTIVE_HIGH'
        得到gpio对应的描述符 
     */
	desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
	if (IS_ERR(desc))
		goto out;

	pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
		 __func__, propname, np, index,
		 PTR_ERR_OR_ZERO(desc));

out:
	of_node_put(gpiospec.np);

	return desc;
}

4. pinctrl原理

一个gpio端口被多个功能所复用,设置使用哪个功能需要用到pinctrl。5728的pinctrl控制器在dts中定义为dra7_pmx_core:

dra7.dtsi:

		l4_cfg: l4@4a000000 {
			compatible = "ti,dra7-l4-cfg", "simple-bus";
			#address-cells = <1>;
			#size-cells = <1>;
			ranges = <0 0x4a000000 0x22c000>;

			scm: scm@2000 {
				compatible = "ti,dra7-scm-core", "simple-bus";
				reg = <0x2000 0x2000>;
				#address-cells = <1>;
				#size-cells = <1>;
				ranges = <0 0x2000 0x2000>;


                /* 经过两层父节点的地址转换,实际地址为:0x1400 + 0x2000 + 0x4a000000 = 0x4a003400 */
				dra7_pmx_core: pinmux@1400 {
					compatible = "ti,dra7-padconf",
						     "pinctrl-single";
					reg = <0x1400 0x0468>;
					#address-cells = <1>;
					#size-cells = <0>;
					#interrupt-cells = <1>;
					interrupt-controller;
					pinctrl-single,register-width = <32>;
					pinctrl-single,function-mask = <0x3fffffff>;
				};

                ...
            }
            ...
        }

pinctrl对应AM572x芯片手册“CONTROL MODULE”一章,提供了端口多功能配置等一系列功能:

The CTRL_MODULE_CORE submodule has registers for the following features:
• Pad configuration with following controls:
    – Pad I/O multiplexing
    – Pad pullup and pulldown configuration
    – Pad wake-up detection enabling
    – Pad wake-up event status
    – Pad input buffer enable
    – Pad slew rate control
• Device thermal management control and status registers
• PBIAS cell and MMC1 I/O cells control
• IRQ_CROSSBAR and DMA_CROSSBAR control
• Control the priority of initiator accesses to the external SDRAM
• Control the priority of initiators connected to L3_MAIN interconnect
• Memory region lock registers
• Mapping of the device non-maskable interrupt (NMI) to respective cores
• Controls for the DDR2/DDR3 I/O Cells
• Controls for the DDR2/DDR3 associated vref-generation cells
• AVS Class 0 associated registers
• ABB associated registers
• PCIe related registers
• Standard eFuse logic
• Other miscellaneous functions:
    – Status of the system boot settings
    – DSP1 and DSP2 reset vector address
    – Settings assoaciated with USB, SATA and HDMI PHYs
    – DSS PLLs multiplexing and enabling
    – Force MPU write nonposted transactions
    – Firewalls error status
    – Settings related to different peripheral modules
    – Others
The CTRL_MODULE_WKUP submodule has registers for the following features:
    • Basic EMIF configuration settings
    • XTAL Oscilator control
    • Efuse I/O compensation
    • Other functions

芯片寄存器基地址也是dts中配置的0x4a002000:
在这里插入图片描述

其中配置端口功能复用的寄存器从0x4a003400开始:
Linux gpio、pinctl 驱动解析_第1张图片

dts中增加的pinctrl配置,也都是从0x3400的偏移开始的:

&dra7_pmx_core {

	serial_parallel_pins_default: serial_parallel_pins_default {
		pinctrl-single,pins = <
			DRA7XX_CORE_IOPAD(0x34F4, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D0.gpio3_4 */
            DRA7XX_CORE_IOPAD(0x34FC, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D2.gpio3_6 */
            DRA7XX_CORE_IOPAD(0x3500, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D3.gpio3_7 */
			DRA7XX_CORE_IOPAD(0x3508, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D5.gpio3_9 */
            DRA7XX_CORE_IOPAD(0x3518, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D9.gpio3_13 */
            DRA7XX_CORE_IOPAD(0x351C, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D10.gpio3_14 */
		>;
	};

};

你可能感兴趣的:(Arm,Linux)