티스토리 뷰

Study/Linux

[Linux] GPIO Descriptor Driver Interface

생각많은 소심남 2015. 10. 12. 17:56

* 이글은 Linux Documentation에 있는 글을 번역하고 개인 의견을 첨부한 것이다. 


Linux기반의 controller driver를 개발하려면 자신이 직접 설계하고 코드를 구현할 수도 있겠지만, 가장 쉬운 방법은 기존에 작성되어 있는 코드를 활용하는 것이다. 물론 License 같은게 걸리면 직접 설계를 해야 되지만.. 아무튼 기존에 구현되어 있는 structure를 활용하기 위해서는 다음 header 파일을 include해야 한다.

#include <linux/gpio/driver.h>


GPIO의 내부 정의

GPIO driver 내부에서 각각의 GPIO는 0부터 n(controller에 의해서 정의된 GPIO 갯수)사이의 번호를 가지고, 이 번호는 내부에서만 쓰인다. 다시말해서 driver 외부에서는 어떤 GPIO Descriptor가 어떤 번호를 가지고 있는지 알수 없다.(또한 이 번호 GPIO에 대해 각각 하나만 가진다.)

 이 내부 번호의 상위 개념으로 각 GPIO는 integer 기반의 GPIO에 맞는 global 번호를 가진다. 이때문에 이전 포스트에서 언급했던 descriptor 기반의 interface 뿐만 아니라 과거의 integer 기반의 interface에서도 사용할 수 있다. 이 내부적으로 각각의 GPIO를 구분하기 위해서 각 GPIO는 각각의 base number를 가진다. 이번호는 자동적으로 지정되고, 결국 각 GPIO가 가지는 global number는 base + hardware number( 앞에서 정의한 0~n 사이의 번호)로 계산할 수 있다. 현재는 이런 integer 기반의 표현방식이 구식으로 인식되고 있긴 하지만 아직도 사용자나 모듈내에서 사용되고 있기 때문에 이런 개념이 필요하다.

 예를 들어서 어떤 platform이 32~159 사이의 GPIO number를 사용할 수 있는데, 이런 환경은 gpio controller가 32를 base number를 가지고 128개의 GPIO를 쓸 수 있는 뜻이다. 혹은 0~63까지의 번호가 GPIO controller의 한 부분을 차지하고 64~79까지는 또다른 종류의 GPIO controller에 할당되어 있고, 80~95까지는 또 다른 FPGA가 가질 수도 있다. 물론 각각의 base number는 다를 것이다.

 그리고 이 번호는 항상 연속될 필요는 없다. 예를 들어서 I2C 규격으로 정의된 GPIO expander같은 경우는 2000-2063의 GPIO number를 가질 수도 있다.


Controller Drivers: gpio_chip

gpiolib framework을 사용하는 컨트롤러라면 그 컨트롤러는 드라이버 내부에서 gpio_chip이라는 structure 형태로 표현된다. 몇가지 member들이 있는데, 이들이 제공하는 기능은 다음과 같다.

 - GPIO 방향을 지정하는 함수

 - GPIO 값에 접근할 수 있는 함수

 - 해당 GPIO에 연결되어 있는 IRQ 번호를 반환하는 함수

 - 해당 함수가 sleep 상태가 필요한지를 알려주는 flag 

 - debugfs에 남아있는 로그를 dump할 수 있는 기능

 - base number 확인

 - 문제가 발생했을 때에 대한 diagonstic용 label

일단 gpio_chip을 이용해서 구현된 경우라면 controller 여러 개에 대해서도 지원할 수 있어야 하고, 각 instance는 gpiochip_add()를 통해서 처리된다. 물론 이를 제거하는 함수(gpiochip_remove())도 존재하나, 거의 사용되지 않는다.

(그 뒤에 뭐라고 막 써져 있는데 해석 불가...)

Most often a gpio_chip is part of an instance-specific structure with state not
exposed by the GPIO interfaces, such as addressing, power management, and more.
Chips such as codecs will have complex non-GPIO state.

Any debugfs dump method should normally ignore signals which haven't been
requested as GPIOs. They can use gpiochip_is_requested(), which returns either
NULL or the label associated with that GPIO when it was requested.


IRQ 기능을 제공하는 GPIO Driver

 보통 IRQ Handling은 interrupt controller에서 이뤄지지만, GPIO driver가 interrupt 처리 기능을 제공하는 경우도 있다. 또 몇몇 경우에 따라서는 GPIO logic이 system 상의 interrupt controller와 함께 동작하는 케이스도 있다.

 GPIO에서 IRQ를 처리하는 부분은 irq_chip라고 정의된 structure를 활용하는데 내부의 member 함수를 사용하기 위해서는 <linux/irq.h>를 추가해야 된다. 결국 이 부분이 동작하기 위해서는 gpio 영역과 irq 영역이 동시에 수행되어야 한다는 것이다.(이때문에 irq_chip과 gpio_chip이 따로 존재함) 이 struct를 통해서 처리되는 irq는 크게 두가지가 있다.

 - Chained GPIO : 부모쪽의 IRQ handler(일반적으로는 System interrupt controller)로부터 GPIO만을 위한 fast IRQ handler가 연쇄적으로 수행되는 형태이다. 보통 SoC에 내장되어 있는 형태로 되어 있다. 이 형태로 쓰여지는 IRQ는 보통 함수명에 chained 라는 문구가 포함되어 있다. 일반적인 IRQ handling과 유사하게 set_handler() 함수를 통해서 IRQ를 등록하고, System Interrupt controller에 의해서 Chained된 GPIO IRQ handler가 호출되면 바로 수행된다. 빠른 IRQ 수행에 적합한 형태라고 할 수 있다. 

 - Nested Threaded GPIO : 위와 같이 내장되어 있는 형태가 아니라 Bus를 통해서 신호가 전달되는 방법도 있는데, 이 경우는 IRQ가 발생시 Bus traffic이 생겨서 빠르게 처리할 수 없다. 이런 건 보통 chip 밖에 expander로 나와있는 형태로 되어 있고, IRQ를 처리하기 위해서 따로 thread를 생성해서 거기로 처리한다. 물론 이 thread가 다른 IRQ handler가 처리되는 부분을 방해할 가능성을 방지하기 위해서 handling 전에 GPIO를 제외한 나머지 IRQ line을 masking한다. 

두가지 방식의 큰 차이는 처리되는 프로세스 자체도 틀리다는 것이다. chained GPIO 방식은 일종의 parent handler? system IRQ로부터 파생되어서 처리되기 때문에 많은 부분을 처리하지 못하고 빠르게 끝난다. documentation에서도 실제 처리되는 프로세스는 다음과 같이 된다.

- drivers/gpio/gpio-omap.c

static void omap_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
   ...
   chained_irq_enter(irqchip, desc);
   ...
   generic_handle_irq(irq_find_mapping(bank->chip.irqdomain, bit));
   ...
   chained_irq_exit(irqchip, desc);
}

로 되어 있고 실제로 IRQ를 통해서 처리되어야 할 부분은 enter와 exit 안에서 끝나야 한다. 원래 gpio_chip structure안을 보면 can_sleep이라는 flag가 따로 존재하는데 위와 같은 경우는 빠르게 처리해야 할 부분이므로 이 flag에 대해서는 set할 수 없다.

 반면 threaded GPIO인 경우는 단순히 한줄로도 IRQ 처리가 가능하다.

- drivers/gpio/gpio-tc3589x.c

static irqreturn_t tc3589x_gpio_irq(int irq, void *data)
{
      ...
      handle_nested_irq(irq);

}

앞 경우와는 다르게 thread로 처리할 수 있고, GPIO에 access하는 동안에 해당 thread는 sleep도 가능하다. 때문에 앞의 구조체중 can_sleep flag도 set 할 수 있게 된다. 

물론 위와 같이 GPIO IRQ handling을 하기 위해서는 사전에 gpio_chip과 irq_chip 사이의 환경설정이라던가 resource allocation같은 기타 설정이 되어 있어야 하는데 Kernel Build시 설정되는 Kconfig 중 GPIOLIB_IRQCHIP이라는 symbol을 설정해주면 resource allocation과 여러가지 callback과 관련 helper function을 사용할 수 있다. 

 * gpiochip_irqchip_add() : irqchip을 gpiochip에 추가함으로써 IRQ와 관련한 callback 함수들이 GPIO 사용시 쓸 수 있도록 연결된다. 물론 어디서 왔는지에 대한 정보가 callback 처리시 필요한데, 이때의 포인터는 contrainer_of()를 사용해서 얻을 수 있다.

 * gpiochip_set_chained_irqchip() : 앞에서 잠깐 언급된 것처럼 chained_GPIO IRQ를 설정할 때 이 함수를 사용한다. 만약 nested threaded 방식으로 사용하고 싶으면 이 함수의 인자중 handler 부분을 NULL로 처리하면 사용할 수 있다.

이렇게 GPIO를 이용해서 IRQ를 처리하는 데 있어서 유의해야 할 점은 gpio_chip이나 irq_chip이나 서로 독립적이라는 것이다. 다시 말해서 어떤 IRQ를 요구하는 이벤트가 특정 irq_chip에 종속되지 않고, 아무 irq_chip으로든 처리될 수 있다는 것이다. 이 같은 전제는 gpiod_to_irq() 함수를 놓고 보면 확실하게 알 수 있는데, 이 함수는 해당 시점에 특정 GPIO line 에 맵핑된 IRQ 를 알려주는 것일 뿐, IRQ관련 설정이 되기 이전에 들어있는 값은 의미가 없다. 다시 말해서 맨 처음에 이 함수를 불렀다고 IRQ가 유지되는게 아니기 때문에 항상 관련 기능을 사용하기 전에 이 부분을 미리 확인하는 부분이 있어야 한다.

 만약 이런식으로 GPIO와 IRQ간에 독립성이 존재한다면, 동시에 IRQ가 발생해서 Resource를 할당해야 할 경우 경쟁 문제가 발생할 수 있다. 이 부분은 특정 시점에 어떤 부분이 수행되고 말것인지를 결정하는 요소가 필요하다는 이야기인데, 이런 이유로 인해서 GPIO와 IRQ handling 사이에도 lock mechanism이 존재한다.


IRQ Locking

 해당 기능과 관련하여 제공되는 함수는 두가지인데,

int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)

는 해당 GPIO의 IRQ handling에 대해서 우선적으로 처리할 수 있게 하고, 그외의 GPIO 처리에 대해서는 잠시 미루게해준다.

void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)

Lock이 걸린후 해당 IRQ를 처리하고 나면 반드시 위와 같은 unlock 함수를 사용해서 풀어줘야 다른 GPIO handling에 대해서도 대처할 수 있게 된다. 앞에서 언급했던 것처럼 이런 GPIO를 통해서 IRQ를 처리하는 것 자체가 짧은 시간내에 이뤄지는 것이기 때문에 이를 처리하는 structure인 irq_chip의 시작과 끝 부분에 이런 lock/unlock 부분을 삽입한다. 그래서 일반적으로 irq_chip내의 callback function인 irq_startup()과 irq_shutdown() 안에 해당 함수를 집어넣는 형태가 코드내에서 보인다.


driver내에서 자신의 Descriptor 호출

가끔 GPIO driver 내에서 gpiolib 에서 제공하는 API를 가지고 자기 자신의 descriptor를 호출하는 경우가 발생한다는데,(어떤 케이스인지는 모르겠지만...) 그럴 경우가 있는 경우 해당 함수를 쓰면 된다.

struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc,
						    const char *label)
void gpiochip_free_own_desc(struct gpio_desc *desc)

( 이 부분은 어떤 케이스인지 이해를 못해서 설명을 잘 못하겠다.)

Descriptors requested with gpiochip_request_own_desc() must be released with
gpiochip_free_own_desc().

These functions must be used with care since they do not affect module use
count. Do not use the functions to request gpio descriptors not owned by the
calling driver.


댓글