Linux GPIO 的注冊與申請
來源:程序員人生 發布時間:2014-11-11 09:14:37 閱讀次數:3004次
Linux GPIO 的注冊與申請
Linux Kernel, GPIO, ARM
在Linux kernel代碼中,常常會使用 GPIO 來作為1個特殊的信號,如作為芯片的片選信號等。
GPIO 申請的函數,我們常常用到,如 gpio_request ,那末 GPIO 是什么時候,和如何注冊的,本文就來探索1下。
基于的平臺上 freesccale 的 i.MX6
先從函數 gpio_request 的實現開始。
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc;
struct gpio_chip *chip;
int status = -EINVAL;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
goto done;
// 這兒從 gpio_desc 數組中取了1個 gpio_desc 結構體
// 后面的代碼基本上都是基于這個結構體進行的操作
// 我們是從數組中取了1個 gpio 的描寫,這個描寫應當是在 gpio 注冊的時候添加到這個數組的
// 以這個數組為線索,看看 gpio 是如何注冊的
desc = &gpio_desc[gpio];
chip = desc->chip;
if (chip == NULL)
goto done;
if (!try_module_get(chip->owner))
goto done;
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
desc_set_label(desc, label ? : "?");
status = 0;
} else {
status = -EBUSY;
module_put(chip->owner);
goto done;
}
if (chip->request) {
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
status = chip->request(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
if (status < 0) {
desc_set_label(desc, NULL);
module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
}
}
done:
if (status)
pr_debug("gpio_request: gpio-%d (%s) status %d
",
gpio, label ? : "?", status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
以數組 gpio_desc 為線索。
既然我們申請 GPIO 的時候是從這個數字中取數據,那末注冊 GPIO 的時候就應當往這個數字中添加數據了。
反過來,往這個數組添加數據的地方應當也就是注冊 GPIO 的地方了。
這個數組定義在 Gpiolib.c 文件中:
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
搜索發現, gpiochip_add 函數中有給數組 gpio_desc 賦值。
看看誰調用了函數 gpiochip_add 。
平臺相干目錄下的 Gpio.c 文件中的 mxc_gpio_init 函數調用了 gpiochip_add :
if (!initialed)
/* its a serious configuration bug when it fails */
BUG_ON(gpiochip_add(&port[i].chip) < 0);
繼續往上找,平臺相干目錄下的 Devices.c 有以下函數:
int mx6q_register_gpios(void)
{
/* 7 ports for Mx6 */
return mxc_gpio_init(mxc_gpio_ports, 7);
}
mxc_gpio_ports 的定義:
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⑴",
.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⑵",
.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⑶",
.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⑷",
.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⑸",
.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⑹",
.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
},
};
繼續往上,找到了同目錄下 Irq.c 文件中的 mx6_init_irq 函數調用了 mx6q_register_gpios 。
board.c 文件中將 mx6_init_irq 函數賦值給了 machine_desc 結構體的 init_irq 函數:
MACHINE_START(MX6XXXX, "Freescale i.MX 6 Board")
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_board_init,
.timer = &mxc_timer,
.reserve = mx6q_reserve,
MACHINE_END
arch/arm/kernel/irq.c 文件中有以下函數:
void __init init_IRQ(void)
{
machine_desc->init_irq();
}
init/main.c 文件中的 start_kernel 函數調用了 init_IRQ 。
至于 start_kernel 函數什么時候被調用,有時間再作研究。
總結1下 GPIO 的注冊進程:
start_kernel 函數會調用 init_IRQ 函數。
init_IRQ 函數調用了 machine_desc 結構體的 init_irq 函數。
machine_desc 結構體在 board.c 文件中定義,其中 init_irq 被賦值為 mx6_init_irq 。
mx6_init_irq 函數中調用了 mx6q_register_gpios 函數。
mx6q_register_gpios 函數的定義見前文,其中調用了函數 mxc_gpio_init 。
函數 mxc_gpio_init 的實現:
int mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
{
int i, j;
static bool initialed;
/* save for local usage */
// port 是前面定義的數組 mxc_gpio_ports , cnt 是數組中元素的個數
mxc_gpio_ports = port;
gpio_table_size = cnt;
printk(KERN_INFO "MXC GPIO hardware
");
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);
/*
static struct irq_chip gpio_irq_chip = {
.name = "GPIO",
.irq_ack = gpio_ack_irq,
.irq_mask = gpio_mask_irq,
.irq_unmask = gpio_unmask_irq,
.irq_set_type = gpio_set_irq_type,
.irq_set_wake = gpio_set_wake_irq,
};
*/
/**
* handle_level_irq - Level type irq handler
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq
*
* Level type interrupts are active as long as the hardware line has
* the active level. This may require to mask the interrupt and unmask
* it after the associated handler has acknowledged the device, so the
* interrupt line is back to inactive.
*/
irq_set_chip_and_handler(j, &gpio_irq_chip,
handle_level_irq);
set_irq_flags(j, IRQF_VALID);
}
/* register gpio chip */
// mxc_gpio_direction_input 將對應 gpio 設置為輸入, mxc_gpio_direction_output 將對應 gpio 設置為輸出,并會設置1個初始值
// 這兒的輸入/輸出是對 cpu 來講的
port[i].chip.direction_input = mxc_gpio_direction_input;
port[i].chip.direction_output = mxc_gpio_direction_output;
// 獲得/設置 gpio 狀態
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)
/* its a serious configuration bug when it fails */
// 添加 gpio chip , 調用的是我們前面用到的1個線索函數, 該函數中有給 gpio_desc 數組賦值
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;
if (cpu_is_mx2()) {
/* setup one handler for all GPIO interrupts */
irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler);
irq_set_handler_data(port[0].irq, port);
}
return 0;
}
gpiochip_add 函數的實現:
/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
* Context: potentially before irqs or kmalloc will work
*
* Returns a negative errno if the chip can't be registered, such as
* because the chip->base is invalid or already associated with a
* different chip. Otherwise it returns zero as a success code.
*
* When gpiochip_add() is called very early during boot, so that GPIOs
* can be freely used, the chip->dev device must be registered before
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
* for GPIOs will fail rudely.
*
* If chip->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*/
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;
}
/* these GPIO numbers must not be managed by another gpio_chip */
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 成員的 chip 成員
gpio_desc[id].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,
* we may expose the wrong direction in sysfs.
*/
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;
// 創建裝備, 并添加對應的 sysfs
status = gpiochip_export(chip);
if (status)
goto fail;
return 0;
fail:
/* failures here can mean systems won't boot... */
pr_err("gpiochip_add: gpios %d..%d (%s) failed to register
",
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
gpio_desc 結構體的定義:
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
#define ID_SHIFT 16 /* add new flags before this one */
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈