硬件资源从硬编码到设备树
1. 硬编码
在没有设备树的系统中,硬件设备通常由板级代码(如 board.c 文件)硬编码注册到内核中。
比如这样:
// SPDX-License-Identifier: GPL-2.0
/** Renesas Technology Europe RSK+ 7203 Support.** Copyright (C) 2008 - 2010 Paul Mundt*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/smsc911x.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/leds.h>
#include <asm/machvec.h>
#include <asm/io.h>
#include <cpu/sh7203.h>//以太网控制器
static struct smsc911x_platform_config smsc911x_config = {.phy_interface = PHY_INTERFACE_MODE_MII,.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,.irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,.flags = SMSC911X_USE_32BIT | SMSC911X_SWAP_FIFO,
};static struct resource smsc911x_resources[] = {[0] = {.start = 0x24000000,.end = 0x240000ff,.flags = IORESOURCE_MEM,},[1] = {.start = 64,.end = 64,.flags = IORESOURCE_IRQ,},
};static struct platform_device smsc911x_device = {.name = "smsc911x",.id = -1,.num_resources = ARRAY_SIZE(smsc911x_resources),.resource = smsc911x_resources,.dev = {.platform_data = &smsc911x_config,},
};//LED
static struct gpio_led rsk7203_gpio_leds[] = {{.name = "green",.gpio = GPIO_PE10,.active_low = 1,}, {.name = "orange",.default_trigger = "nand-disk",.gpio = GPIO_PE12,.active_low = 1,}, {.name = "red:timer",.default_trigger = "timer",.gpio = GPIO_PC14,.active_low = 1,}, {.name = "red:heartbeat",.default_trigger = "heartbeat",.gpio = GPIO_PE11,.active_low = 1,},
};static struct gpio_led_platform_data rsk7203_gpio_leds_info = {.leds = rsk7203_gpio_leds,.num_leds = ARRAY_SIZE(rsk7203_gpio_leds),
};static struct platform_device led_device = {.name = "leds-gpio",.id = -1,.dev = {.platform_data = &rsk7203_gpio_leds_info,},
};//按键
static struct gpio_keys_button rsk7203_gpio_keys_table[] = {{.code = BTN_0,.gpio = GPIO_PB0,.active_low = 1,.desc = "SW1",}, {.code = BTN_1,.gpio = GPIO_PB1,.active_low = 1,.desc = "SW2",}, {.code = BTN_2,.gpio = GPIO_PB2,.active_low = 1,.desc = "SW3",},
};static struct gpio_keys_platform_data rsk7203_gpio_keys_info = {.buttons = rsk7203_gpio_keys_table,.nbuttons = ARRAY_SIZE(rsk7203_gpio_keys_table),.poll_interval = 50, /* default to 50ms */
};static struct platform_device keys_device = {.name = "gpio-keys-polled",.dev = {.platform_data = &rsk7203_gpio_keys_info,},
};static struct platform_device *rsk7203_devices[] __initdata = {&smsc911x_device,&led_device,&keys_device,
};static int __init rsk7203_devices_setup(void)
{/* Select pins for SCIF0 */gpio_request(GPIO_FN_TXD0, NULL);gpio_request(GPIO_FN_RXD0, NULL);/* Setup LAN9118: CS1 in 16-bit Big Endian Mode, IRQ0 at Port B */__raw_writel(0x36db0400, 0xfffc0008); /* CS1BCR */gpio_request(GPIO_FN_IRQ0_PB, NULL);return platform_add_devices(rsk7203_devices,ARRAY_SIZE(rsk7203_devices));
}
device_initcall(rsk7203_devices_setup);
2. 要将硬编码的驱动程序转换为使用设备树的方式,主要涉及以下几个步骤:
2.1 编写设备树源文件(DTS)
首先,你需要为你的硬件平台创建一个设备树源文件(DTS)。这个文件描述了硬件的结构,包括每个设备的节点及其属性。例如,描述 LED 和按键的 DTS 文件如下:
/dts-v1/;
/ {compatible = "your,board-name"; // 替换为你的板子名称leds {compatible = "gpio-leds";green {label = "Green LED";gpios = <&gpio PE10 GPIO_ACTIVE_LOW>;};orange {label = "Orange LED";gpios = <&gpio PE12 GPIO_ACTIVE_LOW>;default-trigger = "nand-disk";};};gpio_keys {compatible = "gpio-keys";key0 {label = "SW1";gpios = <&gpio PB0 GPIO_ACTIVE_LOW>;};key1 {label = "SW2";gpios = <&gpio PB1 GPIO_ACTIVE_LOW>;};key2 {label = "SW3";gpios = <&gpio PB2 GPIO_ACTIVE_LOW>;};};
};
2.2 生成设备树二进制文件(DTB)
编写好 DTS 文件后,可以使用设备树编译器(DTC)将其编译为 DTB 文件:
dtc -I dts -O dtb -o your_board.dtb your_board.dts
2.3 修改内核驱动程序
接下来,你需要修改你的驱动程序,以便它可以从设备树中读取设备的信息。主要涉及以下几个步骤:
-
包含设备树相关的头文件:
#include <linux/of.h> #include <linux/of_device.h>
-
在驱动的 probe 函数中,使用
of_device_get_match_data()
获取设备树信息:static int your_driver_probe(struct platform_device *pdev) {struct device_node *np = pdev->dev.of_node;struct gpio_led *led;// 获取 GPIO LED 的信息led = devm_kmalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);if (!led)return -ENOMEM;if (of_property_read_u32(np, "gpio", &led->gpio)) {dev_err(&pdev->dev, "No GPIO defined\n");return -EINVAL;}// 继续初始化 LED 等 }
-
将
platform_device
的初始化改为使用of_match_table
:static const struct of_device_id your_device_ids[] = {{ .compatible = "your,leds-gpio", },{ /* sentinel */ } };MODULE_DEVICE_TABLE(of, your_device_ids);static struct platform_driver your_driver = {.probe = your_driver_probe,.remove = your_driver_remove,.driver = {.name = "your_driver",.of_match_table = your_device_ids,}, };
2.4 修改 Makefile 和 Kconfig
确保你的驱动程序可以被编译和配置。通常需要在 Kconfig 中添加一个选项,以便选择你的驱动程序:
config YOUR_DRIVERtristate "Your Driver"depends on OFhelpSay Y here to enable support for your driver.
并在 Makefile 中添加你的源文件:
obj-m += your_driver.o
2.5 测试和验证
- 将编译生成的 DTB 文件拷贝到你的设备的启动分区中。
- 重新启动设备,确保内核可以加载新的设备树。
- 使用
dmesg
查看内核日志,确认驱动程序正确加载。
2.6 调试
如果驱动程序没有按预期工作,可以通过以下方式进行调试:
- 检查设备树的匹配是否正确。
- 查看 GPIO 是否正确配置和初始化。
- 使用
printk
或dev_info
调试信息。
总结
转换到设备树的主要好处是提高了可移植性和灵活性,使你能够更容易地支持不同版本的硬件。如果你在这个过程中遇到具体问题,随时可以问我!