본문 바로가기
device driver

charter device driver - 6. led_class_platform 만들기

by jsh91 2023. 5. 26.
     ledclass {

         compatible = "arrow,classleds";
         reg = <0x7e200000 0xb4>;
         pinctrl-names = "default";
         pinctrl-0 = <&led_pins>;

         red {
             label = "red";
         };

         green {
             label = "green";
         };

         blue {
             label = "blue";
         };
     };​
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/leds.h>

#define GPIO_27			27
#define GPIO_22			22
#define GPIO_26			26

#define GPFSEL2_offset 	 0x08
#define GPSET0_offset    0x1C
#define GPCLR0_offset 	 0x28


#define GPIO_27_INDEX 	1 << (GPIO_27 % 32)
#define GPIO_22_INDEX 	1 << (GPIO_22 % 32)
#define GPIO_26_INDEX 	1 << (GPIO_26 % 32)


#define GPIO_27_FUNC	1 << ((GPIO_27 % 10) * 3)
#define GPIO_22_FUNC 	1 << ((GPIO_22 % 10) * 3)
#define GPIO_26_FUNC 	1 << ((GPIO_26 % 10) * 3)

#define FSEL_27_MASK 	0b111 << ((GPIO_27 % 10) * 3) 
#define FSEL_22_MASK 	0b111 << ((GPIO_22 % 10) * 3) 
#define FSEL_26_MASK 	0b111 << ((GPIO_26 % 10) * 3) 

#define GPIO_SET_FUNCTION_LEDS (GPIO_27_FUNC | GPIO_22_FUNC | GPIO_26_FUNC)
#define GPIO_MASK_ALL_LEDS 	(FSEL_27_MASK | FSEL_22_MASK | FSEL_26_MASK)
#define GPIO_SET_ALL_LEDS (GPIO_27_INDEX  | GPIO_22_INDEX  | GPIO_26_INDEX)

struct led_dev
{
	u32 led_mask; 
	void __iomem *base;
	struct led_classdev cdev;
};

static void led_control(struct led_classdev *led_cdev, enum led_brightness b)
{

	struct led_dev *led = container_of(led_cdev, struct led_dev, cdev);

	/*echo 1 > /sys/class/leds/blue/brightness 할 경우 write 하는 값이 b로 들어온다.*/
	pr_info("led_brightness : [%d]\n",b);


	if (b != LED_OFF)	
		iowrite32(led->led_mask, led->base + GPSET0_offset); /* LED ON */
	else
		iowrite32(led->led_mask, led->base + GPCLR0_offset); /* LED OFF */
}

static int probe(struct platform_device *pdev)
{

	int ret = 0;
	int count;
	struct resource *r;
	u32 GPFSEL_read, GPFSEL_write;
	void __iomem *g_ioremap_addr;
	struct device_node *child;
	struct device *dev = &pdev->dev;

	pr_info("probe enter\n");

	/*
	devoce tree에서 reg값을 return 
	address 값을 직접 사용하지 못함, 그러므로 ioremap 함수에 정보를 전달할 목적으로 사용
	*/
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		pr_err("platform_get_resource fail\n");
		return -EINVAL;
	}
	pr_info("r->start = 0x%08lx\n", (long unsigned int)r->start);
	pr_info("r->end = 0x%08lx\n", (long unsigned int)r->end);
	pr_info("r->name = %s\n", (char *)r->name);

	/* devm_ 접두사는 Device Managed의 약자이며 device 해제 시 자동으로 iounmap을 호출 */
	g_ioremap_addr = devm_ioremap(dev, r->start, resource_size(r));
	if (!g_ioremap_addr) {
		pr_err("ioremap failed \n");
		return -ENOMEM;
	}

	/* node의 child를 알아온다*/
	count = of_get_child_count(dev->of_node);
	if (!count)
		return -EINVAL;

	pr_info("nodes num : [%d]\n", count);

	GPFSEL_read = ioread32(g_ioremap_addr + GPFSEL2_offset); 

	GPFSEL_write = (GPFSEL_read & ~GPIO_MASK_ALL_LEDS) |
					  (GPIO_SET_FUNCTION_LEDS & GPIO_MASK_ALL_LEDS);

	iowrite32(GPFSEL_write, g_ioremap_addr + GPFSEL2_offset); 
	iowrite32(GPIO_SET_ALL_LEDS, g_ioremap_addr + GPCLR0_offset);

	/*node의 child node를 순회한다
		ledclass node 아래로 3개의 노드가 존재한다.
		
		device tree
	     ledclass {

         compatible = "arrow,classleds";
         reg = <0x7e200000 0xb4>;
         pinctrl-names = "default";
         pinctrl-0 = <&led_pins>;

         red {
             label = "red";
         };

         green {
             label = "green";
         };

         blue {
             label = "blue";
         };
     };

	*/
	for_each_child_of_node(dev->of_node, child){

		struct led_dev *led_device;
		struct led_classdev *cdev;
		led_device = devm_kzalloc(dev, sizeof(*led_device), GFP_KERNEL);
		if (!led_device)
			return -ENOMEM;

		cdev = &led_device->cdev;

		led_device->base = g_ioremap_addr;
		
		of_property_read_string(child, "label", &cdev->name);

		if (strcmp(cdev->name,"red") == 0) {
			led_device->led_mask = GPIO_27_INDEX;
		}
		else if (strcmp(cdev->name,"green") == 0) {
			led_device->led_mask = GPIO_22_INDEX;
		}
		else if (strcmp(cdev->name,"blue") == 0) {
			led_device->led_mask = GPIO_26_INDEX;
		}
		else {
			pr_info("Bad device tree value\n");
			return -EINVAL;
		}

		/*led 디폴트 값 설정*/
		led_device->cdev.brightness = LED_OFF;
		led_device->cdev.brightness_set = led_control;

		/* struct led_classdev 등록 */
		ret = devm_led_classdev_register(dev, &led_device->cdev);
		if (ret) {
			dev_err(dev, "fail to register %s\n", cdev->name);
			of_node_put(child);
			return ret;
		}
	}
	
	pr_info("probe exit\n");

	return 0;
}

static int remove(struct platform_device *pdev)
{

	pr_info("remove enter\n");
	pr_info("remove exit\n");

	return 0;
}

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

MODULE_DEVICE_TABLE(of, my_of_ids);

static struct platform_driver led_platform_driver = {
	.probe = probe,
	.remove = remove,
	.driver = {
		.name = "classled_name",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

static int led_class_init(void)
{
	int ret;
	pr_info("led_class_init enter\n");

	ret = platform_driver_register(&led_platform_driver);
	if (ret !=0)
	{
		pr_err("platform fail returned %d\n", ret);
		return ret;

	}

	pr_info("led_class_init exit\n");
	return 0;
}

static void led_class_exit(void)
{
	pr_info("led_class_exit enter\n");

	platform_driver_unregister(&led_platform_driver);

	pr_info("led_class_exit exit\n");
}

module_init(led_class_init);
module_exit(led_class_exit);

MODULE_LICENSE("GPL");

 

 

 

Linux 커널은 다양한 디바이스와 시스템 기능을 모델링하고 관리하기 위해 많은 구조체를 제공합니다. struct led_classdev와 같은 디바이스 모델링 구조체에는 다음과 같은 것들이 있습니다.

1. struct device: 이는 Linux 디바이스 모델의 핵심 구조체로, 모든 시스템 디바이스를 대표합니다.
2. struct platform_device: 이 구조체는 플랫폼 디바이스를 대표하며, 메모리 매핑된 I/O와 같은 시스템 전체에 걸친 리소스를 사용하는 디바이스에 대한 정보를 담고 있습니다.
3. struct usb_device: 이 구조체는 USB 디바이스를 대표하며, USB 디바이스에 대한 정보와 동작을 담고 있습니다.
4. struct input_dev: 이 구조체는 입력 디바이스 (예: 키보드, 마우스)를 대표하며, 입력 이벤트를 생성하고 처리하는데 필요한 정보와 메서드를 담고 있습니다.
5. struct net_device: 이 구조체는 네트워크 인터페이스를 대표하며, 데이터 전송 및 수신과 같은 네트워킹 동작을 담당합니다.
6. struct i2c_client: 이 구조체는 I2C 디바이스를 대표하며, I2C 통신을 위한 정보와 메서드를 담고 있습니다.
7. struct spi_device: 이 구조체는 SPI 디바이스를 대표하며, SPI 통신을 위한 정보와 메서드를 담고 있습니다.

 

[   80.109539] led_class_init enter
[   80.110116] probe enter
[   80.110129] r->start = 0x3f200000
[   80.110140] r->end = 0x3f2000b3
[   80.110150] r->name = ledclass
[   80.110185] nodes num : [3]
[   80.110642] probe exit
[   80.110959] led_class_init exit

 

'devm_led_classdev_register' 등록으로 아래 경로에 파일 생성

root@raspberrypi:/home/board# ls /sys/class/leds/
blue  default-on  green  input0::capslock  input0::numlock  input0::scrolllock  led0  led1  mmc0  red

 

misc가 아닌 'devm_led_classdev_register'로 등록되었으므로 /sys/class 경로에 파일 생성

 

led on, off 방법

echo 1 > /sys/class/leds/red/brightness

[  169.027422] led_brightness : [1]

echo 0 > /sys/class/leds/red/brightness
[  174.299456] led_brightness : [0]

 

root@raspberrypi:/# find ./ -name *classled_name*
./sys/bus/platform/drivers/classled_name

댓글