驱动开发中最基础的就是控制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