在linux中有如下定义
/*
* IO resources have these defined flags.
*/
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
#define IORESOURCE_READONLY 0x00004000
#define IORESOURCE_CACHEABLE 0x00008000
#define IORESOURCE_RANGELENGTH 0x00010000
#define IORESOURCE_SHADOWABLE 0x00020000
#define IORESOURCE_SIZEALIGN 0x00040000 /* size indicates alignment */
#define IORESOURCE_STARTALIGN 0x00080000 /* start field is alignment */
#define IORESOURCE_MEM_64 0x00100000
#define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */
#define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */
#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
#define IORESOURCE_DISABLED 0x10000000
#define IORESOURCE_UNSET 0x20000000 /* No address assigned yet */
#define IORESOURCE_AUTO 0x40000000
#define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */
/* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
#define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */
其中最常用的就是这几种
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
今天我们主要来分析前三种资源,即IO、MEM、REG
几乎每一种外设都是通过读写设备上的寄存器来进行的,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:
(1)I/O映射方式(I/O-mapped)
典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为'I/O地址空间'或者'I/O端口空间',CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元,IO地址空间,这个空间从kernel编程上来看,只能通过专门的接口函数才能访问.硬件层面上,cpu需要用特殊指令才能访问或需要用特殊访问方式才能访问,不能直接用指针来寻址,.在嵌入式中,基本上没有io address space。
(2)内存映射方式(Memory-mapped)
RISC指令系统的CPU(如MIPS ARM PowerPC等)通常只实现一个物理地址空间,像这种情况,外设的I/O端口的物理地址就被映射到内存地址空间中,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。
若果搜索内核代码,会发现,只有极少量的寄存器是通过 IORESOURCE_REG来表示的,绝大多数寄存器都是通过IORESOURCE_IO 或IORESOURCE_MEM来表示的。
我们的ARM平台对寄存的访问和内存一样,所以通常我们ARM中定义寄存器资源都是采用IORESOURCE_MEM来表示。
在内核中,形容资源,使用这样一个结构体来表示的,当然这个结构体也可以做成一个树的节点被链入一棵树中。
/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start; /* 这段资源的起始 */
resource_size_t end; /* 这段资源的结束 */
const char *name; /* 这个资源的名字,方便用户查看 */
unsigned long flags; /* 标记属于那种资源 */
struct resource *parent, *sibling, *child; /* 作为树的节点,链入树中 */
};
比如我们要使用一段寄存器作为资源传参给具体的驱动:
static struct resource xxxx = {
/* addr */
.start = 0x04014000,
.end = 0x04014003,
.flags = IORESOURCE_MEM,
}
一般来说,在系统运行时,外设的I/O资源的物理地址是已知的,有硬件决定,查看手册即可知道。但是CPU通常并没有为这些已知的外设I/O的物理地址分配虚拟地址,所以驱动程序并不能直接通过物理地址来访问I/O的地址资源,而必须将它们映射到核心虚拟地址空间(通过页表),然后才能根据映射所得到的核心虚拟地址范围,通过访问指令来访问这些I/O内存资源。linux中在io.h头文件中申明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心的虚拟地址空间。
一般我们,使用I/O内存首先要申请,然后才能映射,使用I/O端口首先要申请,或者叫请求,对于I/O端口的请求意思是让内核知道你要访问这个端口,这样内核知道了以后它就不会再让别人也访问这个端口了,不然两个用户同时访问一个硬件可能会有问题的。
这边先以平台总线中添加一段MEM资源为例,分析一下,资源的如何加入资源树。
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers by initializing
* its fields.
* It is the first half of device_register(), if called by
* that function, though it can also be called separately, so one
* may use @dev's fields. In particular, get_device()/put_device()
* may be used for reference counting of @dev after calling this
* function.
*
* All fields in @dev must be initialized by the caller to 0, except
* for those explicitly set to some other value. The simplest
* approach is to use kzalloc() to allocate the structure containing
* @dev.
*
* NOTE: Use put_device() to give up your reference instead of freeing
* @dev directly once you have called this function.
*/
void device_initialize(struct device *dev)
{
/* 初始化dev信息 */
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
/**
* arch_setup_pdev_archdata - Allow manipulation of archdata before its used
* @pdev: platform device
*
* This is called before platform_device_add() such that any pdev_archdata may
* be setup before the platform_notifier is called. So if a user needs to
* manipulate any relevant information in the pdev_archdata they can do:
*
* platform_device_alloc()
* ... manipulate ...
* platform_device_add()
*
* And if they don't care they can just call platform_device_register() and
* everything will just work out.
*/
void __weak arch_setup_pdev_archdata(struct platform_device *pdev)
{
/* 预留给特殊架构的 */
}
/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 of platform_device_register(), though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
int i, ret;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus; /* 增加总线的父设备为平台设备 */
pdev->dev.bus = &platform_bus_type; /* 设备挂接在平台总线 */
switch (pdev->id) {
default: /* 自己设置设备标号 */
dev_set_name(&pdev->dev, '%s.%d', pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE: /* -1表示不需要设备标号 */
dev_set_name(&pdev->dev, '%s', pdev->name);
break;
case PLATFORM_DEVID_AUTO: /* 由总线分配设备标号 */
/*
* Automatically allocated device ID. We mark it as such so
* that we remember it must be freed, and we append a suffix
* to avoid namespace collision with explicit IDs.
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, '%s.%d.auto', pdev->name, pdev->id);
break;
}
/* 对该设备的资源插入资源树,如果已经有设备插入则会插入失败 */
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev); /* 如果资源没设置名字,则和设备名一样 */
p = r->parent;
if (!p) { /* 如果没设置资源的父节点,则检查是否是IORESOURCE_MEM或IORESOURCE_IO资源,如果说是,则把他们插入到这个资源所在的资源树中 */
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource; /* iomem资源树,信息 */
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource; /* ioport资源树,信息 */
}
/* 这里p必须存在,即如果自己没设置,默认也只添加 IO和MEM资源到资源树中 */
if (p && insert_resource(p, r)) {
dev_err(&pdev->dev, 'failed to claim resource %dn', i);
ret = -EBUSY;
goto failed;
}
}
pr_debug('Registering platform device '%s'. Parent at %sn',
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
/* 增加设备,节点,sysfs信息,做匹配等 */
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
if (r->parent)
release_resource(r);
}
err_out:
return ret;
}
这里我们先分析一下资源树的总信息,后面分析如何把资源加入资源树。(kernel/resource.c)
struct resource ioport_resource = {
.name = 'PCI IO',
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
struct resource iomem_resource = {
.name = 'PCI mem',
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
如果我们是32位的处理器们可以看到iomem资源,即PCI mem资源的大小为0~0xffff,ffff.
而ioport则是采用一个宏来表示的,搜索后发现
/*
* This is the limit of PC card/PCI/ISA IO space, which is by default
* 64K if we have PC card, PCI or ISA support. Otherwise, default to
* zero to prevent ISA/PCI drivers claiming IO space (and potentially
* oopsing.)
*
* Only set this larger if you really need inb() et.al. to operate over
* a larger address space. Note that SOC_COMMON ioremaps each sockets
* IO space area, and so inb() et.al. must be defined to operate as per
* readb() et.al. on such platforms.
*/
#ifndef IO_SPACE_LIMIT
#if defined(CONFIG_PCMCIA_SOC_COMMON) || defined(CONFIG_PCMCIA_SOC_COMMON_MODULE)
#define IO_SPACE_LIMIT ((resource_size_t)0xffffffff)
#elif defined(CONFIG_PCI) || defined(CONFIG_ISA) || defined(CONFIG_PCCARD)
#define IO_SPACE_LIMIT ((resource_size_t)0xffff)
#else
#define IO_SPACE_LIMIT ((resource_size_t)0)
#endif
#endif
如果定义了PCI设备其值才不为0,否则值为0。
即如果没PCI相关的宏,是不能使用IORESOURCE_IO定义资源的,否则肯定在注册资源树的时候就失败了。
下面我们看一下平台总线中,如果把一段MEM插入到资源树中。(先说一下,系统采用多叉树的策略插入资源的)
/**
* insert_resource - Inserts a resource in the resource tree
* @parent: parent of the new resource
* @new: new resource to insert
*
* Returns 0 on success, -EBUSY if the resource can't be inserted.
*/
int insert_resource(struct resource *parent, struct resource *new)
{
struct resource *conflict;
conflict = insert_resource_conflict(parent, new);
return conflict ? -EBUSY : 0;
}
/**
* insert_resource_conflict - Inserts resource in the resource tree
* @parent: parent of the new resource
* @new: new resource to insert
*
* Returns 0 on success, conflict resource if the resource can't be inserted.
*
* This function is equivalent to request_resource_conflict when no conflict
* happens. If a conflict happens, and the conflicting resources
* entirely fit within the range of the new resource, then the new
* resource is inserted and the conflicting resources become children of
* the new resource.
*/
struct resource *insert_resource_conflict(struct resource *parent, struct resource *new)
{
struct resource *conflict;
write_lock(&resource_lock);
conflict = __insert_resource(parent, new);
write_unlock(&resource_lock);
return conflict;
}
/*
* Insert a resource into the resource tree. If successful, return NULL,
* otherwise return the conflicting resource (compare to __request_resource())
*/
static struct resource * __insert_resource(struct resource *parent, struct resource *new)
{
struct resource *first, *next;
for (;; parent = first) {
first = __request_resource(parent, new);
if (!first)
return first;
if (first == parent)
return first;
if (WARN_ON(first == new)) /* duplicated insertion */
return first;
if ((first->start > new->start) || (first->end < new->end))
break;
if ((first->start == new->start) && (first->end == new->end))
break;
}
for (next = first; ; next = next->sibling) {
/* Partial overlap? Bad, and unfixable */
if (next->start < new->start || next->end > new->end)
return next;
if (!next->sibling)
break;
if (next->sibling->start > new->end)
break;
}
new->parent = parent;
new->sibling = next->sibling;
new->child = first;
next->sibling = NULL;
for (next = first; next; next = next->sibling)
next->parent = new;
if (parent->child == first) {
parent->child = new;
} else {
next = parent->child;
while (next->sibling != first)
next = next->sibling;
next->sibling = new;
}
return NULL;
}
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
resource_size_t start = new->start;
resource_size_t end = new->end;
struct resource *tmp, **p;
if (end < start) /* 输入的参数范围有误 */
return root;
if (start < root->start) /* 输入的参数不再资源总范围内 */
return root;
if (end > root->end) /* 输入的参数不再资源总范围内 */
return root;
p = &root->child;
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
首先对re__insert_resource函数里面举例进行分析:
上一篇:代码示例_中断
下一篇:uboot常用的函数
推荐阅读最新更新时间:2026-03-25 12:03
- 用于 7VIN 至 16VIN、1.5V 和 1.2V 输出的 LTM4628EV DC/DC 模块稳压器的典型应用电路
- 使用 Analog Devices 的 LTC3728LIGN 的参考设计
- DER-406 - 适用于 A19 灯的 5.76 W 高 PF 非隔离降压-升压型 TRIAC 调光 LED 驱动器
- ADR5045B 5V 输出精密微功率并联模式电压基准的典型应用
- LT3970EDDB-3.42 2.5V 降压转换器的典型应用
- MC78M08BDTG 8V 电流调节器的典型应用
- LT1021DCN8-5 精密电压基准的典型应用
- DER-282 - 100W, 扁平(11 mm), LLC DC-DC转换器
- REF193 低压差开尔文连接电压基准的典型应用电路
- LT3088EM 线性稳压器用于添加软启动的典型应用



Linux技术手册
智能机械臂
现代雷达系统的信号设计
BFR340T






京公网安备 11010802033920号