pinctrl子系统 - pinctrl与gpio之间的耦合(六)

1,gpio subsystem和pinctrl subsystem的关系

作为软件工程师,我们期望的硬件设计应该如下图所示:

GPIO的HW block应该和其他功能复用的block是对等关系的,它们共同输入到一个复用器block,这个block的寄存器控制哪一个功能电路目前是active的。pin configuration是全局的,不论哪种功能是active的,都可以针对pin进行电气特性的设定。这样的架构下,上图中红色边框的三个block是完全独立的HW block,其控制寄存器在SOC datasheet中应该是分成三个章节描述,同时,这些block的寄存器应该分别处于不同的地址区间。

对于软件工程师,我们可以让pin control subsystem和GPIO subsystem完全独立,各自进行初始化,各自映射自己的寄存器地址空间,对于pin control subsystem而言,GPIO和其他的HW block没有什么不同,都是使用自己提供服务的一个软件模块而已。然而实际上SOC的设计并非总是向软件工程师期望的那样,有的SOC的设计框架图如下:

这时候,GPIO block是alway active的,而红色边框的三个block是紧密的捆绑在一起,它们的寄存器占据了一个memory range(datasheet中用一个章节描述这三个block)。这时候,对于软件工程师来说就有些纠结了,本来不属于我的GPIO控制也被迫要参与进来。这时候,硬件寄存器的控制都是pin controller来处理,GPIO相关的操作都要经过pin controller driver,这时候,pin controller driver要作为GPIO driver的back-end出现。

2,为什么是这种关系

任何一个gpio chip,在使用GPIO的时候(通常是gpio subsystem的consumer申请GPIO资源的时候),都需要向系统的pinctrl subsystem申请管脚,并将管脚配置为GPIO功能。

思路是简单、直接的,但实际操作起来,却有点棘手,下面以一个最简单的例子说明:

假设某一个gpio chip只包括2个gpio,这两个gpio分别和uart进行功能复用。

如果这两个管脚是同时控制的,要么是gpio,要么是uart,就很好处理了,按照pinctrl subsystem的精神,抽象出两个function:gpio和uart,gpio chip在使用gpio功能的时候,调用pinctrl set state,将它们切换为gpio即可。

但是,如果这两个gpio可以分开控制(很多硬件都是这样的设计的),麻烦就出现了,每个gpio要单独抽象为一个function,因此我们可以抽象出3个function:gpio1、gpio2和uart。

然后考虑一下一个包含32个gpio的chip(一般硬件的gpio bank都是32个),如果它们都可以单独控制,则会出现32个function。而系统又不止有一个chip,灾难就发生了,我们的device tree文件将会被一坨坨的gpio functions撑爆!

规范是我们追求的目标,但有限度,不能让上面的事情发生,怎么办呢?在pinctrl subsystem的标准框架上打个洞,只要碰到这种情况,直接就走后门就是了。

3,pinctrl中和gpio有关的后门

后门是什么呢?其实很简单,参考下面的API:

static inline int pinctrl_request_gpio(unsigned gpio) ;
static inline void pinctrl_free_gpio(unsigned gpio) ;
static inline int pinctrl_gpio_direction_input(unsigned gpio);
static inline int pinctrl_gpio_direction_output(unsigned gpio);


当gpio driver需要使用某个管脚的时候,直接调用pinctrl_request_gpio,向pinctrl subsystem申请。
pinctrl subsystem会维护一个gpio number到pin number的map,将gpio subsystem传来的gpio number转换为pin number之后,调用struct pinmux_ops中有关的回调函数即可:
struct pinmux_ops {
        ...
        int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
                                     struct pinctrl_gpio_range *range,
                                     unsigned offset);
        void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
                                    struct pinctrl_gpio_range *range,
                                    unsigned offset);
        int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
                                    struct pinctrl_gpio_range *range,
                                    unsigned offset,
                                    bool input);
};
对gpio driver来说,要做的事情就是提供gpio number到pin number的map。
而pinctrl subsystem呢,做自己分内的事情即可:管理系统的pin资源,并根据gpio subsystem的请求,将某些pin设置为GPIO功能。

4,gpio range----gpio number到pin number的map

4.1 gpio range

那么,怎么提供gpio number和pin number的map呢?是通过一个名称为gpio range的数据结构(pinctrl subsystem提供的),如下:

/* include/linux/pinctrl/pinctrl.h */

/**
  * struct pinctrl_gpio_range - each pin controller can provide subranges of
  * the GPIO number space to be handled by the controller
  * @node: list node for internal use
  * @name: a name for the chip in this range
  * @id: an ID number for the chip in this range
  * @base: base offset of the GPIO range
  * @pin_base: base pin number of the GPIO range if pins == NULL
  * @pins: enumeration of pins in GPIO range or NULL
  * @npins: number of pins in the GPIO range, including the base number
  * @gc: an optional pointer to a gpio_chip
  */
struct pinctrl_gpio_range {
        struct list_head node;
        const char *name;
        unsigned int id;
         unsigned int base;
        unsigned int pin_base;
        unsigned const *pins;
        unsigned int npins;
        struct gpio_chip *gc;
};

该数据结构很容易理解,总结来说,就是:gpio controller(gc)中的gpio(base)到gpio(base + npins - 1),和pin controller中的pin(pin_base)到pin(pin_base + npins - 1)是一一对应的。

有了这个对应关系之后,pinctrl subsystem就可以将任意一个gpio controller中的gpio number转换为相应的pin controller中的pin number。

4.2 gpio number到pin number的map,代码流程
int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data)
    msm_gpio_init(pctrl);
        gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio);

gpiochip_add_pin_range:

int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
               unsigned int gpio_offset, unsigned int pin_offset,
               unsigned int npins)
{
    struct gpio_pin_range *pin_range;
    struct gpio_device *gdev = gc->gpiodev;
    int ret;

    //分配 struct gpio_pin_range内存
    pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
    if (!pin_range) {
        chip_err(gc, "failed to allocate pin ranges\n");
        return -ENOMEM;
    }

    /* Use local offset as range ID */
    //初始化pin_range,资源来自于gpio_chip和gpio_device
    pin_range->range.id = gpio_offset;
    pin_range->range.gc = gc;
    pin_range->range.name = gc->label;
    pin_range->range.base = gdev->base + gpio_offset;
    pin_range->range.pin_base = pin_offset;
    pin_range->range.npins = npins;
    pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
            &pin_range->range);
    if (IS_ERR(pin_range->pctldev)) {
        ret = PTR_ERR(pin_range->pctldev);
        chip_err(gc, "could not create pin range\n");
        kfree(pin_range);
        return ret;
    }
    chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
         gpio_offset, gpio_offset + npins - 1,
         pinctl_name,
         pin_offset, pin_offset + npins - 1);

    //将pin_range node挂到gpio_deviced的pin_ranges链表
    list_add_tail(&pin_range->node, &gdev->pin_ranges);

    return 0;
}

pinctrl_find_and_add_gpio_range,register a GPIO range for a controller:

struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
        struct pinctrl_gpio_range *range)
{
    struct pinctrl_dev *pctldev;

    pctldev = get_pinctrl_dev_from_devname(devname);

    /*
     * If we can't find this device, let's assume that is because
     * it has not probed yet, so the driver trying to register this
     * range need to defer probing.
     */
    if (!pctldev) {
        return ERR_PTR(-EPROBE_DEFER);
    }
    pinctrl_add_gpio_range(pctldev, range);

    return pctldev;
}

void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
                struct pinctrl_gpio_range *range)
{
    mutex_lock(&pctldev->mutex);
    //将pinctrl_gpio_range node挂到pinctrl_dev的gpio_ranges链表
    list_add_tail(&range->node, &pctldev->gpio_ranges);
    mutex_unlock(&pctldev->mutex);
}
4.3 gpio-ranges debug info
static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
{
    struct pinctrl_dev *pctldev = s->private;
    struct pinctrl_gpio_range *range;

    seq_puts(s, "GPIO ranges handled:\n");

    mutex_lock(&pctldev->mutex);

    /* Loop over the ranges */
    list_for_each_entry(range, &pctldev->gpio_ranges, node) {
        if (range->pins) {
            int a;
            seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
                range->id, range->name,
                range->base, (range->base + range->npins - 1));
            for (a = 0; a < range->npins - 1; a++)
                seq_printf(s, "%u, ", range->pins[a]);
            seq_printf(s, "%u}\n", range->pins[a]);
        }
        else
            seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
                range->id, range->name,
                range->base, (range->base + range->npins - 1),
                range->pin_base,
                (range->pin_base + range->npins - 1));
    }

    mutex_unlock(&pctldev->mutex);

    return 0;
}
DEFINE_SHOW_ATTRIBUTE(pinctrl_gpioranges);
cat /sys/kernel/debug/pinctrl/f000000.pinctrl/gpio-ranges

GPIO ranges handled:
0: f000000.pinctrl GPIOS [341 - 511] PINS [0 - 170]

5,gpio_request()流程

gpio_request()
    *desc = gpio_to_desc(gpio);
    gpiod_request(desc, label);
        gpiod_request_commit(desc, label);
            offset = gpio_chip_hwgpio(desc);
            gc->request(gc, offset);
                pl061->gc.request = gpiochip_generic_request;
                    pinctrl_gpio_request(gc->gpiodev->base + offset); //drivers/pinctrl/core.c
                        pin = gpio_to_pin(range, gpio);
                        pinmux_request_gpio(pctldev, range, pin, gpio);
                            pin_request(pctldev, pin, owner, range);
                                ops->gpio_request_enable(pctldev, gpio_range, pin);
                                    .gpio_request_enable    = msm_pinmux_request_gpio,
                                        msm_pinmux_set_mux(pctldev, g->funcs[pctrl->soc->gpio_func], offset);

可以看到GPIO子系统是通过pinctrl子系统来实现的。

参考链接:

linux内核中的GPIO系统之(5):gpio subsysem和pinctrl subsystem之间的耦合

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值