장치 트리 파일 수정
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