Linux gpio、pinctl 驅動解析

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 <linux/gpio.h>使用 : */ 

    /* 獲取 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
	 * <linux/pinctrl/pinctrl-state.h>
	 */
	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 <linux/pinctrl/consumer.h>

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 = <SPI 4 IRQ_TYPE_LEVEL_HIGH>;
            #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開始:
在這裏插入圖片描述

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 */
		>;
	};

};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章