第20章 Linux芯片级移植及底层驱动之GPIO驱动

20.6 GPIO驱动

    在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的用于描述底层GPIO控制器的gpio_chip结构体,并要求具体的SoC实现gpio_chip结构体的成员函数,最后通过gpiochip_add()注册gpio_chip。GPIO驱动可以存在于drivers/gpio目录中,但是在GPIO兼有多种功能且需要复杂配置的情况下,GPIO的驱动部分往往直接移到drivers/pinctrl目录下并连同pinmux一起实现,而不存在于drivers/gpio目录中。

    GPIO控制器的gpio_chip结构体封装了底层硬件的GPIO enable()/disable()等操作,其定义如代码清单20.15所示。

代码清单20.15 描述GPIO控制器的gpio_chip结构体

include/linux/gpio/driver.h

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);
        int                     (*get_multiple)(struct gpio_chip *chip, unsigned long *mask,  unsigned long *bits);
        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);
        bool be_bits;
        void __iomem *reg_dat;
        void __iomem *reg_set;
        void __iomem *reg_clr;
        void __iomem *reg_dir;
        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.
         */

        /**
         * @irq:
         *
         * Integrates interrupt chip functionality with the GPIO chip. Can be
         * used to handle IRQs for most practical cases.
         */
        struct gpio_irq_chip irq;
#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
};

    通过这层封装,每个具体的要用到GPIO的设备驱动都使用通用的GPIO API来操作GPIO,这些API主要用于GPIO的申请、释放和设置:

  asm-generic/gpio.h

int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_set_debounce(unsigned gpio, unsigned debounce);
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);

void devm_gpio_free(struct device *dev, unsigned int gpio);

注意:

      内核中针对内存、IRQ、时钟、GPIO、pinctrl、Regulator都有以devm_开头的API,使用这部分API的时候,内核会有类似于Java的资源自动回收机制,在代码中进行出错处理时,无须释放相关的资源。

    对于GPIO,内核会创建/sys节点/sys/class/gpio/gpioN/,通过这个节点可以echo值来改变GPIO的方向、设置并获取GPIO的值。

    在拥有设备树支持的情况下,可以通过设备树来描述某GPIO控制器提供的GPIO引脚被具体设备使用的情况。在GPIO控制器对应的节点中,需定义#gpio-cells和gpio-controller属性,具体的设备节点则通过xxx-gpios属性来引用GPIO控制器节点及GPIO引脚。

    如VEXPRESS电路板DT文件arch/arm/boot/dts/vexpress-v2m.dtsi中有如下GPIO控制器节点:

    v2m_sysreg: sysreg@00000 {
            compatible = "arm,vexpress-sysreg";
            reg = <0x00000 0x1000>;
            gpio-controller;
            #gpio-cells = <2>;
    };

    VEXPRESS电路板上的MMC控制器会使用该节点GPIO控制器提供的GPIO引脚,则具体的mmci@05000设备节点会通过-gpios属性引用GPIO

    mmci@05000 {
            compatible = "arm,pl180", "arm,primecell";
            reg = <0x05000 0x1000>;
            interrupts = <9 10>;
            cd-gpios = <&v2m_sysreg 0 0>;
            wp-gpios = <&v2m_sysreg 1 0>;
            …
};

备注:

    其中cd-gpios用于SD/MMC卡的探测,而wp-gpios用于写保护,MMC主机控制器驱动会通过如下方法获取这两个GPIO,见于drivers/mmc/host/mmci.c:

static void mmci_dt_populate_generic_pdata(struct device_node *np, struct mmci_platform_data *pdata)
{
                                ...
        pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
        pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);

                                ...
}

你可能感兴趣的:(Linux驱动开发)