概述
本文浅要讲解下全志T507 gpio口的控制原理。
GPIO控制原理
首先看一下pinctrl的如下设备树
pio: pinctrl@0300b000 {
compatible = "allwinner,sun50iw9p1-pinctrl";
reg = <0x0 0x0300b000 0x0 0x400>;
interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>, /* AW1823_GIC_Spec: GPIOA: 83-32=51 */
<GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
device_type = "pio";
clocks = <&clk_pio>, <&clk_losc>, <&clk_hosc>;
gpio-controller;
interrupt-controller;
#interrupt-cells = <3>;
#size-cells = <0>;
#gpio-cells = <6>;
/* takes the debounce time in usec as argument */
input-debounce = <0 0 0 0 0 0 0>;
/*clk_losc_pins_a: clk_losc@0 {
allwinner,pins = "PG10";
allwinner,function = "x32kfout";
allwinner,muxsel = <3>;
allwinner,drive = <2>;
allwinner,pull = <1>;
};*/
...//中间省略
uart0_pins_a: uart0@0 {
allwinner,pins = "PH0", "PH1";
allwinner,pname = "uart0_tx", "uart0_rx";
allwinner,function = "uart0";
allwinner,muxsel = <2>;
allwinner,drive = <1>;
allwinner,pull = <1>;
};
uart0_pins_b: uart0@1 {
allwinner,pins = "PH0", "PH1";
allwinner,function = "uart0"; /* io_disabled */
allwinner,muxsel = <7>;
allwinner,drive = <1>;
allwinner,pull = <0>;
};
...//以下省略
实际应用过程中,我们只需要在设备树中配置Pin脚的复用功能,拉高拉低就可以了。
如果想深入了解下其中的原理呢,linux系统是如何去控制对应寄存器以实现我们的需求的呢?
以串口uart0的pin脚为例
uart0: uart@05000000 {
compatible = "allwinner,sun50i-uart";
device_type = "uart0";
reg = <0x0 0x05000000 0x0 0x400>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_uart0>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&uart0_pins_a>; //引用pin脚
pinctrl-1 = <&uart0_pins_b>; //引用pin脚
uart0_port = <0>;
uart0_type = <2>;
status = "okay";
};
问题来了,如何使用两个pin脚,需要看源代码,如何查看源代码呢?
要想查找源码,这一句是关键。设备和驱动probe的匹配就是通过compatible关键字匹配上的。
compatible = "allwinner,sun50i-uart"
搜索关键字,可以找到在kernel/linux-4.9/drivers/tty/serial/sunxi-uart.c中
static const struct of_device_id sunxi_uart_match[] = {
{ .compatible = "allwinner,sun8i-uart", },
{ .compatible = "allwinner,sun50i-uart", },
{ .compatible = "allwinner,sun3i-uart", },
{},
};
找到uart的probe
static int sw_uart_probe(struct platform_device *pdev)
port->ops = &sw_uart_ops
.config_port = sw_uart_config_port
sw_uart_request_port
sw_uart_request_gpio
static int sw_uart_request_port(struct uart_port *port)
{
struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
struct platform_device *pdev;
struct resource *mem_res;
int ret;
SERIAL_DBG("request port(ioremap & request io) %d\n", sw_uport->id);
pdev = to_platform_device(port->dev);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*获取uart0的物理地址,这个在哪里呢?看设备树:uart0: uart@05000000
基地址找到了,0x05000000*/
if (mem_res == NULL) {
SERIAL_MSG("uart%d, get MEM resource failed\n", sw_uport->id);
ret = -ENXIO;
}
/* request memory resource */
if (!request_mem_region(mem_res->start, resource_size(mem_res), SUNXI_UART_DEV_NAME)) {
SERIAL_MSG("uart%d, request mem region failed\n", sw_uport->id);
return -EBUSY;
}
port->membase = ioremap(mem_res->start, resource_size(mem_res));
if (!port->membase) {
SERIAL_MSG("uart%d, ioremap failed\n", sw_uport->id);
release_mem_region(mem_res->start, resource_size(mem_res));
return -EBUSY;
}
/* request io resource */
ret = sw_uart_request_gpio(sw_uport);//获取uart0的gpio资源
if (ret < 0) {
release_mem_region(mem_res->start, resource_size(mem_res));
return ret;
}
return 0;
}
然后看sw_uart_request_gpio函数
static int sw_uart_request_gpio(struct sw_uart_port *sw_uport)
{
if (sw_uport->card_print)
return 0;
sw_uport->pctrl = devm_pinctrl_get(sw_uport->port.dev);//此时pinctrl子系统该登场了
if (IS_ERR_OR_NULL(sw_uport->pctrl)) {
SERIAL_MSG("UART%d devm_pinctrl_get() failed! return %ld\n", sw_uport->id, PTR_ERR(sw_uport->pctrl));
return -1;
}
return sw_uart_select_gpio_state(sw_uport->pctrl, PINCTRL_STATE_DEFAULT, sw_uport->id);
}
从pinctrl子系统获取我们所需的接口函数。
static int sw_uart_select_gpio_state(struct pinctrl *pctrl, char *name, u32 no)
{
int ret = 0;
struct pinctrl_state *pctrl_state = NULL;
pctrl_state = pinctrl_lookup_state(pctrl, name); //查找pinctrl状态
if (IS_ERR(pctrl_state)) {
SERIAL_MSG("UART%d pinctrl_lookup_state(%s) failed! return %p \n", no, name, pctrl_state);
return -1;
}
ret = pinctrl_select_state(pctrl, pctrl_state);//设置pinctrl状态
if (ret < 0)
SERIAL_MSG("UART%d pinctrl_select_state(%s) failed! return %d \n", no, name, ret);
return ret;
}
直接查找相应函数pinctrl_lookup_state,可以找到如下定义
/**
* pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
* @p: the pinctrl handle to retrieve the state from
* @name: the state name to retrieve
*/
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
const char *name)
{
struct pinctrl_state *state;
state = find_state(p, name);
if (!state) {
if (pinctrl_dummy_state) {
/* create dummy state */
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
state = create_state(p, name);
} else
state = ERR_PTR(-ENODEV);
}
return state;
}
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
这些都是Pinctrl子系统提供给外部的接口。
pinctrl子系统
看下pinctrl的注册过程,通过pinctrl的compatilbe值进行查找
static int sun50iw9p1_pinctrl_probe(struct platform_device *pdev)
{
return sunxi_pinctrl_init(pdev, &sun50iw9p1_pinctrl_data);
//初始化的Pin脚是对应的T507 sun50iw9p1的pin脚。
}
static struct of_device_id sun50iw9p1_pinctrl_match[] = {
{ .compatible = "allwinner,sun50iw9p1-pinctrl", },
{}
};
t507的pinctrl寄存器基地址及偏移量。
static const struct sunxi_pinctrl_desc sun50iw9p1_pinctrl_data = {
.pins = sun50iw9p1_pins,
.npins = ARRAY_SIZE(sun50iw9p1_pins),
.pin_base = 0,
.banks = ARRAY_SIZE(sun50iw9p1_bank_base),
.bank_base = sun50iw9p1_bank_base,
.irq_banks = ARRAY_SIZE(sun50iw9p1_irq_bank_base),
.irq_bank_base = sun50iw9p1_irq_bank_base,
};
找到对应 pinctrl probe,这个函数比较重要,所以代码都贴了。
int sunxi_pinctrl_init(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc)
{
struct device_node *node = pdev->dev.of_node;
struct pinctrl_desc *pctrl_desc;
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
struct resource *res;
int i, ret, last_pin;
struct clk *clk;
struct sunxi_pinctrl_function *func;
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
if (!pctl)
return -ENOMEM;
platform_set_drvdata(pdev, pctl);
raw_spin_lock_init(&pctl->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//此处获取pinctrl的物理地址,在设备树pio: pinctrl@0300b000
pctl->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pctl->membase))
return PTR_ERR(pctl->membase);
pctl->dev = &pdev->dev;
pctl->desc = desc;
pctl->irq_array = devm_kcalloc(&pdev->dev,
IRQ_PER_BANK * pctl->desc->irq_banks,
sizeof(*pctl->irq_array),
GFP_KERNEL);
if (!pctl->irq_array)
return -ENOMEM;
pctl->wake_mask = devm_kcalloc(&pdev->dev,
pctl->desc->irq_banks,
sizeof(*pctl->wake_mask),
GFP_KERNEL);
if (!pctl->wake_mask)
return -ENOMEM;
pctl->cur_mask = devm_kcalloc(&pdev->dev,
pctl->desc->irq_banks,
sizeof(*pctl->cur_mask),
GFP_KERNEL);
if (!pctl->cur_mask)
return -ENOMEM;
/*
* alloc a temp buffer for count the pin
* functions,and then we shuold free it
*/
func = kzalloc(pctl->desc->npins * sizeof(*pctl->functions) * 7,
GFP_KERNEL);
if (!func)
return -ENOMEM;
/* count the pin functions for using in sunxi_pinctrl_build_state */
pctl->nfunctions = sunxi_pinctrl_count_function(pctl, func);
kfree(func);
ret = sunxi_pinctrl_build_state(pdev);
if (ret) {
dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
return ret;
}
pins = devm_kzalloc(&pdev->dev,
pctl->desc->npins * sizeof(*pins),
GFP_KERNEL);
if (!pins)
return -ENOMEM;
for (i = 0; i < pctl->desc->npins; i++)
pins[i] = pctl->desc->pins[i].pin;
pctrl_desc = devm_kzalloc(&pdev->dev,
sizeof(*pctrl_desc),
GFP_KERNEL);
if (!pctrl_desc)
return -ENOMEM;
pctrl_desc->name = dev_name(&pdev->dev);
pctrl_desc->owner = THIS_MODULE;
pctrl_desc->pins = pins;
pctrl_desc->npins = pctl->desc->npins;
pctrl_desc->confops = &sunxi_pconf_ops;
pctrl_desc->pctlops = &sunxi_pctrl_ops;
pmxops = devm_kmemdup(&pdev->dev, &sunxi_pmx_ops, sizeof(sunxi_pmx_ops),
GFP_KERNEL);
if (!pmxops)
return -ENOMEM;
if (desc->disable_strict_mode)
pmxops->strict = false;
pctrl_desc->pmxops = pmxops;
pctl->pctl_dev = pinctrl_register(pctrl_desc,
&pdev->dev, pctl);
if (IS_ERR(pctl->pctl_dev)) {
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
return PTR_ERR(pctl->pctl_dev);
}
pctl->regs_backup = devm_kzalloc(&pdev->dev,
pctl->desc->banks * BANK_MEM_SIZE + /* bank regs */
pctl->desc->irq_banks * IRQ_MEM_SIZE + /* irq regs */
4, /* power mode reg */
GFP_KERNEL);
pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
if (!pctl->chip) {
ret = -ENOMEM;
goto pinctrl_error;
}
last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
pctl->chip->owner = THIS_MODULE;
pctl->chip->request = gpiochip_generic_request,
pctl->chip->free = gpiochip_generic_free,
pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input,
pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output,
pctl->chip->get = sunxi_pinctrl_gpio_get,
pctl->chip->set = sunxi_pinctrl_gpio_set,
pctl->chip->set_debounce = sunxi_pinctrl_gpio_set_debounce,
pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate,
pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq,
pctl->chip->of_gpio_n_cells = 6,
pctl->chip->can_sleep = false,
pctl->chip->ngpio = round_up(last_pin + 1, PINS_PER_BANK) -
pctl->desc->pin_base;
pctl->chip->label = dev_name(&pdev->dev);
pctl->chip->parent = &pdev->dev;
pctl->chip->base = pctl->desc->pin_base;
ret = gpiochip_add(pctl->chip);
if (ret)
goto pinctrl_error;
for (i = 0; i < pctl->desc->npins; i++) {
const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),
pin->pin.number - pctl->desc->pin_base,
pin->pin.number, 1);
if (ret)
goto gpiochip_error;
}
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto gpiochip_error;
}
ret = clk_prepare_enable(clk);
if (ret)
goto gpiochip_error;
pctl->irq = devm_kcalloc(&pdev->dev,
pctl->desc->irq_banks,
sizeof(*pctl->irq),
GFP_KERNEL);
if (!pctl->irq) {
ret = -ENOMEM;
goto clk_error;
}
for (i = 0; i < pctl->desc->irq_banks; i++) {
pctl->irq[i] = platform_get_irq(pdev, i);
if (pctl->irq[i] < 0) {
ret = pctl->irq[i];
goto clk_error;
}
}
pctl->domain = irq_domain_add_linear(node,
pctl->desc->irq_banks * IRQ_PER_BANK,
&sunxi_pinctrl_irq_domain_ops,
pctl);
if (!pctl->domain) {
dev_err(&pdev->dev, "Couldn't register IRQ domain\n");
ret = -ENOMEM;
goto clk_error;
}
for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) {
int irqno = irq_create_mapping(pctl->domain, i);
irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip,
handle_edge_irq);
irq_set_chip_data(irqno, pctl);
}
for (i = 0; i < pctl->desc->irq_banks; i++) {
/* Mask and clear all IRQs before registering a handler */
unsigned bank_base = pctl->desc->irq_bank_base[i];
writel(0, pctl->membase +
sunxi_irq_ctrl_reg_from_bank(i, bank_base));
writel(0xffffffff, pctl->membase +
sunxi_irq_status_reg_from_bank(i, bank_base));
irq_set_chained_handler_and_data(pctl->irq[i],
sunxi_pinctrl_irq_handler,
pctl);
}
#ifdef CONFIG_DEBUG_FS
sunxi_pinctrl_debugfs();
#endif
sunxi_pinctrl_setup_debounce(pctl, node);
dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
return 0;
clk_error:
clk_disable_unprepare(clk);
gpiochip_error:
gpiochip_remove(pctl->chip);
pinctrl_error:
pinctrl_unregister(pctl->pctl_dev);
return ret;
}
有几个重要的部分
1、sunxi_pctrl_ops
static const struct pinctrl_ops sunxi_pctrl_ops = {
.dt_node_to_map = sunxi_pctrl_dt_node_to_map,
.dt_free_map = sunxi_pctrl_dt_free_map,
.get_groups_count = sunxi_pctrl_get_groups_count,
.get_group_name = sunxi_pctrl_get_group_name,
.get_group_pins = sunxi_pctrl_get_group_pins,
};
sunxi_pctrl_dt_node_to_map可以查到
sunxi_pctrl_parse_muxsel_prop
sunxi_pctrl_parse_function_prop
sunxi_pctrl_find_pins_prop
看其中一个函数,就明白了,这里解析到设备树的allwinner,muxsel的对应值。其他的设备树值也在其他几个函数解析,这里不展开说。
static int sunxi_pctrl_parse_muxsel_prop(struct device_node *node,
u32 *muxsel)
{
int ret;
ret = of_property_read_u32(node, "muxsel", muxsel);
if (!ret)
return ret;
return of_property_read_u32(node, "allwinner,muxsel", muxsel) ;
}
2、pctl->chip->set = sunxi_pinctrl_gpio_set
这一行定义了gpio设置的操作方法,其他的设置方向等也一起做了定义。
再往下看,终于看到操作寄存器了!
static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
unsigned offset, int value)
{
struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->parent);
u32 reg = sunxi_data_reg(offset);
u8 index = sunxi_data_offset(offset);
unsigned long flags;
u32 regval;
raw_spin_lock_irqsave(&pctl->lock, flags);
regval = readl(pctl->membase + reg);
if (value)
regval |= BIT(index);
else
regval &= ~(BIT(index));
writel(regval, pctl->membase + reg);//寄存器层面操作
raw_spin_unlock_irqrestore(&pctl->lock, flags);
}
看到这里的彦祖们应该能够理清楚pinctrl如何操作到寄存器的情况了,主要是通过pinctrl子系统找到设备树中的对应pin寄存器。这一部分只是举例说明,有兴趣的可以自行挖掘下。
总结
通过以上分析,主要是理清一下gpio口、pinctrl和设备树之间的关系,控制过程及原理,看问题可以更全面到位。