背景: PCB版过硬件认证时需一些PIN的复用功能控制,因通用GPIO库未提供相关接口,需自己添加接口供外部驱动调用。
平台:PX30
OS:android8.1
1.先通过dts中的pin-control 配置原理找到设置复用功能的接口函数。
配置的dts写法
uart0 {
uart0_xfer: uart0-xfer {
rockchip,pins =
<0 RK_PB2 RK_FUNC_1 &pcfg_pull_up>,
<0 RK_PB3 RK_FUNC_1 &pcfg_pull_up>;
};
uart0_cts: uart0-cts {
rockchip,pins =
<0 RK_PB4 RK_FUNC_1 &pcfg_pull_none>;
};
uart0_rts: uart0-rts {
rockchip,pins =
<0 RK_PB5 RK_FUNC_1 &pcfg_pull_none>;
};
uart0_rts_gpio: uart0-rts-gpio {
rockchip,pins =
<0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
根据原理图可知GPIO0-B2,GPIO0-B3,只有GPIO和uart两种功能复用,并且在dts 默认配置成串口,根据dts 解析找到驱动在kernel/drivers/pinctrl/pinctrl-rockchip.c中进行设置,找到函数rockchip_set_mux:
static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
{
struct rockchip_pinctrl *info = bank->drvdata;
int iomux_num = (pin / 8);
struct regmap *regmap;
int reg, ret, mask, mux_type;
u8 bit;
u32 data, rmask, route_reg, route_val;
ret = rockchip_verify_mux(bank, pin, mux);
if (ret < 0)
return ret;
if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY)
return 0;
dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n",
bank->bank_num, pin, mux);
//gpio0 B2
if ((bank->bank_num == 0) && (pin == 10))
{
printk("setting mux of GPIO%d-%d to %d\n",
bank->bank_num, pin, mux);
dump_stack();
}
regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
? info->regmap_pmu : info->regmap_base;
/* get basic quadrupel of mux registers and the correct reg inside */
mux_type = bank->iomux[iomux_num].type;
reg = bank->iomux[iomux_num].offset;
if (mux_type & IOMUX_WIDTH_4BIT) {
if ((pin % 8) >= 4)
reg += 0x4;
bit = (pin % 4) * 4;
mask = 0xf;
} else if (mux_type & IOMUX_WIDTH_3BIT) {
if ((pin % 8) >= 5)
reg += 0x4;
bit = (pin % 8 % 5) * 3;
mask = 0x7;
} else {
bit = (pin % 8) * 2;
mask = 0x3;
}
if (bank->recalced_mask & BIT(pin))
rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask);
if (bank->route_mask & BIT(pin)) {
if (rockchip_get_mux_route(bank, pin, mux, &route_reg,
&route_val)) {
ret = regmap_write(regmap, route_reg, route_val);
if (ret)
return ret;
}
}
if (mux_type & IOMUX_WRITABLE_32BIT) {
ret = regmap_read(regmap, reg, &data);
if (ret)
return ret;
data &= ~(mask << bit);
data |= (mux & mask) << bit;
ret = regmap_write(regmap, reg, data);
} else {
data = (mask << (bit + 16));
rmask = data | (data >> 16);
data |= (mux & mask) << bit;
ret = regmap_update_bits(regmap, reg, rmask, data);
}
return ret;
}
查看函数结构,最后也是通过写寄存器的方式去更新GPIO的功能状态。但是此接口使用的参数:
struct rockchip_pinctrl,struct rockchip_pin_bank,都是函数内部结构体,不可作为外部接口使用,
通过通用的gpio_direction_output等通用接口的封装过程,发现接口都是通过gpio_chip接口进行封装的。找到回调函数:
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,
.to_irq = rockchip_gpio_to_irq,
.set_mux = rockchip_gpio_set_mux,
.owner = THIS_MODULE,
};
其中.set_mux = rockchip_gpio_set_mux为自己添加,实现原型
static int rockchip_gpio_set_mux(struct gpio_chip *gc, unsigned offset ,int value)
{
int ret = 0;
struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
ret = rockchip_set_mux(bank, offset, value);
return ret;
}
函数接口部分已经实现完成了,最后只需要将接口通用化。
2.添加通用接口函数
kernel/include/linux/gpio/driver.h中
struct gpio_chip {
int (*request)(struct gpio_chip *chip,
unsigned offset);
void (*free)(struct gpio_chip *chip,
unsigned offset);
int (*get_direction)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
.......
int (*set_mux)(struct gpio_chip *chip,
unsigned offset, int value);
}
kernel/drivers/gpio/gpiolib.c中添加外部接口调用函数
int gpiod_set_mux(struct gpio_desc *desc, int value)
{
struct gpio_chip *chip;
int status = -EINVAL;
if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
chip = desc->chip;
status = chip->set_mux(chip, gpio_chip_hwgpio(desc),value);
return status;
}
EXPORT_SYMBOL_GPL(gpiod_set_mux);
最后封装函数为通用接口,及加上函数声明即可:
kernel/include/asm-generic/gpio.h中进行函数封装
static inline int gpio_set_mux(unsigned gpio,int value)
{
return gpiod_set_mux(gpio_to_desc(gpio), value);
}
kernel/include/linux/gpio/consumer.h中添加声明
int gpiod_set_mux(struct gpio_desc *desc, int value);
到这儿,函数接口已经添加完成,可正常使用,提供一个使用实例(此驱动为自己编写的一个字符驱动,此接口可当做GPIO通用接口使用,不局限场景):
//设置pin10,11(GPIO0-B2,B3)模式
void set_mode(int value)
{
switch(value)
{
case MODE_UART:
gpio_set_mux(MEDICAL_BP_UART_TX,RK_FUNC_1);
gpio_set_mux(MEDICAL_BP_UART_RX,RK_FUNC_1);
gpio_free(MEDICAL_BP_UART_RX);
gpio_free(MEDICAL_BP_UART_TX);
break;
case MODE_GPIO:
gpio_request(MEDICAL_BP_UART_TX,"medical_uart_tx");
gpio_request(MEDICAL_BP_UART_RX,"medical_uart_tx");
gpio_direction_output(MEDICAL_BP_UART_TX,0);
gpio_direction_output(MEDICAL_BP_UART_RX,0); //init to GPIO low
break;
default:break;
}
}
调用时需注意,首先需要将pin request 成GPIO,然后通过GPIO的属性去修改他的mux复用功能,pin func 功能可查看mcu,pin说明去确认复用类型,对应修改参数即可。设置完成属性后,因属性已不在是GPIO FUNC ,则需要释放此GPIO,这样就能达到设置属性的功能。
如有疑问,或编辑有误地方,还请提出,谢谢观看!