[ROC-RK3568-PC] [Firefly-Android] 10min带你了解GPIO复用

博主主页:Systemcall小酒屋
博主简介:Neutionwei,C站嵌入式领域优质创作者之一,一枚热爱开源技术、喜欢分享技术心得的极客,注重简约风格,热衷于用简单的案例讲述复杂的技术,“假传万卷书,真传一案例”,这是厦大一位教数学的院士说过的一句话,另外“成就是最好的老师”,技术既要沉淀,也得分享,成就感的正反馈是支持我持续耕耘的动力!
专栏目录:Firefly-RK356x板卡
专栏说明:本专栏主要基于Firefly旗下的RK356x板卡进行入门篇讲述,欢迎订阅,博主会持续更新!

GPIO 除了通用输入输出、中断功能外,还可能有复用功能,如GPIO0_B4, 就有如下几个功能:

func0 func1 func2 func3 func4
GPIO0_B4 I2C1_SDA CAN0_RX_M0 PCIE20_BUTTONRSTn MCU_JTAG_TCK

那么在使用作普通GPIO时,就需要注意是否被复用为其他功能,这里可以用io命令查看iomux来判断是否复用。 假如通过io命令发现GPIO0_B4有复用作I2C1_SDA,使用GPIO0_B4作gpio 或者其他功能时就需要将I2C1 disabled掉。

&i2c1 {
    status = "disabled";
};

gpio_demo: gpio_demo {
    status = "okay";
    compatible = "firefly,rk356x-gpio";
    firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
    firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */
};

注意:此处GPIO0_B4仅作示例!

io命令使用参考:

https://blog.csdn.net/qq_30624591/article/details/122235282

引脚在运行时又如何切换功能呢?下面以 I2C4_m0 为例,详细介绍可以参考RKDocs/common/PIN-Ctrl/Rockchip-Developer-Guide-Linux-Pin-Ctrl-CN.pdf

通过TRM可知,I2C4_m0包含 I2C4_SDA_M0I2C4_SCL_M0 引脚,它们的功能定义如下:

Pad# func0 func1 func2 func3 func4 func5
I2C4_SDA_M0/GPIO4_B2 GPIO4_B2 I2C4_SDA EBC_VCOM GMAC1_RXER_M1 SPI3_MOSI_M0 I2S2_SDI_M1
I2C4_SCL_M0/GPIO4_B3 GPIO4_B3 I2C4_SDA EBC_GDOE ETH1_REFCLKO_25MM1 SPI3_CLK_M0 I2S2_SDO_M1

kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi 里有:

i2c4: i2c@fe5d0000 {
	compatible = "rockchip,rk3399-i2c";
	reg = <0x0 0xfe5d0000 0x0 0x1000>;
	clocks = <&cru CLK_I2C4>, <&cru PCLK_I2C4>;
	clock-names = "i2c", "pclk";
	interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
	pinctrl-names = "default", "gpio";
	pinctrl-0 = <&i2c4m0_xfer>;
	pinctrl-1 = <&i2c4mo_gpio>;  //此处源码未添加
	#address-cells = <1>;
	#size-cells = <0>;
	status = "disabled";
};

复用相关的是 pinctrl- 开头的属性:

  • pinctrl-names 定义了状态名称列表: default (i2c 功能) 和 gpio 两种状态
  • pinctrl-0 定义了状态 0 (即 default)时需要设置的 pinctrl: &i2c4m0_xfer
  • pinctrl-1 定义了状态 1 (即 gpio)时需要设置的 pinctrl: &i2c4mo_gpio

这些 pinctrl 在 kernel/arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi 中这样定义:

pinctrl: pinctrl {
	compatible = "rockchip,rk3568-pinctrl";
	rockchip,grf = <&grf>;
	rockchip,pmu = <&pmugrf>;
	#address-cells = <2>;
	#size-cells = <2>;
	ranges;

	i2c4 {
		/omit-if-no-ref/
		i2c4m0_xfer: i2c4m0-xfer {
			rockchip,pins =
				/* i2c4_sclm0 */
				<4 RK_PB3 1 &pcfg_pull_none_smt>,
				/* i2c4_sdam0 */
				<4 RK_PB2 1 &pcfg_pull_none_smt>;
		};
	};
	// 此处源码未添加,仅作示例
	gpio {
		/omit-if-no-ref/
		i2c4m0_gpio: i2c4m0-gpio {
			rockchip,pins =
				<4 RK_PB2 0 &pcfg_pull_none>,
				<4 RK_PB3 0 &pcfg_pull_none>;
        };
	};
};

RK_FUNC_1RK_FUNC_GPIO 的定义在 kernel/include/dt-bindings/pinctrl/rockchip.h ,此处简写作0和1:

#define RK_FUNC_GPIO    0
#define RK_FUNC_1   1
#define RK_FUNC_2   2
#define RK_FUNC_3   3
#define RK_FUNC_4   4
#define RK_FUNC_5   5
#define RK_FUNC_6   6
#define RK_FUNC_7   7

其实就是前面表格中功能的选择,例如RK_FUNC_1代表<4 RK_PB3 1 &pcfg_pull_none_smt>里面的1

在复用时,如果选择了 default (即 i2c 功能),系统会应用 i2c4m0_xfer 这个 pinctrl,最终将 GPIO4_B2GPIO4_B3 两个针脚切换成对应的 i2c 功能;而如果选择了 gpio ,系统会应用 i2c4m0_gpio 这个 pinctrl,将 GPIO4_B2GPIO4_B3 两个针脚还原为 GPIO 功能。

我们看看如下 i2c 的驱动程序是如何切换复用功能的:

static int rockchip_i2c_probe(struct platform_device *pdev)
{
	struct rockchip_i2c *i2c = NULL; struct resource *res;
	struct device_node *np = pdev->dev.of_node;
	int ret;
	...
	i2c->sda_gpio = of_get_gpio(np, 0);
	if (!gpio_is_valid(i2c->sda_gpio)) {
		dev_err(&pdev->dev, "sda gpio is invalid\n");
		return -EINVAL;
	}
	ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
	if (ret) {
		dev_err(&pdev->dev, "failed to request sda gpio\n");
		return ret;
	}
	i2c->scl_gpio = of_get_gpio(np, 1);
	if (!gpio_is_valid(i2c->scl_gpio)) {
		dev_err(&pdev->dev, "scl gpio is invalid\n");
		return -EINVAL;
	}
	ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
	if (ret) {
		dev_err(&pdev->dev, "failed to request scl gpio\n");
		return ret;
	}
	i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
	if (IS_ERR(i2c->gpio_state)) {
		dev_err(&pdev->dev, "no gpio pinctrl state\n");
		return PTR_ERR(i2c->gpio_state);
	}
	pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
	gpio_direction_input(i2c->sda_gpio);
	gpio_direction_input(i2c->scl_gpio);
	pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);
	...
}

首先是调用 of_get_gpio 取出设备树中 i2c4 结点所定义的两个 gpio:

gpios = <&gpio1 GPIO_B3 GPIO_ACTIVE_LOW>, <&gpio1 GPIO_B4 GPIO_ACTIVE_LOW>;

然后是调用 devm_gpio_request 来申请 gpio,接着是调用 pinctrl_lookup_state 来查找 gpio 状态,而默认状态 default 已经由框架保存到 i2c->dev->pins->default_state中。

最后调用 pinctrl_select_state 来选择是 default 还是 gpio 功能。

下面是常用的复用 API 定义:

#include 
struct device {
	//...
	#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
	#endif
	//...
};

struct dev_pin_info {
	struct pinctrl *p;
	struct pinctrl_state *default_state;
#ifdef CONFIG_PM
   	struct pinctrl_state *sleep_state;
   	struct pinctrl_state *idle_state;
#endif
};

struct pinctrl_state * pinctrl_lookup_state(struct pinctrl *p, const char *name);
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);

你可能感兴趣的:(Android,rk3566,rk3568,gpio,android)