Linux 4.x之Gpio分析(一)Gpiolib庫1

下面的內(nèi)容均在imx6平臺上舉例钦听,這一次分析希望將整個GPIO子系統(tǒng)的所有細節(jié)整理清晰责语。

第一篇從gpiolib入手僻他,后面的邊分析邊寫.

開始之前給自己提幾個問題

  1. 驅(qū)動開發(fā)中的GPIO API 究竟是怎么實現(xiàn)的兽狭?
  2. GPIO的中斷又是怎么實現(xiàn)的萧朝?
  3. GPIO號和IRQ的號碼怎么映射的岔留?

1.芯片定義

我們在驅(qū)動程序中會用到gpio_request(x),這里的x便是gpio的編號,而GPIO通常會分組检柬,在原理圖經(jīng)常會看見GPIO2_5類似的標識贸诚,通常我們先會翻閱一下datesheet來了解一下這塊芯片的定義是如何。

在imx6芯片中的將GPIO分成了若干組厕吉,每組為32個管腳酱固。

我們簡單的閱讀一下芯片手冊:

IMX6的gpio控制結(jié)構(gòu):

iomux_block.jpg

我們的芯片會引出一堆引腳,而在芯片的內(nèi)部會集成很多control头朱,(我們稱這些control為block)其中的引腳可以功能復(fù)用运悲,比如說A1 A2腳支持I2C功能也支持GPIO功能,我可以將I2C1_control的管腳連接過來项钮,或者將GPIO1_1的管腳連接上來班眯,這個就是IOMUX的作用:提供PIN不同的功能切換。

所以我們在使用芯片之前會先根據(jù)主板的配置先將各個管腳配置為正確的功能的Pin.

寄存器:

  • Data register (GPIO_DR)
  • GPIO direction register (GPIO_GDIR)
  • Pad sample register (GPIO_PSR)
  • Interrupt control registers (GPIO_ICR1, GPIO_ICR2)
  • Interrupt mask register (GPIO_IMR)
  • Interrupt status register (GPIO_ISR)
  • GPIO edge select register (GPIO_EDEG_SEL )

簡單介紹一下

  • DR 當作為輸出時控制管腳高低電平
  • GDIR 控制管腳作為輸入還是輸出
  • PSR 作為輸出時獲取管腳的高低電平
  • ICR1 ICR2 是配置中斷的觸發(fā)方式 高/低電平觸發(fā) 上升/下降沿 觸發(fā)
  • IMR 中斷屏蔽寄存器
  • ISR 中斷狀態(tài)寄存器 哪個管腳觸發(fā)了中斷
  • EDEG_SEL 邊緣觸發(fā)模式(上/下沿都觸發(fā))

GPIO有7組,每組32個烁巫,最后一組14個

2.設(shè)備樹

我們了解了一下內(nèi)部的結(jié)構(gòu)署隘,接著就要開始看代碼了,在看代碼之前看看設(shè)備樹亚隙,才能定位到代碼在哪里磁餐。

這里只列出一個gpio-controller

 aliases {
                ethernet0 = &fec;
                can0 = &can1;
                can1 = &can2;
                gpio0 = &gpio1;
                gpio1 = &gpio2;
                .....
 }
gpio1: gpio@0209c000 {
                            compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
                            reg = <0x0209c000 0x4000>;
                            interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
                                         <0 67 IRQ_TYPE_LEVEL_HIGH>;
                            gpio-controller;
                            #gpio-cells = <2>;
                            interrupt-controller;
                            #interrupt-cells = <2>;
                    };

我們通過compatible的值可以找到對應(yīng)的驅(qū)動代碼路徑 driver/gpio/gpio-mxc.c

GPIO1控制寄存器的地址是0x0209c000,長度0x4000阿弃,使用66和67號中斷诊霹。

這里疑惑的地方是為什只用了兩個中斷號,而不是32個渣淳?

我們知道一個GPIO控制是通過總線連接到CPU柴墩,中斷連接到中斷控制器(GIC),那么中斷線太多就以為著GIC的數(shù)量也需要增加钟些,如果GPIO控制器內(nèi)部進行判斷是哪個管腳觸發(fā)的那么就可以避免GIC的數(shù)量。
而我們的芯片內(nèi)部只有一個GIC.

IRQ Source Interrupt Description
32 IOMUXC General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition whileboot.
33 DAP Debug Access Port interrupt request
98 GPIO1 Combined interrupt indication for GPIO1 signals 0 - 15.
99 GPIO1 Combined interrupt indication for GPIO1 signals 16 - 31.

這里中斷號的來源是芯片手冊扶歪,因為GIC的連接是這樣的胀茵,這里0-31是內(nèi)部使用结洼,32開始用作外設(shè)枷畏。
驅(qū)動中用編號0直接開始編號敷硅。

地址參考寄存器MAP

Absolute address Register name Widt (bits) Access Reset value
209_C000 GPIO data register (GPIO1_DR) 32 R/W 0000_0000h
209_C004 GPIO direction register (GPIO1_GDIR) 32 R/W 0000_0000h
209_C008 GPIO pad status register (GPIO1_PSR) 32 R 0000_0000h
209_C00C GPIO interrupt configuration register1 (GPIO1_ICR1) 32 R/W 0000_0000h
209_C010 GPIO interrupt configuration register2 (GPIO1_ICR2) 32 R/W 0000_0000h
209_C014 GPIO interrupt mask register (GPIO1_IMR) 32 R/W 0000_0000h
209_C018 GPIO interrupt status register (GPIO1_ISR) 32 w1c 0000_0000h
209_C018 GPIO interrupt status register (GPIO1_ISR) 32 w1c 0000_0000h
209_C01C GPIO edge select register (GPIO1_EDGE_SEL) 32 R/W 0000_0000h
20A_0000 GPIO data register (GPIO2_DR) 32 R/W 0000_0000h

3.代碼

從設(shè)備樹中我們知道了代碼的路徑位置,我們先看看gpio這個目錄的Makefile:

obj-$(CONFIG_GPIO_DEVRES)       += devres.o
obj-$(CONFIG_GPIOLIB)           += gpiolib.o
obj-$(CONFIG_GPIOLIB)           += gpiolib-legacy.o
obj-$(CONFIG_OF_GPIO)           += gpiolib-of.o
obj-$(CONFIG_GPIO_SYSFS)        += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI)         += gpiolib-acpi.o

# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC)      += gpio-generic.o

obj-$(CONFIG_GPIO_74X164)       += gpio-74x164.o
.....(省略一些無關(guān)緊要的內(nèi)容)
obj-$(CONFIG_GPIO_MXC)          += gpio-mxc.o


這里分三類:

  1. gpiolib的代碼
  2. gpio-generic代碼
  3. gpio特定平臺的代碼

代碼的結(jié)構(gòu):

GPIOLIB 調(diào)用 GPIO_GENRIC 調(diào)用 GPIO_MXC

接著我們開始分析代碼:

代碼主要講了初始化GPIO控制器和GPIO的中斷控制器鞠值,這里先分析gpio控制器和如何與gpiolib聯(lián)系

static const struct of_device_id mxc_gpio_dt_ids[] = {
        { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
        { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
        { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
        { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
        { /* sentinel */ }
};
enum mxc_gpio_hwtype {
        IMX1_GPIO,      /* runs on i.mx1 */
        IMX21_GPIO,     /* runs on i.mx21 and i.mx27 */
        IMX31_GPIO,     /* runs on i.mx31 */
        IMX35_GPIO,     /* runs on all other i.mx */
};


static struct platform_device_id mxc_gpio_devtype[] = {
        {
                .name = "imx1-gpio",
                .driver_data = IMX1_GPIO,
        }, {
                .name = "imx21-gpio",
                .driver_data = IMX21_GPIO,
        }, {
                .name = "imx31-gpio",
                .driver_data = IMX31_GPIO,
        }, {
                .name = "imx35-gpio",
                .driver_data = IMX35_GPIO,
        }, {
                /* sentinel */
        }
};

這里是為了兼容性考慮媚创,這個分支感覺寫的很有水準以后可以借鑒一下渗钉。

接著來看看porbe 的流程

static int mxc_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct mxc_gpio_port *port;
    struct resource *iores;
    int irq_base;
    int err;

    mxc_gpio_get_hw(pdev); //1

    port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
    if (!port)
        return -ENOMEM;
        
    //從設(shè)備樹中獲取寄存器地址彤恶,IOREMAP就是將物理地址轉(zhuǎn)化稱虛擬地址
    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    port->base = devm_ioremap_resource(&pdev->dev, iores);
    if (IS_ERR(port->base))
        return PTR_ERR(port->base);
    //從設(shè)備樹中獲取中斷號,上面也解釋了為啥是2個钞钙。
    port->irq_high = platform_get_irq(pdev, 1);
    port->irq = platform_get_irq(pdev, 0);
    if (port->irq < 0)
        return port->irq;

    //關(guān)中斷,清理中斷觸發(fā)狀態(tài)声离。
    //中斷屏蔽位的作用就是是否要關(guān)心這個中斷
    //中斷狀態(tài)位的作用就是芒炼,當中斷發(fā)生了,我們需要查一下是誰發(fā)生了中斷术徊。
    /* disable the interrupt and clear the status */
    writel(0, port->base + GPIO_IMR);
    writel(~0, port->base + GPIO_ISR);
    
    if (mxc_gpio_hwtype == IMX21_GPIO) {
        /*
         * Setup one handler for all GPIO interrupts. Actually setting
         * the handler is needed only once, but doing it for every port
         * is more robust and easier.
         */
        irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
    } else {
        /* setup one handler for each entry */
        //2
        //函數(shù)修改父中斷的流控函數(shù)本刽,當發(fā)生中斷,中斷控制器會去調(diào)用這個函數(shù)
        irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
        irq_set_handler_data(port->irq, port);
        if (port->irq_high > 0) {
            /* setup handler for GPIO 16 to 31 */
            irq_set_chained_handler(port->irq_high,
                        mx3_gpio_irq_handler);
            irq_set_handler_data(port->irq_high, port);
        }
    }
    
    //3
    err = bgpio_init(&port->bgc, &pdev->dev, 4,
             port->base + GPIO_PSR,
             port->base + GPIO_DR, NULL,
             port->base + GPIO_GDIR, NULL, 0);
    if (err)
        goto out_bgio;
//設(shè)置gpio和軟件中斷號映射的關(guān)系
    port->bgc.gc.to_irq = mxc_gpio_to_irq;
//設(shè)置gpio的編號基數(shù)
    port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
                         pdev->id * 32;
//將GPIO控制器計入到gpiolib驅(qū)動的鏈表中赠涮,也就是之后我們可以gpio_direction去操作io了
    err = gpiochip_add(&port->bgc.gc);
    if (err)
        goto out_bgpio_remove;

    irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
    if (irq_base < 0) {
        err = irq_base;
        goto out_gpiochip_remove;
    }
//向中斷控制器注冊并創(chuàng)建映射關(guān)系
    port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
                         &irq_domain_simple_ops, NULL);
    if (!port->domain) {
        err = -ENODEV;
        goto out_irqdesc_free;
    }
//初始化gpio中斷控制器
    /* gpio-mxc can be a generic irq chip */
    mxc_gpio_init_gc(port, irq_base);

    list_add_tail(&port->node, &mxc_gpio_ports);

    return 0;

out_irqdesc_free:
    irq_free_descs(irq_base, 32);
out_gpiochip_remove:
    gpiochip_remove(&port->bgc.gc);
out_bgpio_remove:
    bgpio_remove(&port->bgc);
out_bgio:
    dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
    return err;
}

先看 mxc_gpio_get_hw吧子寓,其實也就是初始化了兩個全局變量:

static enum mxc_gpio_hwtype mxc_gpio_hwtype;
static struct mxc_gpio_hwdata *mxc_gpio_hwdata;

static void mxc_gpio_get_hw(struct platform_device *pdev)
{
    const struct of_device_id *of_id =
            of_match_device(mxc_gpio_dt_ids, &pdev->dev);
    enum mxc_gpio_hwtype hwtype;

    if (of_id)
        pdev->id_entry = of_id->data;
    hwtype = pdev->id_entry->driver_data;

    if (mxc_gpio_hwtype) {
        /*
         * The driver works with a reasonable presupposition,
         * that is all gpio ports must be the same type when
         * running on one soc.
         */
        BUG_ON(mxc_gpio_hwtype != hwtype);
        return;
    }

    if (hwtype == IMX35_GPIO)
        mxc_gpio_hwdata = &imx35_gpio_hwdata;
    else if (hwtype == IMX31_GPIO)
        mxc_gpio_hwdata = &imx31_gpio_hwdata;
    else
        mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;

    mxc_gpio_hwtype = hwtype;
}

看一眼這兩個變量是何方神圣:


enum mxc_gpio_hwtype {
        IMX1_GPIO,      /* runs on i.mx1 */
        IMX21_GPIO,     /* runs on i.mx21 and i.mx27 */
        IMX31_GPIO,     /* runs on i.mx31 */
        IMX35_GPIO,     /* runs on all other i.mx */
};

struct mxc_gpio_hwdata {
        unsigned dr_reg;
        unsigned gdir_reg;
        unsigned psr_reg;
        unsigned icr1_reg;
        unsigned icr2_reg;
        unsigned imr_reg;
        unsigned isr_reg;
        int edge_sel_reg;
        unsigned low_level;
        unsigned high_level;
        unsigned rise_edge;
        unsigned fall_edge;
};

static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
        .dr_reg         = 0x00,//寫如內(nèi)容驅(qū)動IO
        .gdir_reg       = 0x04,//控制IO的方向 input output
        .psr_reg        = 0x08,//讀IO口電平
        .icr1_reg       = 0x0c,//中斷1 0-15
        .icr2_reg       = 0x10,//中斷2 16-31 控制電平觸發(fā)方式 00 low 01 hig 10 rise 11 fall
        .imr_reg        = 0x14,//中斷屏蔽位
        .isr_reg        = 0x18,//中斷狀態(tài)寄存器,中斷是否發(fā)生
        .edge_sel_reg   = 0x1c,//覆蓋ICR寄存器笋除,選擇EDGE方式(上升沿下降沿均觸發(fā)這個意思把斜友?)
        .low_level      = 0x00,
        .high_level     = 0x01,
        .rise_edge      = 0x02,
        .fall_edge      = 0x03,
};

原來不就是記錄一下控制寄存器是 imx35,和對應(yīng)的寄存器的偏移位置而已垃它。
我們先看port這個變量的結(jié)構(gòu)鲜屏,這里就是為了初始化這個結(jié)構(gòu)體。

struct mxc_gpio_port {
        struct list_head node;
        void __iomem *base; //gpio寄存器基地址
        int irq;            //gpio中斷號0-15用這個
        int irq_high;       //gpio中斷號16-31用這個
        struct irq_domain *domain; //irq_domain放到中斷里面再介紹吧
        struct bgpio_chip bgc;//這個結(jié)構(gòu)存放了寄存器的信息主要在gpio-genirc中用
        u32 both_edges;//雙邊沿觸發(fā)的標識位国拇,主要是為了兼容不帶邊雙沿觸發(fā)寄存器的芯片
};

struct bgpio_chip {
        struct gpio_chip gc;

        unsigned long (*read_reg)(void __iomem *reg);
        void (*write_reg)(void __iomem *reg, unsigned long data);

        void __iomem *reg_dat;
        void __iomem *reg_set;
        void __iomem *reg_clr;
        void __iomem *reg_dir;

        /* Number of bits (GPIOs): <register width> * 8. */
        int bits;

        /*
         * Some GPIO controllers work with the big-endian bits notation,
         * e.g. in a 8-bits register, GPIO7 is the least significant bit.
         */
        unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);


        /*
         * Used to lock bgpio_chip->data. Also, this is needed to keep
         * shadowed and real data registers writes together.
         */
        spinlock_t lock;

        /* Shadowed data register to clear/set bits safely. */
        unsigned long data;

        /* Shadowed direction registers to clear/set direction safely. */
        unsigned long dir;
};

bgpio_chip 我們可以在代碼bgpio_init那里讀代碼分析它洛史,暫時先不看了。

好酱吝!我們接著看probe中注釋2代碼:

static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
    u32 irq_stat;
    struct mxc_gpio_port *port = irq_get_handler_data(irq);
    struct irq_chip *chip = irq_get_chip(irq);

    chained_irq_enter(chip, desc);
    
   //讀中斷狀態(tài)寄存器確定哪個管腳發(fā)生中斷也殖,并且如何中斷屏蔽位為0,忽略這個中斷
    irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
    //調(diào)用中斷處理程序
    mxc_gpio_irq_handler(port, irq_stat);

    chained_irq_exit(chip, desc);
}

/* handle 32 interrupts in one status register */
static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
{
    while (irq_stat != 0) {
        //fls或去最高有效位(從低位往左數(shù)最后的有效bit位)
        //這里就是得到哪個管腳觸發(fā)的中斷务热,一一處理毕源,而且優(yōu)先級就是按照這個規(guī)則來了。
        int irqoffset = fls(irq_stat) - 1;

        //這里的做法是為了讓沒有雙邊沿觸發(fā)的芯片陕习,用輪流高低電平觸發(fā)的方式解決霎褐。
        //imx6芯片忽略就好,也看了半天才看清楚原來是這樣该镣。
        if (port->both_edges & (1 << irqoffset))
            mxc_flip_edge(port, irqoffset);
        //irq_find_mapping 這個再說吧冻璃,先記一下
        generic_handle_irq(irq_find_mapping(port->domain, irqoffset));

        irq_stat &= ~(1 << irqoffset);
    }
}

static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
{
    void __iomem *reg = port->base;
    u32 bit, val;
    int edge;

    /*0-15 ICR1 16-31 IRC2*/
    reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
    bit = gpio & 0xf;
    val = readl(reg);
    edge = (val >> (bit << 1)) & 3;
    val &= ~(0x3 << (bit << 1));
    if (edge == GPIO_INT_HIGH_LEV) {
        edge = GPIO_INT_LOW_LEV;
        pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
    } else if (edge == GPIO_INT_LOW_LEV) {
        edge = GPIO_INT_HIGH_LEV;
        pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
    } else {
        pr_err("mxc: invalid configuration for GPIO %d: %x\n",
               gpio, edge);
        return;
    }
    writel(val | (edge << (bit << 1)), reg);
}

//中斷處理程序看完了,我們接著看注釋3

    err = bgpio_init(&port->bgc, &pdev->dev, 4,
             port->base + GPIO_PSR,
             port->base + GPIO_DR, NULL,
             port->base + GPIO_GDIR, NULL, 0);
             
    int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
               unsigned long sz, void __iomem *dat, void __iomem *set,
               void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
               unsigned long flags)
{
        int ret;

        if (!is_power_of_2(sz))
                return -EINVAL;

        bgc->bits = sz * 8; //寄存器位數(shù) 4*8=32
      
        spin_lock_init(&bgc->lock);
        bgc->gc.dev = dev;
        bgc->gc.label = dev_name(dev);
        bgc->gc.base = -1;
        bgc->gc.ngpio = bgc->bits; //gpio數(shù)量
        bgc->gc.request = bgpio_request;

        //1
        ret = bgpio_setup_io(bgc, dat, set, clr);
      
        //2
        ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN,
                                    flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
        
        //3
        ret = bgpio_setup_direction(bgc, dirout, dirin);
        
        bgc->data = bgc->read_reg(bgc->reg_dat);
        if (bgc->gc.set == bgpio_set_set &&
                        !(flags & BGPIOF_UNREADABLE_REG_SET))
                bgc->data = bgc->read_reg(bgc->reg_set);
        if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR))
                bgc->dir = bgc->read_reg(bgc->reg_dir);

        return ret;

我們這里主要對gc這個參數(shù)進行了初始化损合,這個結(jié)構(gòu)gpio控制器省艳,最重要的參io的數(shù)量,對應(yīng)的操作函數(shù)。

struct gpio_chip {
        const char              *label;
        struct device           *dev;
        struct module           *owner;
        struct list_head        list;

        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_debounce)(struct gpio_chip *chip,
                                                unsigned offset,
                                                unsigned debounce);
                                                
                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_debounce)(struct gpio_chip *chip,
                                                unsigned offset,
                                                unsigned debounce);

        int                     (*to_irq)(struct gpio_chip *chip,
                                                unsigned offset);

        void                    (*dbg_show)(struct seq_file *s,
                                                struct gpio_chip *chip);
        int                     base;   
        u16                     ngpio;
        struct gpio_desc        *desc;
        const char              *const *names;
        bool                    can_sleep;
        bool                    irq_not_threaded;
        bool                    exported;

}

接著我們就看看三個setup函數(shù)如何去初始化這些接口.

static int bgpio_setup_io(struct bgpio_chip *bgc,
                          void __iomem *dat,
                          void __iomem *set,
                          void __iomem *clr)
{

        bgc->reg_dat = dat;
        if (!bgc->reg_dat)
                return -EINVAL;
        if (set && clr) {
                bgc->reg_set = set;
                bgc->reg_clr = clr;
                bgc->gc.set = bgpio_set_with_clear;
                bgc->gc.set_multiple = bgpio_set_multiple_with_clear;
        } else if (set && !clr) {
                bgc->reg_set = set;
                bgc->gc.set = bgpio_set_set;
                bgc->gc.set_multiple = bgpio_set_multiple_set;
        } else {
                bgc->gc.set = bgpio_set;
                bgc->gc.set_multiple = bgpio_set_multiple;
        }

        bgc->gc.get = bgpio_get;
        return 0;
}

配置了get和set函數(shù)嫁审,這里我們沒有clr寄存器

static int bgpio_setup_accessors(struct device *dev,
                 struct bgpio_chip *bgc,
                 bool bit_be,
                 bool byte_be)
{

    switch (bgc->bits) {
    case 8:
        bgc->read_reg   = bgpio_read8;
        bgc->write_reg  = bgpio_write8;
        break;
    case 16:
        if (byte_be) {
            bgc->read_reg   = bgpio_read16be;
            bgc->write_reg  = bgpio_write16be;
        } else {
            bgc->read_reg   = bgpio_read16;
            bgc->write_reg  = bgpio_write16;
        }
        break;
    case 32:
        if (byte_be) {
            bgc->read_reg   = bgpio_read32be;
            bgc->write_reg  = bgpio_write32be;
        } else {
            bgc->read_reg   = bgpio_read32;
            bgc->write_reg  = bgpio_write32;
        }
        break;
#if BITS_PER_LONG >= 64
    case 64:
        if (byte_be) {
            dev_err(dev,
                "64 bit big endian byte order unsupported\n");
            return -EINVAL;
        } else {
            bgc->read_reg   = bgpio_read64;
            bgc->write_reg  = bgpio_write64;
        }
        break;
#endif /* BITS_PER_LONG >= 64 */
    default:
        dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
        return -EINVAL;
    }

    bgc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask;

    return 0;
}

這里配置寄存器讀寫大小端和位數(shù)的問題跋炕,像bgpio_get等函數(shù)都是會調(diào)用對應(yīng)寄存器讀,這里很簡單的流程就不在追下去了律适,接著是最后一個setup

static int bgpio_setup_direction(struct bgpio_chip *bgc,
                 void __iomem *dirout,
                 void __iomem *dirin)
{
    if (dirout && dirin) {
        return -EINVAL;
    } else if (dirout) {
        bgc->reg_dir = dirout;
        bgc->gc.direction_output = bgpio_dir_out;
        bgc->gc.direction_input = bgpio_dir_in;
    } else if (dirin) {
        bgc->reg_dir = dirin;
        bgc->gc.direction_output = bgpio_dir_out_inv;
        bgc->gc.direction_input = bgpio_dir_in_inv;
    } else {
        bgc->gc.direction_output = bgpio_simple_dir_out;
        bgc->gc.direction_input = bgpio_simple_dir_in;
    }

    return 0;
}

這里我們只有一個dirout的寄存器辐烂,作為輸出只用將哪一個寄存器是能即可遏插。
這里init就分析完了,如何或去電平高低,設(shè)置io口方向纠修,和輸出電平高低的功能都OK了胳嘲。

最后probe 中一共最關(guān)鍵的函數(shù)gpiochip_add 我們再看看如何將GPIO控制器和gpiolib關(guān)聯(lián)。

關(guān)于中斷我們放到下一張講解扣草。

接著我們看一下gpiochip_add

int gpiochip_add(struct gpio_chip *chip)
{
        unsigned long   flags;
        int             status = 0;
        unsigned        id;
        int             base = chip->base;
        struct gpio_desc *descs;

        descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
        if (!descs)
                return -ENOMEM;

        spin_lock_irqsave(&gpio_lock, flags);

        if (base < 0) {
                base = gpiochip_find_base(chip->ngpio);
                if (base < 0) {
                        status = base;
                        spin_unlock_irqrestore(&gpio_lock, flags);
                        goto err_free_descs;
                }
                chip->base = base;
 }

        status = gpiochip_add_to_list(chip);
        if (status) {
                spin_unlock_irqrestore(&gpio_lock, flags);
                goto err_free_descs;
        }

        for (id = 0; id < chip->ngpio; id++) {
                struct gpio_desc *desc = &descs[id];

                desc->chip = chip;

                /* REVISIT: most hardware initializes GPIOs as inputs (often
                 * with pullups enabled) so power usage is minimized. Linux
                 * code should set the gpio direction first thing; but until
                 * it does, and in case chip->get_direction is not set, we may
                 * expose the wrong direction in sysfs.
                 */
                desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
        }

        chip->desc = descs;
         spin_unlock_irqrestore(&gpio_lock, flags);

#ifdef CONFIG_PINCTRL
        INIT_LIST_HEAD(&chip->pin_ranges);
#endif

        of_gpiochip_add(chip);
        acpi_gpiochip_add(chip);

        status = gpiochip_export(chip);
        if (status)
                goto err_remove_chip;

        pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
                chip->base, chip->base + chip->ngpio - 1,
                chip->label ? : "generic");

        return 0;


這里最關(guān)鍵的就是將控制器加入到鏈表中了牛,然后初始化了各個desc也就是管腳的信息
gpiochip_add_to_list(chip);

struct gpio_desc *gpio_to_desc(unsigned gpio)
{
        struct gpio_chip *chip;
        unsigned long flags;

        spin_lock_irqsave(&gpio_lock, flags);

        list_for_each_entry(chip, &gpio_chips, list) {
                if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
                        spin_unlock_irqrestore(&gpio_lock, flags);
                        return &chip->desc[gpio - chip->base];
                }
        }

        spin_unlock_irqrestore(&gpio_lock, flags);

        if (!gpio_is_valid(gpio))
                WARN(1, "invalid GPIO %d\n", gpio);

        return NULL;
}

static bool _gpiod_get_raw_value(const struct gpio_desc *desc)
{
        struct gpio_chip        *chip;
        bool value;
        int offset;

        chip = desc->chip;
        offset = gpio_chip_hwgpio(desc);
        value = chip->get ? chip->get(chip, offset) : false;
        trace_gpio_value(desc_to_gpio(desc), 1, value);
        return value;
}



int gpiod_get_value_cansleep(const struct gpio_desc *desc)
{
        int value;

        might_sleep_if(extra_checks);
        if (!desc)
                return 0;

        value = _gpiod_get_raw_value(desc);
        if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
                value = !value;

        return value;
}


這里發(fā)現(xiàn)gpio庫的變化非常大,類似gpio_direction_input的實現(xiàn)都消失了辰妙,并不了解為什么?

如果我們在驅(qū)動中需要獲得某個關(guān)鍵的電平:
int val =gpiod_get_value_cansleep(gpio_to_desc(37));

首先會通過鏈表查找到控制器鹰祸,然后調(diào)用控制器的get函數(shù)或去寄存器的值。

后來發(fā)現(xiàn)編譯時會自動生成gpiolib庫的頭文件:

include/asm-generic/gpio.h
static inline int gpio_direction_input(unsigned gpio)
{
        return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
        return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}

4.小結(jié)

開始提了3個問題

1. 驅(qū)動開發(fā)中的GPIO API 究竟是怎么實現(xiàn)的密浑?

這里我們花了大力氣閱讀了芯片手冊和源碼.重點關(guān)注了三個問題 gpio-mxc.c gpio-genric.c gpiolib.c

gpio-mxc 重點在維護的結(jié)構(gòu)體mxc_gpio_port福荸。

最重要的兩個結(jié)構(gòu):

  • struct bgpio_chip
  • struct gpio_chip gc;
struct mxc_gpio_port {
    struct list_head node;
    void __iomem *base;
    int irq;
    int irq_high;
    struct irq_domain *domain;
    struct bgpio_chip bgc;
    u32 both_edges;
};

struct bgpio_chip {
        struct gpio_chip gc;

        unsigned long (*read_reg)(void __iomem *reg);
        void (*write_reg)(void __iomem *reg, unsigned long data);
        void __iomem *reg_dat;
        void __iomem *reg_set;
        void __iomem *reg_clr;
        void __iomem *reg_dir;

        int bits;
        unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);
        spinlock_t lock;
        unsigned long data;
        unsigned long dir;
};

通過bgpio_init() 完成了對struct bgpio_chip的初始化,尤其是和gpiolib交互的重要數(shù)據(jù)結(jié)構(gòu)struct gpio_chip,至此完成對寄存器操作的抽象.

最后使用gpiolib_add()將我們的GPIO控制器加入到了gpiolib.c的控制器鏈表中肴掷,完成了gpiolib庫的封裝

2. GPIO的中斷又是怎么實現(xiàn)的敬锐?

我們分析一部分代碼,中斷函數(shù)的注冊和當中斷發(fā)生后如何區(qū)分是哪個IO口發(fā)生的中斷呆瞻,中斷控制器的實現(xiàn)我們還沒分析到台夺,相信后面分析會將這個流程連接起來。

3. GPIO號和IRQ的號碼怎么映射的痴脾?

中斷號和IO后映射問題,關(guān)鍵在函數(shù)irq_find_mapping中颤介,我們暫時還沒有跳轉(zhuǎn)過代碼去研究這一部分。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赞赖,一起剝皮案震驚了整個濱河市滚朵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌前域,老刑警劉巖辕近,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匿垄,居然都是意外死亡移宅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門椿疗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏峰,“玉大人,你說我怎么就攤上這事届榄∏城牵” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵铝条,是天一觀的道長靖苇。 經(jīng)常有香客問我席噩,道長,這世上最難降的妖魔是什么顾复? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鲁捏,結(jié)果婚禮上芯砸,老公的妹妹穿的比我還像新娘。我一直安慰自己给梅,他們只是感情好假丧,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著动羽,像睡著了一般包帚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上运吓,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天渴邦,我揣著相機與錄音,去河邊找鬼拘哨。 笑死谋梭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的倦青。 我是一名探鬼主播瓮床,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼产镐!你這毒婦竟也來了隘庄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤癣亚,失蹤者是張志新(化名)和其女友劉穎丑掺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體述雾,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡吼鱼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绰咽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菇肃。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖取募,靈堂內(nèi)的尸體忽然破棺而出琐谤,到底是詐尸還是另有隱情,我是刑警寧澤玩敏,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布斗忌,位于F島的核電站质礼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏织阳。R本人自食惡果不足惜眶蕉,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唧躲。 院中可真熱鬧造挽,春花似錦、人聲如沸弄痹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肛真。三九已至谐丢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚓让,已是汗流浹背乾忱。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留历极,地道東北人饭耳。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像执解,于是被迫代替她去往敵國和親寞肖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容