|
|
il y a 1 semaine | |
|---|---|---|
| .. | ||
| clk | il y a 1 semaine | |
| nvmem | il y a 1 semaine | |
| pic | il y a 1 semaine | |
| pinctrl | il y a 1 semaine | |
| regulator | il y a 1 semaine | |
| reset | il y a 1 semaine | |
| INDEX.md | il y a 1 semaine | |
| README.md | il y a 1 semaine | |
| README_zh.md | il y a 1 semaine | |
| SUMMARY.md | il y a 1 semaine | |
The RT-Thread Device Driver Model (DM) is a comprehensive framework that provides a standardized, hierarchical approach to managing hardware devices and their drivers. Enabled through the RT_USING_DM configuration option, it introduces a sophisticated device-driver matching mechanism, bus abstraction, and seamless integration with device tree (Open Firmware) support.
The DM framework significantly enhances RT-Thread's capability to handle complex hardware systems, particularly those requiring dynamic device discovery, power management, clock control, and other hardware resource management.
The DM framework consists of several key components:
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ (Uses device APIs: clk, regulator, pinctrl, gpio, etc.) │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────────────┴────────────────────────────────────────┐
│ Device Driver Model (DM) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Bus │ │ Driver │ │ Device │ │ Platform │ │
│ │ Subsys │ │ Subsys │ │ Subsys │ │ Layer │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ OFW (Open Firmware/Device Tree) │ │
│ │ - FDT parsing - Property reading - IRQ mapping │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────────────┴────────────────────────────────────────┐
│ Hardware Resources │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Clocks │ │Regulator │ │ Resets │ │ GPIOs │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Pinctrl │ │ DMA │ │ IRQs │ │ Memory │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
The bus subsystem (rt_bus) provides the foundation for organizing devices and drivers:
Key Structures:
struct rt_bus {
struct rt_object parent;
const char *name;
rt_list_t list;
rt_list_t dev_list; /* List of devices on this bus */
rt_list_t drv_list; /* List of drivers on this bus */
rt_bool_t (*match)(rt_driver_t drv, rt_device_t dev);
rt_err_t (*probe)(rt_device_t dev);
rt_err_t (*remove)(rt_device_t dev);
rt_err_t (*shutdown)(rt_device_t dev);
};
Drivers implement hardware-specific functionality and are bound to compatible devices:
Key Structures:
struct rt_driver {
struct rt_object parent;
struct rt_bus *bus;
rt_list_t node;
rt_uint32_t ref_count;
/* Device operations */
rt_err_t (*init)(rt_device_t dev);
rt_err_t (*open)(rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close)(rt_device_t dev);
rt_ssize_t (*read)(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_ssize_t (*write)(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
/* Lifecycle callbacks */
int (*probe)(struct rt_device *dev);
int (*remove)(struct rt_device *dev);
int (*shutdown)(struct rt_device *dev);
};
The platform device model is the most common usage of DM in RT-Thread, handling memory-mapped devices:
Key Structures:
struct rt_platform_device {
struct rt_device parent;
int dev_id;
const char *name;
const struct rt_ofw_node_id *id;
void *priv;
};
struct rt_platform_driver {
struct rt_driver parent;
const char *name;
const struct rt_ofw_node_id *ids; /* Compatible strings */
rt_err_t (*probe)(struct rt_platform_device *pdev);
rt_err_t (*remove)(struct rt_platform_device *pdev);
rt_err_t (*shutdown)(struct rt_platform_device *pdev);
};
The OFW subsystem provides device tree support:
The typical lifecycle of a device in the DM framework:
Device Tree Platform Driver
│ │ │
│ Parse DT │ │
├──────────────────>│ │
│ │ │
│ Create Platform │ │
│ Device │ │
│ │ │
│ Register Device │ │
│ on Platform Bus │ │
│ │ │
│ │ Match Device │
│ │ with Driver │
│ ├─────────────────>│
│ │ │
│ │ Driver Probe │
│ │<─────────────────┤
│ │ │
│ │ Initialize HW │
│ │ │
│ │ Register APIs │
│ │ │
│ │ RUNNING │
│ │ │
│ │ Driver Remove │
│ ├─────────────────>│
│ │ │
│ │ Cleanup │
│ │<─────────────────┤
│ │ │
The Device Driver Model is enabled through the main Kconfig option:
config RT_USING_DM
bool "Enable device driver model with device tree"
default n
help
Enable device driver model with device tree (FDT). It will use more memory
to parse and support device tree feature.
Location in menuconfig: RT-Thread Components → Device Drivers → Enable device driver model with device tree
config RT_USING_DEV_BUS
bool "Using Device Bus device drivers"
default y if RT_USING_SMART
default n
menuconfig RT_USING_OFW
bool "Using Open Firmware (OFW)"
select RT_USING_ADT
select RT_USING_ADT_REF
select RT_USING_ADT_BITMAP
select RT_USING_MEMBLOCK
depends on RT_USING_DM
default n
Location: RT-Thread Components → Device Drivers → Using Open Firmware (OFW)
config RT_USING_BUILTIN_FDT
bool "Using builtin fdt in kernel"
depends on RT_USING_OFW
default n
config RT_BUILTIN_FDT_PATH
string "Builtin fdt path, will rebuild if have dts"
depends on RT_USING_BUILTIN_FDT
default "rtthread.dtb"
When RT_USING_DM and RT_USING_OFW are enabled, RT-Thread can parse and use device tree (FDT) to describe hardware:
/ {
compatible = "myboard,example";
#address-cells = <1>;
#size-cells = <1>;
clocks {
osc24M: osc24M_clk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "osc24M";
};
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@10000000 {
compatible = "myvendor,uart";
reg = <0x10000000 0x1000>;
interrupts = <32>;
clocks = <&osc24M>;
clock-names = "baudclk";
status = "okay";
};
};
regulators {
vcc_3v3: regulator-vcc-3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-boot-on;
};
};
};
#include <drivers/ofw.h>
struct rt_ofw_node *np = dev->ofw_node;
/* Read string property */
const char *status;
rt_ofw_prop_read_string(np, "status", &status);
/* Read u32 property */
rt_uint32_t freq;
rt_ofw_prop_read_u32(np, "clock-frequency", &freq);
/* Read array */
rt_uint32_t reg[2];
rt_ofw_prop_read_u32_array(np, "reg", reg, 2);
/* Get memory resources */
rt_uint64_t addr, size;
rt_dm_dev_get_address(dev, 0, &addr, &size);
/* Get IRQ */
int irq = rt_dm_dev_get_irq(dev, 0);
/* Map MMIO */
void *base = rt_dm_dev_iomap(dev, 0);
/* Get clock */
struct rt_clk *clk = rt_clk_get_by_name(dev, "baudclk");
The Device Driver Model supports numerous hardware subsystems. Each module provides standardized APIs for both application layer and driver implementation:
| Module | Description | Documentation |
|---|---|---|
| clk | Clock management framework | clk.md |
| regulator | Voltage/current regulation | regulator.md |
| pinctrl | Pin multiplexing and configuration | pinctrl.md |
| reset | Reset controller management | reset.md |
| pmdomain | Power domain management | pmdomain.md |
| pic | Platform interrupt controller | pic.md |
| nvmem | Non-volatile memory framework | nvmem.md |
| mailbox | Mailbox/doorbell communication | mailbox.md |
| thermal | Thermal management | thermal.md |
| mfd | Multi-function device | mfd.md |
| dma | DMA engine management | dma.md |
| iio | Industrial I/O subsystem | iio.md |
| phy | PHY (physical layer) framework | phy.md |
| phye | Ethernet PHY framework | phye.md |
| pci | PCI bus support | pci.md |
| ofw | Open Firmware/Device Tree | ofw.md |
/* Find device by master_id and device_id */
rt_device_t rt_dm_device_find(int master_id, int device_id);
/* Set device name */
int rt_dm_dev_set_name(rt_device_t dev, const char *format, ...);
int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix);
/* Get device name */
const char *rt_dm_dev_get_name(rt_device_t dev);
int rt_dm_dev_get_name_id(rt_device_t dev);
/* Get address resources */
int rt_dm_dev_get_address_count(rt_device_t dev);
rt_err_t rt_dm_dev_get_address(rt_device_t dev, int index,
rt_uint64_t *out_address, rt_uint64_t *out_size);
rt_err_t rt_dm_dev_get_address_by_name(rt_device_t dev, const char *name,
rt_uint64_t *out_address, rt_uint64_t *out_size);
/* Map MMIO regions */
void *rt_dm_dev_iomap(rt_device_t dev, int index);
void *rt_dm_dev_iomap_by_name(rt_device_t dev, const char *name);
/* Get IRQ resources */
int rt_dm_dev_get_irq_count(rt_device_t dev);
int rt_dm_dev_get_irq(rt_device_t dev, int index);
int rt_dm_dev_get_irq_by_name(rt_device_t dev, const char *name);
/* Read various property types */
int rt_dm_dev_prop_read_u8_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint8_t *out_values);
int rt_dm_dev_prop_read_u32_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint32_t *out_values);
int rt_dm_dev_prop_read_string_index(rt_device_t dev, const char *propname,
int index, const char **out_string);
/* Simplified single-value reading */
#define rt_dm_dev_prop_read_u32(dev, propname, out_value) \
rt_dm_dev_prop_read_u32_array_index(dev, propname, 0, 1, out_value)
/* Register platform driver */
rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv);
/* Register platform device */
rt_err_t rt_platform_device_register(struct rt_platform_device *pdev);
/* Create platform device from device tree */
rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np);
#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/ofw.h>
struct mydevice_data {
void *base;
int irq;
struct rt_clk *clk;
};
static rt_err_t mydevice_probe(struct rt_platform_device *pdev)
{
struct mydevice_data *data;
struct rt_device *dev = &pdev->parent;
/* Allocate private data */
data = rt_calloc(1, sizeof(*data));
if (!data)
return -RT_ENOMEM;
/* Map MMIO region */
data->base = rt_dm_dev_iomap(dev, 0);
if (!data->base) {
rt_free(data);
return -RT_ERROR;
}
/* Get IRQ */
data->irq = rt_dm_dev_get_irq(dev, 0);
/* Get clock */
data->clk = rt_clk_get_by_name(dev, "baudclk");
if (data->clk) {
rt_clk_prepare_enable(data->clk);
}
/* Store private data */
pdev->priv = data;
/* Initialize hardware */
/* ... */
return RT_EOK;
}
static rt_err_t mydevice_remove(struct rt_platform_device *pdev)
{
struct mydevice_data *data = pdev->priv;
/* Cleanup hardware */
/* ... */
/* Release resources */
if (data->clk) {
rt_clk_disable_unprepare(data->clk);
rt_clk_put(data->clk);
}
rt_free(data);
return RT_EOK;
}
static const struct rt_ofw_node_id mydevice_ofw_ids[] = {
{ .compatible = "myvendor,mydevice" },
{ /* sentinel */ }
};
static struct rt_platform_driver mydevice_driver = {
.name = "mydevice",
.ids = mydevice_ofw_ids,
.probe = mydevice_probe,
.remove = mydevice_remove,
};
/* Auto-register driver at boot */
RT_PLATFORM_DRIVER_EXPORT(mydevice_driver);
mydev: mydevice@10000000 {
compatible = "myvendor,mydevice";
reg = <0x10000000 0x1000>;
interrupts = <32>;
clocks = <&osc24M>;
clock-names = "baudclk";
status = "okay";
};
static rt_err_t mydevice_probe(struct rt_platform_device *pdev)
{
rt_err_t ret;
struct mydevice_data *data;
data = rt_calloc(1, sizeof(*data));
if (!data)
return -RT_ENOMEM;
data->base = rt_dm_dev_iomap(&pdev->parent, 0);
if (!data->base) {
ret = -RT_ERROR;
goto err_free;
}
data->clk = rt_clk_get_by_name(&pdev->parent, "baudclk");
if (!data->clk) {
ret = -RT_ERROR;
goto err_unmap;
}
ret = rt_clk_prepare_enable(data->clk);
if (ret)
goto err_put_clk;
/* Success */
pdev->priv = data;
return RT_EOK;
err_put_clk:
rt_clk_put(data->clk);
err_unmap:
rt_iounmap(data->base);
err_free:
rt_free(data);
return ret;
}
DM adds memory overhead:
Recommendations:
#define DBG_TAG "mydriver"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
LOG_D("Debug message");
LOG_I("Info message");
LOG_W("Warning message");
LOG_E("Error message");
/* In menuconfig, enable OFW debugging */
RT-Thread Components → Device Drivers → Using Open Firmware (OFW)
Traditional RT-Thread drivers:
rt_device_t dev = rt_device_find("uart0");
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
With DM:
/* Device automatically created from device tree */
rt_device_t dev = rt_device_find("uart0");
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
/* API remains the same for users */
Driver implementation changes:
components/drivers/core/, components/drivers/ofw/