0
点赞
收藏
分享

微信扫一扫

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


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

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

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​​命令使用参考:

​​javascript:void(0)​​

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

通过TRM可知,​​I2C4_m0​​​包含 ​​I2C4_SDA_M0​​​ 与 ​​I2C4_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_1​​​,​​RK_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_B2​​​ 和 ​​GPIO4_B3​​​ 两个针脚切换成对应的 i2c 功能;而如果选择了 gpio ,系统会应用 ​​i2c4m0_gpio​​​ 这个 pinctrl,将 ​​GPIO4_B2​​​ 和 ​​GPIO4_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 <linux/pinctrl/consumer.h>
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);


举报

相关推荐

0 条评论