[documentation][device_driver_model] Add main DM documentation (EN/CN)
Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com>
This commit is contained in:
parent
c9db19d201
commit
17ecb56cb5
|
|
@ -0,0 +1,670 @@
|
|||
# RT-Thread Device Driver Model (DM)
|
||||
|
||||
## Overview
|
||||
|
||||
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.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Bus-Driver-Device Architecture**: Hierarchical organization of hardware components
|
||||
- **Device Tree Integration**: Full Open Firmware (OFW) support for hardware description
|
||||
- **Dynamic Device Management**: Runtime device discovery and driver binding
|
||||
- **Resource Management**: Unified APIs for clocks, regulators, resets, power domains, etc.
|
||||
- **Platform Device Model**: Abstraction for memory-mapped and non-discoverable devices
|
||||
- **Reference Counting**: Safe resource lifecycle management
|
||||
- **Extensible Framework**: Easy addition of new bus types and device classes
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
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 │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 1. Bus Subsystem
|
||||
|
||||
The bus subsystem (`rt_bus`) provides the foundation for organizing devices and drivers:
|
||||
|
||||
- **Bus Registration**: Buses are registered with the system and maintain lists of devices and drivers
|
||||
- **Device-Driver Matching**: Each bus implements a match function to pair compatible drivers with devices
|
||||
- **Probe/Remove**: Buses coordinate the lifecycle of device-driver bindings
|
||||
- **Lock Management**: Thread-safe operations on device and driver lists
|
||||
|
||||
**Key Structures**:
|
||||
```c
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
#### 2. Driver Subsystem
|
||||
|
||||
Drivers implement hardware-specific functionality and are bound to compatible devices:
|
||||
|
||||
- **Driver Registration**: Drivers register with a specific bus
|
||||
- **Device Operations**: Standard operations (init, open, close, read, write, control)
|
||||
- **Probe/Remove Callbacks**: Initialization and cleanup when bound to devices
|
||||
- **Reference Counting**: Tracks the number of active device bindings
|
||||
|
||||
**Key Structures**:
|
||||
```c
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
#### 3. Platform Device Model
|
||||
|
||||
The platform device model is the most common usage of DM in RT-Thread, handling memory-mapped devices:
|
||||
|
||||
- **Platform Bus**: A virtual bus for devices not on a physical bus
|
||||
- **Device Tree Integration**: Automatic device creation from FDT nodes
|
||||
- **Resource Abstraction**: Unified access to memory regions, IRQs, clocks, etc.
|
||||
- **Driver Binding**: Automatic matching based on compatible strings
|
||||
|
||||
**Key Structures**:
|
||||
```c
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
#### 4. OFW (Open Firmware) Support
|
||||
|
||||
The OFW subsystem provides device tree support:
|
||||
|
||||
- **FDT Parsing**: Flattened Device Tree (FDT) binary format parsing
|
||||
- **Device Node Management**: Hierarchical device tree node representation
|
||||
- **Property Reading**: APIs to read device tree properties
|
||||
- **Resource Mapping**: Translation of DT resources to system resources (IRQ, memory, etc.)
|
||||
|
||||
### Device Lifecycle
|
||||
|
||||
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 │
|
||||
│ │<─────────────────┤
|
||||
│ │ │
|
||||
```
|
||||
|
||||
## Kconfig Configuration
|
||||
|
||||
### Main DM Option
|
||||
|
||||
The Device Driver Model is enabled through the main Kconfig option:
|
||||
|
||||
```kconfig
|
||||
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`
|
||||
|
||||
### Related Options
|
||||
|
||||
#### Device Bus
|
||||
```kconfig
|
||||
config RT_USING_DEV_BUS
|
||||
bool "Using Device Bus device drivers"
|
||||
default y if RT_USING_SMART
|
||||
default n
|
||||
```
|
||||
|
||||
#### Open Firmware (OFW)
|
||||
```kconfig
|
||||
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)`
|
||||
|
||||
#### Builtin FDT
|
||||
```kconfig
|
||||
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"
|
||||
```
|
||||
|
||||
## Device Tree Integration
|
||||
|
||||
### Device Tree Basics
|
||||
|
||||
When `RT_USING_DM` and `RT_USING_OFW` are enabled, RT-Thread can parse and use device tree (FDT) to describe hardware:
|
||||
|
||||
#### Example Device Tree Node
|
||||
|
||||
```dts
|
||||
/ {
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Using Device Tree in Drivers
|
||||
|
||||
#### Getting Device Node
|
||||
|
||||
```c
|
||||
#include <drivers/ofw.h>
|
||||
|
||||
struct rt_ofw_node *np = dev->ofw_node;
|
||||
```
|
||||
|
||||
#### Reading Properties
|
||||
|
||||
```c
|
||||
/* 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);
|
||||
```
|
||||
|
||||
#### Getting Resources
|
||||
|
||||
```c
|
||||
/* 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");
|
||||
```
|
||||
|
||||
## DM-Dependent Modules
|
||||
|
||||
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](clk/README.md) |
|
||||
| **regulator** | Voltage/current regulation | [regulator.md](regulator/README.md) |
|
||||
| **pinctrl** | Pin multiplexing and configuration | [pinctrl.md](pinctrl/README.md) |
|
||||
| **reset** | Reset controller management | [reset.md](reset/README.md) |
|
||||
| **pmdomain** | Power domain management | [pmdomain.md](pmdomain/README.md) |
|
||||
| **pic** | Platform interrupt controller | [pic.md](pic/README.md) |
|
||||
| **nvmem** | Non-volatile memory framework | [nvmem.md](nvmem/README.md) |
|
||||
| **mailbox** | Mailbox/doorbell communication | [mailbox.md](mailbox/README.md) |
|
||||
| **thermal** | Thermal management | [thermal.md](thermal/README.md) |
|
||||
| **mfd** | Multi-function device | [mfd.md](mfd/README.md) |
|
||||
| **dma** | DMA engine management | [dma.md](dma/README.md) |
|
||||
| **iio** | Industrial I/O subsystem | [iio.md](iio/README.md) |
|
||||
| **phy** | PHY (physical layer) framework | [phy.md](phy/README.md) |
|
||||
| **phye** | Ethernet PHY framework | [phye.md](phye/README.md) |
|
||||
| **pci** | PCI bus support | [pci.md](pci/README.md) |
|
||||
| **ofw** | Open Firmware/Device Tree | [ofw.md](ofw/README.md) |
|
||||
|
||||
## Common APIs
|
||||
|
||||
### Device Management
|
||||
|
||||
```c
|
||||
/* 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);
|
||||
```
|
||||
|
||||
### Device Tree APIs
|
||||
|
||||
```c
|
||||
/* 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);
|
||||
```
|
||||
|
||||
### Property Reading
|
||||
|
||||
```c
|
||||
/* 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)
|
||||
```
|
||||
|
||||
### Platform Device APIs
|
||||
|
||||
```c
|
||||
/* 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);
|
||||
```
|
||||
|
||||
## Writing a Platform Driver
|
||||
|
||||
### Basic Driver Structure
|
||||
|
||||
```c
|
||||
#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);
|
||||
```
|
||||
|
||||
### Device Tree Binding
|
||||
|
||||
```dts
|
||||
mydev: mydevice@10000000 {
|
||||
compatible = "myvendor,mydevice";
|
||||
reg = <0x10000000 0x1000>;
|
||||
interrupts = <32>;
|
||||
clocks = <&osc24M>;
|
||||
clock-names = "baudclk";
|
||||
status = "okay";
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Resource Management
|
||||
|
||||
- Always check return values when getting resources
|
||||
- Use reference counting for shared resources (clocks, regulators)
|
||||
- Clean up resources in the remove callback
|
||||
- Handle probe deferral for dependencies
|
||||
|
||||
### 2. Device Tree
|
||||
|
||||
- Use standard bindings when available
|
||||
- Document custom properties in driver documentation
|
||||
- Use labels for phandle references
|
||||
- Keep device tree and driver in sync
|
||||
|
||||
### 3. Error Handling
|
||||
|
||||
```c
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Thread Safety
|
||||
|
||||
- Use spinlocks for hardware register access
|
||||
- Use mutexes for long operations
|
||||
- Consider interrupt context in ISRs
|
||||
- Protect shared data structures
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memory Usage
|
||||
|
||||
DM adds memory overhead:
|
||||
- Device tree parsing requires memory for FDT and node structures
|
||||
- Each device/driver adds object overhead
|
||||
- Additional metadata for resource management
|
||||
|
||||
**Recommendations**:
|
||||
- Only enable DM for systems that need it
|
||||
- Consider memory constraints when designing device trees
|
||||
- Use static allocation where possible
|
||||
|
||||
### Boot Time
|
||||
|
||||
- Device tree parsing happens early in boot
|
||||
- Driver probing can be deferred
|
||||
- Parallel initialization where possible
|
||||
|
||||
## Debugging
|
||||
|
||||
### Enable Debug Output
|
||||
|
||||
```c
|
||||
#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");
|
||||
```
|
||||
|
||||
### DM-Specific Debugging
|
||||
|
||||
```
|
||||
/* In menuconfig, enable OFW debugging */
|
||||
RT-Thread Components → Device Drivers → Using Open Firmware (OFW)
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Device not probing**: Check compatible string matches
|
||||
2. **Resource not found**: Verify device tree properties
|
||||
3. **Probe deferral loop**: Check for circular dependencies
|
||||
4. **Memory access fault**: Verify MMIO mapping is correct
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Traditional RT-Thread Drivers
|
||||
|
||||
Traditional RT-Thread drivers:
|
||||
```c
|
||||
rt_device_t dev = rt_device_find("uart0");
|
||||
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
|
||||
```
|
||||
|
||||
With DM:
|
||||
```c
|
||||
/* 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:
|
||||
- Add platform driver structure
|
||||
- Implement probe/remove callbacks
|
||||
- Use DM APIs for resource management
|
||||
- Add device tree binding
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Open Firmware (OFW) Documentation](../ofw/dtc.md)
|
||||
- [Device Framework](../framework/device.md)
|
||||
- For module-specific documentation, see the links in the DM-Dependent Modules section
|
||||
|
||||
## References
|
||||
|
||||
- [Device Tree Specification](https://devicetree-specification.readthedocs.io/)
|
||||
- [Linux Kernel Driver Model](https://www.kernel.org/doc/html/latest/driver-api/driver-model/index.html)
|
||||
- RT-Thread Source Code: `components/drivers/core/`, `components/drivers/ofw/`
|
||||
|
|
@ -0,0 +1,670 @@
|
|||
# RT-Thread 设备驱动模型 (DM)
|
||||
|
||||
## 概述
|
||||
|
||||
RT-Thread 设备驱动模型(Device Driver Model,简称 DM)是一个全面的框架,提供了标准化的层次化方法来管理硬件设备及其驱动程序。通过 `RT_USING_DM` 配置选项启用,它引入了复杂的设备-驱动匹配机制、总线抽象以及与设备树(Open Firmware)的无缝集成。
|
||||
|
||||
DM 框架显著增强了 RT-Thread 处理复杂硬件系统的能力,特别是那些需要动态设备发现、电源管理、时钟控制和其他硬件资源管理的系统。
|
||||
|
||||
### 主要特性
|
||||
|
||||
- **总线-驱动-设备架构**:硬件组件的层次化组织
|
||||
- **设备树集成**:完整的 Open Firmware (OFW) 支持用于硬件描述
|
||||
- **动态设备管理**:运行时设备发现和驱动绑定
|
||||
- **资源管理**:统一的时钟、电压调节器、复位、电源域等 API
|
||||
- **平台设备模型**:内存映射和不可发现设备的抽象
|
||||
- **引用计数**:安全的资源生命周期管理
|
||||
- **可扩展框架**:轻松添加新的总线类型和设备类
|
||||
|
||||
## 架构
|
||||
|
||||
### 核心组件
|
||||
|
||||
DM 框架由几个关键组件组成:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 应用层 │
|
||||
│ (使用设备 API: clk, regulator, pinctrl, gpio 等) │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────────────┴────────────────────────────────────────┐
|
||||
│ 设备驱动模型 (DM) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 总线 │ │ 驱动 │ │ 设备 │ │ 平台 │ │
|
||||
│ │ 子系统 │ │ 子系统 │ │ 子系统 │ │ 层 │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ OFW (开放固件/设备树) │ │
|
||||
│ │ - FDT 解析 - 属性读取 - IRQ 映射 │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────────────┴────────────────────────────────────────┐
|
||||
│ 硬件资源 │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 时钟 │ │ 调节器 │ │ 复位 │ │ GPIO │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 引脚控制 │ │ DMA │ │ 中断 │ │ 内存 │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 1. 总线子系统
|
||||
|
||||
总线子系统(`rt_bus`)为组织设备和驱动程序提供了基础:
|
||||
|
||||
- **总线注册**:总线在系统中注册并维护设备和驱动程序列表
|
||||
- **设备-驱动匹配**:每个总线实现匹配函数以配对兼容的驱动程序和设备
|
||||
- **探测/移除**:总线协调设备-驱动绑定的生命周期
|
||||
- **锁管理**:设备和驱动程序列表的线程安全操作
|
||||
|
||||
**核心结构**:
|
||||
```c
|
||||
struct rt_bus {
|
||||
struct rt_object parent;
|
||||
const char *name;
|
||||
rt_list_t list;
|
||||
rt_list_t dev_list; /* 此总线上的设备列表 */
|
||||
rt_list_t drv_list; /* 此总线上的驱动程序列表 */
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
#### 2. 驱动子系统
|
||||
|
||||
驱动程序实现硬件特定功能并绑定到兼容的设备:
|
||||
|
||||
- **驱动注册**:驱动程序向特定总线注册
|
||||
- **设备操作**:标准操作(init、open、close、read、write、control)
|
||||
- **探测/移除回调**:绑定到设备时的初始化和清理
|
||||
- **引用计数**:跟踪活动设备绑定的数量
|
||||
|
||||
**核心结构**:
|
||||
```c
|
||||
struct rt_driver {
|
||||
struct rt_object parent;
|
||||
struct rt_bus *bus;
|
||||
rt_list_t node;
|
||||
rt_uint32_t ref_count;
|
||||
|
||||
/* 设备操作 */
|
||||
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);
|
||||
|
||||
/* 生命周期回调 */
|
||||
int (*probe)(struct rt_device *dev);
|
||||
int (*remove)(struct rt_device *dev);
|
||||
int (*shutdown)(struct rt_device *dev);
|
||||
};
|
||||
```
|
||||
|
||||
#### 3. 平台设备模型
|
||||
|
||||
平台设备模型是 RT-Thread 中 DM 最常见的用法,处理内存映射设备:
|
||||
|
||||
- **平台总线**:用于非物理总线上设备的虚拟总线
|
||||
- **设备树集成**:从 FDT 节点自动创建设备
|
||||
- **资源抽象**:统一访问内存区域、中断、时钟等
|
||||
- **驱动绑定**:基于 compatible 字符串的自动匹配
|
||||
|
||||
**核心结构**:
|
||||
```c
|
||||
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 字符串 */
|
||||
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
#### 4. OFW(开放固件)支持
|
||||
|
||||
OFW 子系统提供设备树支持:
|
||||
|
||||
- **FDT 解析**:扁平设备树(FDT)二进制格式解析
|
||||
- **设备节点管理**:层次化设备树节点表示
|
||||
- **属性读取**:读取设备树属性的 API
|
||||
- **资源映射**:将 DT 资源转换为系统资源(IRQ、内存等)
|
||||
|
||||
### 设备生命周期
|
||||
|
||||
DM 框架中设备的典型生命周期:
|
||||
|
||||
```
|
||||
设备树 平台 驱动
|
||||
│ │ │
|
||||
│ 解析 DT │ │
|
||||
├──────────────────>│ │
|
||||
│ │ │
|
||||
│ 创建平台 │ │
|
||||
│ 设备 │ │
|
||||
│ │ │
|
||||
│ 在平台总线上 │ │
|
||||
│ 注册设备 │ │
|
||||
│ │ │
|
||||
│ │ 匹配设备 │
|
||||
│ │ 与驱动 │
|
||||
│ ├─────────────────>│
|
||||
│ │ │
|
||||
│ │ 驱动探测 │
|
||||
│ │<─────────────────┤
|
||||
│ │ │
|
||||
│ │ 初始化硬件 │
|
||||
│ │ │
|
||||
│ │ 注册 API │
|
||||
│ │ │
|
||||
│ │ 运行中 │
|
||||
│ │ │
|
||||
│ │ 驱动移除 │
|
||||
│ ├─────────────────>│
|
||||
│ │ │
|
||||
│ │ 清理 │
|
||||
│ │<─────────────────┤
|
||||
│ │ │
|
||||
```
|
||||
|
||||
## Kconfig 配置
|
||||
|
||||
### 主 DM 选项
|
||||
|
||||
通过主 Kconfig 选项启用设备驱动模型:
|
||||
|
||||
```kconfig
|
||||
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.
|
||||
```
|
||||
|
||||
**在 menuconfig 中的位置**:`RT-Thread Components → Device Drivers → Enable device driver model with device tree`
|
||||
|
||||
### 相关选项
|
||||
|
||||
#### 设备总线
|
||||
```kconfig
|
||||
config RT_USING_DEV_BUS
|
||||
bool "Using Device Bus device drivers"
|
||||
default y if RT_USING_SMART
|
||||
default n
|
||||
```
|
||||
|
||||
#### 开放固件(OFW)
|
||||
```kconfig
|
||||
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
|
||||
```
|
||||
|
||||
**位置**:`RT-Thread Components → Device Drivers → Using Open Firmware (OFW)`
|
||||
|
||||
#### 内置 FDT
|
||||
```kconfig
|
||||
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"
|
||||
```
|
||||
|
||||
## 设备树集成
|
||||
|
||||
### 设备树基础
|
||||
|
||||
当启用 `RT_USING_DM` 和 `RT_USING_OFW` 时,RT-Thread 可以解析和使用设备树(FDT)来描述硬件:
|
||||
|
||||
#### 设备树节点示例
|
||||
|
||||
```dts
|
||||
/ {
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 在驱动中使用设备树
|
||||
|
||||
#### 获取设备节点
|
||||
|
||||
```c
|
||||
#include <drivers/ofw.h>
|
||||
|
||||
struct rt_ofw_node *np = dev->ofw_node;
|
||||
```
|
||||
|
||||
#### 读取属性
|
||||
|
||||
```c
|
||||
/* 读取字符串属性 */
|
||||
const char *status;
|
||||
rt_ofw_prop_read_string(np, "status", &status);
|
||||
|
||||
/* 读取 u32 属性 */
|
||||
rt_uint32_t freq;
|
||||
rt_ofw_prop_read_u32(np, "clock-frequency", &freq);
|
||||
|
||||
/* 读取数组 */
|
||||
rt_uint32_t reg[2];
|
||||
rt_ofw_prop_read_u32_array(np, "reg", reg, 2);
|
||||
```
|
||||
|
||||
#### 获取资源
|
||||
|
||||
```c
|
||||
/* 获取内存资源 */
|
||||
rt_uint64_t addr, size;
|
||||
rt_dm_dev_get_address(dev, 0, &addr, &size);
|
||||
|
||||
/* 获取 IRQ */
|
||||
int irq = rt_dm_dev_get_irq(dev, 0);
|
||||
|
||||
/* 映射 MMIO */
|
||||
void *base = rt_dm_dev_iomap(dev, 0);
|
||||
|
||||
/* 获取时钟 */
|
||||
struct rt_clk *clk = rt_clk_get_by_name(dev, "baudclk");
|
||||
```
|
||||
|
||||
## DM 依赖模块
|
||||
|
||||
设备驱动模型支持众多硬件子系统。每个模块为应用层和驱动实现提供标准化的 API:
|
||||
|
||||
| 模块 | 描述 | 文档 |
|
||||
|------|------|------|
|
||||
| **clk** | 时钟管理框架 | [clk.md](clk/README_zh.md) |
|
||||
| **regulator** | 电压/电流调节 | [regulator.md](regulator/README_zh.md) |
|
||||
| **pinctrl** | 引脚复用和配置 | [pinctrl.md](pinctrl/README_zh.md) |
|
||||
| **reset** | 复位控制器管理 | [reset.md](reset/README_zh.md) |
|
||||
| **pmdomain** | 电源域管理 | [pmdomain.md](pmdomain/README_zh.md) |
|
||||
| **pic** | 平台中断控制器 | [pic.md](pic/README_zh.md) |
|
||||
| **nvmem** | 非易失性内存框架 | [nvmem.md](nvmem/README_zh.md) |
|
||||
| **mailbox** | 邮箱/门铃通信 | [mailbox.md](mailbox/README_zh.md) |
|
||||
| **thermal** | 热管理 | [thermal.md](thermal/README_zh.md) |
|
||||
| **mfd** | 多功能设备 | [mfd.md](mfd/README_zh.md) |
|
||||
| **dma** | DMA 引擎管理 | [dma.md](dma/README_zh.md) |
|
||||
| **iio** | 工业 I/O 子系统 | [iio.md](iio/README_zh.md) |
|
||||
| **phy** | PHY(物理层)框架 | [phy.md](phy/README_zh.md) |
|
||||
| **phye** | 以太网 PHY 框架 | [phye.md](phye/README_zh.md) |
|
||||
| **pci** | PCI 总线支持 | [pci.md](pci/README_zh.md) |
|
||||
| **ofw** | 开放固件/设备树 | [ofw.md](ofw/README_zh.md) |
|
||||
|
||||
## 常用 API
|
||||
|
||||
### 设备管理
|
||||
|
||||
```c
|
||||
/* 通过 master_id 和 device_id 查找设备 */
|
||||
rt_device_t rt_dm_device_find(int master_id, int device_id);
|
||||
|
||||
/* 设置设备名称 */
|
||||
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);
|
||||
|
||||
/* 获取设备名称 */
|
||||
const char *rt_dm_dev_get_name(rt_device_t dev);
|
||||
int rt_dm_dev_get_name_id(rt_device_t dev);
|
||||
```
|
||||
|
||||
### 设备树 API
|
||||
|
||||
```c
|
||||
/* 获取地址资源 */
|
||||
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);
|
||||
|
||||
/* 映射 MMIO 区域 */
|
||||
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);
|
||||
|
||||
/* 获取 IRQ 资源 */
|
||||
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);
|
||||
```
|
||||
|
||||
### 属性读取
|
||||
|
||||
```c
|
||||
/* 读取各种属性类型 */
|
||||
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);
|
||||
|
||||
/* 简化的单值读取 */
|
||||
#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)
|
||||
```
|
||||
|
||||
### 平台设备 API
|
||||
|
||||
```c
|
||||
/* 注册平台驱动 */
|
||||
rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv);
|
||||
|
||||
/* 注册平台设备 */
|
||||
rt_err_t rt_platform_device_register(struct rt_platform_device *pdev);
|
||||
|
||||
/* 从设备树创建平台设备 */
|
||||
rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np);
|
||||
```
|
||||
|
||||
## 编写平台驱动
|
||||
|
||||
### 基本驱动结构
|
||||
|
||||
```c
|
||||
#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;
|
||||
|
||||
/* 分配私有数据 */
|
||||
data = rt_calloc(1, sizeof(*data));
|
||||
if (!data)
|
||||
return -RT_ENOMEM;
|
||||
|
||||
/* 映射 MMIO 区域 */
|
||||
data->base = rt_dm_dev_iomap(dev, 0);
|
||||
if (!data->base) {
|
||||
rt_free(data);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* 获取 IRQ */
|
||||
data->irq = rt_dm_dev_get_irq(dev, 0);
|
||||
|
||||
/* 获取时钟 */
|
||||
data->clk = rt_clk_get_by_name(dev, "baudclk");
|
||||
if (data->clk) {
|
||||
rt_clk_prepare_enable(data->clk);
|
||||
}
|
||||
|
||||
/* 存储私有数据 */
|
||||
pdev->priv = data;
|
||||
|
||||
/* 初始化硬件 */
|
||||
/* ... */
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t mydevice_remove(struct rt_platform_device *pdev)
|
||||
{
|
||||
struct mydevice_data *data = pdev->priv;
|
||||
|
||||
/* 清理硬件 */
|
||||
/* ... */
|
||||
|
||||
/* 释放资源 */
|
||||
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,
|
||||
};
|
||||
|
||||
/* 启动时自动注册驱动 */
|
||||
RT_PLATFORM_DRIVER_EXPORT(mydevice_driver);
|
||||
```
|
||||
|
||||
### 设备树绑定
|
||||
|
||||
```dts
|
||||
mydev: mydevice@10000000 {
|
||||
compatible = "myvendor,mydevice";
|
||||
reg = <0x10000000 0x1000>;
|
||||
interrupts = <32>;
|
||||
clocks = <&osc24M>;
|
||||
clock-names = "baudclk";
|
||||
status = "okay";
|
||||
};
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 资源管理
|
||||
|
||||
- 获取资源时始终检查返回值
|
||||
- 对共享资源(时钟、调节器)使用引用计数
|
||||
- 在移除回调中清理资源
|
||||
- 处理依赖项的探测延迟
|
||||
|
||||
### 2. 设备树
|
||||
|
||||
- 尽可能使用标准绑定
|
||||
- 在驱动文档中记录自定义属性
|
||||
- 使用标签进行 phandle 引用
|
||||
- 保持设备树和驱动同步
|
||||
|
||||
### 3. 错误处理
|
||||
|
||||
```c
|
||||
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;
|
||||
|
||||
/* 成功 */
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 线程安全
|
||||
|
||||
- 对硬件寄存器访问使用自旋锁
|
||||
- 对长操作使用互斥锁
|
||||
- 在 ISR 中考虑中断上下文
|
||||
- 保护共享数据结构
|
||||
|
||||
## 性能考虑
|
||||
|
||||
### 内存使用
|
||||
|
||||
DM 增加了内存开销:
|
||||
- 设备树解析需要 FDT 和节点结构的内存
|
||||
- 每个设备/驱动添加对象开销
|
||||
- 资源管理的额外元数据
|
||||
|
||||
**建议**:
|
||||
- 仅为需要它的系统启用 DM
|
||||
- 设计设备树时考虑内存限制
|
||||
- 尽可能使用静态分配
|
||||
|
||||
### 启动时间
|
||||
|
||||
- 设备树解析在启动早期进行
|
||||
- 驱动探测可以延迟
|
||||
- 尽可能并行初始化
|
||||
|
||||
## 调试
|
||||
|
||||
### 启用调试输出
|
||||
|
||||
```c
|
||||
#define DBG_TAG "mydriver"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
LOG_D("调试消息");
|
||||
LOG_I("信息消息");
|
||||
LOG_W("警告消息");
|
||||
LOG_E("错误消息");
|
||||
```
|
||||
|
||||
### DM 特定调试
|
||||
|
||||
```
|
||||
/* 在 menuconfig 中启用 OFW 调试 */
|
||||
RT-Thread Components → Device Drivers → Using Open Firmware (OFW)
|
||||
```
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **设备未探测**:检查 compatible 字符串是否匹配
|
||||
2. **资源未找到**:验证设备树属性
|
||||
3. **探测延迟循环**:检查循环依赖
|
||||
4. **内存访问错误**:验证 MMIO 映射正确
|
||||
|
||||
## 迁移指南
|
||||
|
||||
### 从传统 RT-Thread 驱动
|
||||
|
||||
传统 RT-Thread 驱动:
|
||||
```c
|
||||
rt_device_t dev = rt_device_find("uart0");
|
||||
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
|
||||
```
|
||||
|
||||
使用 DM:
|
||||
```c
|
||||
/* 设备从设备树自动创建 */
|
||||
rt_device_t dev = rt_device_find("uart0");
|
||||
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
|
||||
/* 用户 API 保持不变 */
|
||||
```
|
||||
|
||||
驱动实现变化:
|
||||
- 添加平台驱动结构
|
||||
- 实现探测/移除回调
|
||||
- 使用 DM API 进行资源管理
|
||||
- 添加设备树绑定
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [开放固件(OFW)文档](../ofw/dtc.md)
|
||||
- [设备框架](../framework/device.md)
|
||||
- 有关模块特定文档,请参阅 DM 依赖模块部分中的链接
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [设备树规范](https://devicetree-specification.readthedocs.io/)
|
||||
- [Linux 内核驱动模型](https://www.kernel.org/doc/html/latest/driver-api/driver-model/index.html)
|
||||
- RT-Thread 源代码:`components/drivers/core/`、`components/drivers/ofw/`
|
||||
Loading…
Reference in New Issue