硬件平台
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"; compa tible = "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命令使用参考:
引脚在运行时又如何切换功能呢?下面以 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 = ; 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 invalidn"); 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 gpion"); return ret; } i2c->scl_gpio = of_get_gpio(np, 1); if (!gpio_is_valid(i2c->scl_gpio)) { dev_err(&pdev->dev, "scl gpio is invalidn"); 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 gpion"); 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 staten"); 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);了解更多请关注 sunychip.com
|