GPIO Subsystem -1-

GPIO (general-purpose I/O) 서브시스템은  David Brownell에 의해 커널 v2.6.21에서 처음 소개되었다. 그 후 v2.6.28에서 gpio에도 sysfs 인터페이스를 채용하여 유저스페이스에서도 쉽게 다룰 수 있게 하였다. 최근(v4.6, v4.8 이상)에는 gpio controller를 export 방법을 사용하는 클래스 디바이스가 아닌 character 디바이스로 바꾸어 유저스페이스에 노출시켜 gpio 핀들을 ioctl() 함수로 제어할 수 있도록 지원한다.

 

gpio 핀의 입/출력 모드 설정 및 gpio 핀 값의 설정등에 대해 board/machine 코드에서 gpio를 직접 다룬 legacy 환경에서 개발하였던 커널 디바이스 개발자들은 최근에 많은 혼란을 겪고 있을 것이다. 예전에는 gpio의 사용이 무척 쉬웠지만, 지금은 그렇지 않다. 최근에는 디바이스 트리와 연계하여 pin control subsystem, gpio control subsystem 및 irq subsystem까지 연동하여 표준화시킨 방법들을 사용해야 한다. 이러한 방법을 완전히 알기 전까지는 무척 어렵다고 말할 수 있다. 결국 개발자들은 새로운 방법을 학습하고 사용하도록 다시금 도전해야 한다.

 

GPIO pin H/W 특성

GPIO 핀의 H/W 특성에 대해 잠시 살펴보자.

  • 오늘날의 GPIO는 기존 PIO 처럼 단순히 입력모드로 설정한 후 0과 1을 입력받고, 출력모드도 설정한 후 0과 1을 출력하는 것으로 끝나지 않는다.
  • GPIO 핀에 연결된 핀이 GPIO 용도 이외에 핀을 다중화하여 멀티 펑션을 선택할 수 있도록 Pin Controller의 핵심 기능인  Pin multiplexing을 사용한다.
  • 또한 핀에 대한 전압 전위, 전류 강도(bias-pull-up, bias-pull-down, drive-strength, …) 등을 설정할 수 있는 Pin Configuration 역할도 있다.
  • 아울러 특정 핀은 인터럽트 입력 특성(edge, level)등에 대한 처리와 인터럽트 컨트롤러로의 전달도 가능하다.

 

다음 그림은 라즈베리파이 SoC(rpi)의 GPIO 핀들 중 하나의 핀에 대한 블록 다이어그램이다.

  • GPIO 회로가 Pin Controller 회로 및 인터럽트 처리 회로들과 어울려 복잡하게 구성되어 있음을 알 수 있다. 이러한 구성은 각 벤더의 GPIO 마다 모두 다르다.

 

위와 같은 H/W 기능을 구현 입장에서 살펴보자. 최근의 리눅스 커널은 Pin Controller와 GPIO Controller 그리고 GPIO 내부의 Interrupt Controller 등에 대한 디바이스 드라이버 구현을 각각 하게 하는 것으로 최대한 단순화하도록 한다. 최대한 단순화된 GPIO 디바이스 드라이버의 기능은 다음과 같다.

  • 입/출력 모드 설정(direction)
    • input
    • output
  • input 모드에서 0과 1 입력 값을 읽기
  • output 모드에서 0과 1 출력하기
  • H/W 구성 시 극성에 따라 다른 active-high 및 active-low 방식 설정
  • output 모드에서 H/W 방식에 따른 설정. open-drain 및 open-source는 커널 v.4.6-rc1에서 추가된 기능
    • push-pull
    • open-drain
    • open-source
    • 참고: gpio: create an API to detect open drain/source on lines

 

GPIO pin 블록다이어그램 회로

GPIO의 입/출력에 관여하는 중요한 소자의 분류가 두 가지가 있다.

  • 입력 전류에 대해 반응하는 트랜지스터(Transistor) 소자
    • Base, Collector, Emitter 세 개의 핀을 사용한다.
    • 두 가지의 극성을 사용한다.
      • NPN형
        • Bias에 high 전압의 전류를 소량 가하면 Collector와 Emitter 간에 전류가 통한다.
      • PNP형
        • Bias에 low 전압의 전류를 소량 가하면 Collector와 Emitter 간에 전류가 통한다.
  • 입력 전압에 대해 반응하는 전계효과 트랜지스터(FET: Field Effect Transistor) 소자
    • Gate, Drain, Source 세 개의 핀을 사용한다.
    • 두 가지의 극성을 사용한다.
      • N channel
        • Gate에 high 전압을 가하면 Drain과 Source 핀에 전류가 통한다.
      • P channel
        • Gate에 low 전압을 가하면 Drain과 Source 핀에 전류가 통한다.

 

다음 그림은 트랜지스터와 여러 가지 종류의 FET를 보여준다.

 

다음 그림은 입력 모드에 대한 설명이다. (아래 그림들은 모두 MOSFET(Metal Oxide Semiconductor FET)  기준으로 작성되었다.)

  • active-high 구성 및 active-low 구성에 대해 gpio 핀으로 입력 전압이 high(1)와 low(0)로 가해지는 구성에 따라 각각 다름을 알 수 있다.
  • active-high 구성에서는 스위치가 눌려지지 않았을 때에 low(0) 입력 상태이고, 눌렸을 때 high(1) 입력 상태이다.
  • active-low 구성에서는 스위치가 눌려지지 않았을 때에  high(1) 입력 상태이고, 눌렸을 때 low(0) 입력 상태이다.

 

다음 그림은 위의 그림과 동일한 입력 모드이지만 내부에 pull-down 및 pull-up 구성을 하였을 때 외부 저항(R) 소자를 줄일 수 있는 방법이다. 동작은 동일하다.

  • GPIO controller가 아니라 pin controller를 통해 각 핀의 bias-pull-up 및 bias-pull-down을 설정할 수 있다.

 

다음 그림은 출력 모드에 대한 설명이다.  기본 출력 모드는 push-pull 출력 방식을 사용한다. push-pull 방식의 특징은 write 신호와 동일한 신호로 출력이 전달된다.

  • gpio 핀은 푸쉬-풀 연결한 MOSFET 출력과 연결되어 있음을 알 수 있다. 외부 회로에 사용된 LED의 전원은 gpio 전원과 동일한 전원을 사용해야한다. 다른 전원을 사용하려면 push-pull 구성이 아닌 open-drain 또는 open-source 설정을 사용해야 한다.
  • active-high: high(1) write 값과 동일한 high(1) 출력에서 active 되는 설계에 사용한다.
  • active-low:  low(0) write 값과 동일한 low(0) 출력으로 active 되는 설계에 사용한다.

 

다음 그림은 push-pull 출력 뿐만 아니라 single-ended 출력도 선택 가능한 gpio controller에서 사용하는 방법이다. 아래와 같은 출력 모드를 사용하는 이유는 외부 회로가 gpio에 사용하는 전압과 서로 다른 전압 레벨을 사용하는 경우에 신호 전달이 용이하도록 하기 위함도 있고, 더 큰 전압을 드라이브 하기 위함도 있다. gpio controller에 따라 주로 open-drain을 지원하지만 open-drain 및 open-source 둘 다 지원하기도 한다.

  • open-drain: MOSFET의 drain 핀이 어떠한 전원에 연결되지 않고 오픈된 모습으로 패키지의 외부로 나왔다.
    •  low(0) value가 출력되는 경우 active가 되고,  그 외의 경우는 출력이 고정(float)되지 않는다.
    • 따라서 커널 구현에서는 active 값인 low(0) value가 출력되는 경우에만 아래 그림과 같이 output 모드로 설정되고, 그 외의 경우 input 모드로 전환된다.
  • open-source: 위와 유사하지만 MOSFET의 source 핀이 오픈된 모습으로 패키지의 외부로 나온 경우이다.
    •  high(0) value가 출력되는 경우 active가 되고,  그 외의 경우는 출력이 고정(float)되지 않는다.
    • 따라서 커널 구현에서는 active 값인 high(1) value가 출력되는 경우에만 아래 그림과 같이 output 모드로 설정되고, 그 외의 경우 input 모드로 전환된다.

 

다음 그림과 같이 극성을 바꿔 설계한 경우도 있다. 구동은 위의 그림과 반대로 동작한다.

 

 

GPIO subsystem

GPIO 서브시스템은 각 GPIO Controller 칩별로 다른 구현 부분을 제외하고 자주 사용될 수 있는 core 부분을 제공한다. core에서는 DTB 파싱부분과 API, 그리고 유저 스페이스와 연결된 sysctrl 인터페이스를 제공한다.

 

GPIO subsystem과 협력하는 다른 subsystem은 다음과 같다.

  • Pin Control Subsystem
  • Interrupt Subsystem with IRQ Domain

 

다음 그림은 GPIO subsystem의 구조이다.

 

디바이스 드라이버 구현 모델

GPIO Controller에 대한 디바이스 드라이버의 구현은 크게 3가지 방법으로 구현할 수 있다. 최근 추세는 주체가 GPIO 디바이스 드라이버가 아니라 Pin Controller 디바이스 드라이버를 위주로 구현하고 있다. 따라서 특정 gpio 디바이스 드라이버 코드는 커널 디렉토리의 drivers/gpio 뿐만 아니라 drivers/pinctrl 디렉토리에 위치할 수 있다.

  • GPIO only
  • GPIO + IRQ
  • Pinctrl + GPIO + [ IRQ ]

 

다음 그림은 단순 입출력만 제어가능한 GPIO 장치용 드라이버의 사례이다.

  • gpio 본연의 역할로 핀을 input 또는 output 모드로 설정하고 값을 읽거나 출력하여 사용하는 것만 구현한다.

 

다음 그림은 external 인터럽트를 처리할 수 있는 GPIO 장치용 드라이버의 사례이다.

  • GPIO Controller에 external 인터럽트를 처리할 수 있도록 작은 IRQ Controller가 포함된 경우에는 irq_chip에 대한 구현을 한 후, SoC에 내장된 진째 인터럽트 컨트롤러의 child 형태로 연결해야 한다. 두 가지 형태로 구성할 수 있는데 사용해야 하는 주요 관련 함수는 다음과 같다.
    • chained irq:
      • irq context에서 핸들러 동작 (SoC 내부에 gpio가 있는 경우)
      • gpiochip_irqchip_add()
      • gpiochip_set_chained_irqchip()
    • nested irq:
      • process context에서 핸들러 동작 (i2c 뒤에 gpio가 붙을 때 i2c가 sleep 처리가 필요한 경우를 위해 사용)
      • request_threaded_irq()
      • gpiochip_irqchip_add_nested()
      • gpiochip_set_nested_irqchip()

 

다음 그림은 추가적으로 pin controller와도 연동되는 모습을 보여준다.

  • 구현 모델에 따라 pinctrl 드라이버에서 gpio까지 구현하는 드라이버도 있고, 각각 분리되어 구현된 드라이버도 있다.
  • gpio controller를 정의한 디바이스 트리 노드에서 “gpio-ranges” 속성을 통해 pin controll 그룹과 연동할 수 있다.
    • pinctrl_add_gpio_range() 같은 API를 통해서도 pinctrl과 gpio 서브시스템과 실시간 협력할 수 있다.

 

GPIO Kernel APIs

Legacy GPIO APIs

다음과 같은 Legacy GPIO API들이 제공된다. (deprecated)

  • int gpio_request(unsigned gpio, const char *label)
  • int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
  • int gpio_request_array(const struct gpio *array, size_t num)
  • int gpio_free(unsigned gpio)
  • void gpio_free_array(const struct gpio *array, size_t num)
  • int gpio_direction_input(unsigned gpio)
  • int gpio_direction_output(unsigned gpio)
  • int gpio_get_value(unsigned gpio)
  • int gpio_set_value(unsigned gpio, int value)
  • unsigned gpio_to_irq(unsigned gpio)
  • unsigned irq_to_gpio(unsigned irq)

 

Pin Control Back-Ends APIs

Pin Controller가 GPIO Controller와 같이 협력하여 동작하면 서로 요청할 것이 있다. 그러한 요청을 위해 별도의 API들이 요구되었다.

 

다음 그림은 gpio와 관련된 양쪽 controller에서 자주 사용하는 대표적인 API들이다.

 

GPIO Controller Side

GPIO controller가 pin controller에 요청하는 함수들이다. extern으로 시작하는 함수들은 pinctrl susbstem core에서 제공하는 함수들이다.

  • int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, unsigned gpio_offset, unsigned pin_offset, unsigned npins)
  • int gpiochip_add_pingroup_range(struct gpio_chip *chip, struct pinctrl_dev *pctldev, unsigned int gpio_offset, const char *pin_group)
  • void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
  • extern int pinctrl_request_gpio(unsigned gpio)
  • extern void pinctrl_free_gpio(unsigned gpio)
  • extern int pinctrl_gpio_direction_input(unsigned gpio)
  • extern int pinctrl_gpio_direction_output(unsigned gpio)

 

Pin Controller Side

Pin Controller가 GPIO controller에 요청하기 위한 pinmux_ops 구조체내의 후크 함수와 멤버 변수이다.

  • int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset)
  • void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset)
  • int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input)
  • bool strict

 

GPIO 디스크립터 기반 API들 (GPIOLIB)

다음과 같은 GPIO 디스크립터 기반 API들이 제공된다. (함수형과 인자들은 생략하였다.)

  • gpio_to_desc()
  • desc_to_gpio()
  • gpiod_to_chip()
  • gpiod_get_direction()
  • gpiod_direction_input()
  • gpiod_direction_output_raw()
  • gpiod_direction_output()
  • gpiod_set_debounce()
  • gpiod_is_active_low()
  • gpiod_get_raw_value()
  • gpiod_get_value()
  • gpiod_set_raw_value()
  • gpiod_set_value()
  • gpiod_set_raw_array_value()
  • gpiod_set_array_value()
  • gpiod_cansleep()
  • gpiod_to_irq()
  • gpiod_get_raw_value_cansleep()
  • gpiod_get_value_cansleep()
  • gpiod_set_raw_value_cansleep()
  • gpiod_set_value_cansleep()
  • gpiod_set_raw_array_value_cansleep()
  • gpiod_set_array_value_cansleep()
  • gpiod_add_lookup_table()
  • gpiod_remove_lookup_table()
  • gpiod_count()
  • gpiod_get()
  • gpiod_get_optional()
  • gpiod_get_index()
  • fwnode_get_named_gpiod()
  • gpiod_get_index_optional()
  • gpiod_put()
  • gpiod_get_array()
  • gpiod_get_array_optional()
  • gpiod_put_array()

 

GPIO Controller APIs (GPIOLIB)

다음과 같은 GPIO controller와 관련된 API들이 제공된다. (함수형과 인자들은 생략하였다.)

  • gpiochip_add_data() & devm_gpiochip_add_data()
  • gpiochip_get_data()
  • gpiochip_remove() & devm_gpiochip_remove()
  • gpiochip_find()
  • gpiochip_set_chained_irqchip()
  • gpiochip_set_nested_irqchip()
  • gpiochip_irqchip_add_key()
  • gpiochip_generic_request()
  • gpiochip_generic_config()
  • gpiochip_generic_free()
  • gpiochip_is_requested()
  • gpiochip_request_own_desc()
  • gpiochip_free_own_desc()
  • gpiochip_lock_as_irq()
  • gpiochip_unlock_as_irq()
  • gpiochip_line_is_irq()
  • gpiochip_line_is_open_drain()
  • gpiochip_line_is_open_source()
  • gpiochip_line_is_persistent()

 

GPIO Aggregator

Virtual gpio chip은 커널 v5.8-rc1 부터 지원한다.

 


GPIO 디스크립터

drivers/gpio/gpiolib.h

struct gpio_desc {
        struct gpio_device      *gdev;
        unsigned long           flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED  0
#define FLAG_IS_OUT     1
#define FLAG_EXPORT     2       /* protected by sysfs_lock */
#define FLAG_SYSFS      3       /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6       /* value has active low */
#define FLAG_OPEN_DRAIN 7       /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8      /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9      /* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED  11      /* GPIO is hogged */
#define FLAG_SLEEP_MAY_LOOSE_VALUE 12   /* GPIO may loose value in sleep */

        /* Connection label */
        const char              *label;
        /* Name of the GPIO */
        const char              *name;
};
  • flags
    • gpio 핀 플래그
  • *label
    • gpio 핀 라벨명
    • “/sys/class/gpio”에서 export 명령으로 노출된 경우 label 명이 지정되지 않은 경우 “sysfs”라는 라벨명이 사용된다.
    • interrupt에 사용되는 gpio 핀인 경우 label 명이 지정되지 않은 경우 “interrupt”라는 라벨명이 사용된다.
    • 디바이스 트리에서 요청되어 사용되는 경우 consumer id 명이 지정된다.
      • 예) “<con_id>-gpios = <>”
  • *name
    • gpio 핀 명
    • 디바이스 트리에서 line 명으로 설정되는 경우 지정된다.
      • 예) gpio-line-names = “MMC-CD”, “NC”;

 

GPIO 핀에 대한 플래그를 의미하는 gpio 디스크립터 플래그 비트들의 특징은 다음과 같다.

  • FLAG_REQUESTED
    • 사용자(Consumer)로 부터 gpio 핀 사용 요청이된 경우 설정된다.
    • 다른 사용자가 접근 시 -EBUSY 에러
  • FLAG_IS_OUT
    • 입력(0)/출력(1) 모드
  • FLAG_EXPORT
    • gpiod_export() 또는 “/sys/class/gpio”에서 export 명령으로 노출된 경우이다.
  • FLAG_SYSFS
    • “/sys/class/gpio”에서 export 명령으로 노출된 경우 설정된다.
  • FLAG_ACTIVE_LOW
    • gpio 상태가 low(0) 값에서 active 반응하도록 설계된 회로에서 설정된다.
    • 디바이스 트리에서 “X-gpios = <&phandle  gpio_num  GPIO_ACTIVE_LOW>” 속성 값이 적용된다.
  • FLAG_OPEN_DRAIN
    • gpio 내부 회로 설정이 open drain인 경우 설정된다.
  • FLAG_OPEN_SOURCE
    • gpio 내부 회로 설정이 open source인 경우 설정된다.
  • FLAG_USED_AS_IRQ
    • gpio 핀이 irq에 연결된 경우 설정된다.
  • FLAG_IS_HOGGED
    • gpio 핀이 hog된 경우 설정된다.
  • FLAG_SLEEP_MAY_LOOSE_VALUE
    • gpio 핀이 절전 sleep 시 gpio value 값을 잃어버린다.
    • 이 비트가 설정된 경우 wakeup 시 gpio controller가 다시 gpio 값을 설정한다.

 

gpio 번호와 gpio 디스크립터 변환

gpio_to_desc()

drivers/gpio/gpiolib.c

/**
 * gpio_to_desc - Convert a GPIO number to its descriptor
 * @gpio: global GPIO number
 *
 * Returns:
 * The GPIO descriptor associated with the given GPIO, or %NULL if no GPIO
 * with the given number exists in the system.
 */
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
        struct gpio_device *gdev;
        unsigned long flags;

        spin_lock_irqsave(&gpio_lock, flags);

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

        spin_unlock_irqrestore(&gpio_lock, flags);

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

        return NULL;
}
EXPORT_SYMBOL_GPL(gpio_to_desc);

gpio 번호를 gpio 디스크립터로 변환한다.

  • 코드 라인 14~24에서 gpio_lock 스핀락을 획득한 상태에서 전역 gpio_devices 리스트를 순회하며 인자로 요청한 gpio 번호가 포함된 gpio_device가 관리하고 있는 디스크립터를 반환한다.

 

다음 그림은 446번 gpio를 찾아 디스크립터를 반환하는 모습을 보여준다.

 

desc_to_gpio()

drivers/gpio/gpiolib.c

/**
 * desc_to_gpio - convert a GPIO descriptor to the integer namespace
 * @desc: GPIO descriptor
 *
 * This should disappear in the future but is needed since we still
 * use GPIO numbers for error messages and sysfs nodes.
 *
 * Returns:
 * The global GPIO number for the GPIO specified by its descriptor.
 */
int desc_to_gpio(const struct gpio_desc *desc)
{
        return desc->gdev->base + (desc - &desc->gdev->descs[0]);
}
EXPORT_SYMBOL_GPL(desc_to_gpio);

 

인자로 주어진 gpio 디스크립터를 사용하여 gpio 번호로 변환하여 알아온다.

  • pio 디스크립터에는 gpio_device 구조체를 가리키는 gdev 포인터 멤버가 있다. 이를 통해 gpio_device에 있는 gpio 시작 번호(base)를 기준으로 gpio 디스크립터의 offset 위치를 반환한다.
    • gpio 디스크립터 배열은 base 번호부터 ngpio 수 만큼 일차원 배열로 구성되어 있다.

 

주요 GPIO 디스크립터 기반 APIs

gpio 디스크립터 획득(gpio 디스크립터 요청 및 입/출력 모드 설정)

다음 그림은 gpiod_get() 함수와 gpiod_get_array()가 gpio 핀 또는 gpio 핀들을 사용하기 위해 획득하는 모습을 보여준다.

 

다음 그림은 gpiod_get() 함수와 gpiod_get_array()가 처리되는 함수 호출 구조이다.

 

gpiod_get()

drivers/gpio/gpiolib.c

/**
 * gpiod_get - obtain a GPIO for a given GPIO function
 * @dev:        GPIO consumer, can be NULL for system-global GPIOs
 * @con_id:     function within the GPIO consumer
 * @flags:      optional GPIO initialization flags
 *
 * Return the GPIO descriptor corresponding to the function con_id of device
 * dev, -ENOENT if no GPIO has been assigned to the requested function, or
 * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
 */
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,
                                         enum gpiod_flags flags)
{
        return gpiod_get_index(dev, con_id, 0, flags);
}
EXPORT_SYMBOL_GPL(gpiod_get);

Consumer 디바이스의 con_id 문자열에 해당하는 gpio 디스크립터를 반환한다. 또한 flags 값이 주어진 경우 gpio 디스크립터의 플래그에 관련 플래그를 추가할 수 있다. 모드 설정 관련한 플래그가 주어진 경우 HW도 설정한다.

  • 특정 Consumer 디바이스가 아닌 글로벌 액세스 용도의 gpio들은 dev 값에 null을 사용한다.

 

gpiod_get_index()

drivers/gpio/gpiolib.c

/**
 * gpiod_get_index - obtain a GPIO from a multi-index GPIO function
 * @dev:        GPIO consumer, can be NULL for system-global GPIOs
 * @con_id:     function within the GPIO consumer
 * @idx:        index of the GPIO to obtain in the consumer
 * @flags:      optional GPIO initialization flags
 *
 * This variant of gpiod_get() allows to access GPIOs other than the first
 * defined one for functions that define several GPIOs.
 *
 * Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
 * requested function and/or index, or another IS_ERR() code if an error
 * occurred while trying to acquire the GPIO.
 */
struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
                                               const char *con_id,
                                               unsigned int idx,
                                               enum gpiod_flags flags)
{
        struct gpio_desc *desc = NULL;
        int status;
        enum gpio_lookup_flags lookupflags = 0;

        dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);

        if (dev) {
                /* Using device tree? */
                if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
                        dev_dbg(dev, "using device tree for GPIO lookup\n");
                        desc = of_find_gpio(dev, con_id, idx, &lookupflags);
                } else if (ACPI_COMPANION(dev)) {
                        dev_dbg(dev, "using ACPI for GPIO lookup\n");
                        desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
                }
        }

        /*
         * Either we are not using DT or ACPI, or their lookup did not return
         * a result. In that case, use platform lookup as a fallback.
         */
        if (!desc || desc == ERR_PTR(-ENOENT)) {
                dev_dbg(dev, "using lookup tables for GPIO lookup\n");
                desc = gpiod_find(dev, con_id, idx, &lookupflags);
        }

        if (IS_ERR(desc)) {
                dev_dbg(dev, "lookup for GPIO %s failed\n", con_id);
                return desc;
        }

        status = gpiod_request(desc, con_id);
        if (status < 0)
                return ERR_PTR(status);

        status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
        if (status < 0) {
                dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
                gpiod_put(desc);
                return ERR_PTR(status);
        }

        return desc;
}
EXPORT_SYMBOL_GPL(gpiod_get_index);

Consumer 디바이스의 con_id 문자열에 해당하는 gpio 디스크립터를 반환한다. 또한 flags 값이 주어진 경우 gpio 디스크립터의 플래그에 관련 플래그를 추가할 수 있다. 모드 설정 관련한 플래그가 주어진 경우 HW도 설정한다.

  • 코드 라인 26~35에서 인자로 전달받은 Consumer 디바이스가 주어진 경우 디바이스 트리 또는 ACPI에서 gpio 디스크립터를 구해온다.
    • 특정 Consumer 디바이스가 아닌 글로벌 액세스 용도의 gpio들은 dev 값에 null을 사용한다.
  • 코드 라인 41~44에서 gpio 디스크립터가 발견되지 않은 경우 플랫폼 gpio 룩업 테이블을 통해 검색한다.
    • 전역 gpio_lookup_list에 있는 gpiod_lookup_table 노드에 등록된 디바이스의 문자열과 비교한 후 일치하는 gpio_device에 있는 gpio 디스크립터 배열을 사용하여 찾아온다.

 

gpiod_configure_flags()

drivers/gpio/gpiolib.c

/**
 * gpiod_configure_flags - helper function to configure a given GPIO
 * @desc:       gpio whose value will be assigned
 * @con_id:     function within the GPIO consumer
 * @lflags:     gpio_lookup_flags - returned from of_find_gpio() or
 *              of_get_gpio_hog()
 * @dflags:     gpiod_flags - optional GPIO initialization flags
 *
 * Return 0 on success, -ENOENT if no GPIO has been assigned to the
 * requested function and/or index, or another IS_ERR() code if an error
 * occurred while trying to acquire the GPIO.
 */
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
                unsigned long lflags, enum gpiod_flags dflags)
{
        int status;

        if (lflags & GPIO_ACTIVE_LOW)
                set_bit(FLAG_ACTIVE_LOW, &desc->flags);
        if (lflags & GPIO_OPEN_DRAIN)
                set_bit(FLAG_OPEN_DRAIN, &desc->flags);
        if (lflags & GPIO_OPEN_SOURCE)
                set_bit(FLAG_OPEN_SOURCE, &desc->flags);
        if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE)
                set_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, &desc->flags);

        /* No particular flag request, return here... */
        if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
                pr_debug("no flags found for %s\n", con_id);
                return 0;
        }

        /* Process flags */
        if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
                status = gpiod_direction_output(desc,
                                !!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));
        else
                status = gpiod_direction_input(desc);

        return status;
}

룩업 플래그(lflags)에 설정된 플래그들을 gpio 디스크립터에도 추가 반영한다. 또한 디렉션 플래그dflags)에 따라 gpio 입/출력 모드를 HW에 설정한다.

 

gpiod_request()

drivers/gpio/gpiolib.c

int gpiod_request(struct gpio_desc *desc, const char *label)
{
        int status = -EPROBE_DEFER;
        struct gpio_device *gdev;

        VALIDATE_DESC(desc);
        gdev = desc->gdev;

        if (try_module_get(gdev->owner)) {
                status = __gpiod_request(desc, label);
                if (status < 0)
                        module_put(gdev->owner);
                else
                        get_device(&gdev->dev);
        }

        if (status)
                gpiod_dbg(desc, "%s: status %d\n", __func__, status);

        return status;
}

인자로 전달받은 gpio 디스크립터와 라벨명으로 gpio 핀 사용을 위해 H/W에 필요한 요청을 한다. 다른 사용자가 이미 사용 요청을 먼저한 경우 -EBUSY 에러를 반환한다.

  • 절전 등의 이유로 off, sleep 되어 있는 상태를 해제하도록 요청하는데 이러한 기능이 없으면 HW에 아무런 설정을 하지 않는다.

 

__gpiod_request()

drivers/gpio/gpiolib.c

/* 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.
 */
static int __gpiod_request(struct gpio_desc *desc, const char *label)
{
        struct gpio_chip        *chip = desc->gdev->chip;
        int                     status;
        unsigned long           flags;

        spin_lock_irqsave(&gpio_lock, flags);

        /* 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;
                goto done;
        }

        if (chip->request) {
                /* chip->request may sleep */
                spin_unlock_irqrestore(&gpio_lock, flags);
                status = chip->request(chip, gpio_chip_hwgpio(desc));
                spin_lock_irqsave(&gpio_lock, flags);

                if (status < 0) {
                        desc_set_label(desc, NULL);
                        clear_bit(FLAG_REQUESTED, &desc->flags);
                        goto done;
                }
        }
        if (chip->get_direction) {
                /* chip->get_direction may sleep */
                spin_unlock_irqrestore(&gpio_lock, flags);
                gpiod_get_direction(desc);
                spin_lock_irqsave(&gpio_lock, flags);
        }
done:
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
}

인자로 전달받은 gpio 디스크립터와 라벨명으로 gpio 핀 사용을 위해 H/W에 필요한 요청을 한다. 다른 사용자가 이미 사용 요청을 먼저한 경우 -EBUSY 에러를 반환한다.

  • 코드 라인 11~23에서 스핀락을 획득한 상태에서 요청받은 gpio 디스크립터의 사용 요청 플래그를 검사한다. 만일 이미 누군가 점유하여 사용 중인 경우 -EBUSY 결과로 함수를 빠져나간다. 처음 사용자인 경우 라벨을 지정한다. 라벨명이 없으면 “?”를 사용한다.
  • 코드 라인 25~36에서 gpio controller 후크 함수 (*request)를 통해 HW에 핀 사용 요청을 한다.
    • 절전 등의 이유로 off, sleep 되어 있는 상태를 해제하도록 요청하는데 이러한 기능이 없으면 HW에 아무런 설정을 하지 않는다.
  • 코드 라인 37~42에서 gpio controller 후크 함수 (*get_direction)이 구현되어 있는 경우 HW로 부터 direction 방향을 알아와서 gpio 디스크립터의 flags의 입출력 모드 비트만 싱크시킨다.
    • desc->flags (FLAG_IS_OUT)

 

gpiochip에  소속된 gpio 디스크립터 획득

gpiochip_request_own_desc()

drivers/gpio/gpiolib.c

/**
 * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
 * @chip: GPIO chip
 * @hwnum: hardware number of the GPIO for which to request the descriptor
 * @label: label for the GPIO
 *
 * Function allows GPIO chip drivers to request and use their own GPIO
 * descriptors via gpiolib API. Difference to gpiod_request() is that this
 * function will not increase reference count of the GPIO chip module. This
 * allows the GPIO chip module to be unloaded as needed (we assume that the
 * GPIO chip driver handles freeing the GPIOs it has requested).
 *
 * Returns:
 * A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error
 * code on failure.
 */
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
                        const char *label)
{
    struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
    int err;

    if (IS_ERR(desc)) {
        chip_err(chip, "failed to get GPIO descriptor\n");
        return desc;
    }

    err = __gpiod_request(desc, label);
    if (err < 0)
        return ERR_PTR(err);

    return desc;
}
EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);

인자로 전달받은 gpio_chip에서 gpio HW 핀 번호(hwnum)에 해당하는 gpio 디스크립터를 찾아 gpio 핀 사용 요청을 한다. 다른 사용자가 이미 사용 요청을 먼저한 경우 -EBUSY 에러를 반환한다.

 

GPIO 입력 모드 설정

gpiod_direction_input()

drivers/gpio/gpiolib.c

/*
 * Drivers MUST set GPIO direction before making get/set calls.  In
 * some cases this is done in early boot, before IRQs are enabled.
 *
 * As a rule these aren't called more than once (except for drivers
 * using the open-drain emulation idiom) so these are natural places
 * to accumulate extra debugging checks.  Note that we can't (yet)
 * rely on gpio_request() having been called beforehand.
 */

/**
 * gpiod_direction_input - set the GPIO direction to input
 * @desc:       GPIO to set to input
 *
 * Set the direction of the passed GPIO to input, such as gpiod_get_value() can
 * be called safely on it.
 *
 * Return 0 in case of success, else an error code.
 */
int gpiod_direction_input(struct gpio_desc *desc)
{
        struct gpio_chip        *chip;
        int                     status = -EINVAL;

        VALIDATE_DESC(desc);
        chip = desc->gdev->chip;

        if (!chip->get || !chip->direction_input) {
                gpiod_warn(desc,
                        "%s: missing get() or direction_input() operations\n",
                        __func__);
                return -EIO;
        }

        status = chip->direction_input(chip, gpio_chip_hwgpio(desc));
        if (status == 0)
                clear_bit(FLAG_IS_OUT, &desc->flags);

        trace_gpio_direction(desc_to_gpio(desc), 1, status);

        return status;
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);

인자로 전달받은 gpio 디스크립터에 해당하는 핀을 input 모드로 H/W 설정한다.

  • 디스크립터 플래그 중 FLAG_IS_OUT은 0으로 클리어된다.

 

GPIO 값 읽기

gpiod_get_value()

drivers/gpio/gpiolib.c

/**
 * gpiod_get_value() - return a gpio's value
 * @desc: gpio whose value will be returned
 *
 * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into
 * account, or negative errno on failure.
 *
 * This function should be called from contexts where we cannot sleep, and will
 * complain if the GPIO chip functions potentially sleep.
 */
int gpiod_get_value(const struct gpio_desc *desc)
{
        int value;

        VALIDATE_DESC(desc);
        /* Should be using gpio_get_value_cansleep() */
        WARN_ON(desc->gdev->chip->can_sleep);

        value = _gpiod_get_raw_value(desc);
        if (value < 0)
                return value;

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

        return value;
}
EXPORT_SYMBOL_GPL(gpiod_get_value);

인자로 요청한 gpio 디스크립터에 해당하는 gpio 핀 로지컬 값을 읽어온다.

  • active-low가 동작하는 gpio 입력 회로인 경우 읽은 raw value 값을 뒤집어서 출력한다.
    • raw value가 0이면 active가 된 것이므로 1을 반환한다.
    • raw value가 0이면 active되지 않은 것이므로 0을 반환한다.

 

주의: 아래 legacy API인 gpio_get_value() 함수는 active-low 및 active-high와 관련된 플래그가 없다. 따라서 읽어온 raw value 값을 그대로 반환함을  유의해야 한다.

static inline int gpio_get_value(unsigned int gpio)
{
        return __gpio_get_value(gpio);
}

 

_gpiod_get_raw_value()

drivers/gpio/gpiolib.c

/* I/O calls are only valid after configuration completed; the relevant
 * "is this a valid GPIO" error checks should already have been done.
 *
 * "Get" operations are often inlinable as reading a pin value register,
 * and masking the relevant bit in that register.
 *
 * When "set" operations are inlinable, they involve writing that mask to
 * one register to set a low value, or a different register to set it high.
 * Otherwise locking is needed, so there may be little value to inlining.
 *
 *------------------------------------------------------------------------
 *
 * IMPORTANT!!!  The hot paths -- get/set value -- assume that callers
 * have requested the GPIO.  That can include implicit requesting by
 * a direction setting call.  Marking a gpio as requested locks its chip
 * in memory, guaranteeing that these table lookups need no more locking
 * and that gpiochip_remove() will fail.
 *
 * REVISIT when debugging, consider adding some instrumentation to ensure
 * that the GPIO was actually requested.
 */

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

        chip = desc->gdev->chip;
        offset = gpio_chip_hwgpio(desc);
        value = chip->get ? chip->get(chip, offset) : -EIO;
        value = value < 0 ? value : !!value;
        trace_gpio_value(desc_to_gpio(desc), 1, value);
        return value;
}

인자로 요청한 gpio 디스크립터에 해당하는 gpio 핀 raw 값을 읽어온다. (HW)

 

GPIO 출력 모드 설정

gpio controller가 push-pull 출력만 사용하는 경우에는 gpio 입력 모드 설정과 같이 심플하다. 그러나 single-ended 출력 모드를 지원하는 경우 open-drain과 open-source 둘 중 하나를 선택하여 사용한다.

  • push-pull 출력 모드
  • single-ended 출력 모드
    • open-drain
    • open-source

 

gpio 출력 모드 설정을 위해 자체 gpio controller 개발자는 (*set_config) 후크 함수의 구현에 대해 다음과 같은 선택을 할 수 있다.

  • 1) gpio controller 쪽에 설정 함수를 구현하지 않고, pin controller 쪽에 구현된 pinconf 함수를 호출하도록 generic 인터페이스 함수를 사용한다.
  • 2) 단일 출력 모드만 지원하는 gpio controller인 경우 (*set_config)를 비워둔다.
  • 3) gpio controller 쪽에 설정 함수를 구현하여 호출한다.

 

gpiod_direction_output()

drivers/gpio/gpiolib.c

/**
 * gpiod_direction_output - set the GPIO direction to output
 * @desc:       GPIO to set to output
 * @value:      initial output value of the GPIO
 *
 * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
 * be called safely on it. The initial value of the output must be specified
 * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
 * account.
 *
 * Return 0 in case of success, else an error code.
 */
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
        VALIDATE_DESC(desc);
        if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
                value = !value;
        else
                value = !!value;
        return _gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);

gpio 번호에 해당하는 gpio 핀을 출력모드로 설정하고 로지컬 출력 값을 기록한다. active-low를 사용하는 경우 로지컬 출력 값은 극성이 반대가 되어 raw 출력 값으로 변환된다.

  • gpio 핀이 active-low 또는 active-high 두 가지 방법에 따라 로지컬 출력 값이 high(1)인 경우 실제 gpio 핀에 가해지는 raw 출력 값이 달라진다.
    • active-high: 로지컬 출력 값이 high(1)일 때 gpio 핀에 가해지는 raw 출력 값은 high(1)이다.
    • active-low: 로지컬 출력 값이 high(1)일 때 gpio 핀에 가해지는 raw 출력 값은 high(0)이다.

 

주의: 아래 legacy API인 gpio_direction_output() 함수는 active-low 및 active-high와 관련된 플래그가 없다. 따라서 인자로 전달된 val 값을 그대로 gpio 출력 값에 기록함을 유의해야 한다.

static inline int gpio_direction_output(unsigned gpio, int value)
{       
        return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}

 

gpiod_direction_output_raw()

drivers/gpio/gpiolib.c

/**
 * gpiod_direction_output_raw - set the GPIO direction to output
 * @desc:       GPIO to set to output
 * @value:      initial output value of the GPIO
 *
 * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
 * be called safely on it. The initial value of the output must be specified
 * as raw value on the physical line without regard for the ACTIVE_LOW status.
 *
 * Return 0 in case of success, else an error code.
 */
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
        VALIDATE_DESC(desc);
        return _gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);

gpio 디스크립터에 해당하는 gpio 핀을 출력모드로 설정하고 raw 출력 값을 기록한다.

 

_gpiod_direction_output_raw()

drivers/gpio/gpiolib.c

static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
        struct gpio_chip *gc = desc->gdev->chip;
        int val = !!value;
        int ret;

        /* GPIOs used for IRQs shall not be set as output */
        if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
                gpiod_err(desc,
                          "%s: tried to set a GPIO tied to an IRQ as output\n",
                          __func__);
                return -EIO;
        }

        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
                /* First see if we can enable open drain in hardware */
                ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
                                                  PIN_CONFIG_DRIVE_OPEN_DRAIN);
                if (!ret)
                        goto set_output_value;
                /* Emulate open drain by not actively driving the line high */
                if (val)
                        return gpiod_direction_input(desc);
        }
        else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
                ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
                                                  PIN_CONFIG_DRIVE_OPEN_SOURCE);
                if (!ret)
                        goto set_output_value;
                /* Emulate open source by not actively driving the line low */
                if (!val)
                        return gpiod_direction_input(desc);
        } else {
                gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
                                            PIN_CONFIG_DRIVE_PUSH_PULL);
        }

set_output_value:
        if (!gc->set || !gc->direction_output) {
                gpiod_warn(desc,
                       "%s: missing set() or direction_output() operations\n",
                       __func__);
                return -EIO;
        }

        ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
        if (!ret)
                set_bit(FLAG_IS_OUT, &desc->flags);
        trace_gpio_value(desc_to_gpio(desc), 0, val);
        trace_gpio_direction(desc_to_gpio(desc), 0, ret);
        return ret;
}

gpio 디스크립터에 해당하는 gpio 핀을 출력모드로 설정하고 raw 출력 값을 기록한다.

  • 코드 라인 8~13에서 인자로 전달받은 gpio 디스크립터의 플래그 중 인터럽트 연동이 되어 있는 경우 gpio 핀이 입력 모드로 사용되어야 하므로 -EIO 에러 결과를 반환한다.
  • 코드 라인 15~25에서 gpio 디스크립터 플래그 중 open-drain 플래그가 사용되면 open-drain 설정을 한다. 만일 실패하면 deactivate 상태로 만들기 위해 입력 모드로 설정을 바꿔 놓는다.
  • 코드 라인 26~33에서gpio 디스크립터 플래그 중 open-source 플래그가 사용되면 open-source 설정을 한다. 만일 실패하면 deactivate 상태로 만들기 위해 입력 모드로 설정을 바꿔 놓는다.
  • 코드 라인 35~38에서gpio 디스크립터 플래그에  open-drain 및 open-source 두 플래그가 사용되지 않은 경우에는 push-pull 설정을 한다.
  • 코드 라인 41~46에서 출력 값 설정용 후크 함수(*set)와 출력 모드 설정용 후크 함수 (*direction_output)이 구현되어 있지 않은 경우 경고 메시지를 출력하고 -EIO 값을 반환한다.
  • 코드 라인 48~50에서 출력 모드로 설정(HW)하고 와 출력 값(HW)을 기록한다. 그런 후 gpio 디스크립터의 플래그에 FLAG_IS_OUT 비트를 설정한다.

 

gpio_set_drive_single_ended()

drivers/gpio/gpiolib.c

static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset,
                                       enum pin_config_param mode)
{
        unsigned long config = { PIN_CONF_PACKED(mode, 0) };

        return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
}

gpio 핀에 대한 설정을 한다.

  • 이 함수에 사용할 수 있는 파라메터는 모든 설정이지만 실제 이 함수의 목적은 다음 3가지 출력 모드 중 하나를 사용한다.
    • PIN_CONFIG_DRIVE_PUSH_PULL
    • PIN_CONFIG_DRIVE_OPEN_DRAIN
    • PIN_CONFIG_DRIVE_OPEN_SOURCE

 

gpiochip_generic_config()

drivers/gpio/gpiolib.c

/**
 * gpiochip_generic_config() - apply configuration for a pin
 * @chip: the gpiochip owning the GPIO
 * @offset: the offset of the GPIO to apply the configuration
 * @config: the configuration to be applied
 */
int gpiochip_generic_config(struct gpio_chip *chip, unsigned offset,
                            unsigned long config)
{
        return pinctrl_gpio_set_config(chip->gpiodev->base + offset, config);
}
EXPORT_SYMBOL_GPL(gpiochip_generic_config);

pin에 대한 설정을 하기 위해 pin contrller에 구현된 pinconf 함수를 호출한다.

 

GPIO 값 설정

gpiod_set_value()

drivers/gpio/gpiolib.c

/**
 * gpiod_set_value() - assign a gpio's value
 * @desc: gpio whose value will be assigned
 * @value: value to assign
 *
 * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
 * account
 *
 * This function should be called from contexts where we cannot sleep, and will
 * complain if the GPIO chip functions potentially sleep.
 */
void gpiod_set_value(struct gpio_desc *desc, int value)
{
        VALIDATE_DESC_VOID(desc);
        /* Should be using gpiod_set_value_cansleep() */
        WARN_ON(desc->gdev->chip->can_sleep);
        if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
                value = !value;
        _gpiod_set_raw_value(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_set_value);

인자로 주어진 gpio 디스크립터에 연결된 gpio 핀에 로지컬 value 값을 기록한다.

  • active-low 플래그 설정이 된 경우 로지컬 value를 반전시킨 raw value를 기록한다.

 

주의: 아래 legacy API인 gpio_set_value() 함수는 active-low 및 active-high와 관련된 플래그가 없다. 따라서 인자로 주어진 value 값을 그대로 raw value 값으로 기록함을 유의해야 한다.

static inline void gpio_set_value(unsigned int gpio, int value)
{
        __gpio_set_value(gpio, value);
}

 

_gpiod_set_raw_value()

drivers/gpio/gpiolib.c

static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
{
        struct gpio_chip        *chip;

        chip = desc->gdev->chip;
        trace_gpio_value(desc_to_gpio(desc), 0, value);
        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
                _gpio_set_open_drain_value(desc, value);
        else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
                _gpio_set_open_source_value(desc, value);
        else
                chip->set(chip, gpio_chip_hwgpio(desc), value);
}

인자로 주어진 gpio 디스크립터에 연결된 gpio 핀에 raw value 값을 기록한다.

  • 코드 라인 7~8에서  open drain gpio 핀에 값을 지정한다.
    • 인자로 주어진 value 값이 low(0)인 경우 output 모드로 설정하고 0 값을 출력한다. 그렇지 않은 경우 입력 모드로 설정한다.
  • 코드 라인 9~10에서  open source gpio 핀에 값을 지정한다.
    • 인자로 주어진 value 값이 high(1)인 경우 output 모드로 설정하고 1 값을 출력하다. 그렇지 않은 경우 입력 모드로 설정한다.
  • 코드 라인 11~12에서 push-pull gpio 핀에 value를 설정한다.

 

_gpio_set_open_drain_value()

drivers/gpio/gpiolib.c

/*
 *  _gpio_set_open_drain_value() - Set the open drain gpio's value.
 * @desc: gpio descriptor whose state need to be set.
 * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
 */
static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
{
        int err = 0;
        struct gpio_chip *chip = desc->gdev->chip;
        int offset = gpio_chip_hwgpio(desc);

        if (value) {
                err = chip->direction_input(chip, offset);
                if (!err)
                        clear_bit(FLAG_IS_OUT, &desc->flags);
        } else {
                err = chip->direction_output(chip, offset, 0);
                if (!err)
                        set_bit(FLAG_IS_OUT, &desc->flags);
        }
        trace_gpio_direction(desc_to_gpio(desc), value, err);
        if (err < 0)
                gpiod_err(desc,
                          "%s: Error in set_value for open drain err %d\n",
                          __func__, err);
}

open drain gpio 핀에 값을 설정하기 위해 인자로 주어진 value 값이 low(0)인 경우 output 모드로 설정하고 0 값을 출력한다. 그렇지 않은 경우 입력 모드로 설정한다.

 

_gpio_set_open_source_value()

drivers/gpio/gpiolib.c

/*
 *  _gpio_set_open_source_value() - Set the open source gpio's value.
 * @desc: gpio descriptor whose state need to be set.
 * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
 */
static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
{
        int err = 0;
        struct gpio_chip *chip = desc->gdev->chip;
        int offset = gpio_chip_hwgpio(desc);

        if (value) {
                err = chip->direction_output(chip, offset, 1);
                if (!err)
                        set_bit(FLAG_IS_OUT, &desc->flags);
        } else {
                err = chip->direction_input(chip, offset);
                if (!err)
                        clear_bit(FLAG_IS_OUT, &desc->flags);
        }
        trace_gpio_direction(desc_to_gpio(desc), !value, err);
        if (err < 0)
                gpiod_err(desc,
                          "%s: Error in set_value for open source err %d\n",
                          __func__, err);
}

open source gpio 핀에 값을 설정하기 위해 인자로 주어진 value 값이 high(1)인 경우 output 모드로 설정하고 1 값을 출력한다. 그렇지 않은 경우 입력 모드로 설정한다.

 

참고

 

 

6 thoughts to “GPIO Subsystem -1-”

  1. 안녕하세요. 좋은글 감사합니다.

    한가지 질문이 있는데요

    리눅스 GPIO핀에 센서를 연결하고나서

    센서의 비정상적인 동작을 센서 케이블의 단선으로 가정한다면

    GPIO핀에 센서가 연결되었는지 안되었는지 확인하는 방법이 있는지 궁금합니다.

    하드웨어적으로는 방법이 있는것 같은데

    소프트웨어적으로 발견할 수 있는지 알고싶습니다.

  2. 안녕하세요?

    GPIO 1 라인을 통신 목적으로 사용하는 경우에는 장비의 탈착 여부를 감시할 방법이 없습니다.
    따라서 하드웨어 설계 시 GPIO 1 라인을 추가하여 장비의 접속 여부를 파악하셔야 합니다.
    불행히도 소프트웨어 로직만으로는 통신이 동작하는 정상 링크와 그렇지 않은 링크 단절만 파악할 수 있습니다.

  3. 안녕하세요? F5를 눌러 다시 갱신 시도를 해보시기 바랍니다. 혹시 여전히 엑박이 나오시면 어떤 그림인지 알려주시면 고맙겠습니다.

    1. 디바이스 모델 구현에서 CASE B)가 엑박이 뜹니다. F5해도 계속 엑박인 것 같습니다.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다