STM32MP1 장치 트리 기반 LED 드라이버 구현

장치 트리 파일 수정

STM32MP1 보드의 장치 트리 파일 경로: /linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts/stm32mp157d-atk.dts

led_controller {
    compatible = "stm32mp1-led";
    status = "okay";
    reg = <0x50000A28 0x04   /* RCC_MP_AHB4ENSETR */
           0x5000A000 0x04   /* GPIOI_MODER */
           0x5000A004 0x04   /* GPIOI_OTYPER */
           0x5000A008 0x04   /* GPIOI_OSPEEDR */
           0x5000A00C 0x04   /* GPIOI_PUPDR */
           0x5000A018 0x04>; /* GPIOI_BSRR */
};

컴파일 및 배포:

cd /linux/atk-mpl/linux/my_linux/linux-5.4.31
make dtbs
cp arch/arm/boot/dts/stm32mp157d-atk.dtb /tftpboot/
chmod 777 /tftpboot/stm32mp157d-atk.dtb

U-Boot 부팅 설정:

setenv bootcmd 'tftp c2000000 uImage; tftp c4000000 stm32mp157d-atk.dtb; bootm c2000000 - c4000000'
saveenv
boot

LED 드라이버 개발

드라이버 파일: /linux/atk-mpl/Drivers/4_dtsled/dtsled.c

#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/io.h>

#define LED_DEVICE_COUNT 1
#define DEVICE_NAME "led_ctrl"
#define LED_ON 1
#define LED_OFF 0

static void __iomem *RCC_BASE;
static void __iomem *GPIO_MODE;
static void __iomem *GPIO_TYPE;
static void __iomem *GPIO_SPEED;
static void __iomem *GPIO_PULL;
static void __iomem *GPIO_SETRESET;

struct led_device {
    dev_t dev_id;
    struct cdev char_dev;
    struct device *dev;
    struct device_node *node;
};

struct led_device led_dev;

void led_control(u8 state) {
    u32 reg_val = readl(GPIO_SETRESET);
    if(state == LED_ON) {
        reg_val |= (1 << 16);
    } else {
        reg_val |= (1 << 0);
    }
    writel(reg_val, GPIO_SETRESET);
}

static int led_open(struct inode *inode, struct file *file) {
    file->private_data = &led_dev;
    return 0;
}

static ssize_t led_write(struct file *file, const char __user *buf, 
                        size_t len, loff_t *offset) {
    u8 cmd;
    if(copy_from_user(&cmd, buf, 1)) return -EFAULT;
    led_control(cmd);
    return 1;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_write,
};

static int __init driver_init(void) {
    u32 reg_values[12];
    struct property *prop;
    const char *status;
    int ret;

    // 장치 트리 노드 탐색
    led_dev.node = of_find_node_by_path("/led_controller");
    if(!led_dev.node) return -ENODEV;

    // 레지스터 매핑
    RCC_BASE = of_iomap(led_dev.node, 0);
    GPIO_MODE = of_iomap(led_dev.node, 1);
    GPIO_SETRESET = of_iomap(led_dev.node, 5);

    // GPIO 초기화
    u32 val = readl(RCC_BASE);
    val |= (1 << 8);
    writel(val, RCC_BASE);

    val = readl(GPIO_MODE);
    val &= ~0x3;
    val |= 0x1;
    writel(val, GPIO_MODE);

    // 문자 장치 등록
    alloc_chrdev_region(&led_dev.dev_id, 0, LED_DEVICE_COUNT, DEVICE_NAME);
    cdev_init(&led_dev.char_dev, &fops);
    cdev_add(&led_dev.char_dev, led_dev.dev_id, 1);

    led_dev.dev = device_create(
        class_create(THIS_MODULE, DEVICE_NAME), 
        NULL, led_dev.dev_id, NULL, DEVICE_NAME
    );

    return 0;
}

static void __exit driver_exit(void) {
    iounmap(RCC_BASE);
    iounmap(GPIO_MODE);
    iounmap(GPIO_SETRESET);
    device_destroy(led_dev.dev->class, led_dev.dev_id);
    cdev_del(&led_dev.char_dev);
    unregister_chrdev_region(led_dev.dev_id, LED_DEVICE_COUNT);
}

module_init(driver_init);
module_exit(driver_exit);
MODULE_LICENSE("GPL");

컴파일 및 테스트

Makefile:

KERNEL_SRC := /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31
obj-m := dtsled.o

build:
    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules

clean:
    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

테스트 애플리케이션:

arm-none-linux-gnueabihf-gcc led_test.c -o led_test

실행 절차:

# 타겟 시스템에서 실행
depmod
modprobe dtsled
./led_test /dev/led_ctrl 1  # LED 켜기
./led_test /dev/led_ctrl 0  # LED 끄기
rmmod dtsled

태그: Linux Device Tree STM32MP1 GPIO Driver Embedded Linux

6월 24일 23:57에 게시됨