博主主页: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_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
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);