代碼:u-boot-xlnx-xilinx
文章目錄
DM模型
先看一下uboot的驅動模型Driver Mode。
DM 是 U-Boot 中的驅動框架,全稱 Driver Mode。
udevice 描述具體的某一個硬件設備。
driver 是與這個設備匹配的驅動。
uclass 是同一類設備的抽象,提供管理同一類設備的抽象接口。
struct udevice
/**
* struct udevice - An instance of a driver
*
* This holds information about a device, which is a driver bound to a
* particular port or peripheral (essentially a driver instance).
*
* A device will come into existence through a 'bind' call, either due to
* a U_BOOT_DEVICE() macro (in which case platdata is non-NULL) or a node
* in the device tree (in which case of_offset is >= 0). In the latter case
* we translate the device tree information into platdata in a function
* implemented by the driver ofdata_to_platdata method (called just before the
* probe method if the device has a device tree node.
*
* All three of platdata, priv and uclass_priv can be allocated by the
* driver, or you can use the auto_alloc_size members of struct driver and
* struct uclass_driver to have driver model do this automatically.
*
* @driver: The driver used by this device
* @name: Name of device, typically the FDT node name
* @platdata: Configuration data for this device
* @parent_platdata: The parent bus's configuration data for this device
* @uclass_platdata: The uclass's configuration data for this device
* @of_offset: Device tree node offset for this device (- for none)
* @driver_data: Driver data word for the entry that matched this device with
* its driver
* @parent: Parent of this device, or NULL for the top level device
* @priv: Private data for this device
* @uclass: Pointer to uclass for this device
* @uclass_priv: The uclass's private data for this device
* @parent_priv: The parent's private data for this device
* @uclass_node: Used by uclass to link its devices
* @child_head: List of children of this device
* @sibling_node: Next device in list of all devices
* @flags: Flags for this device DM_FLAG_...
* @req_seq: Requested sequence number for this device (-1 = any)
* @seq: Allocated sequence number for this device (-1 = none). This is set up
* when the device is probed and will be unique within the device's uclass.
* @devres_head: List of memory allocations associated with this device.
* When CONFIG_DEVRES is enabled, devm_kmalloc() and friends will
* add to this list. Memory so-allocated will be freed
* automatically when the device is removed / unbound
*/
struct udevice {
const struct driver *driver;
const char *name;
void *platdata;
void *parent_platdata;
void *uclass_platdata;
int of_offset;
ulong driver_data;
struct udevice *parent;
void *priv;
struct uclass *uclass;
void *uclass_priv;
void *parent_priv;
struct list_head uclass_node;
struct list_head child_head;
struct list_head sibling_node;
uint32_t flags;
int req_seq;
int seq;
#ifdef CONFIG_DEVRES
struct list_head devres_head;
#endif
};
struct driver
/**
* struct driver - A driver for a feature or peripheral
*
* This holds methods for setting up a new device, and also removing it.
* The device needs information to set itself up - this is provided either
* by platdata or a device tree node (which we find by looking up
* matching compatible strings with of_match).
*
* Drivers all belong to a uclass, representing a class of devices of the
* same type. Common elements of the drivers can be implemented in the uclass,
* or the uclass can provide a consistent interface to the drivers within
* it.
*
* @name: Device name
* @id: Identiies the uclass we belong to
* @of_match: List of compatible strings to match, and any identifying data
* for each.
* @bind: Called to bind a device to its driver
* @probe: Called to probe a device, i.e. activate it
* @remove: Called to remove a device, i.e. de-activate it
* @unbind: Called to unbind a device from its driver
* @ofdata_to_platdata: Called before probe to decode device tree data
* @child_post_bind: Called after a new child has been bound
* @child_pre_probe: Called before a child device is probed. The device has
* memory allocated but it has not yet been probed.
* @child_post_remove: Called after a child device is removed. The device
* has memory allocated but its device_remove() method has been called.
* @priv_auto_alloc_size: If non-zero this is the size of the private data
* to be allocated in the device's ->priv pointer. If zero, then the driver
* is responsible for allocating any data required.
* @platdata_auto_alloc_size: If non-zero this is the size of the
* platform data to be allocated in the device's ->platdata pointer.
* This is typically only useful for device-tree-aware drivers (those with
* an of_match), since drivers which use platdata will have the data
* provided in the U_BOOT_DEVICE() instantiation.
* @per_child_auto_alloc_size: Each device can hold private data owned by
* its parent. If required this will be automatically allocated if this
* value is non-zero.
* @per_child_platdata_auto_alloc_size: A bus likes to store information about
* its children. If non-zero this is the size of this data, to be allocated
* in the child's parent_platdata pointer.
* @ops: Driver-specific operations. This is typically a list of function
* pointers defined by the driver, to implement driver functions required by
* the uclass.
* @flags: driver flags - see DM_FLAGS_...
*/
struct driver {
char *name;
enum uclass_id id;
const struct udevice_id *of_match;
int (*bind)(struct udevice *dev);
int (*probe)(struct udevice *dev);
int (*remove)(struct udevice *dev);
int (*unbind)(struct udevice *dev);
int (*ofdata_to_platdata)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*child_post_remove)(struct udevice *dev);
int priv_auto_alloc_size;
int platdata_auto_alloc_size;
int per_child_auto_alloc_size;
int per_child_platdata_auto_alloc_size;
const void *ops; /* driver-specific operations */
uint32_t flags;
};
uclass_driver
/**
* struct uclass_driver - Driver for the uclass
*
* A uclass_driver provides a consistent interface to a set of related
* drivers.
*
* @name: Name of uclass driver
* @id: ID number of this uclass
* @post_bind: Called after a new device is bound to this uclass
* @pre_unbind: Called before a device is unbound from this uclass
* @pre_probe: Called before a new device is probed
* @post_probe: Called after a new device is probed
* @pre_remove: Called before a device is removed
* @child_post_bind: Called after a child is bound to a device in this uclass
* @init: Called to set up the uclass
* @destroy: Called to destroy the uclass
* @priv_auto_alloc_size: If non-zero this is the size of the private data
* to be allocated in the uclass's ->priv pointer. If zero, then the uclass
* driver is responsible for allocating any data required.
* @per_device_auto_alloc_size: Each device can hold private data owned
* by the uclass. If required this will be automatically allocated if this
* value is non-zero.
* @per_device_platdata_auto_alloc_size: Each device can hold platform data
* owned by the uclass as 'dev->uclass_platdata'. If the value is non-zero,
* then this will be automatically allocated.
* @per_child_auto_alloc_size: Each child device (of a parent in this
* uclass) can hold parent data for the device/uclass. This value is only
* used as a falback if this member is 0 in the driver.
* @per_child_platdata_auto_alloc_size: A bus likes to store information about
* its children. If non-zero this is the size of this data, to be allocated
* in the child device's parent_platdata pointer. This value is only used as
* a falback if this member is 0 in the driver.
* @ops: Uclass operations, providing the consistent interface to devices
* within the uclass.
* @flags: Flags for this uclass (DM_UC_...)
*/
struct uclass_driver {
const char *name;
enum uclass_id id;
int (*post_bind)(struct udevice *dev);
int (*pre_unbind)(struct udevice *dev);
int (*pre_probe)(struct udevice *dev);
int (*post_probe)(struct udevice *dev);
int (*pre_remove)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*init)(struct uclass *class);
int (*destroy)(struct uclass *class);
int priv_auto_alloc_size;
int per_device_auto_alloc_size;
int per_device_platdata_auto_alloc_size;
int per_child_auto_alloc_size;
int per_child_platdata_auto_alloc_size;
const void *ops;
uint32_t flags;
};
##驅動初始化
驅動模型初始化函數入口爲initf_dm和initr_dm,initr_dm爲代碼重定位後的。
看一下initr_dm,
initr_dm
static int initr_dm(void)
{
int ret;
/* Save the pre-reloc driver model and start a new one */
gd->dm_root_f = gd->dm_root; //把initf_dm初始化保存
gd->dm_root = NULL;
#ifdef CONFIG_TIMER
gd->timer = NULL;
#endif
ret = dm_init_and_scan(false); //掃描設備和驅動進行初始化
if (ret)
return ret;
#ifdef CONFIG_TIMER_EARLY
ret = dm_timer_init();
if (ret)
return ret;
#endif
return 0;
}
dm_init_and_scan
掃描設備和驅動進行初始化,掃描U_BOOT_DEVICE定義設備,dtb中定義設備,並尋找匹配對應的驅動,並綁定和probe。
int dm_init_and_scan(bool pre_reloc_only)
{
int ret;
ret = dm_init(); //初始化根驅動gd->dm_root
ret = dm_scan_platdata(pre_reloc_only);初始化用U_BOOT_DEVICE定義的設備驅動,並綁定驅動,進行probe
if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);初始化用dtb中定義的設備驅動,並綁定驅動,進行probe
}
ret = dm_scan_other(pre_reloc_only);
return 0;
}
#define U_BOOT_DEVICE(__name) \
ll_entry_declare(struct driver_info, __name, driver_info)
看一下dm_scan_fdt–》dm_scan_fdt_node
dm_scan_fdt_node
int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
bool pre_reloc_only)
{
int ret = 0, err;
//遍歷dtb
for (offset = fdt_first_subnode(blob, offset);
offset > 0;
offset = fdt_next_subnode(blob, offset)) {
if (pre_reloc_only &&
!fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
continue;
if (!fdtdec_get_is_enabled(blob, offset)) {
dm_dbg(" - ignoring disabled device\n");
continue;
}
err = lists_bind_fdt(parent, blob, offset, NULL); //綁定驅動
}
return ret;
}
lists_bind_fdt
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
struct udevice **devp)
{
struct driver *driver = ll_entry_start(struct driver, driver);//驅動都用U_BOOT_DRIVER定義,插入u_boot_list_2_ 段
const int n_ents = ll_entry_count(struct driver, driver);
const struct udevice_id *id;
struct driver *entry;
struct udevice *dev;
bool found = false;
const char *name, *compat_list, *compat;
int compat_length, i;
int result = 0;
int ret = 0;
name = fdt_get_name(blob, offset, NULL);
compat_list = fdt_getprop(blob, offset, "compatible", &compat_length); //compatible用來匹配的關鍵詞
/*
* Walk through the compatible string list, attempting to match each
* compatible string in order such that we match in order of priority
* from the first string to the last.
*/
for (i = 0; i < compat_length; i += strlen(compat) + 1) {
compat = compat_list + i;
for (entry = driver; entry != driver + n_ents; entry++) {
ret = driver_check_compatible(entry->of_match, &id,
compat); //匹配驅動和設備
if (!ret)
break;
}
if (entry == driver + n_ents)
continue;
上面匹配成功,這裏進行bing
ret = device_bind_with_driver_data(parent, entry, name,
id->data, offset, &dev);
found = true;
if (devp)
*devp = dev;
}
break;
}
return result;
}
U_BOOT_DRIVER
/* Declare a new U-Boot driver */
#define U_BOOT_DRIVER(__name) \
ll_entry_declare(struct driver, __name, driver)
#define ll_entry_declare(_type, _name, _list) \
_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_"#_list"_2_"#_name)))
device_bind_common
static int device_bind_common(struct udevice *parent, const struct driver *drv,
const char *name, void *platdata,
ulong driver_data, int of_offset,
uint of_platdata_size, struct udevice **devp)
{
struct udevice *dev;
struct uclass *uc;
int size, ret = 0;
ret = uclass_get(drv->id, &uc); //這裏根據id查找同類uclass驅,如果沒有,就新建一個uclass
dev = calloc(1, sizeof(struct udevice));//分配一個udevice,代表設備
//基本初始化
INIT_LIST_HEAD(&dev->sibling_node);
INIT_LIST_HEAD(&dev->child_head);
INIT_LIST_HEAD(&dev->uclass_node);
#ifdef CONFIG_DEVRES
INIT_LIST_HEAD(&dev->devres_head);
#endif
dev->platdata = platdata;//指向設備platdata
dev->driver_data = driver_data;
dev->name = name;//驅動名字
dev->of_offset = of_offset;
dev->parent = parent;
dev->driver = drv; //指向驅動
dev->uclass = uc;
dev->seq = -1;
dev->req_seq = -1;
if (CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DM_SEQ_ALIAS)) {
/*
* Some devices, such as a SPI bus, I2C bus and serial ports
* are numbered using aliases.
*
* This is just a 'requested' sequence, and will be
* resolved (and ->seq updated) when the device is probed.
*/
if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
if (uc->uc_drv->name && of_offset != -1) {
fdtdec_get_alias_seq(gd->fdt_blob,
uc->uc_drv->name, of_offset,
&dev->req_seq);
}
}
}
if (drv->platdata_auto_alloc_size) {
bool alloc = !platdata;
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
if (of_platdata_size) {
dev->flags |= DM_FLAG_OF_PLATDATA;
if (of_platdata_size <
drv->platdata_auto_alloc_size)
alloc = true;
}
}
if (alloc) {
dev->flags |= DM_FLAG_ALLOC_PDATA;
dev->platdata = calloc(1,
drv->platdata_auto_alloc_size);
if (!dev->platdata) {
ret = -ENOMEM;
goto fail_alloc1;
}
if (CONFIG_IS_ENABLED(OF_PLATDATA) && platdata) {
memcpy(dev->platdata, platdata,
of_platdata_size);
}
}
}
size = uc->uc_drv->per_device_platdata_auto_alloc_size;
if (size) {
dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA;
dev->uclass_platdata = calloc(1, size);
if (!dev->uclass_platdata) {
ret = -ENOMEM;
goto fail_alloc2;
}
}
if (parent) {
size = parent->driver->per_child_platdata_auto_alloc_size;
if (!size) {
size = parent->uclass->uc_drv->
per_child_platdata_auto_alloc_size;
}
if (size) {
dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
dev->parent_platdata = calloc(1, size);
if (!dev->parent_platdata) {
ret = -ENOMEM;
goto fail_alloc3;
}
}
}
/* put dev into parent's successor list */
if (parent)
list_add_tail(&dev->sibling_node, &parent->child_head); //驅動拓撲結構
ret = uclass_bind_device(dev);//uclass驅動的bind
if (ret)
goto fail_uclass_bind;
/* if we fail to bind we remove device from successors and free it */
if (drv->bind) {
ret = drv->bind(dev); //driver 驅動的bind
if (ret)
goto fail_bind;
}
if (parent && parent->driver->child_post_bind) {
ret = parent->driver->child_post_bind(dev);
if (ret)
goto fail_child_post_bind;
}
if (uc->uc_drv->post_bind) {
ret = uc->uc_drv->post_bind(dev);
if (ret)
goto fail_uclass_post_bind;
}
if (parent)
dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
if (devp)
*devp = dev;
dev->flags |= DM_FLAG_BOUND;
return 0;
}