<이번 blog에서 소개할 내용>
a) gpio device tree
b) gpio driver
- kernel gpio framework 포함
- gpio interrupt
- kernel config
c) userspace(sysfs) gpio 사용법
d) led driver, gpio-keys driver 소개
1. Atmel SAMA5D3 GPIO Device Tree
그럼 먼저 sama5d3.dtsi 파일의 gpio 관련 부분을 살펴 보는 것으로 설명을 시작해 보도록 하자. 아래 내용을 보면 알 수 있듯이 SAMA5D3에는 5개의 gpio controller 즉, pioA ~ pioE가 존재한다. SAMA5D3 datasheet에 따르면, 근데 실제로는 gpio controller라기 보다는 PIO(Parallel I/O) controller가 맞는 표현이다.
============================================================
pioA: gpio@fffff200 {
compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
reg = <0xfffff200 0x100>;
interrupts = <6 IRQ_TYPE_LEVEL_HIGH 1>; /* interrupt source 임을 표시 */
#gpio-cells = <2>;
gpio-controller; /* gpio controller 임을 표시 */
interrupt-controller; /* interrupt controller 임을 표시 - 위의 내용에 의하면
interrupt source이면서, 동시에 controller이기도 함 */
#interrupt-cells = <2>;
clocks = <&pioA_clk>;
};
pioB: gpio@fffff400 {
<생략>
};
pioC: gpio@fffff600 {
<생략>
};
pioD: gpio@fffff800 {
<생략>
};석
pioE: gpio@fffffa00 {
<생략>
}
============================================================
Atmel에서 말하는 PIO controller는 pin muxing과 GPIO 처리를 모두 관장하는 장치로 이해할 수 있을 듯하다. 그림 1-1에 PIO controller의 block diagram을 옮겨 보았다.
<1-1 PIO controller block diagram>
GPIO pin number = PIO bank number * 32 + pin offset
예를 들어, PIOE 23번 pin은 위의 산술식에 의하면, 4(PIOE) * 32 + 23 = 151이 된다(참고: PIOA bank number는 1이 아니고 0임).
<1-2 PIO 5 banks>
끝으로 위의 device tree 내용 중 몇가지 사항을 추가로 정리해 보면 다음과 같다.
a) gpio controller는 main interrupt controller 입장에서 볼때, interrupt source에 해당한다.
b) 또한, gpio controller에 연결되는 주변 장치 관점에서 볼 때는 interrupt controller로 볼 수도 있다.
c) gpio controller는 아래와 같은 peripheral clock을 사용한다.
============================
pioA_clk: pioA_clk {
#clock-cells = <0>;
reg = <6>;
};
============================
2. GPIO 관련 Device Driver 분석
1절의 device tree에 있는 compatible string "atmel,at91sam9x5-gpio" 으로 device drivers 디렉토리를 검색해 보면, 아래 파일을 쉽게 찾을 수 있다.
drivers/pinctrl/pinctrl-at91.c
이제 부터는 이 코드의 probe 함수를 분석해 보도록 하자.
==============================================================
/* This structure is replicated for each GPIO block allocated at probe time */
static const struct gpio_chip at91_gpio_template = {
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
.get_direction = at91_gpio_get_direction,
.direction_input = at91_gpio_direction_input,
.get = at91_gpio_get,
.direction_output = at91_gpio_direction_output,
.set = at91_gpio_set,
.set_multiple = at91_gpio_set_multiple,
.dbg_show = at91_gpio_dbg_show,
.can_sleep = false,
.ngpio = MAX_NB_GPIO_PER_BANK,
};
static const struct of_device_id at91_gpio_of_match[] = {
{ .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, },
{ .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops },
{ /* sentinel */ }
};
static int at91_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *res;
struct at91_gpio_chip *at91_chip = NULL;
struct gpio_chip *chip;
struct pinctrl_gpio_range *range;
int ret = 0;
int irq, i;
int alias_idx = of_alias_get_id(np, "gpio"); /* gpio bank 번호 알아내기 */
uint32_t ngpio;
char **names;
BUG_ON(alias_idx >= ARRAY_SIZE(gpio_chips));
if (gpio_chips[alias_idx]) {
ret = -EBUSY;
goto err;
}
irq = platform_get_irq(pdev, 0); /* interrupt request number 구하기 */
if (irq < 0) {
ret = irq;
goto err;
}
/* struct at91_gpio_chip 영역 할당 */
at91_chip = devm_kzalloc(&pdev->dev, sizeof(*at91_chip), GFP_KERNEL);
if (!at91_chip) {
ret = -ENOMEM;
goto err;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* gpio bank address 구하기 */
at91_chip->regbase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_chip->regbase)) {
ret = PTR_ERR(at91_chip->regbase);
goto err;
}
/* at91_chip 주요 field 내용 채우기 */
at91_chip->ops = (struct at91_pinctrl_mux_ops *)
of_match_device(at91_gpio_of_match, &pdev->dev)->data;
at91_chip->pioc_virq = irq;
at91_chip->pioc_idx = alias_idx;
at91_chip->clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(at91_chip->clock)) {
dev_err(&pdev->dev, "failed to get clock, ignoring.\n");
ret = PTR_ERR(at91_chip->clock);
goto err;
}
ret = clk_prepare_enable(at91_chip->clock); /* gpio controller 관련 clock 준비 */
if (ret) {
dev_err(&pdev->dev, "failed to prepare and enable clock, ignoring.\n");
goto clk_enable_err;
}
at91_chip->chip = at91_gpio_template; /* atmel 용 struct gpio_chip API 대입 */
/* struct gpio_chip의 각 field 채우기 */
chip = &at91_chip->chip;
chip->of_node = np;
chip->label = dev_name(&pdev->dev);
chip->parent = &pdev->dev;
chip->owner = THIS_MODULE;
chip->base = alias_idx * MAX_NB_GPIO_PER_BANK;
if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) {
if (ngpio >= MAX_NB_GPIO_PER_BANK)
pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n",
alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK);
else
chip->ngpio = ngpio;
}
names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio,
GFP_KERNEL);
if (!names) {
ret = -ENOMEM;
goto clk_enable_err;
}
for (i = 0; i < chip->ngpio; i++)
names[i] = kasprintf(GFP_KERNEL, "pio%c%d", alias_idx + 'A', i);
chip->names = (const char *const *)names;
range = &at91_chip->range;
range->name = chip->label;
range->id = alias_idx;
range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK;
range->npins = chip->ngpio;
range->gc = chip;
ret = gpiochip_add(chip); /* gpio chip 등록 */
if (ret)
goto gpiochip_add_err;
gpio_chips[alias_idx] = at91_chip;
gpio_banks = max(gpio_banks, alias_idx + 1);
ret = at91_gpio_of_irq_setup(pdev, at91_chip); /* gpio interrupt controller setup */
if (ret)
goto irq_setup_err;
dev_info(&pdev->dev, "at address %p\n", at91_chip->regbase);
return 0;
irq_setup_err:
gpiochip_remove(chip);
gpiochip_add_err:
clk_enable_err:
clk_disable_unprepare(at91_chip->clock);
err:
dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
return ret;
}
static struct platform_driver at91_gpio_driver = {
.driver = {
.name = "gpio-at91",
.of_match_table = at91_gpio_of_match,
},
.probe = at91_gpio_probe,
};
static struct platform_driver * const drivers[] = {
&at91_gpio_driver,
&at91_pinctrl_driver,
};
static int __init at91_pinctrl_init(void)
{
return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
arch_initcall(at91_pinctrl_init);
==============================================================
다음 내용은 kernel gpio framework에서 제공하는 API 함수들을 열거한 것이다. device driver에서 GPIO를 제어하고자 할 경우에는 아래 함수들을 적절히 조합하여 사용하면 된다.
#include <linux/gpio.h>
int gpio_is_valid(int number);
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
int gpio_cansleep(unsigned gpio);
/* GPIO INPUT: return zero or nonzero, might sleep */
int gpio_get_value_cansleep(unsigned gpio);
/* GPIO OUTPUT, might sleep */
void gpio_set_value_cansleep(unsigned gpio, int value);
/* request GPIO, returning 0 or negative errno.
* non-null labels may be useful for diagnostics.
*/
int gpio_request(unsigned gpio, const char *label);
/* release previously-claimed GPIO */
void gpio_free(unsigned gpio);
<example>
err = gpio_request(30, "sample_name");
gpio_direction_input(30);
gpio_export(30, true);
gpio_get_value(30);
/* map GPIO numbers to IRQ numbers */
int gpio_to_irq(unsigned gpio);
/* map IRQ numbers to GPIO numbers (avoid using this) */
int irq_to_gpio(unsigned irq);
<example>
irq_num = gpio_to_irq(30)
request_irq(irq_num, handler, 0, "gpio_test", NULL);
set_irq_type(irq_num, IRQ_TYPE_EDGE_RISING);
free_irq(irq_num, NULL);
gpio_free(30);
<TBD> 위의 API를 적절히 이용한 제대로 된 sample code 추가 !3. Userspace(sysfs) GPIO 사용법
<TBD>
4. led driver and gpio-keys driver 소개
<TBD>
(2주 전에 정리를 했던 부분인데...) 추가 정리가 좀 더 필요하지만, 일단 blog에 게시하고자 한다.
References
1. http://www.wiki.xilinx.com/Linux+GPIO+Driver
2. http://michaelhleonard.com/gpio-interface-access-conventions-on-linux/
Slowboot
블로그 관리자가 댓글을 삭제했습니다.
답글삭제