본문 바로가기
device driver

charter device driver - 8. interrupt platform driver 만들기

by jsh91 2023. 5. 30.
#include <linux/module.h>
#include <linux/platform_device.h> 	
#include <linux/interrupt.h> 		
#include <linux/gpio/consumer.h>
#include <linux/miscdevice.h>
#include <linux/of_device.h>

static char *KEYS_NAME = "KEY";

/* interrupt handler */
static irqreturn_t keys_isq(int irq, void *data)
{
	struct device *dev = data;
	dev_info(dev, "interrupt received. key: %s\n", KEYS_NAME);
	return IRQ_HANDLED;
}

static struct miscdevice mydev_misc = {
		.minor = MISC_DYNAMIC_MINOR,
		.name = "mydev",
};

static int my_probe(struct platform_device *pdev)
{	
	int ret_val, irq;
	struct gpio_desc *gpio;
	struct device *dev = &pdev->dev;

	dev_info(dev, "my_probe() is enter.\n");
	
	/* Linux IRQ number를 알아내는 방법 1 
	 * devm_gpiod_get - Resource-managed gpiod_get()
	 devm_gpiod_get(struct device *dev,	const char *con_id,	enum gpiod_flags flags)
    @dev:	device 
    @con_id:	device tree에서 gpio를 찾을때 사용
	ex)
	compatible = "vendor,mydevice";
    	...
    	gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
    --->gpio-names = "my_gpio";
    	...
	};

    @flags:	gpio 초기 상태를 설정
	enum gpiod_flags {
	GPIOD_ASIS	= 설정을 변경하지 않음
	GPIOD_IN	= 입력모드
	GPIOD_OUT_LOW	= low 로 출력모드
	GPIOD_OUT_HIGH	= high 로 출력모드
	GPIOD_OUT_LOW_OPEN_DRAIN  = 저전력 오픈드레인 출력모드
	GPIOD_OUT_HIGH_OPEN_DRAIN = 고전력 오픈드레인 출력모드
	*/
	gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
	if (IS_ERR(gpio)) {
		dev_err(dev, "failed to get gpio\n");
		return PTR_ERR(gpio);
	}

	/* 
	gpio 디스크립터를 사용하여 IRQ number를 알아낸다.
	
	 */
	irq = gpiod_to_irq(gpio);
	if (irq < 0)
		return irq;
	dev_info(dev, "gpiod_to_irq IRQ number : %d\n", irq);

	/* Linux IRQ number를 알아내는 방법 2 */
	irq = platform_get_irq(pdev, 0);
	if (irq < 0){
		dev_err(dev, "failed to get irq number\n");
		return -EINVAL;
	}
	dev_info(dev, "platform_get_irq irq number : %d\n", irq);
	
	/* 
	인터럽터를 할당 
	devm_request_irq(struct device *dev, irq number , irq_handler_t handler,
		 unsigned long irqflags, const char *devname, void *dev_id)
	
	handler : 인터럽터 발생시 수행할 함수 등록

	irqflags 리스트
	#define IRQF_TRIGGER_NONE	: 설정하지 않음
	#define IRQF_TRIGGER_RISING	: 상승에지에서 트리거
	#define IRQF_TRIGGER_FALLING : 하강에지에서 트리거
	#define IRQF_TRIGGER_HIGH	: 상승일때 트리거
	#define IRQF_TRIGGER_LOW	: 하강일때 트리거
	#define IRQF_TRIGGER_MASK	: 모튼 인터럽트 타입의 비트마스크
	#define IRQF_TRIGGER_PROBE	: 인터럽트 트리거 타입을 자동으로 감지

	*/
	ret_val = devm_request_irq(dev, irq, keys_isq, 
				   IRQF_TRIGGER_FALLING,
				   KEYS_NAME, dev);
	if (ret_val) {
		dev_err(dev, "Failed to request interrupt \n");
		return ret_val;
	}
	
	ret_val = misc_register(&mydev_misc);
	if (ret_val != 0)
	{
		dev_err(dev, "failed to register misc\n");
		return ret_val;
	}

	dev_info(dev, "mydev: got minor %i\n",mydev_misc.minor);
	dev_info(dev, "my_probe() is exited.\n");

	return 0;
}

static int my_remove(struct platform_device *pdev)
{	
	dev_info(&pdev->dev, "my_remove() function \n");
	misc_deregister(&mydev_misc);
	return 0;
}

static const struct of_device_id my_of_ids[] = {
	{ .compatible = "arrow,key"},
	{},
};

MODULE_DEVICE_TABLE(of, my_of_ids);

static struct platform_driver my_platform_driver = {
	.probe = my_probe,
	.remove = my_remove,
	.driver = {
		.name = "intkey_driver_name",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

module_platform_driver(my_platform_driver);

MODULE_LICENSE("GPL");
&gpio {
	...
    ...
     key_pin: key_pin {
         brcm,pins = <23>;
         brcm,function = <0>; /* Input */
         brcm,pull = <1>; /* Pull down */
     };
     
---
&soc {
	...
    ...
     irq_key {
         compatible = "arrow,key";
         pinctrl-names = "default";
         pinctrl-0 = <&key_pin>;
         gpios = <&gpio 23 0>;
         interrupts = <23 1>;
         interrupt-parent = <&gpio>;
     };

 

dev_info(error 등등) log 출력 의미

[platform driver name] [device name(device tree node name)] : message

1.[platform driver name] 

static struct platform_driver my_platform_driver = {
	.probe = my_probe,
	.remove = my_remove,
	.driver = {
------> .name = "intkey_driver_name",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

2. [device name(device tree node name)] 

---> irq_key {
         compatible = "arrow,key";
         pinctrl-names = "default";
         pinctrl-0 = <&key_pin>;
         gpios = <&gpio 23 0>;
         interrupts = <23 1>;
         interrupt-parent = <&gpio>;
     };

 

insmod

[  229.282086] intkey_driver_name soc:irq_key: my_probe() is enter.
[  229.282290] intkey_driver_name soc:irq_key: gpiod_to_irq IRQ number : 166
[  229.282353] intkey_driver_name soc:irq_key: platform_get_irq irq number : 166
[  229.282718] intkey_driver_name soc:irq_key: mydev: got minor 60
[  229.282733] intkey_driver_name soc:irq_key: my_probe() is exited.

 

dmesg 출력시 -w 옵션을 사용하면 실시간으로 dmesg 출력을 확인 할 수 있다

dmesg -w

 

인터럽트 정보 확인하기

cat /proc/interrupt

root@raspberrypi:/home/board# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
 17:        776          0          0          0  ARMCTRL-level   1 Edge      3f00b880.mailbox
 18:        978          0          0          0  ARMCTRL-level   2 Edge      VCHIQ doorbell
 34:          0          0          0          0  ARMCTRL-level  42 Edge      vc4
 35:          0          0          0          0  ARMCTRL-level  43 Edge      3f004000.txp
 42:        247          0          0          0  ARMCTRL-level  50 Edge      DMA IRQ
 44:       8823          0          0          0  ARMCTRL-level  52 Edge      DMA IRQ
 45:          0          0          0          0  ARMCTRL-level  53 Edge      DMA IRQ
 48:          0          0          0          0  ARMCTRL-level  56 Edge      DMA IRQ
 49:          0          0          0          0  ARMCTRL-level  57 Edge      DMA IRQ
 56:     144660          0          0          0  ARMCTRL-level  64 Edge      dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
 57:          1          0          0          0  ARMCTRL-level  65 Edge      vc4 hvs
 64:          0          0          0          0  ARMCTRL-level  72 Edge      vc4 hdmi cec
 66:          5          0          0          0  ARMCTRL-level  74 Edge      vc4 crtc
 69:          0          0          0          0  ARMCTRL-level  77 Edge      vc4 crtc
 70:          0          0          0          0  ARMCTRL-level  78 Edge      vc4 crtc
 77:          0          0          0          0  ARMCTRL-level  85 Edge      3f804000.i2c, 3f805000.i2c
 78:          0          0          0          0  ARMCTRL-level  86 Edge      3f204000.spi
 80:       1004          0          0          0  ARMCTRL-level  88 Edge      mmc0
 81:       4337          0          0          0  ARMCTRL-level  89 Edge      uart-pl011
 86:      10901          0          0          0  ARMCTRL-level  94 Edge      mmc1
161:          0          0          0          0  bcm2836-timer   0 Edge      arch_timer
162:       5150       7474       5737       4333  bcm2836-timer   1 Edge      arch_timer
165:          0          0          0          0  bcm2836-pmu   9 Edge      arm-pmu
166:          1          0          0          0  pinctrl-bcm2835  23 Edge      KEY   <<<<<<<<-----------------------------------
FIQ:              usb_fiq
IPI0:          0          0          0          0  CPU wakeup interrupts
IPI1:          0          0          0          0  Timer broadcast interrupts
IPI2:       5735      10509      10759       8110  Rescheduling interrupts
IPI3:        573       1368       1024       1685  Function call interrupts
IPI4:          0          0          0          0  CPU stop interrupts
IPI5:        477       2070        570         87  IRQ work interrupts
IPI6:          0          0          0          0  completion interrupts
Err:          0

 

 

인터럽트 발생시

[  237.898983] intkey_driver_name soc:irq_key: interrupt received. key: KEY

 

댓글