1. Chỉnh sửa file .config để bật driver GPIO và I2C cho Raspberry Pi CM5
Bước đầu tiên là xác định và bật các driver cần thiết cho phần cứng đặc thù của Compute Module 5. Chúng ta cần đảm bảo driver GPIO (General Purpose Input/Output) và I2C (Inter-Integrated Circuit) được compile trực tiếp vào kernel hoặc dưới dạng module tùy theo yêu cầu.
Thao tác này được thực hiện bằng công cụ menuconfig trong môi trường Yocto hoặc make nếu build từ source. Chúng ta sẽ bật BRCM_BCM2835_GPIO và BRCM_BCM2835_I2C để hỗ trợ chipset Broadcom BCM2711 trên CM5.
Chạy lệnh sau để vào giao diện cấu hình kernel:
bitbake -c menuconfig virtual/kernel
Sau khi giao diện menuconfig mở ra, hãy di chuyển theo đường dẫn sau và đánh dấu Y (compile vào kernel) hoặc M (compile thành module):
Device Drivers -> GPIO Support -> Broadcom BCM2835 GPIO driver (BRCM_BCM2835_GPIO)
Device Drivers -> I2C support -> Broadcom BCM2835 I2C driver (BRCM_BCM2835_I2C)
Lưu ý: Đối với các driver I2C và GPIO quan trọng cho boot, nên chọn Y để tích hợp trực tiếp. Các driver mở rộng có thể chọn M.
Sau khi chọn xong, nhấn Esc để quay lại và chọn Save để lưu file .config.
Kết quả mong đợi: File meta-raspberrypi/recipes-kernel/linux/linux-raspberrypi-custom_%.bbappend hoặc file config tương ứng trong layer của bạn sẽ được cập nhật với các dòng CONFIG_BRCM_BCM2835_GPIO=y và CONFIG_BRCM_BCM2835_I2C=y.
Để verify nhanh, chạy lệnh tìm kiếm trong file config đã sinh ra:
grep "BRCM_BCM2835" $(bitbake -e | grep "LINUX_SRC" | cut -d= -f2)/.config
2. Tích hợp module kernel mở rộng và driver phần cứng riêng
Khi driver mặc định không đáp ứng được yêu cầu phần cứng chuyên biệt (ví dụ: cảm biến analog riêng, giao tiếp SPI custom), ta cần tích hợp driver bên ngoài vào cây source của kernel.
Trước tiên, hãy tạo một directory trong layer Yocto của bạn để chứa các file driver này. Giả sử layer của bạn là meta-my-project.
Tạo thư mục chứa driver và copy file source driver vào:
mkdir -p meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom/files/drivers/my-custom
cp /path/to/your/driver.c meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom/files/drivers/my-custom/
cp /path/to/your/driver.h meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom/files/drivers/my-custom/
Tiếp theo, tạo file Makefile cho driver này để định nghĩa cách compile. File này sẽ được merge vào Makefile chính của kernel.
Đường dẫn đầy đủ: meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom/files/drivers/my-custom/Makefile
obj-m += my_custom_driver.o
my_custom_driver-objs := driver.o
Bây giờ, ta cần chỉnh sửa file .bbappend để Yocto biết phải copy các file này vào source tree trước khi compile. Chỉnh sửa file meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom/linux-raspberrypi-custom_%.bbappend (tạo mới nếu chưa có).
Nội dung file bbappend cần thêm dòng sau vào biến SRC_URI:
SRC_URI += "file://drivers/my-custom"
Đồng thời, cần chỉ định nơi copy các file này vào trong source tree của kernel bằng biến do_compile hoặc do_patch tùy thuộc vào cấu trúc. Cách đơn giản nhất là dùng FILESEXTRAPATHS và thêm logic vào do_compile_append.
Tuy nhiên, với Yocto, cách chuẩn là thêm vào do_patch_append để copy file sau khi patch:
do_patch_append() {
install -d ${S}/drivers/my-custom
cp ${WORKDIR}/drivers/my-custom/*.c ${S}/drivers/my-custom/
cp ${WORKDIR}/drivers/my-custom/*.h ${S}/drivers/my-custom/
cp ${WORKDIR}/drivers/my-custom/Makefile ${S}/drivers/my-custom/
}
addtask do_patch_append after do_patch before do_compile
Để kernel nhận diện driver này, bạn phải thêm dòng vào file Makefile của kernel chính (thường là drivers/Makefile) để include obj-$(CONFIG_MY_CUSTOM) += my-custom/. Bạn có thể dùng patch file để làm việc này thay vì sửa trực tiếp.
Tạo file patch 0001-add-custom-driver.patch trong thư mục files của recipe kernel:
diff --git a/drivers/Makefile b/drivers/Makefile
index ... .. ...
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_SERIAL) += serial/
obj-$(CONFIG_NET) += net/
obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_MY_CUSTOM) += my-custom/
obj-$(CONFIG_INPUT) += input/
Trong file .bbappend, thêm dòng này để apply patch:
SRC_URI += "file://0001-add-custom-driver.patch"
Kết quả mong đợi: Khi chạy bitbake -c compile virtual/kernel, driver my_custom_driver.ko sẽ được tạo ra trong thư mục modules của build directory.
Verify bằng lệnh sau trên host sau khi build xong:
find ${TMPDIR}/workspace/virtual/kernel -name "my_custom_driver.ko"
3. Cấu hình Device Tree Overlay (DTS) cho phần cứng mở rộng
Raspberry Pi CM5 sử dụng Device Tree để mô tả phần cứng. Để hỗ trợ các board mở rộng (HAT) hoặc cấu hình GPIO/I2C đặc thù, ta cần tạo Device Tree Overlay (.dtbo) thay vì sửa file DTS gốc.
Tạo thư mục chứa file overlay trong layer của bạn:
mkdir -p meta-my-project/recipes-devtools/devicetree/dtbo-raspberrypi-custom/files/overlays
Viết file my-hat-overlay.dts để định nghĩa phần cứng. File này mô tả các node mới và kết nối chúng với các bus hiện có.
Đường dẫn đầy đủ: meta-my-project/recipes-devtools/devicetree/dtbo-raspberrypi-custom/files/overlays/my-hat-overlay.dts
/dts-v1/;
/plugin/;
/ {
compatible = "rpi-4-model-b", "rpi-400", "rpi-cm4";
fragment@0 {
target = ;
__overlay__ {
my-sensor@42 {
compatible = "vendor,my-sensor";
reg = ;
interrupt-parent = ;
interrupts = ;
};
};
};
fragment@1 {
target = ;
__overlay__ {
my-led {
gpios = ;
linux,gpio-hog-active-high;
};
};
};
};
Tiếp theo, tạo file my-hat-overlay.dtsi nếu cần chia nhỏ, nhưng với overlay đơn giản, file .dts ở trên là đủ. Để compile file này thành .dtbo, ta cần recipe riêng trong Yocto.
Tạo file recipe meta-my-project/recipes-devtools/devicetree/dtbo-raspberrypi-custom_%.bb:
SUMMARY = "Custom Device Tree Overlays for Raspberry Pi CM5"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a038486a8c83e13oa"
SRC_URI = "file://overlays/my-hat-overlay.dts"
S = "${WORKDIR}"
do_compile() {
${DTCC} -O dtb -o ${WORKDIR}/my-hat-overlay.dtbo ${WORKDIR}/my-hat-overlay.dts
}
do_install() {
install -d ${D}${nonarch_base_libdir}/firmware/rpi/overlays
install -m 0644 ${WORKDIR}/my-hat-overlay.dtbo ${D}${nonarch_base_libdir}/firmware/rpi/overlays/
}
FILES:${PN} = "${nonarch_base_libdir}/firmware/rpi/overlays"
Để sử dụng overlay này trên hệ thống, ta cần cấu hình nó trong file config.txt hoặc cmdline.txt của boot partition. Trong Yocto, điều này được làm qua file config.txt trong recipe boot-image-raspberrypi hoặc layer tương tự.
Tạo file config.txt tùy chỉnh trong layer của bạn: meta-my-project/recipes-bsp/config/config.txt-custom
# Enable the custom overlay
dtoverlay=my-hat-overlay
# Other standard settings for CM5
arm_boost=1
disable_splash=1
Thêm file này vào recipe boot image của bạn (thường là boot-image-raspberrypi hoặc raspberrypi-image) bằng cách append vào SRC_URI và do_install_append.
Trong file bbappend của recipe boot image:
SRC_URI += "file://config.txt-custom"
do_install_append() {
install -m 0644 ${WORKDIR}/config.txt-custom ${D}${bootloader_dir}/config.txt
}
# Định nghĩa biến bootloader_dir nếu chưa có
BOOTLOADER_DIR = "${D}${bootloader_dir}"
Kết quả mong đợi: Khi boot, kernel sẽ tải file my-hat-overlay.dtbo và kích hoạt driver cho cảm biến I2C tại địa chỉ 0x42 và LED tại GPIO 23.
Verify bằng cách chạy lệnh sau trên board đã boot:
dtc -I dtb -O dts /proc/device-tree | grep "my-sensor"
cat /sys/kernel/debug/devices_present | grep my-sensor
4. Compile kernel và tạo image uImage/bzImage riêng biệt
Sau khi đã cấu hình xong .config, tích hợp driver và tạo overlay, bước cuối là compile toàn bộ kernel và tạo file image boot.
Trong môi trường Yocto, việc này được tự động hóa qua task do_compile và do_install của recipe kernel. Tuy nhiên, để tạo file uImage (dùng cho U-Boot) hoặc zImage (dùng cho Raspberry Pi Bootloader), ta cần đảm bảo biến IMAGE_CMD hoặc IMAGE_TYPE được thiết lập đúng.
Đối với Raspberry Pi, bootloader mặc định thường dùng kernel7.img hoặc kernel8.img (thực chất là zImage được rename). Nếu bạn cần uImage cho U-Boot custom, hãy chỉnh sửa recipe kernel.
Chỉnh sửa file meta-my-project/recipes-kernel/linux/linux-raspberrypi-custom_%.bbappend để thêm biến cấu hình compile:
IMAGE_CMD:append = " uImage"
# Hoặc nếu muốn tạo cả zImage và uImage
IMAGE_CMD:append = " zImage uImage"
Nếu bạn muốn tạo file uImage thủ công từ vmlinuz (kernel image) bằng công cụ mkimage của U-Boot, hãy thêm logic vào do_compile_append trong recipe:
do_compile_append() {
if [ -f ${S}/arch/arm/boot/uImage ]; then
echo "U-Boot image already exists"
else
${MKIMAGE} -A arm -O linux -T kernel -C none -a 0x80000 -e 0x80000 -n "Linux-CM5-Custom" -d ${S}/arch/arm/boot/zImage ${S}/arch/arm/boot/uImage
fi
}
Để compile, chạy lệnh:
bitbake virtual/kernel
Quá trình này sẽ thực hiện: Configure -> Patch -> Compile Kernel -> Compile Modules -> Create Images.
Sau khi build xong, tìm file image trong thư mục deploy của Yocto:
find ${DEPLOY_DIR_IMAGE} -name "uImage*" -o -name "zImage*"
Đối với Raspberry Pi CM5, file kernel chính thường được copy vào thư mục boot của image rootfs. Đảm bảo recipe boot image của bạn đã copy file uImage hoặc zImage vào đúng vị trí và đổi tên thành kernel.img nếu cần.
Chỉnh sửa recipe boot image (ví dụ boot-image-raspberrypi) để copy file:
do_install_append() {
install -m 0644 ${DEPLOY_DIR_IMAGE}/uImage-${PV} ${D}${bootloader_dir}/kernel.img
}
Kết quả mong đợi: Trong thư mục deploy/images của build directory, bạn sẽ thấy file uImage hoặc kernel.img với kích thước hợp lý (thường 10-20MB tùy driver).
Verify tính toàn vẹn của image bằng lệnh kiểm tra checksum hoặc xem thông tin header:
mkimage -l ${DEPLOY_DIR_IMAGE}/uImage-${PV}
ls -lh ${DEPLOY_DIR_IMAGE}/uImage-${PV}
Để kiểm tra xem kernel đã boot thành công với driver và overlay mới, hãy flash image vào SD Card và gắn vào Raspberry Pi CM5, sau đó quan sát console log:
journalctl -k | grep -E "my-custom|my-sensor|BRCM_BCM2835"
Nếu thấy log "my_custom_driver: module loaded" hoặc "my-sensor: probe successful", thì quá trình tùy chỉnh kernel đã thành công.
Điều hướng series:
Mục lục: Series: Xây dựng hệ điều hành Linux tùy biến từ source cho Raspberry Pi CM5
« Phần 2: Cấu hình Yocto Project và tạo Project Layer tùy chỉnh
Phần 4: Cấu hình Bootloader và tạo Image Boot cho CM5 »