gpio 데이터를 매핑하여 사용할 수 있는 방법은 다음과 같이 약 3가지 방법이 있다.
- 디바이스 트리로 정의 (가장 최근 Trend)
- ACPI 마저도 등록하여 사용할 수 있다.
- Legacy Board/Machine specific code에서 플랫폼 데이터로 정의
- 사실 GPIO 매핑을 플랫폼 데이터에 등록하여 사용하는 경우는 많지 않았다. 그냥 핀 번호로 사용하는 경우가 대부분이었다.
- ACPI 펌웨어 테이블에 정의
디바이스 트리를 사용하는 GPIO 매핑
GPIO Controller 노드
디바이스 노드 내부에 “gpio-controller” 속성이 있으면 GPIO Controller 노드를 의미한다.
cell 개수
#gpio-cells = <2> 속성은 two-cell 데이터 argument를 사용함을 의미한다.
- gpio1 controller는 2개의 cell을 사용하여 디바이스 드라이버가 2개의 argument를 받아 처리한다.
- gpio2 controller는 1개의 cell을 사용하여 디바이스 드라이버가 1개의 argument를 받아 처리한다.
- 지정되지 않는 경우 2 cell 방식을 사용한다.
gpio1: gpio1 { gpio-controller; #gpio-cells = <2>; }; gpio2: gpio2 { gpio-controller; #gpio-cells = <1>; }; [...] enable-gpios = <&gpio2 2>; data-gpios = <&gpio1 12 0>, <&gpio1 13 0>, <&gpio1 14 0>, <&gpio1 15 0>;
Pin control subsystem과의 연동
pin control 서브시스템과의 연동은 gpio controller 노드에서 “gpio-ranges” 속성을 사용하여 한다. 다음 코드를 알아보자.
“gpio-ranges” 속성이 가리키는 phandle은 연계된 pin controller 노드를 가리켜야 한다. 그리고 1~3개의 인자를 사용할 수 있으며, 배열 사용을 지원한다.
- 1~2개 인자를 사용하는 경우 반드시 “gpio-ranges-group-names” 속성을 사용하여야 한다.
- 첫 번째 인자: 시작 gpio 번호(offset)
- 두 번째 인자: “gpio-ranges-group-names” 속성에서 지정한 그룹명에 해당하는 pin 시작 번호와 개수를 사용한다. 따라서 이 항목이 사용되는 경우에는 반드시 0이어야 한다.
gpio-ranges = <&pinctrl 0 0>; gpio-ranges-group-names = "gpioa"
- 3개 인자를 사용하는 경우 “gpio-ranges-group-names” 속성이 필요 없고, 사용되더라도 빈 문자열이어야 한다.
- 첫 번째 인자: 시작 gpio 번호(offset)
- 두 번째 인자: 시작 pin 번호(offset)
- 세 번째 인자: gpio 개수
gpio-ranges = <&pinctrl 0 0 16>;
다음은 배열 형태로 사용된 경우이다.
gpio-ranges = <&pinctrl 0 16 3>, <&pinctrl 14 30 2>;
다음 사용 사례를 살펴본다.
- iomux pin controller 노드
- pctl-gpio-a 노드는 pinmux 노드 2개를 선언하였다.
- 이 벤더의 디바이스 트리 노드 맵 변환은 고유의 방법을 사용하는데 pins 속성이나 groups 속성이 지정되지 않았음을 알 수 있다. 이러한 경우 consumer 디바이스 측에서 사용하는 compact 노드명을 그대로 그룹명으로 사용한다. 아래 두 consumer 디바이스의 노드명을 보면 “uart@FF100000″과 “gpio@FF140000″이 있는데 compact 노드명을 그룹명으로 사용하므로 이들은 각각 “uart”와 “gpio” 그룹명이 된다.
- uart (pin control consumer) 노드
- “default” 스테이트를 사용하여 pinctrl-0 속성에서 지정된 phandle 노드로 pinmux/pinconf 매핑을 등록한다. “uart” 그룹과 pinmux 노드에서 정의한 “uart0” 펑션을 매핑한다.
- 관련 함수: drivers/pinctrl/pinctrl-tb10x.c – tb10x_dt_node_to_map()
- gpioa (pin control consumer) 노드
- “gpio-ranges” 속성을 사용하여 iomux 핀 controller 노드와 연계한다.
- gpio 0번부터 pin controller에서 구현된 그룹명들 중 “gpio-ranges-group-names” 속성에서 지정한 “gpioa” 그룹명에 해당하는 핀들을 gpio 핀들로 등록하게 한다.
iomux: iomux@FF10601c { compatible = "abilis,tb10x-iomux"; reg = <0xFF10601c 0x4>; pctl_gpio_a: pctl-gpio-a { abilis,function = "gpioa"; }; pctl_uart0: pctl-uart0 { abilis,function = "uart0"; }; }; uart@FF100000 { compatible = "snps,dw-apb-uart"; reg = <0xFF100000 0x100>; clock-frequency = <166666666>; interrupts = <25 1>; reg-shift = <2>; reg-io-width = <4>; pinctrl-names = "default"; pinctrl-0 = <&pctl_uart0>; }; gpioa: gpio@FF140000 { compatible = "abilis,tb10x-gpio"; reg = <0xFF140000 0x1000>; gpio-controller; #gpio-cells = <2>; ngpios = <3>; gpio-ranges = <&iomux 0 0>; gpio-ranges-group-names = "gpioa"; };
line 명 지정
gpio controller를 등록하기 전에 gpio_chip->ngpios에 gpio 개수를 등록하거나 “ngpios” 속성을 사용하여 gpio 핀 개수를 지정하여 gpio controller가 사용하는 gpio 핀 개수를 먼저 알고 있어야한다. 그 후 “gpio-line-names” 속성을 사용하여 gpio 핀 개수만큼 gpio 디스크립터들의 이름을 지정한다.
- gpio 디스크립터들의 핀명이 gpio 디바이스 드라이버의 내부 코드로 이미 정해져있는 경우에는 별도로 “gpio-line-names” 속성을 사용할 필요 없다.
gpio-controller@00000000 { compatible = "foo"; reg = <0x00000000 0x1000>; gpio-controller; #gpio-cells = <2>; ngpios = <18>; gpio-line-names = "MMC-CD", "MMC-WP", "VDD eth", "RST eth", "LED R", "LED G", "LED B", "Col A", "Col B", "Col C", "Col D", "Row A", "Row B", "Row C", "Row D", "NMI button", "poweroff", "reset"; }
hog 처리
gpio 컨트롤러의 하위 노드들에서 “gpio-hog” 속성이 발견되는 경우 지정된 gpio 핀 상태를 즉시 설정한다. 커널 v.4.1-rc1에 추가되었다.
- “gpios” 속성에 해당하는 argument의 처리는 벤더가 제공하는 gpio 컨트롤러에서 구현되는데, 대부분의 벤더들은 이의 구현을 하지 않고 gpio simple 변환을 사용한다.
- 2개의 셀을 사용하는 gpio simple 변환은 gpio 시작 번호(offset)를 변환없이 그대로 사용하고, gpio 설정 값에 해당하는 두 번째 인수 역시 그대로 변환 없이 사용한다.
- 참고: gpio: add GPIO hogging mechanism
qe_pio_a: gpio-controller@1400 { compatible = "fsl,qe-pario-bank-a", "fsl,qe-pario-bank"; reg = <0x1400 0x18>; gpio-controller; #gpio-cells = <2>; line_b { gpio-hog; gpios = <6 0>; output-low; line-name = "foo-bar-gpio"; }; };
GPIO Client 노드 (for Consumer)
다음과 같이 “X-gpios = <&gpio_controller Y Z>;” 속성 형식을 사용하는 X 이름을 갖는 gpio 매핑은 Y와 Z 두 개의 셀을 데이터 argument를 사용하는데, 첫 번째 argument는 보통 gpio 번호, 두 번째는 상태를 지정한다.
- led라는 이름으로 0~2번 인덱스 각각에 15~17번 핀을 active_high 상태로 동작하도록 매핑한다.
- power라는 이름으로 1번 핀을 active_low 상태로 동작하도록 매핑한다.
- GPIO_ACTIVE_LOW 상태에 대해서는 착각하기 쉬우므로 주의해야 한다.
foo_device { compatible = "acme,foo"; ... led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */ <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */ <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */ power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; };
코드에서 위의 매핑을 불러서 동작 시키고자할 때에는 다음과 같이 호출한다.
- led 라는 이름의 gpio 0~2번 인덱스의 로지컬 출력을 high로 설정한다.
- 매핑 시 active_high에서 동작하게 하였으므로 로지컬 출력을 high로 하면 gpio 핀은 물리적 high 상태가 된다. 의미적으로는 led가 점등(on)한다.
- power라는 이름의 gpio 로지컬 출력을 high로 설정한다.
- 매핑 시 active_low에서 동작하게 하였으므로 로지컬 출력을 high로 하면 gpio 핀은 물리적 low 상태가 된다. 의미적으로는 파워가 켜진다.(on)
struct gpio_desc *red, *green, *blue, *power; red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH); green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH); blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH); power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);
참고
- Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt | kernel.org
- Documentation/devicetree/bindings/pinctrl/brcm,iproc-gpio.txt | kernel.org
ACPI 펌웨어를 사용하는 GPIO 매핑
디바이스 트리를 사용하는 방법과 아주 유사한 방법으로 아래의 ACPI 디스크립션을 사용하는 방법이 있다. ACPI 5.1에서 소개된 _DSD (Device Specific Data)를 참조하기 바란다.
Device (FOO) { Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, ..., IoRestrictionOutputOnly, "\\_SB.GPI0") {15} // red GpioIo (Exclusive, ..., IoRestrictionOutputOnly, "\\_SB.GPI0") {16} // green GpioIo (Exclusive, ..., IoRestrictionOutputOnly, "\\_SB.GPI0") {17} // blue GpioIo (Exclusive, ..., IoRestrictionOutputOnly, "\\_SB.GPI0") {1} // power }) Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "led-gpios", Package () { ^FOO, 0, 0, 1, ^FOO, 1, 0, 1, ^FOO, 2, 0, 1, } }, Package () { "power-gpios", Package () {^FOO, 3, 0, 0}, }, } }) }
Platform 데이터에 GPIO 매핑 (deprecated)
일부 시스템에서 다음 매크로 함수 및 API를 사용하여 매핑을 플랫폼 데이터에 저장(bound)한 후 이를 lookup하여 사용하였었는데 지금은 사용하지 않는 방법이다.
GPIO_LOOKUP(chip_label, chip_hwnum, con_id, flags) GPIO_LOOKUP_IDX(chip_label, chip_hwnum, con_id, idx, flags)
gpio 룩업 테이블 정의를 한 후
struct gpiod_lookup_table gpios_table = { .dev_id = "foo.0", .table = { GPIO_LOOKUP_IDX("gpio.0", 15, "led", 0, GPIO_ACTIVE_HIGH), GPIO_LOOKUP_IDX("gpio.0", 16, "led", 1, GPIO_ACTIVE_HIGH), GPIO_LOOKUP_IDX("gpio.0", 17, "led", 2, GPIO_ACTIVE_HIGH), GPIO_LOOKUP("gpio.0", 1, "power", GPIO_ACTIVE_LOW), { }, }, };
다음 함수로 등록을 한다.
gpiod_add_lookup_table(&gpios_table);
그런 후 아래와 같은 방식으로 호출하여 사용할 수 있다.
struct gpio_desc *red, *green, *blue, *power; red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH); green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH); blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH); power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);
디바이스 트리 소스
gpio controller 노드를 파싱하고 추가할 때의 함수 호출관계이다.
of_gpiochip_add()
drivers/gpio/gpiolib-of.c
int of_gpiochip_add(struct gpio_chip *chip) { int status; if ((!chip->of_node) && (chip->parent)) chip->of_node = chip->parent->of_node; if (!chip->of_node) return 0; if (!chip->of_xlate) { chip->of_gpio_n_cells = 2; chip->of_xlate = of_gpio_simple_xlate; } if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) return -EINVAL; status = of_gpiochip_add_pin_range(chip); if (status) return status; /* If the chip defines names itself, these take precedence */ if (!chip->names) devprop_gpiochip_set_names(chip); of_node_get(chip->of_node); return of_gpiochip_scan_gpios(chip); }
gpio controller 오퍼레이션을 포함한 gpio_chip 구조체를 등록하고 디바이스 트리의 gpio controller 노드에서 각종 속성들을 파싱하여 처리한다.
- 코드 라인 5~9 인자로 전달받은 gpio_chip 구조체에 디바이스 노드가 지정되지 않은 경우 부모 gpio controller 디바이스의 디바이스 노드를 알아와서 사용한다. 만일 디바이스 노드가 없는 경우 이 함수를 처리하지 않고 에러 없이 빠져나간다.
- 코드 라인 11~13에서 gpio_chip의 (*of_xlate) 후크 함수가 구현되지 않은 경우 two cell 방식의 기본 함수인 og_gpio_simple_xlate()를 사용한다.
- 코드 라인 16~17에서 gpio cell 수가 MAX_PHANDLE_ARGS(16)을 초과하는 경우 에러 결과를 반환한다.
- 코드 라인 19~21에서 pin controller 노드와 연결되어 사용되는 gpio controller인 경우 “gpio-ranges” 속성을 사용하여 연계할 pin controller의 지정된 핀들을 gpio 컨트롤러에 등록한다.
- 코드 라인 24~25에서 gpio controller 노드에서 gpio 이름이 주어지지 않은 경우 부모 gpio controller 노드의 “gpio-line-names” 속성 값들을 읽어서 gpio 디스크립터들의 name으로 사용한다.
- gpio controller 노드에서 “X-gpios ” 속성을 사용하지 않은 경우 “gpio-line-names” 속성에서 사용한 이름들을 gpio 이름으로 가져온다.
- 코드 라인 29에서 gpio controller 노드의 서브 노드에서 “gpio-hog” 속성을 발견하는 경우 hog 매핑들을 읽어온다.
of_gpiochip_add_pin_range()
drivers/gpio/gpiolib-of.c
#ifdef CONFIG_PINCTRL static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { struct device_node *np = chip->of_node; struct of_phandle_args pinspec; struct pinctrl_dev *pctldev; int index = 0, ret; const char *name; static const char group_names_propname[] = "gpio-ranges-group-names"; struct property *group_names; if (!np) return 0; group_names = of_find_property(np, group_names_propname, NULL); for (;; index++) { ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, index, &pinspec); if (ret) break; pctldev = of_pinctrl_get(pinspec.np); of_node_put(pinspec.np); if (!pctldev) return -EPROBE_DEFER; if (pinspec.args[2]) { if (group_names) { of_property_read_string_index(np, group_names_propname, index, &name); if (strlen(name)) { pr_err("%pOF: Group name of numeric GPIO ranges must be the empty string.\n", np); break; } } /* npins != 0: linear range */ ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), pinspec.args[0], pinspec.args[1], pinspec.args[2]); if (ret) return ret; } else { /* npins == 0: special range */ if (pinspec.args[1]) { pr_err("%pOF: Illegal gpio-range format.\n", np); break; } if (!group_names) { pr_err("%pOF: GPIO group range requested but no %s property.\n", np, group_names_propname); break; } ret = of_property_read_string_index(np, group_names_propname, index, &name); if (ret) break; if (!strlen(name)) { pr_err("%pOF: Group name of GPIO group range cannot be the empty string.\n", np); break; } ret = gpiochip_add_pingroup_range(chip, pctldev, pinspec.args[0], name); if (ret) return ret; } } return 0; } #else static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; } #endif
“gpio-ranges” 속성을 사용하여 연계할 pin controller의 지정된 핀들을 gpio 컨트롤러에 등록한다.
- 코드 라인 15에서 “gpio-ranges-group-names” 속성 값을 알아온다.
- 코드 라인 17~21에서 “gpio-ranges” 속성이 배열로 지정될 수 있으므로 반복하며 읽는다.
- 코드 라인 23~26에서 phandle 값으로 지정한 pin controller 디바이스를 알아온다.
- 코드 라인 28~38에서 3개의 argument를 사용한 경우 “gpio-ranges-group-names” 속성 값이 사용된 경우 빈문자열이어야 한다.
- 코드 라인 40~46에서 gpio chip 정보에 핀 range를 등록한다.
- 첫 번째 인수부터 gpio 시작 번호(offset), pin 시작 번호(offset), gpio 개수이다.
- 코드 라인 47~77에서 그 밖의 argument 개수를 사용한 경우 두 번째 인수는 항상 0이어야 한다. “gpio-ranges-group-names” 속성이 반드시 사용되어야 하며 해당 그룹명을 읽어와서 그 그룹에 해당하는 pin들로 gpio chip 정보에 핀 range를 등록한다.
devprop_gpiochip_set_names()
drivers/gpio/gpiolib-devprop.c
/** * devprop_gpiochip_set_names - Set GPIO line names using device properties * @chip: GPIO chip whose lines should be named, if possible * * Looks for device property "gpio-line-names" and if it exists assigns * GPIO line names for the chip. The memory allocated for the assigned * names belong to the underlying firmware node and should not be released * by the caller. */ void devprop_gpiochip_set_names(struct gpio_chip *chip) { struct gpio_device *gdev = chip->gpiodev; const char **names; int ret, i; if (!chip->parent) { dev_warn(&gdev->dev, "GPIO chip parent is NULL\n"); return; } ret = device_property_read_string_array(chip->parent, "gpio-line-names", NULL, 0); if (ret < 0) return; if (ret != gdev->ngpio) { dev_warn(chip->parent, "names %d do not match number of GPIOs %d\n", ret, gdev->ngpio); return; } names = kcalloc(gdev->ngpio, sizeof(*names), GFP_KERNEL); if (!names) return; ret = device_property_read_string_array(chip->parent, "gpio-line-names", names, gdev->ngpio); if (ret < 0) { dev_warn(chip->parent, "failed to read GPIO line names\n"); kfree(names); return; } for (i = 0; i < gdev->ngpio; i++) gdev->descs[i].name = names[i]; kfree(names); }
gpio controller 노드에서 gpio 이름이 주어지지 않은 경우 부모 gpio controller 노드의 “gpio-line-names” 속성 값들을 읽어서 gpio 디스크립터들에서 gpio명으로 사용한다.
- gpio controller 노드에서 “X-gpios ” 속성을 사용하지 않은 경우 “gpio-line-names” 속성에서 지정한 이름들을 사용한다.
- 코드 라인 16~19에서 부모 gpio controller가 없는 경우 경고 메시지를 출력 후 함수를 빠져나간다.
- 코드 라인 21~24에서 부모 gpio controller 노드에서 “gpio-line-names” 속성을 찾아 없으면 함수를 빠져나간다.
- 코드 라인 26~31에서 gpio 컨트롤러에 등록된 gpio 수와 “gpio-line-names” 속성에서 읽어들인 개수가 다르면 경고 메시지를 출력 후 함수를 빠져나간다.
- 코드 라인 33~35에서 “gpio-line-names” 속성에서 읽은 라인 명 개수 만큼 문자열 포인터 배열을 준비한다.
- 코드 라인 37~46에서 “gpio-line-names” 속성에서 문자열을 읽어 names[] 배열에 대입하게 한다. 그 후 그 수 만큼 gpio 디바이스의 디스크립터들 이름으로 지정한다.
of_gpiochip_scan_gpios()
drivers/gpio/gpiolib-of.c
/** * of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions * @chip: gpio chip to act on * * This is only used by of_gpiochip_add to request/set GPIO initial * configuration. * It returns error if it fails otherwise 0 on success. */ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) { struct gpio_desc *desc = NULL; struct device_node *np; const char *name; enum gpio_lookup_flags lflags; enum gpiod_flags dflags; unsigned int i; int ret; for_each_available_child_of_node(chip->of_node, np) { if (!of_property_read_bool(np, "gpio-hog")) continue; for (i = 0;; i++) { desc = of_parse_own_gpio(np, chip, i, &name, &lflags, &dflags); if (IS_ERR(desc)) break; ret = gpiod_hog(desc, name, lflags, dflags); if (ret < 0) { of_node_put(np); return ret; } } } return 0; }
gpio controller 노드의 서브 노드들에서 “gpio_hog” 속성이 발견되면 지정된 gpio 핀 상태를 즉시 설정한다.
- 코드 라인 19~21에서 gpio controller 노드의 서브 노드들에서 “gpio_hog” 속성이 발견되지 않으면 skip 한다.
- 코드 라인 23~27에서 gpio hog 노드를 파싱하여 이름을 name에 대입하고, 로지컬 플래그 lflags에 대입한다. 그리고 파싱한 디렉션 플래그를 dflags에 대입한다.
- 코드 라인 29~30에서 gpio controller 디바이스를 통해 위에서 파싱한 정보를 HW에 설정한다.
of_parse_own_gpio()
drivers/gpio/gpiolib-of.c
/** * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API * @np: device node to get GPIO from * @chip: GPIO chip whose hog is parsed * @idx: Index of the GPIO to parse * @name: GPIO line name * @lflags: gpio_lookup_flags - returned from of_find_gpio() or * of_parse_own_gpio() * @dflags: gpiod_flags - optional GPIO initialization flags * * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno * value on the error condition. */ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, struct gpio_chip *chip, unsigned int idx, const char **name, enum gpio_lookup_flags *lflags, enum gpiod_flags *dflags) { struct device_node *chip_np; enum of_gpio_flags xlate_flags; struct of_phandle_args gpiospec; struct gpio_desc *desc; unsigned int i; u32 tmp; int ret; chip_np = chip->of_node; if (!chip_np) return ERR_PTR(-EINVAL); xlate_flags = 0; *lflags = 0; *dflags = 0; ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp); if (ret) return ERR_PTR(ret); gpiospec.np = chip_np; gpiospec.args_count = tmp; for (i = 0; i < tmp; i++) { ret = of_property_read_u32_index(np, "gpios", idx * tmp + i, &gpiospec.args[i]); if (ret) return ERR_PTR(ret); } desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags); if (IS_ERR(desc)) return desc; if (xlate_flags & OF_GPIO_ACTIVE_LOW) *lflags |= GPIO_ACTIVE_LOW; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; else if (of_property_read_bool(np, "output-low")) *dflags |= GPIOD_OUT_LOW; else if (of_property_read_bool(np, "output-high")) *dflags |= GPIOD_OUT_HIGH; else { pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n", desc_to_gpio(desc), np->name); return ERR_PTR(-EINVAL); } if (name && of_property_read_string(np, "line-name", name)) *name = np->name; return desc; }
gpio hog 노드를 파싱하여 출력 인자 3개에 결과를 대입하고 해당 디스크립터를 찾아온다. 자세한 것은 다음과 같다.
- “line-name” 속성 값을 출력 인자 name에 대입
- “gpios” 속성값을 파싱하여 디스크립터를 찾아 반환하고, 파싱한 설정 값에 OF_GPIO_ACTIVE_LOW(1) 플래그가 발견되면 로지컬 플래그에 해당하는 출력 인자 lflags에 GPIO_ACTIVE_LOW(1)를 대입
- “input”, “output-low”, “output-high” 속성 중 하나에 해당하는 플래그 비트를 디렉션 플래그에 해당하는 출력인자 dflags에 대입
- 코드 라인 28~30에서 gpio controller에 해당하는 노드가 없으면 에러로 함수를 빠져나간다.
- 코드 라인 36~42에서 gpio controller 노드에서 “#gpio-cells” 속성 값을 알아와서 argument 정보를 설정한다.
- 코드 라인 44~49에서 hog 노드에서 argument 수 만큼 “gpios” 속성 값을 읽어서 이 역시 argument 정보에 설정한다.
- 코드 라인 51~53에서 hog 노드에서 “gpios”의 argument를 파싱하여 시작 gpio 번호와 파싱된 플래그 값을 xlate_flags에 대입한다.
- 코드 라인 55~56에서 읽어온 xlate_flags에 ACTIVE_LOW(1) 플래그가 담겨 있으면 lflags에 설정한다.
- 코드 라인 58~68에서 “input”, “output-low” 그리고 “output-high”와 같은 세가지 속성 중 하나를 찾아 dflags에 대입한다. 발견하지 못하면 경고 메시지를 출력하고 에러로 함수를 빠져나간다.
- 코드 라인 70~73에서 “line-name” 속성을 찾아 출력 인자 name에 대입한 후 디스크립터를 반환한다.
- 이 이름은 추후 gpio 디스크립터의 label로 지정된다.
gpiod_hog()
drivers/gpio/gpiolib-of.c
/** * gpiod_hog - Hog the specified GPIO desc given the provided flags * @desc: gpio whose value will be assigned * @name: gpio line name * @lflags: gpio_lookup_flags - returned from of_find_gpio() or * of_get_gpio_hog() * @dflags: gpiod_flags - optional GPIO initialization flags */ int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags) { struct gpio_chip *chip; struct gpio_desc *local_desc; int hwnum; int status; chip = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); local_desc = gpiochip_request_own_desc(chip, hwnum, name); if (IS_ERR(local_desc)) { status = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", name, chip->label, hwnum, status); return status; } status = gpiod_configure_flags(desc, name, lflags, dflags); if (status < 0) { pr_err("setup of hog GPIO %s (chip %s, offset %d) failed, %d\n", name, chip->label, hwnum, status); gpiochip_free_own_desc(desc); return status; } /* Mark GPIO as hogged so it can be identified and removed later */ set_bit(FLAG_IS_HOGGED, &desc->flags); pr_info("GPIO line %d (%s) hogged as %s%s\n", desc_to_gpio(desc), name, (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? (dflags&GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low":""); return 0; }
hog 노드를 파싱하여 얻은 인자들을 gpio conrtroller를 통해 H/W 설정한다.
- 코드 라인 17~26에서 gpio 디스크립터로 gpio controller와 hwgpio 번호를 알아온다. 그 후 이에 해당하는 로컬 gpio 디스크립터를 알아온다.
- 코드 라인 28~34에서 룩업 플래그(lflags)에 설정된 플래그들을 gpio 디스크립터에도 추가 반영한다. 또한 디렉션 플래그dflags)에 따라 gpio 입/출력 모드를 HW에 설정한다.
- 코드 라인37~45에서 gpio 디스크립터에 hog 처리 플래그를 추가하고 hog 정보를 출력한 후 성공(0)을 반환한다.
디바이스 트리를 사용한 gpio 디스크립터 검색
of_find_gpio()
drivers/gpio/gpiolib-of.c
struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) { char prop_name[32]; /* 32 is max size of property name */ enum of_gpio_flags of_flags; struct gpio_desc *desc; unsigned int i; for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, gpio_suffixes[i]); else snprintf(prop_name, sizeof(prop_name), "%s", gpio_suffixes[i]); desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags); if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) break; } if (IS_ERR(desc)) return desc; if (of_flags & OF_GPIO_ACTIVE_LOW) *flags |= GPIO_ACTIVE_LOW; if (of_flags & OF_GPIO_SINGLE_ENDED) { if (of_flags & OF_GPIO_OPEN_DRAIN) *flags |= GPIO_OPEN_DRAIN; else *flags |= GPIO_OPEN_SOURCE; } if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE) *flags |= GPIO_SLEEP_MAY_LOOSE_VALUE; return desc; }
지정한 디바이스 트리 노드에서 인자로 전달 받은 con_id 문자열과 관련된 속성명을 파싱하여 gpio 디스크립터를 찾아온다.
- 코드 라인 10~25에서 인자로 전달받은 디바이스 노드에서 “<con_id>-gpios” 및 “<con_id>-gpio” 속성명으로 검색한다.
- <con_id>가 없는 경우 특정 Consumer 디바이스용이 아닌 글로벌 용도 이므로 “gpios” 및 “gpio” 속성명으로 검색한다.
- 코드 라인 27~28에서 디바이스 트리 노드에서 파싱한 플래그 값이 low active 방식을 사용하는 경우 출력 인자 flags에도 GPIO_ACTIVE_LOW 플래그를 추가한다.
- 코드 라인 30~35에서 디바이스 트리 노드에서 MOSFET 구성이 push-pull이 아니라 single ended 방식인 경우 파싱한 플래그 값이 open drain 사용 유무에 따라 출력 인자 flags에도 open drain 또는 open source 플래그를 추가한다.
- 코드 라인 37~38에서 디바이스 트리 노드에서 파싱한 플래그 값이 sleep시 value를 잃어버린다고 설정되어 있는 경우 출력 인자 flags에도 플래그를 추가한다.
/** * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API * @np: device node to get GPIO from * @propname: property name containing gpio specifier(s) * @index: index of the GPIO * @flags: a flags pointer to fill in * * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags) { struct of_phandle_args gpiospec; struct gpio_chip *chip; struct gpio_desc *desc; int ret; ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, &gpiospec); if (ret) { pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n", __func__, propname, np, index); return ERR_PTR(ret); } chip = of_find_gpiochip_by_xlate(&gpiospec); if (!chip) { desc = ERR_PTR(-EPROBE_DEFER); goto out; } desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags); if (IS_ERR(desc)) goto out; pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n", __func__, propname, np, index, PTR_ERR_OR_ZERO(desc)); out: of_node_put(gpiospec.np); return desc; }
요청한 디바이스 노드(gpio controller 노드)의 “#gpio-cells” 속성 값의 argument 수 만큼 벤더가 제공하는 변환 함수(*of_xlate)를 사용하여 gpio핀 번호와 플래그를 변환한 후 해당 gpiochip에서 디스크립터를 찾아 반환한다. 출력 인자 flags 값에도 반환시킨다.
- two cell을 사용하는 simple 변환 사용 예) “abc-gpios” = <&gpio 12 0>
- 12번 gpio에서 0번 설정 값을 플래그에 대입한다.
- 참고로 4 bit 플래그 값이 모두 0이므로 -> high-active, push-pull, sleep-maintain-value 특성을 가진다.
- 코드 라인 20~26에서 인자로 요청한 프로퍼티명(X-gpios, X-gpio, gpios, gpio)에서 index 배열의 argument를 읽어서 출력 인자 gpiospec에 대입한다.
- 예) “abc-gpios” = <&gpio 12 0>, <&gpio 13, 0>에서 propname=”abc-gpios”, index=1로 검색 요청을 한 경우
- -> index=0을 건너띄고 <&gpio 13, 0> 값을 읽어 gpiospec argument에 대입한다.
- 예) “abc-gpios” = <&gpio 12 0>, <&gpio 13, 0>에서 propname=”abc-gpios”, index=1로 검색 요청을 한 경우
- 코드 라인 28~32에서 첫 번째 argument는 gpio controller 노드를 가리키는 phandle 값이다. 이 phandle로 노드를 검색하여 해당하는 gpio_chip 구조체를 찾아온다.
- 코드 라인 34~36에서 argument 값을 변환한 값에 해당하는 gpio 디스크립터 및 플래그 값을 알아온다.
of_xlate_and_get_gpiod_flags()
drivers/gpio/gpiolib-of.c
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, struct of_phandle_args *gpiospec, enum of_gpio_flags *flags) { int ret; if (chip->of_gpio_n_cells != gpiospec->args_count) return ERR_PTR(-EINVAL); ret = chip->of_xlate(chip, gpiospec, flags); if (ret < 0) return ERR_PTR(ret); return gpiochip_get_desc(chip, ret); }
인자로 전달받은 gpio argument들을 사용하여 GPIO 칩 벤더가 제공하는 (*of_xlate) 변환 함수를 통해 알아온 gpio hw pin 번호와 플래그 값을 사용하여 gpio 디스크립터를 찾아 반환한다.
- 대부분의gpio controller에서 (*of_xlate) 후크 구현 시 of_gpio_simple_xlate() 함수를 사용한다. 이 함수는 phandle 뒤에 위치하는 두 개의 argument 값을 변환 없이 그대로 gpio 번호와 gpio 플래그 값으로 반환한다.
- 예) <gpio hw pin: 0~> <flags>
- 특정 gpio controller는 세 개의 argument를 사용하는 경우도 있으므로 반드시 확인하여 구별해야 한다. 보통 이러한 경우 추가된 인자는 뱅GPIO 뱅크를 가리킨다.
- 예) <gpio bank: 0~> <gpio hw pin: 0~31> <flags>
- 코드 라인 7~8에서 gpio controller에 지정된 cell 수와 인자로 전달받은 augument 인자 수가 다른 경우 에러로 함수를 빠져나간다.
- 코드 라인 10~12에서 gpio controller의 (*of_xlate) 후크에 등록된 함수를 수행한다.
- 벤더가 후크 함수를 제공하지 않으면 디폴트로 of_gpio_simple_xlate() 함수를 사용한다.
- phandle 값 뒤에 3개의 argument들을 요구하는 벤더를 제외하면, 2개의 argument들을 요구하는 대부분의 벤더는 별도의 후크 함수를 제공하지 않는다.
- 코드 라인 14에서 해당 결과 인덱스에 해당하는 gpio 디스크립터를 반환한다.
다음 그림은 gpios 속성의 phandle 뒤에 사용된 argument 수에 따라 gpio 칩 벤더가 제공하는 (*xlate) 함수의 사용사례를 보여준다.
of_gpio_simple_xlate()
drivers/gpio/gpiolib-of.c
/** * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags * @gc: pointer to the gpio_chip structure * @gpiospec: GPIO specifier as found in the device tree * @flags: a flags pointer to fill in * * This is simple translation function, suitable for the most 1:1 mapped * GPIO chips. This function performs only one sanity check: whether GPIO * is less than ngpios (that is specified in the gpio_chip). */ int of_gpio_simple_xlate(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags) { /* * We're discouraging gpio_cells < 2, since that way you'll have to * write your own xlate function (that will have to retrieve the GPIO * number and the flags from a single gpio cell -- this is possible, * but not recommended). */ if (gc->of_gpio_n_cells < 2) { WARN_ON(1); return -EINVAL; } if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) return -EINVAL; if (gpiospec->args[0] >= gc->ngpio) return -EINVAL; if (flags) *flags = gpiospec->args[1]; return gpiospec->args[0]; } EXPORT_SYMBOL(of_gpio_simple_xlate);
2개의 셀을 사용하는 goio simple 변환은 gpio 시작 번호(offset)에 해당하는 첫 번째 argument를 그대로 반환한다. 그리고 gpio 설정 값에 해당하는 두 번째 인수 역시 그대로 변환 없이 출력 인자 flags에 대입한다.
- 코드 라인 20~23에서 셀수가 2개보다 작으면 simple 셀 변환을 할 수 없어서 경고 메시지를 출력하고 에러를 반환한다.
- 코드 라인 25~26에서 필요한 인자 개수가 모자라면 역시 에러를 반환한다.
- 코드 라인 28~29에서 gpio 시작 번호(offset)에 해당하는 첫 번째 argument가 gpio 컨트롤러에 등록된 gpio 수를 초과하면 처리할 수 없으므로 에러를 반환한다.
- 코드 라인 31~32에서 gpio 설정 값에 해당하는 두 번째 인수는 그대로 변환 없이 출력 인자 flags에 대입한다.
- 코드 라인 34에서 gpio 시작 번호(offset)에 해당하는 첫 번째 argument를 변환 없이 그대로 반환한다.
참고
- GPIO Subsystem -1- | 문c
- GPIO Subsystem -2- | 문c
- GPIO Subsystem -3- (Device Tree) | 문c – 현재 글
- GPIO Subsystem -4- (new Interface) | 문c
- Pin Control Subsystem -1- | 문c
- Pin Control Subsystem -2- | 문c
- Interrupts -1- (Interrupt Controller) | 문c
- Interrupts -2- (irq chip) | 문c
- Interrupts -3- (irq domain) | 문c
- Documentation/gpio – kernel.org
- Specifying GPIO information for devices – Documentation/devicetree/bindings/gpio/gpio.txt | kernel.org
- GPIO in the kernel: an introduction | LWN.net
- GPIO in the kernel: future directions | LWN.net
- GPIO for Engineers and Makers | Linus Walleij, Linaro – 다운로드 pdf
- The Linux input driver subsystem | The kernel development community
- 리눅스 어플리케이션에서 GPIO 사용하기 | 방바닥 디자인
- Beaglebone으로 알아보는 linux kernel(3.10.x) programming | SLOWBOOT
- [Linux kernel] GPIO 정리 | 다솜돌이
- [번역] Linux의 새로운 GPIO 사용자 공간 하위 시스템 및 Libgpiod에 대해 자세히 알아보십시오. | Eddy Lab
- Build libgpiod the “New GPIO Interface for User Space | Armbian
안녕하세요 글 잘 봤어요.
근데 궁금한게 있어서 질문 남깁니다.
of_xlate의 역할이 뭔가요?????
of_phandle_args변수는 무슨 역할을 하기 위해 있는건가요? ㅠㅠ
리눅스 모듈 분석하고 있는데 정말 모르겠네요.
메일로 답변주시면 정말 감사하겠습니다!
안녕하세요?
질의하신 부분들은 디바이스 트리에서 곧바로 이해하기 힘든 어려운 부분에 해당합니다.
먼저 (*of_xlate) 후크는 디바이스 트리의 노드 내에서 읽어 들이는 특정 속성 정보가 드라이버마다 표현이 다른 dynamic 한 구성을 하는 경우에 대해서 사용됩니다.
gpio 드라이버에서의 사용 예를 들어보겠습니다. gpio 드라이버를 구동하자마자 초깃값으로 gpio 핀에 대해 특정 설정을 하고 싶을 때 디바이스 트리에서 gpio-hog; 속성을 통해 요청할 수 있습니다. 그런데 요청 시 gpio 핀을 의미하는 번호가 각 드라이버 개발자마다 다음과 같이 다른 사용법을 갖는 경우가 있습니다.
– 2셀(4바이트 2개) 표현을 통해 gpio 핀 번호와 플래그 지정
- gpios = <GPIO_4 GPIO_ACTIVE_HIGH>;
– 3셀(4바이트 3개) 표현을 통해 뱅크 번호 x 32 + gpio 핀 번호와 플래그 지정
- gpios = <BANK_1 GPIO_4 GPIO_ACTIVE_HIGH>;
실제 각각의 gpio 드라이버에서 위와 같이 gpio 핀을 표현하는 셀의 개수와 gpio에 설정할 플래그를 표현하는 개수를 다르게 할 수 있습니다. 이렇게 여러 개의 셀 표현에 대한 변환을 수행하여 최종 목표인 gpio 디스크립터와 설정할 플래그 값을 알아 오는 것이 목적입니다. 대부분 gpio 드라이버는 argument를 표현할 때 2셀 방법을 사용하는데, 이를 위해 커널은 (*of_xlate) 후크에 사용할 of_gpio_simple_xlate() 함수를 제공합니다. 위의 2 셀과 다른 셀 표현을 사용하실 gpio 드라이버 개발자들은 별도의 자신의 변환 함수를 작성하여 (*of_xlate) 후크에 대입하여 사용하면 됩니다.
두 번째 of_phandle_args에 대해 설명해 드립니다.
먼저 알고 있어야 할 항목은 phandle입니다. phandle은 디바이스 트리를 컴파일하면 각 노드에 대해 phandle 속성명이 자동으로 생성되고 이에 대한 값은 일련번호 1부터 노드를 컴파일하는 순서대로 생성됩니다. 만일 특정 노드를 가리킬 일이 있으면 이 일련번호를 직접 사용하는 것이 아니고 노드명에 & 기호를 붙여서 사용하면 해당 노드를 가리키는 phandle 값으로 바뀝니다.
예를 들어
abc-gpios = < &gpio1 13 0>;
라고 할 때 gpio 노드가 컴파일 시 5번째 순서에 컴파일되어 gpio 노드내에 자동으로phandle = <5>;
가 생성됩니다. 그리고 이 노드를 phandle을 사용하여 가리킨 속성 값은abc-gpios = <5 13 0>;
이 됩니다. 즉 phandle은 노드를 가리키는 유니크한 정수입니다.phandle 값 뒤에 작성할 argument들은 phandle 노드에 따라 argument 개수가 달라집니다. 즉 phandle이 가리키는 노드가 원하는 개수만큼의 argument를 대입해서 작성해야 한다는 뜻입니다.
예를 들어 제 시스템에 gpio 제조사가 다른 칩이 2개 사용되어 gpio1과 gpio2가 사용되고 있다고 가정합니다. 첫 번째 gpio1 드라이버는 2셀 방법을 사용하고, 두 번째 gpio2 드라이버가 3셀 방법을 사용하는 경우 첫 번째 gpio1과 연결된 custom 장치는
abc-gpios = < &gpio1 13 0>;
과같이 argument를 2개 사용하여야 하고, 두 번째 gpio2에 연결된 custom 장치는def-gpios = < &gpio2 1 14 0>;
와같이 argument를 3개 사용해야 합니다.이렇게 가변 길이의 argument를 가지고 해당 gpio 드라이버의 (*of_xlate) 후크에 지정된 변환 함수를 사용할 때 of_phandle_args 구조체를 사용하여 운반합니다.
struct of_phandle_args {
struct device_node *np;
int args_count;
uint32_t args[MAX_PHANDLE_ARGS];
};
위의 구조체에서 np는 phandle을 통해 가리킨 디바이스 노드이며 두 번째, args_count는 argument 개수입니다. 또한 args에는 변환에 사용될 argument 값들이 들어가게 됩니다.
이렇게 설명이 복잡하기만 한데, 부디(?) 이해가 되길 바랍니다.