国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析

這篇具有很好參考價(jià)值的文章主要介紹了Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1.簡(jiǎn)介

GPIO是可編程的通用I/O外設(shè)。如下圖所示,RK3588 GPIO控制器包含3個(gè)部分;APB接口模塊和SoC內(nèi)部的APB總線連接,負(fù)責(zé)與SoC交換數(shù)據(jù),位寬為32位;I/O port接口模塊管理外部的引腳,引腳的輸入和輸出都要經(jīng)過(guò)該模塊;中斷探測(cè)模塊負(fù)責(zé)GPIO控制器的中斷上報(bào)與處理。

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析
RK3588 GPIO控制器的特性如下:

  1. 32bits APB總線位寬
  2. 每個(gè)中斷控制器32個(gè)GPIO引腳
  3. 每個(gè)GPIO引腳軟件可控制
  4. 中斷引腳可配置防抖
  5. 中斷模式可配置
  6. 獨(dú)立的控制寄存器支持兩個(gè)虛擬的OS
  7. 在兩個(gè)虛擬的OS模式中,每個(gè)OS都有獨(dú)立的中斷
  8. 非虛擬的OS模式中,支持設(shè)置兩種中斷極性

2.GPIO驅(qū)動(dòng)框架

Linux內(nèi)核GPIO驅(qū)動(dòng)框架如下圖所示。最上層是GPIO使用者驅(qū)動(dòng),如LED Driver、KEY Driver等,GPIO硬件由使用者控制。接下來(lái)是GPIO的抽象層-GPIO庫(kù),實(shí)現(xiàn)位于內(nèi)核drivers\gpio\gpiolib.c文件中,GPIO庫(kù)向上給GPIO使用者提供訪問(wèn)GPIO硬件的接口,向下將所有不同的GPIO Controller統(tǒng)一管理起來(lái)。GPIO控制器驅(qū)動(dòng)負(fù)責(zé)驅(qū)動(dòng)具體的GPIO控制器。最下層是GPIO控制器硬件。

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析

2.1.數(shù)據(jù)結(jié)構(gòu)

數(shù)據(jù)結(jié)構(gòu)的關(guān)系圖如下所示。一個(gè)GPIO控制器對(duì)應(yīng)一個(gè)gpio_device、rockchip_pin_bank、irq_chip_generic,一個(gè)GPIO引腳對(duì)應(yīng)于一個(gè)gpio_desc。所有的gpio_device放到gpio_devices鏈表中,由內(nèi)核統(tǒng)一管理。

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析

2.2.GPIO控制器驅(qū)動(dòng)接口

GPIO控制器驅(qū)動(dòng)需要提供一個(gè)gpio_chip結(jié)構(gòu)體,并將其初始化。gpio_chip結(jié)構(gòu)體主要的成員如下。內(nèi)核使用gpio_irq_chip實(shí)現(xiàn)gpio控制器的中斷功能。

[include/linux/gpio/driver.h]
struct gpio_chip {
	const char		*label;
	// 每個(gè)GPIO控制器都有一個(gè)gpio_device
	struct gpio_device	*gpiodev;
	// 請(qǐng)求GPIO,gpio_request調(diào)用該函數(shù)
	int	(*request)(struct gpio_chip *gc, unsigned int offset);
	// 釋放GPIO,gpio_free調(diào)用該函數(shù)
	void (*free)(struct gpio_chip *gc, unsigned int offset);
	// 獲取GPIO方向,gpiod_get_direction調(diào)用該函數(shù)
	int	(*get_direction)(struct gpio_chip *gc, unsigned int offset);
	// 設(shè)置GPIO為輸入,gpio_direction_input或gpiod_direction_input調(diào)用該函數(shù)
	int	(*direction_input)(struct gpio_chip *gc, unsigned int offset);
	// 設(shè)置GPIO為輸出,gpio_direction_output或gpiod_direction_output調(diào)用該函數(shù)
	int	(*direction_output)(struct gpio_chip *gc,
			unsigned int offset, int value);
	// 獲取GPIO值,gpio_get_value或gpiod_get_value調(diào)用該函數(shù)
	int	(*get)(struct gpio_chip *gc, unsigned int offset);
	// 獲取多個(gè)GPIO值
	int	(*get_multiple)(struct gpio_chip *gc,
			unsigned long *mask, unsigned long *bits);
	// 設(shè)置GPIO值,gpio_set_value或gpiod_set_value調(diào)用該函數(shù)
	void (*set)(struct gpio_chip *gc, unsigned int offset, int value);
	// 設(shè)置多個(gè)GPIO值
	void (*set_multiple)(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits);
	// 設(shè)置GPIO防抖功能,gpio_set_debounce或gpiod_set_debounce調(diào)用該函數(shù)
	int	(*set_config)(struct gpio_chip *gc, unsigned int offset,
				unsigned long config);
	// 獲取gpio引腳虛擬中斷號(hào),gpio_to_irq或gpiod_to_irq調(diào)用該函數(shù)
	int	(*to_irq)(struct gpio_chip *gc, unsigned int offset);
	......
	// gpio range功能
	int	(*add_pin_ranges)(struct gpio_chip *gc);
	int	base;  // 該gpio控制器的基礎(chǔ)編號(hào)
	u16	ngpio; // gpio引腳數(shù)量
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
	// 讀gpio寄存器
	unsigned long (*read_reg)(void __iomem *reg);
	// 寫(xiě)gpio寄存器
	void (*write_reg)(void __iomem *reg, unsigned long data);;
	void __iomem *reg_dat;  // data (in) register for generic GPIO
	void __iomem *reg_set;  // output set register (out=high) for generic GPIO
	void __iomem *reg_clr;  // output clear register (out=low) for generic GPIO
	// direction out setting register for generic GPIO
	void __iomem *reg_dir_out;
	// direction in setting register for generic GPIO
	void __iomem *reg_dir_in;
#endif /* CONFIG_GPIO_GENERIC */

#ifdef CONFIG_GPIOLIB_IRQCHIP
	/**
	 * Integrates interrupt chip functionality with the GPIO chip. Can be
	 * used to handle IRQs for most practical cases.
	 */
	struct gpio_irq_chip irq;
#endif /* CONFIG_GPIOLIB_IRQCHIP */
	......
};
// gpio中斷功能
struct gpio_irq_chip {
	/* GPIO IRQ chip implementation, provided by GPIO driver. */
	struct irq_chip *chip;
	/**
	 * Interrupt translation domain; responsible for mapping between GPIO
	 * hwirq number and Linux IRQ number.
	 */
	struct irq_domain *domain;  // 用于gpio中斷號(hào)映射
	/* Table of interrupt domain operations for this IRQ chip. */
	const struct irq_domain_ops *domain_ops;
	/**
	 * The IRQ handler to use (often a predefined IRQ core function) for
	 * GPIO IRQs, provided by GPIO driver.
	 */
	// gpio控制器中斷處理函數(shù),該函數(shù)會(huì)調(diào)用使用者注冊(cè)的具體pin的中斷處理函數(shù)
	irq_flow_handler_t handler;
	/**
	 * Default IRQ triggering type applied during GPIO driver
	 * initialization, provided by GPIO driver.
	 */
	unsigned int default_type;
	/**
	 * The interrupt handler for the GPIO chip's parent interrupts, may be
	 * NULL if the parent interrupts are nested rather than cascaded.
	 */
	irq_flow_handler_t parent_handler;
	/**
	 * @init_hw: optional routine to initialize hardware before
	 * an IRQ chip will be added. This is quite useful when
	 * a particular driver wants to clear IRQ related registers
	 * in order to avoid undesired events.
	 */
	int (*init_hw)(struct gpio_chip *gc);
	/**
	 * Required for static IRQ allocation. If set, irq_domain_add_simple()
	 * will allocate and map all IRQs during initialization.
	 */
	unsigned int first;
	/* Store old irq_chip irq_enable callback */
	void		(*irq_enable)(struct irq_data *data);
	/* Store old irq_chip irq_disable callback */
	void		(*irq_disable)(struct irq_data *data);
	/* Store old irq_chip irq_unmask callback */
	void		(*irq_unmask)(struct irq_data *data);
	/* Store old irq_chip irq_mask callback */
	void		(*irq_mask)(struct irq_data *data);
};

gpio_chip結(jié)構(gòu)體初始化完成之后,調(diào)用gpiochip_add_data或devm_gpiochip_add_data函數(shù)進(jìn)行注冊(cè),注冊(cè)成功之后,GPIO使用者就可以通過(guò)GPIO驅(qū)動(dòng)訪問(wèn)GPIO硬件。調(diào)用gpiochip_add_data注冊(cè)的GPIO控制器需要調(diào)用gpiochip_remove釋放,使用devm_gpiochip_add_data無(wú)需手動(dòng)釋放。

[include/linux/gpio/driver.h]
gpiochip_add_data(gc, data);
devm_gpiochip_add_data(dev, gc, data);
void gpiochip_remove(struct gpio_chip *gc);

2.3.GPIO消費(fèi)者驅(qū)動(dòng)接口

內(nèi)核中有兩套接口可供GPIO使用者調(diào)用。第一套是內(nèi)核推薦的接口,其基于描述符gpio_desc,以gpiod開(kāi)頭。第二套是傳統(tǒng)的接口,基于gpio number,以gpio開(kāi)頭。兩種接口的對(duì)比如下表所示。

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析

基于描述符的GPIO接口和基于整數(shù)的GPIO接口主要的區(qū)別在于獲取GPIO。基于描述符獲取GPIO接口如下所示。

[include/linux/gpio/consumer.h]
#define GPIOD_FLAGS_BIT_DIR_SET           BIT(0)  // 設(shè)置方向,默認(rèn)輸入
#define GPIOD_FLAGS_BIT_DIR_OUT           BIT(1)  // 輸出方向
#define GPIOD_FLAGS_BIT_DIR_VAL           BIT(2)  // 設(shè)置值,默認(rèn)低電平
#define GPIOD_FLAGS_BIT_OPEN_DRAIN        BIT(3)  // 開(kāi)漏
#define GPIOD_FLAGS_BIT_NONEXCLUSIVE      BIT(4)
/**
 * Optional flags that can be passed to one of gpiod_* to configure direction
 * and output value. These values cannot be OR'd.
 */
enum gpiod_flags {
    GPIOD_ASIS = 0,
    GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,  // 輸入
    // 輸出-低電平
    GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
    // 輸出-高電平
    GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | 
                     GPIOD_FLAGS_BIT_DIR_VAL,
    // 輸出-低電平-開(kāi)漏
    GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN,
    // 輸出-高電平-開(kāi)漏
    GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN,
};
/**
 * gpiod_get - 獲取gpio描述符
 * @dev: GPIO consumer設(shè)備結(jié)構(gòu)體指針
 * @con_id: GPIO consumer引用gpio的屬性前綴,如uart_rts_gpios,前綴為uart_rts
 * @flags: 可選的GPIO初始化標(biāo)志
 */
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags);
/**
 * gpiod_get - 獲取指定的gpio描述符
 * @dev: GPIO consumer設(shè)備結(jié)構(gòu)體指針
 * @con_id: GPIO consumer引用gpio的屬性前綴,如uart_rts_gpios,前綴為uart_rts
 * @idx: GPIO consumer引用gpio的屬性索引,uart_rts_gpios只有一個(gè)屬性
 * @flags: 可選的GPIO初始化標(biāo)志
 */
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags);
// 獲取指定的gpio所有描述符,得到的是一個(gè)gpio數(shù)組
static inline struct gpio_descs *gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)

3.GPIO消費(fèi)者驅(qū)動(dòng)示例

GPIO使用者驅(qū)動(dòng)wireless_bluetooth的設(shè)備樹(shù)如下所示,其引用了GPIO3控制器的的編號(hào)為4的引腳(GPIO引腳編號(hào)定義在include/dt-bindings/pinctrl/rockchip.h頭文件中),低電平有效,即gpiod_set_value/gpio_set_value設(shè)置0時(shí)有效。wireless_bluetooth引用了uart8_gpios的pinctrl設(shè)備節(jié)點(diǎn),uart8_gpios節(jié)點(diǎn)將gpio3控制器的編號(hào)為4的引腳復(fù)用為GPIO功能。GPIO使用者驅(qū)動(dòng)中GPIO屬性名稱的命名規(guī)則為"[-]gpios",name表明該GPIO的用途,新的內(nèi)核推薦使用該命名規(guī)則。當(dāng)然也可以使用之前的規(guī)則,即使用gpios和[-]gpio"作為屬性名稱。

[arch/arm64/boot/dts/rockchip/rk3588s-evb4-lp4x.dtsi]
wireless_bluetooth: wireless-bluetooth {
	compatible = "bluetooth-platdata";
	clocks = <&hym8563>;
	clock-names = "ext_clock";
	// gpio3控制器,4號(hào)引腳,低電平有效
	uart_rts_gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default", "rts_gpio";
	pinctrl-0 = <&uart8m1_rtsn>, <&bt_reset_gpio>, <&bt_wake_gpio>, <&bt_wake_host_irq>;
	pinctrl-1 = <&uart8_gpios>;  // 引用uart_rts_gpios引腳的pinctrl結(jié)點(diǎn)
	BT,reset_gpio    = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;
	BT,wake_gpio     = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>;
	BT,wake_host_irq = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>;
	status = "okay";
};

[arch/arm64/boot/dts/rockchip/rk3588s-evb4-lp4x.dtsi]
&pinctrl {
	......
	wireless-bluetooth {
		uart8_gpios: uart8-gpios {
			// gpio3控制器,4號(hào)引腳,引腳復(fù)用為GPIO功能,不配置上下拉
			rockchip,pins = <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>;
		};
		......
		bt_wake_gpio: bt-wake-gpio {
			rockchip,pins = <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
	......
};

[arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi]
&pinctrl {
	......
	/omit-if-no-ref/
	pcfg_pull_none: pcfg-pull-none {
		// 該屬性定義在drivers/pinctrl/pinconf-generic.c文件中
		bias-disable;
	};
	......
};

[arch/arm64/boot/dts/rockchip/rk3588s.dtsi]
pinctrl: pinctrl {
	......
	// gpio3控制器設(shè)備樹(shù)節(jié)點(diǎn)
	gpio3: gpio@fec40000 {
		compatible = "rockchip,gpio-bank";
		reg = <0x0 0xfec40000 0x0 0x100>;
		// 整個(gè)gpio3控制器使用gic的280+32=312號(hào)中斷,高電平觸發(fā)
		interrupts = <GIC_SPI 280 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;
		gpio-controller;  // gpio控制器
		#gpio-cells = <2>;
		gpio-ranges = <&pinctrl 0 96 32>;
		interrupt-controller; // gpio控制器也是一個(gè)中斷控制器
		#interrupt-cells = <2>;
	};
	......
};

wireless_bluetooth驅(qū)動(dòng)如下所示,在probe函數(shù)中先使用of_get_named_gpio_flags函數(shù)獲取GPIO(整數(shù)),然后調(diào)用devm_gpio_request請(qǐng)求GPIO,在remove函數(shù)中調(diào)用gpio_free釋放GPIO。wireless_bluetooth驅(qū)動(dòng)使用的是基于整數(shù)的GPIO接口,可以使用基于描述符的GPIO接口,如of_get_named_gpiod_flags、devm_gpiod_put。

[net/rfkill/rfkill-bt.c]
static int rfkill_rk_probe(struct platform_device *pdev)
{
	......
	ret = bluetooth_platdata_parse_dt(&pdev->dev, pdata);
	......
	ret = rfkill_rk_setup_gpio(pdev, &pdata->rts_gpio, rfkill->pdata->name,
				   "rts");
	......
	ret = rfkill_rk_setup_gpio(pdev, &pdata->wake_gpio, pdata->name,
				   "wake");
	......
}

static int bluetooth_platdata_parse_dt(struct device *dev,
	struct rfkill_rk_platform_data *data)
{
	......
	// 獲取GPIO
	gpio = of_get_named_gpio_flags(node, "uart_rts_gpios", 0, &flags);
	......
	// 獲取GPIO
	gpio = of_get_named_gpio_flags(node, "BT,wake_gpio", 0, &flags);;
	......
}

static int rfkill_rk_setup_gpio(struct platform_device *pdev,
	struct rfkill_rk_gpio *gpio, const char *prefix,
	const char *name)
{
	......
	// 請(qǐng)求GPIO
	ret = devm_gpio_request(&pdev->dev, gpio->io, gpio->name);
	......
}

static int rfkill_rk_remove(struct platform_device *pdev)
{
	......
	// 判斷GPIO是否有效
	if (gpio_is_valid(rfkill->pdata->rts_gpio.io))
		gpio_free(rfkill->pdata->rts_gpio.io);  // 釋放GPIO
	......
	if (gpio_is_valid(rfkill->pdata->wake_gpio.io))
		gpio_free(rfkill->pdata->wake_gpio.io);
	......
}

可以利用sys文件系統(tǒng)在用戶空間操作gpio,具體如下所示。

echo 400 >  /sys/class/gpio/export    // 導(dǎo)出gpio
echo 400 >  /sys/class/gpio/unexport  // 不導(dǎo)出gpio

cat /sys/class/gpio/gpio400/direction // 查看gpio方向
# gpio方向默認(rèn)輸出低電平
echo in/out > /sys/class/gpio/gpio400/direction  // 設(shè)置gpio方向

cat /sys/class/gpio/gpio400/value
# 非0值-高電平,0-低電平,若配置為中斷引腳,則可在此文件上調(diào)用poll輪詢
echo 非0值/0 > /sys/class/gpio/gpio400/value

cat /sys/class/gpio/gpio400/edge
# 中斷輸入引腳有效
echo none/falling/rising/both > /sys/class/gpio/gpio400/edge

cat /sys/class/gpio/gpio400/active_low
# 寫(xiě)入非零值,真假反轉(zhuǎn)
echo 1 > /sys/class/gpio/gpio400/active_low

cat /sys/kernel/debug/gpio # 查看GPIO的編號(hào)和使用情況

4.驅(qū)動(dòng)分析

RK3588 GPIO驅(qū)動(dòng)由pinctrl驅(qū)動(dòng)調(diào)用of_platform_populate匹配。匹配成功后,rockchip_gpio_probe函數(shù)就會(huì)被調(diào)用。

[drivers/gpio/gpio-rockchip.c]
static const struct of_device_id rockchip_gpio_match[] = {
	{ .compatible = "rockchip,gpio-bank", },
	{ .compatible = "rockchip,rk3188-gpio-bank0" },
	{ },
};
static struct platform_driver rockchip_gpio_driver = {
	.probe		= rockchip_gpio_probe,
	.remove		= rockchip_gpio_remove,
	.driver		= {
		.name	= "rockchip-gpio",
		.of_match_table = rockchip_gpio_match,
	},
};

RK3588驅(qū)動(dòng)的執(zhí)行流程如下,主要的工作有:

  1. 獲取設(shè)備樹(shù)中的GPIO信息并進(jìn)行處理,gpio的bank信息定義在pinctrl驅(qū)動(dòng)中
  2. 注冊(cè)GPIO控制器,即將rockchip_gpiolib_chip注冊(cè)到內(nèi)核中,每個(gè)gpio_chip都對(duì)應(yīng)一個(gè)gpio_device,每個(gè)gpio bank中的gpio pin都有一個(gè)gpio_desc,pin的編號(hào)由gpio_desc距數(shù)組開(kāi)頭的偏移值決定,最后設(shè)置GPIO的Linux編號(hào),初始化gpio sys,具體路勁為/sys/class/gpio。
  3. 注冊(cè)GPIO中斷。GPIO也是一個(gè)中斷控制器,串聯(lián)到GIC中斷控制器下面,當(dāng)GPIO中斷發(fā)生后,GPIO控制器會(huì)將中斷上報(bào)到GIC,GIC在將中斷報(bào)告給CPU。GPIO中斷的處理過(guò)程則正常相反,CPU首先調(diào)用GIC提供的中斷函數(shù),再調(diào)用GPIO驅(qū)動(dòng)的中斷處理函數(shù),最后調(diào)用GPIO消費(fèi)者驅(qū)動(dòng)注冊(cè)中斷處理函數(shù)。
rockchip_gpio_probe
	of_pinctrl_get          // 獲取節(jié)點(diǎn)的別名
	// 獲取rockchip_pin_bank,該數(shù)據(jù)定義在pinctrl驅(qū)動(dòng)中
	rockchip_gpio_find_bank
	// 獲取該gpio控制器的信息
	rockchip_get_bank_data
		devm_ioremap_resource  // 映射寄存器地址
		irq_of_parse_and_map   // 獲取gpio控制器的虛擬中斷號(hào)
		clk_prepare_enable     // 使能時(shí)鐘
		bank->gpio_regs = &gpio_regs_v2  // 獲取gpio寄存器偏移地址
	rockchip_gpiolib_register
		bank->gpio_chip = rockchip_gpiolib_chip  // 設(shè)置gpio_chip
		gc->base = bank->pin_base;  // gpio控制器編號(hào)
		gc->ngpio = bank->nr_pins;  // gpio控制器中pin的數(shù)量
		gc->label = bank->name;     // gpio控制器名稱
		gpiochip_add_data
			// 分配gpio_device
			gdev = kzalloc(sizeof(*gdev), GFP_KERNEL)
			// 分配ngpio個(gè)gpio_desc,每個(gè)gpio對(duì)應(yīng)一個(gè)
			gdev->descs = kcalloc(gc->ngpio, sizeof(gdev->descs[0]...))
			// 若驅(qū)動(dòng)未設(shè)置Linux gpio編號(hào),則內(nèi)核會(huì)查找設(shè)置
			// bank0 = 512 - 32,bank1 = 512 - 2 * 32...
			gpiochip_find_base(gc->ngpio)
			// 若設(shè)備樹(shù)定義了gpio-ranges屬性,還需要建立
			// gpio linux編號(hào)和pinctrl的映射關(guān)系
			gpiochip_add_pin_range
			// 初始化gpio sys,這樣用戶空間可以利用sys文件系統(tǒng)操作gpio
			gpiochip_setup_dev(struct gpio_device *gdev)
		rockchip_interrupts_register  // 注冊(cè)gpio控制器中斷
			// 創(chuàng)建一個(gè)線性映射中斷的irq_domain,最大映射32個(gè)中斷
			// 映射方法由irq_generic_chip_ops決定
			irq_domain_add_linear(..., 32, &irq_generic_chip_ops, NULL)
			// 分配irq_domain_chip_generic,有32個(gè)中斷,一個(gè)irq_chip_generic
			// 該chip上的所有中斷的默認(rèn)處理函數(shù)為handle_level_irq
			irq_alloc_domain_generic_chips(bank->domain, 32, 1,
				"rockchip_gpio_irq", handle_level_irq, clr, 0, 0);
			irq_get_domain_generic_chip // 獲取上面分配的irq_chip_generic
			// 設(shè)置gpio中斷相關(guān)寄存器讀寫(xiě)函數(shù)
			gc->reg_writel = gpio_writel_v2;
			gc->reg_readl = gpio_readl_v2;
			// gpio作為中斷控制器,也應(yīng)該有中斷響應(yīng)、屏蔽、使能、設(shè)置中斷類(lèi)型等函數(shù)
			gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
			gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
			gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
			gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
			gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
			gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
			gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
			gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
			gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
			// 設(shè)置gpio bank的鏈?zhǔn)街袛嗵幚砗瘮?shù),當(dāng)該gpio bank中的gpio發(fā)生中斷時(shí),gic
			// 的中斷處理函數(shù)會(huì)調(diào)用該函數(shù)
			irq_set_chained_handler_and_data(...rockchip_irq_demux...);

GPIO初始化的過(guò)程中用到的數(shù)據(jù)結(jié)構(gòu)如下所示。

[drivers/pinctrl/pinctrl-rockchip.c]
// 3588 gpio信息,主要有g(shù)pio bank編號(hào)、pin數(shù)量、iomux配置寬度、輸出類(lèi)型
static struct rockchip_pin_bank rk3588_pin_banks[] = {
	RK3588_PIN_BANK_FLAGS(0, 32, "gpio0",
			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
	RK3588_PIN_BANK_FLAGS(1, 32, "gpio1",
			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
	RK3588_PIN_BANK_FLAGS(2, 32, "gpio2",
			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
	RK3588_PIN_BANK_FLAGS(3, 32, "gpio3",
			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
	RK3588_PIN_BANK_FLAGS(4, 32, "gpio4",
			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
};

[drivers/gpio/gpio-rockchip.c]
// gpio驅(qū)動(dòng)定義的gpio_chip數(shù)據(jù)結(jié)構(gòu)
static const struct gpio_chip rockchip_gpiolib_chip = {
	.request = gpiochip_generic_request,
	.free = gpiochip_generic_free,
	.set = rockchip_gpio_set,
	.get = rockchip_gpio_get,
	// 獲取方向
	.get_direction	= rockchip_gpio_get_direction,
	// 輸入
	.direction_input = rockchip_gpio_direction_input,
	// 輸出
	.direction_output = rockchip_gpio_direction_output,
	.set_config = rockchip_gpio_set_config,
	.to_irq = rockchip_gpio_to_irq,  // 獲取gpio的中斷號(hào)
	.owner = THIS_MODULE,
};

[kernel/irq/generic-chip.c]
// 獲取gpio中斷號(hào)并進(jìn)行映射
struct irq_domain_ops irq_generic_chip_ops = {
	.map	= irq_map_generic_chip,
	.unmap  = irq_unmap_generic_chip,
	.xlate	= irq_domain_xlate_onetwocell,
};

5.GPIO中斷號(hào)

GPIO設(shè)備樹(shù)中定義的是GPIO bank的中斷號(hào),而每個(gè)GPIO bank中的pin都有中斷號(hào)(虛擬)。GPIO消費(fèi)者驅(qū)動(dòng)使用gpiod_to_irq獲取GPIO pin的中斷號(hào)(虛擬)。gpiod_to_irq內(nèi)部會(huì)調(diào)用rockchip_gpio_to_irq,將GPIO pin的引腳編號(hào)映射為虛擬中斷號(hào)。

gpiod_to_irq
    offset = gpio_chip_hwgpio(desc);
        // 返回descs的偏移,表示gpio引腳的編號(hào)
        return desc - &desc->gdev->descs[0];
    gc->to_irq(gc, offset)
    rockchip_gpio_to_irq
        // gpio中斷控制器irq_domain采用chained形式
        irq_create_mapping(domain, offset);
            // gpio引腳的編號(hào)offset就是hwirq,將hwirq映射為虛擬中斷號(hào)
            irq_create_mapping_affinity(host, hwirq, NULL)
                // 檢查該gpio硬件中斷號(hào)是否被映射
                irq_find_mapping(domain, hwirq);
                    domain->linear_revmap[hwirq]  // 線性映射
                    radix_tree_lookup(&domain->revmap_tree, hwirq) // 非線性映射
                // 獲取虛擬中斷號(hào),并分配irq_desc
                virq = irq_domain_alloc_descs(......);
                    __irq_alloc_descs
                        bitmap_find_next_zero_area // 從位圖中找一個(gè)空閑的bit
                        alloc_descs
                            alloc_desc
                                desc = kzalloc_node(sizeof(*desc), ...)
                                irq_insert_desc(start + i, desc)
                                    radix_tree_insert(&irq_desc_tree, irq, desc);
                                // 在位圖中設(shè)置獲取的虛擬中斷號(hào)
                                bitmap_set(allocated_irqs, start, cnt);
                irq_domain_associate
                    irq_data->hwirq = hwirq;    // 保存gpio硬件中斷號(hào)
                    irq_data->domain = domain;  // 保存gpio中斷控制器irq_domain
                    domain->ops->map(domain, virq, hwirq)
                    gpiochip_irq_map
                        irq_set_chip_data(irq, gc);
                            // 保存私有數(shù)據(jù)
                            desc->irq_data.chip_data = data;
                        irq_set_chip_and_handler
                            // 將irq_chip設(shè)置到irq_data中
                            irq_set_chip(irq, chip)
                            // 設(shè)置中斷處理函數(shù),這里設(shè)置的handle_bad_irq
                            __irq_set_handler(irq, handle, 0, name)
                    irq_domain_set_mapping(domain, hwirq, irq_data);
                        // 線性映射
                        irq_domain_set_mapping(domain, hwirq, irq_data);
                        // 非線性映射
                        radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);

6.GPIO中斷處理流程

GPIO的中斷處理流程如下所示,主要的工作流程如下:

Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-414214.html

到了這里,關(guān)于Linux GPIO模塊-RK3588 GPIO驅(qū)動(dòng)分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • RK3588 Android 12 北斗模塊調(diào)試

    北斗模塊用的MTK RS1612M3 , http://www.sragps.com/web/down.html,可以查看相關(guān)資料,用串口和系統(tǒng)通訊 Android12中主要包括串口設(shè)備樹(shù)修改,GPS2.0加載,gps.default.so編譯和上層應(yīng)用測(cè)試,以下主要記錄測(cè)試中碰到的難點(diǎn) 板子上用的串口8,因此打開(kāi)uart8的設(shè)備樹(shù)配置 uart8 { status = “okay”;

    2024年02月15日
    瀏覽(31)
  • RK3568平臺(tái)開(kāi)發(fā)系列講解(驅(qū)動(dòng)基礎(chǔ)篇)GPIO使用以及gpio-leds驅(qū)動(dòng)講解
  • RK3588平臺(tái)開(kāi)發(fā)系列講解(以太網(wǎng)篇)MDIO底層驅(qū)動(dòng)

    RK3588平臺(tái)開(kāi)發(fā)系列講解(以太網(wǎng)篇)MDIO底層驅(qū)動(dòng)

    平臺(tái) 內(nèi)核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!?? mdio bus是mdio管里phy寄存器的總線,此總線非設(shè)備驅(qū)動(dòng)模型之總線。mdiobus在內(nèi)核中用mii_bus結(jié)構(gòu)體描述,mii_bus定義如下: 目錄:linux/phy.h

    2024年02月08日
    瀏覽(99)
  • RK3588平臺(tái)開(kāi)發(fā)系列講解(驅(qū)動(dòng)基礎(chǔ)篇)設(shè)備樹(shù)常用 of 函數(shù)

    RK3588平臺(tái)開(kāi)發(fā)系列講解(驅(qū)動(dòng)基礎(chǔ)篇)設(shè)備樹(shù)常用 of 函數(shù)

    平臺(tái) 內(nèi)核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!?? ?? 設(shè)備樹(shù)描述了設(shè)備的詳細(xì)信息,這些信息包括數(shù)字類(lèi)型的、字符串類(lèi)型的、數(shù)組類(lèi)型的,我們?cè)诰帉?xiě)驅(qū)動(dòng)的時(shí)候需要獲取到這些信息。比如設(shè)備樹(shù)使用 reg 屬性描述了某個(gè)

    2024年02月08日
    瀏覽(30)
  • RK3588平臺(tái)開(kāi)發(fā)系列講解(視頻篇)RKMedia的VDEC模塊

    RK3588平臺(tái)開(kāi)發(fā)系列講解(視頻篇)RKMedia的VDEC模塊

    沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!?? ??RKMedia是RK提供的一種多媒體處理方案,可實(shí)現(xiàn)音視頻捕獲、音視頻輸出、音視頻編解碼等功能。 RK3688 VDEC解碼模塊,支持H264、H265、MJPEG、JPEG這4種編碼標(biāo)準(zhǔn)。 JPEG H.264/AVC H.265/HEVC

    2024年02月20日
    瀏覽(55)
  • RK3568驅(qū)動(dòng)指南|第十二篇 GPIO子系統(tǒng)-第128章 GPIO入門(mén)實(shí)驗(yàn)

    RK3568驅(qū)動(dòng)指南|第十二篇 GPIO子系統(tǒng)-第128章 GPIO入門(mén)實(shí)驗(yàn)

    瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工藝,搭載一顆四核Cortex-A55處理器和Mali G52 2EE 圖形處理器。RK3568 支持4K 解碼和 1080P 編碼,支持SATA/PCIE/USB3.0 外圍接口。RK3568內(nèi)置獨(dú)立NPU,可用于輕量級(jí)人工智能應(yīng)用。RK3568 支持安卓 11 和 linux 系統(tǒng),主要面向物聯(lián)網(wǎng)

    2024年01月21日
    瀏覽(30)
  • 6.2物聯(lián)網(wǎng)RK3399項(xiàng)目開(kāi)發(fā)實(shí)錄-驅(qū)動(dòng)開(kāi)發(fā)之GPIO使用(wulianjishu666)

    6.2物聯(lián)網(wǎng)RK3399項(xiàng)目開(kāi)發(fā)實(shí)錄-驅(qū)動(dòng)開(kāi)發(fā)之GPIO使用(wulianjishu666)

    物聯(lián)網(wǎng)嵌入式開(kāi)發(fā)源碼例程: 鏈接:https://pan.baidu.com/s/1B3oqq5QBhN-VmTFt9CI-7A?pwd=2ihg? ******************************************************************************************* GPIO, 全稱 General-Purpose Input/Output(通用輸入輸出),是一種軟件運(yùn)行期間能夠動(dòng)態(tài)配置和控制的通用引腳。 RK3399 有 5 組 GP

    2024年04月28日
    瀏覽(19)
  • [rk3588]Linux下docker運(yùn)行安卓鏡像

    [rk3588]Linux下docker運(yùn)行安卓鏡像

    關(guān)于在Linux下docker運(yùn)行Android拿來(lái)掛機(jī)玩游戲一類(lèi)的一直感覺(jué)很有意思,后面就在網(wǎng)上搜集了一下資料,資料有點(diǎn)少且亂,總的嘗試下來(lái)也踩了不少的坑,這里我記錄一下我部署的過(guò)程,有感興趣的朋友可以直接拿去用。 開(kāi)發(fā)板:ArmSoM-sige7 Kernel:5.10.160 OS:Debian11 開(kāi)源docker鏡像

    2024年01月23日
    瀏覽(77)
  • RK3588平臺(tái)開(kāi)發(fā)系列講解(進(jìn)程篇)圖解linux netlink

    RK3588平臺(tái)開(kāi)發(fā)系列講解(進(jìn)程篇)圖解linux netlink

    平臺(tái) 內(nèi)核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!?? ?? netlink 協(xié)議是一種進(jìn)程間通信( Inter Process Communication,IPC )機(jī)制,為的用戶空間和內(nèi)核空間以及內(nèi)核的某些部分之間提供了雙向通信方法。 netlink 套接字支持最大 32 個(gè)協(xié)議

    2024年02月08日
    瀏覽(28)
  • RK3588+FPGA+AD+AI的智能數(shù)據(jù)采集與分析解決方案

    RK3588+FPGA+AD+AI的智能數(shù)據(jù)采集與分析解決方案

    RK3588是瑞芯微新一代旗艦級(jí)高端處理器,具有高算力、低功耗、超強(qiáng)多媒體、豐富數(shù)據(jù)接口等特點(diǎn)。搭載四核A76+四核A55的八核CPU和ARM G610MP4 GPU,內(nèi)置6.0TOPs算力的NPU。 RK3588+復(fù)旦微FPGA方案 有五大技術(shù)優(yōu)勢(shì) 1. 內(nèi)置多種功能強(qiáng)大的嵌入式硬件引擎,支持8K@60fps 的 H.265 和 VP9 解碼器

    2024年03月26日
    瀏覽(24)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包