GPIOLIB实现(一)

驱动开发中最基础的就是控制GPIO来驱动硬件,我们列举一下常用的API.

int  gpio_is_valid(int number);
int  gpio_request()
int  gpio_direction_input(unsigned gpio);
int  gpio_direction_output(unsigned gpio, int value);
int  gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
int  gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
void gpio_free(unsigned gpio);
int  gpio_to_irq(unsigned gpio);
int  irq_to_gpio(unsigned irq); 

gpio相关的代码在driver/gpio/gpiolib.c下:
我们先看一下头文件中对于gpio结构的描述:
include/linux/gpio.h

struct gpio_desc {
        struct gpio_chip        *chip;
        unsigned long           flags;
#ifdef CONFIG_DEBUG_FS
        const char              *label;
#endif
};

我们挑一下比较核心的部分看一下

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];  


//简化的后的主要代码
int gpio_direction_output(unsigned gpio, int value)
{
        struct gpio_chip        *chip;
        struct gpio_desc        *desc = &gpio_desc[gpio];
        int                     status = -EINVAL;
        chip = desc->chip;
        status = gpio_ensure_requested(desc, gpio); //没申请手动帮助申请为auto
        status = chip->direction_output(chip, gpio, value);
        return status;
}

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
我们重点关注gpio_desc什么时候被填充
以及ARCH_NR_GPIOS哪里定义

chip->direction_output(chip, gpio, value);
我们看到调用chip中的函数,这个函数在哪里被赋值,问题等同于gpio_desc的问题

总结一下问题:
1.gpio_desc什么时候被填充
2. ARCH_NR_GPIOS哪里定义

问题1: 在代码中我们寻找到了gpio_desc什么时候被填充

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

        if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
                        && base >= 0) {
                status = -EINVAL;
                goto fail;
        }

        spin_lock_irqsave(&gpio_lock, flags);

        if (base < 0) {
                base = gpiochip_find_base(chip->ngpio);
                if (base < 0) {
                        status = base;
                        goto unlock;
                }
                chip->base = base;
        }


        for (id = base; id < base + chip->ngpio; id++) {
                if (gpio_desc[id].chip != NULL) {
                      status = -EBUSY;
                      break;
                }
        }
        if (status == 0) {
                for (id = base; id < base + chip->ngpio; id++) {
                       //*******这里*************//
                        gpio_desc[id].chip = chip;
                        gpio_desc[id].flags = !chip->direction_input  ? (1 << FLAG_IS_OUT)   : 0;
                }
        }

        of_gpiochip_add(chip);
unlock:
        spin_unlock_irqrestore(&gpio_lock, flags);

        if (status)
                goto fail;

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

        return 0;
fail:
        pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",  chip->base, chip->base + chip->ngpio - 1,  chip->label ? : "generic");
        return status;
}

这里我们搜遍代码也没发现这个函数被调用,那么肯定是其他地方调用了,我们在arch/arm的板文件中找到了答案。
kernel_imx/arch/arm/plat-mxc/gpio.c

int mxc_gpio_init(struct mxc_gpio_port *port, int cnt){

         int i, j;
        static bool initialed;

        /* save for local usage */
        mxc_gpio_ports = port;
        gpio_table_size = cnt;


        for (i = 0; i < cnt; i++) {
                /* disable the interrupt and clear the status */
                __raw_writel(0, port[i].base + GPIO_IMR);
                __raw_writel(~0, port[i].base + GPIO_ISR);
                for (j = port[i].virtual_irq_start;  j < port[i].virtual_irq_start + 32; j++) {
                        irq_set_lockdep_class(j, &gpio_lock_class);
                        irq_set_chip_and_handler(j, &gpio_irq_chip, handle_level_irq);
                        set_irq_flags(j, IRQF_VALID);
                }

                /* register gpio chip */
                port[i].chip.direction_input = mxc_gpio_direction_input;
                port[i].chip.direction_output = mxc_gpio_direction_output;
                port[i].chip.get = mxc_gpio_get;
                port[i].chip.set = mxc_gpio_set;
                port[i].chip.base = i * 32;
                port[i].chip.ngpio = 32;

                spin_lock_init(&port[i].lock);
            /**************这里!*********/
                if (!initialed)
                    BUG_ON(gpiochip_add(&port[i].chip) < 0);

                if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() ||  cpu_is_mx51() || cpu_is_mx53() || cpu_is_mx6q() ||   cpu_is_mx6dl() || cpu_is_mx6sl()) {
                        /* setup one handler for each entry */
                        irq_set_chained_handler(port[i].irq,   mx3_gpio_irq_handler);
                        irq_set_handler_data(port[i].irq, &port[i]);

                        if (port[i].irq_high) {
                                /* setup handler for GPIO 16 to 31 */
                                irq_set_chained_handler(port[i].irq_high, mx3_gpio_irq_handler);
                                irq_set_handler_data(port[i].irq_high,   &port[i]);
                        }
                }
        }
        initialed = true;
        return 0;
}

顺便我们看看这里比较重要的结构体的格式:
其中的结构体我们研究一下头文件
kernel_imx/arch/arm/plat-mxc/include/mach/gpio.h
struct mxc_gpio_port {
void __iomem *base;
int irq;
int irq_high;
int virtual_irq_start;
struct gpio_chip chip;
u32 both_edges;
spinlock_t lock;
};

接着我们继续找哪里调用了?
kernel_imx/arch/arm/mach-mx6/devices.c

int mx6q_register_gpios(void)
{       
        /* 7 ports for Mx6 */
        return mxc_gpio_init(mxc_gpio_ports, 7); 
}     

static struct mxc_gpio_port mxc_gpio_ports[] = {
        {
                .chip.label = "gpio-0",
                .base = IO_ADDRESS(GPIO1_BASE_ADDR),
                .irq = MXC_INT_GPIO1_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO1_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START
        },
        {
                .chip.label = "gpio-1",
                .base = IO_ADDRESS(GPIO2_BASE_ADDR),
                .irq = MXC_INT_GPIO2_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO2_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 1
        },
        {
                .chip.label = "gpio-2",
                .base = IO_ADDRESS(GPIO3_BASE_ADDR),
                .irq = MXC_INT_GPIO3_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO3_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 2
        },
        {
                .chip.label = "gpio-3",
                .base = IO_ADDRESS(GPIO4_BASE_ADDR),
                .irq = MXC_INT_GPIO4_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO4_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 3
        },
        {
                .chip.label = "gpio-4",
                .base = IO_ADDRESS(GPIO5_BASE_ADDR),
                .irq = MXC_INT_GPIO5_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO5_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 4
        },     
        {       
                .chip.label = "gpio-5",
                .base = IO_ADDRESS(GPIO6_BASE_ADDR),
                .irq = MXC_INT_GPIO6_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO6_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 5
        },     
        {       
                .chip.label = "gpio-6",
                .base = IO_ADDRESS(GPIO7_BASE_ADDR),
                .irq = MXC_INT_GPIO7_INT15_0_NUM,
                .irq_high = MXC_INT_GPIO7_INT31_16_NUM,
                .virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 6
        },     
};             

kernel_imx/arch/arm/mach-mx6/irq.c

void mx6_init_irq(void)
{
        void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
        struct irq_desc *desc;
        unsigned int i;

        /* start offset if private timer irq id, which is 29.
         * ID table:
         * Global timer, PPI -> ID27
         * A legacy nFIQ, PPI -> ID28
         * Private timer, PPI -> ID29
         * Watchdog timers, PPI -> ID30
         * A legacy nIRQ, PPI -> ID31
         */
        gic_init(0, 29, IO_ADDRESS(IC_DISTRIBUTOR_BASE_ADDR),
                IO_ADDRESS(IC_INTERFACES_BASE_ADDR));

        if (enable_wait_mode) {
                /* Mask the always pending interrupts - HW bug. */
                __raw_writel(0x00400000, gpc_base + 0x0c);
                __raw_writel(0x20000000, gpc_base + 0x10);
        }


        for (i = MXC_INT_START; i <= MXC_INT_END; i++) {
                desc = irq_to_desc(i);
                desc->irq_data.chip->irq_set_wake = mx6_gic_irq_set_wake;
        }
        /***********这里**************/
        mx6q_register_gpios();
#ifdef CONFIG_CPU_FREQ_GOV_INTERACTIVE
        for (i = 0; i < ARRAY_SIZE(mxc_irq_tuner); i++)
                cpufreq_gov_irq_tuner_register(mxc_irq_tuner[i]);
#endif
#ifdef CONFIG_PCI_MSI
        imx_msi_init();
#endif
}

最后:板文件中定义

/*
 * initialize __mach_desc_MX6Q_SABRESD data structure.
 */
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
        /* Maintainer: Freescale Semiconductor, Inc. */
        .boot_params = MX6_PHYS_OFFSET + 0x100,
        .fixup = fixup_mxc_board,
        .map_io = mx6_map_io,
        .init_irq = mx6_init_irq,
        .init_machine = mx6_sabresd_board_init,
        .timer = &mx6_sabresd_timer,
        .reserve = mx6q_sabresd_reserve,
MACHINE_END

在kernel启动时候
kernel_imx/init/main.c —>arch/arm/kernel/setup.c
start_kernel() —> setup_arch(&command_line)
后面会获取宏中定义的结构相关的内容,在合适的时候去调用。

问题2:
ARCH_NR_GPIOS在哪里定义?
在板级头文件中没有发现定义,默认的定义在:
kernel_imx/include/asm-generic/gpio.h 下
另外static inline bool gpio_is_valid(int number)的实现也在。

#ifndef ARCH_NR_GPIOS
# define ARCH_NR_GPIOS 256
#endif

看来是8*32=256板文件也就没有定义这个值了,其他平台的板文件都能搜索到定义

头文件包含关系
linux/gpio.h ===>
asm/gpio.h===>kernel_imx/arch/arm/include/asm/gpio.h
mach/gpio.h—>
plat-mxc/include/mach/gpio.h —->asm-generic/gpio.h

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值