gpio子系统分析

参考博客:

https://blog.csdn.net/yangguoyu8023/article/details/121892008

 

gpiolib相关数据结构:

数据结构主要定义在 include/linux/gpio/driver.h 和 /drivers/gpio/gpiolib.h 中
/**
 * struct gpio_chip - abstract a GPIO controller
 * @label: a functional name for the GPIO device, such as a part
 *    number or the name of the SoC IP-block implementing it.
 * @gpiodev: the internal state holder, opaque struct
 * @parent: optional parent device providing the GPIOs
 * @owner: helps prevent removal of modules exporting active GPIOs
 * @request: optional hook for chip-specific activation, such as
 *    enabling module power and clock; may sleep
 * @free: optional hook for chip-specific deactivation, such as
 *    disabling module power and clock; may sleep
 * @get_direction: returns direction for signal "offset", 0=out, 1=in,
 *    (same as GPIOF_DIR_XXX), or negative error
 * @direction_input: configures signal "offset" as input, or returns error
 * @direction_output: configures signal "offset" as output, or returns error
 * @get: returns value for signal "offset", 0=low, 1=high, or negative error
 * @set: assigns output value for signal "offset"
 * @set_multiple: assigns output values for multiple signals defined by "mask"
 * @set_config: optional hook for all kinds of settings. Uses the same
 *    packed config format as generic pinconf.
 * @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
 *    implementation may not sleep
 * @dbg_show: optional routine to show contents in debugfs; default code
 *    will be used when this is omitted, but custom code can show extra
 *    state (such as pullup/pulldown configuration).
 * @base: identifies the first GPIO number handled by this chip;
 *    or, if negative during registration, requests dynamic ID allocation.
 *    DEPRECATION: providing anything non-negative and nailing the base
 *    offset of GPIO chips is deprecated. Please pass -1 as base to
 *    let gpiolib select the chip base in all possible cases. We want to
 *    get rid of the static GPIO number space in the long run.
 * @ngpio: the number of GPIOs handled by this controller; the last GPIO
 *    handled is (base + ngpio - 1).
 * @names: if set, must be an array of strings to use as alternative
 *      names for the GPIOs in this chip. Any entry in the array
 *      may be NULL if there is no alias for the GPIO, however the
 *      array must be @ngpio entries long.  A name can include a single printk
 *      format specifier for an unsigned int.  It is substituted by the actual
 *      number of the gpio.
 * @can_sleep: flag must be set iff get()/set() methods sleep, as they
 *    must while accessing GPIO expander chips over I2C or SPI. This
 *    implies that if the chip supports IRQs, these IRQs need to be threaded
 *    as the chip access may sleep when e.g. reading out the IRQ status
 *    registers.
 * @read_reg: reader function for generic GPIO
 * @write_reg: writer function for generic GPIO
 * @pin2mask: some generic GPIO controllers work with the big-endian bits
 *    notation, e.g. in a 8-bits register, GPIO7 is the least significant
 *    bit. This callback assigns the right bit mask.
 * @reg_dat: data (in) register for generic GPIO
 * @reg_set: output set register (out=high) for generic GPIO
 * @reg_clr: output clear register (out=low) for generic GPIO
 * @reg_dir: direction setting register for generic GPIO
 * @bgpio_bits: number of register bits used for a generic GPIO i.e.
 *    <register width> * 8
 * @bgpio_lock: used to lock chip->bgpio_data. Also, this is needed to keep
 *    shadowed and real data registers writes together.
 * @bgpio_data:    shadowed data register for generic GPIO to clear/set bits
 *    safely.
 * @bgpio_dir: shadowed direction register for generic GPIO to clear/set
 *    direction safely.
 * @irqchip: GPIO IRQ chip impl, provided by GPIO driver
 * @irqdomain: Interrupt translation domain; responsible for mapping
 *    between GPIO hwirq number and linux irq number
 * @irq_base: first linux IRQ number assigned to GPIO IRQ chip (deprecated)
 * @irq_handler: the irq handler to use (often a predefined irq core function)
 *    for GPIO IRQs, provided by GPIO driver
 * @irq_default_type: default IRQ triggering type applied during GPIO driver
 *    initialization, provided by GPIO driver
 * @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number,
 *    provided by GPIO driver for chained interrupt (not for nested
 *    interrupts).
 * @irq_nested: True if set the interrupt handling is nested.
 * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
 *    bits set to one
 * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
 *    be included in IRQ domain of the chip
 * @lock_key: per GPIO IRQ chip lockdep class
 *
 * A gpio_chip can help platforms abstract various sources of GPIOs so
 * they can all be accessed through a common programing interface.
 * Example sources would be SOC controllers, FPGAs, multifunction
 * chips, dedicated GPIO expanders, and so on.
 *
 * Each chip controls a number of signals, identified in method calls
 * by "offset" values in the range 0..(@ngpio - 1).  When those signals
 * are referenced through calls like gpio_get_value(gpio), the offset
 * is calculated by subtracting @base from the gpio number.
 */
struct gpio_chip {
    const char        *label;
    struct gpio_device    *gpiodev;
    struct device        *parent;
    struct module        *owner;
 
    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,
                        unsigned offset);
    int            (*direction_output)(struct gpio_chip *chip,
                        unsigned offset, int value);
    int            (*get)(struct gpio_chip *chip,
                        unsigned offset);
    void            (*set)(struct gpio_chip *chip,
                        unsigned offset, int value);
    void            (*set_multiple)(struct gpio_chip *chip,
                        unsigned long *mask,
                        unsigned long *bits);
    int            (*set_config)(struct gpio_chip *chip,
                          unsigned offset,
                          unsigned long config);
    int            (*to_irq)(struct gpio_chip *chip,
                        unsigned offset);
 
    void            (*dbg_show)(struct seq_file *s,
                        struct gpio_chip *chip);
    int            base;
    u16            ngpio;
    const char        *const *names;
    bool            can_sleep;
 
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
    unsigned long (*read_reg)(void __iomem *reg);
    void (*write_reg)(void __iomem *reg, unsigned long data);
    unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
    void __iomem *reg_dat;    // 获取value
    void __iomem *reg_set;    // 设置value
    void __iomem *reg_clr;    // 设置清除value
    void __iomem *reg_dir;    // direction
    int bgpio_bits;
    spinlock_t bgpio_lock;
    unsigned long bgpio_data;
    unsigned long bgpio_dir;
#endif
 
#ifdef CONFIG_GPIOLIB_IRQCHIP
    /*
     * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
     * to handle IRQs for most practical cases.
     */
    struct irq_chip        *irqchip;
    struct irq_domain    *irqdomain;
    unsigned int        irq_base;
    irq_flow_handler_t    irq_handler;
    unsigned int        irq_default_type;
    unsigned int        irq_chained_parent;
    bool            irq_nested;
    bool            irq_need_valid_mask;
    unsigned long        *irq_valid_mask;
    struct lock_class_key    *lock_key;
#endif
 
#if defined(CONFIG_OF_GPIO)
    /*
     * If CONFIG_OF is enabled, then all GPIO controllers described in the
     * device tree automatically may have an OF translation
     */
 
    /**
     * @of_node:
     *
     * Pointer to a device tree node representing this GPIO controller.
     */
    struct device_node *of_node;
 
    /**
     * @of_gpio_n_cells:
     *
     * Number of cells used to form the GPIO specifier.
     */
    unsigned int of_gpio_n_cells;
 
    /**
     * @of_xlate:
     *
     * Callback to translate a device tree GPIO specifier into a chip-
     * relative GPIO number and flags.
     */
    int (*of_xlate)(struct gpio_chip *gc,
            const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
重点:
void __iomem *reg_dat;    // 获取value
void __iomem *reg_set;    // 设置value
void __iomem *reg_clr;    // 设置清除value
void __iomem *reg_dir;    // direction
gpio_chip此数据结构,很多结构体指针,此结构是为了抽象GPIO的所有操作。
gpio_chip的抽象,其实就是对GPIO一组bank的抽象,通常在硬件上,一个芯片对于IO口来说,分为了很多组bank,每个bank分成了N组gpio,比如Bst a1000芯片,gpio_chip的抽象就是一个synopsys gpio控制器,控制四组bank,每组bank控制32个gpio。
 
gpio_device结构体:
如果说gpio_chip是对一个bank的gpio硬件的具体抽象的话,那么gpio_device就是软件层面上对一个bank的gpio进行管理的单元
 
gpio_desc结构体代表一个gpio的属性
 
注册GPIO资源,gpiochip_add_data或者devm_gpiochip_add_data
该函数传入的是gpio_chip,也就是一个bank的描述,那么实际上,需要kernel管理的gpio bank的部分,都需要调用到这个接口,注册进入内核
static int dwapb_gpio_probe(struct platform_device *pdev)
{
        unsigned int i;
        struct dwapb_gpio *gpio;
        int err;
        struct device *dev = &pdev->dev;
        struct dwapb_platform_data *pdata = dev_get_platdata(dev);

        if (!pdata) {
                pdata = dwapb_gpio_get_pdata(dev);    // 分配内存,解析设备树,获取中断
                if (IS_ERR(pdata))
                        return PTR_ERR(pdata);
        }

        if (!pdata->nports)
                return -ENODEV;

        gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
        if (!gpio)
                return -ENOMEM;

        gpio->dev = &pdev->dev;
        gpio->nr_ports = pdata->nports;

        err = dwapb_get_reset(gpio);    // 复位
        if (err)
                return err;

        gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
                                   sizeof(*gpio->ports), GFP_KERNEL);
        if (!gpio->ports)
                return -ENOMEM;

        gpio->regs = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(gpio->regs))
                return PTR_ERR(gpio->regs);

        err = dwapb_get_clks(gpio);    // 此处调用devm_clk_bulk_get_optional和clk_bulk_prepare_enable来获取和使能多组clk
        if (err)
                return err;

        /*xiangning.he 2020.4.20*/
        dev_info(&pdev->dev, "gpio clock frequency :%ld\n", clk_get_rate(gpio->clks[0].clk));

        gpio->flags = (uintptr_t)device_get_match_data(dev);    // 获取compatible的data数据

        for (i = 0; i < gpio->nr_ports; i++) {    // nr_ports表示组数,为4
                err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);    // 调用devm_gpiochip_add_data向内核注册gpio_chip
                if (err)
                        return err;
        }

        platform_set_drvdata(pdev, gpio);    // 设置设备私有数据,可以使用platform_get_drvdata/dev_get_drvdata来进行获取私有数据

        return 0;
}

 

dwapb_gpio_get_pdata
static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
{
        struct fwnode_handle *fwnode;
        struct dwapb_platform_data *pdata;
        struct dwapb_port_property *pp;
        int nports;if (pp->idx == 0)
        int i;

        nports = device_get_child_node_count(dev);    // 获取bank组数
        if (nports == 0)
                return ERR_PTR(-ENODEV);

        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return ERR_PTR(-ENOMEM);

        pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
        if (!pdata->properties)
                return ERR_PTR(-ENOMEM);

        pdata->nports = nports;

        i = 0;
        device_for_each_child_node(dev, fwnode)  {    // 利用fwnode循环遍历每组bank gpio
                pp = &pdata->properties[i++];
                pp->fwnode = fwnode;

                if (fwnode_property_read_u32(fwnode, "reg", &pp->idx) ||
                    pp->idx >= DWAPB_MAX_PORTS) {
                        dev_err(dev,
                                "missing/invalid port index for port%d\n", i);
                        fwnode_handle_put(fwnode);
                        return ERR_PTR(-EINVAL);
                }

                if (fwnode_property_read_u32(fwnode, "ngpios", &pp->ngpio) &&
                    fwnode_property_read_u32(fwnode, "snps,nr-gpios", &pp->ngpio)) {
                        dev_info(dev,
                                 "failed to get number of gpios for port%d\n",
                                 i);
                        pp->ngpio = DWAPB_MAX_GPIOS;
                }

                pp->irq_shared  = false;    // 设置私有gpio中断,说明每组32个gpio只有一个能够在硬件上显示为中断gpio,连接gic
                pp->gpio_base   = -1;

                /*
                 * Only port A can provide interrupts in all configurations of
                 * the IP.
                 */
                if (pp->idx == 0)
                        dwapb_get_irq(dev, fwnode, pp);
        }

        return pdata;
}


----
static void dwapb_get_irq(struct device *dev, struct fwnode_handle *fwnode,
                          struct dwapb_port_property *pp)
{
        struct device_node *np = NULL;
        int irq = -ENXIO, j;

        if (fwnode_property_read_bool(fwnode, "interrupt-controller"))
                np = to_of_node(fwnode);    // fwnode转换成device_node

        for (j = 0; j < pp->ngpio; j++) {
                if (np)
                        irq = of_irq_get(np, j);
                else if (has_acpi_companion(dev))
                        irq = platform_get_irq_optional(to_platform_device(dev), j);
                if (irq > 0)
                        pp->irq[j] = irq;    // 实际上就一个irq
        }
}
 
dwapb_gpio_add_port
static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
                               struct dwapb_port_property *pp,
                               unsigned int offs)
{
        struct dwapb_gpio_port *port;
        void __iomem *dat, *set, *dirout;
        int err;

        port = &gpio->ports[offs];
        port->gpio = gpio;
        port->idx = pp->idx;

#ifdef CONFIG_PM_SLEEP
        port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL);
        if (!port->ctx)
                return -ENOMEM;
#endif

        dat = gpio->regs + GPIO_EXT_PORTA + pp->idx * GPIO_EXT_PORT_STRIDE;    // 读取状态寄存器
        set = gpio->regs + GPIO_SWPORTA_DR + pp->idx * GPIO_SWPORT_DR_STRIDE;
    // 设置状态寄存器
        dirout = gpio->regs + GPIO_SWPORTA_DDR + pp->idx * GPIO_SWPORT_DDR_STRIDE;
    // 方向寄存器

        /* This registers 32 GPIO lines per port */
        err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
                         NULL, 0);    // 通知kernel,后面通过这些寄存器来设置gpio的状态、gpio的值、gpio的方向等
        if (err) {
                dev_err(gpio->dev, "failed to init gpio chip for port%d\n",
                        port->idx);
                return err;
        }

#ifdef CONFIG_OF_GPIO
        port->gc.of_node = to_of_node(pp->fwnode);
#endif
        port->gc.ngpio = pp->ngpio;
        port->gc.base = pp->gpio_base;

        /* Only port A support debounce */
        if (pp->idx == 0)
                port->gc.set_config = dwapb_gpio_set_config;

        /* Only port A can provide interrupts in all configurations of the IP */
        if (pp->idx == 0)
                dwapb_configure_irqs(gpio, port, pp);    // 第一组配置irq

        err = devm_gpiochip_add_data(gpio->dev, &port->gc, port);    // 向内核注册gpio_chip
        if (err) {
                dev_err(gpio->dev, "failed to register gpiochip for port%d\n",
                        port->idx);
                return err;
        }
        return 0;
}

 

dwapb_configure_irqs
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
                                 struct dwapb_gpio_port *port,
                                 struct dwapb_port_property *pp)
{
        struct dwapb_gpio_port_irqchip *pirq;
        struct gpio_chip *gc = &port->gc;
        struct gpio_irq_chip *girq;
        int err;

        pirq = devm_kzalloc(gpio->dev, sizeof(*pirq), GFP_KERNEL);
        if (!pirq)
                return;

        if (dwapb_convert_irqs(pirq, pp)) {    // 确保是否有irq
                dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx);
                goto err_kfree_pirq;
        }

        girq = &gc->irq;
        girq->handler = handle_bad_irq;
        girq->default_type = IRQ_TYPE_NONE;

        port->pirq = pirq;
        pirq->irqchip.name = DWAPB_DRIVER_NAME;
        pirq->irqchip.irq_ack = dwapb_irq_ack;    // 中断确认的处理逻辑
        pirq->irqchip.irq_mask = dwapb_irq_mask;
    // 中断屏蔽的处理逻辑
        pirq->irqchip.irq_unmask = dwapb_irq_unmask;
    // 中断解除屏蔽的处理逻辑
        pirq->irqchip.irq_set_type = dwapb_irq_set_type;    // 设置中断触发类型,falling,raising,both,none
        pirq->irqchip.irq_enable = dwapb_irq_enable;    // 中断使能的处理逻辑
        pirq->irqchip.irq_disable = dwapb_irq_disable;
    // 中断禁用的处理逻辑
#ifdef CONFIG_PM_SLEEP
        pirq->irqchip.irq_set_wake = dwapb_irq_set_wake;
#endif

        if (!pp->irq_shared) {
                girq->num_parents = pirq->nr_irqs;
                girq->parents = pirq->irq;
                girq->parent_handler_data = gpio;
                girq->parent_handler = dwapb_irq_handler;
        } else {
                /* This will let us handle the parent IRQ in the driver */
                girq->num_parents = 0;
                girq->parents = NULL;
                girq->parent_handler = NULL;

                /*
                 * Request a shared IRQ since where MFD would have devices
                 * using the same irq pin
                 */
                err = devm_request_irq(gpio->dev, pp->irq[0],
                                       dwapb_irq_handler_mfd,
                                       IRQF_SHARED, DWAPB_DRIVER_NAME, gpio);    // 配置成gpio私有中断
                if (err) {
                        dev_err(gpio->dev, "error requesting IRQ\n");
                        goto err_kfree_pirq;
                }
        }

        girq->chip = &pirq->irqchip;

        return;

err_kfree_pirq:
        devm_kfree(gpio->dev, pirq);
}
posted @ 2024-03-23 23:04  lethe1203  阅读(94)  评论(0编辑  收藏  举报