arm64架构的linux中断分析(三)

news/2024/9/22 20:35:24/

文章目录

    • 4. 中断的设备树及其处理
      • 4.1 设备树
      • 4.2 内核对设备树的处理
        • 4.2.1 irq_domain_translate
        • 4.2.2 irq_domain_alloc_irqs
        • 4.2.3 irq_create_mapping

4. 中断的设备树及其处理

4.1 设备树

		gpio0: gpio0@fdd60000 {compatible = "rockchip,gpio-bank";reg = <0x0 0xfdd60000 0x0 0x100>;interrupt-parent = <&gic>;interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;gpio-controller;#gpio-cells = <2>;interrupt-controller;#interrupt-cells = <2>;};

这里是瑞芯微3568的gpio0模块,瑞芯微每个gpio有32个引脚,对于Interrupt controller,我们需要定义interrupt-controller和#interrupt-cells的属性,中断相关参数含义:

  1. interrupt-parent:表明该外设的interrupt request line物理的连接到了哪一个中断控制器上。
  2. interrupts:这个属性描述了具体该外设产生的中断的细节信息,包括硬件中断号和触发类型等。
  3. interrupt-controller:表明该device node就是一个中断控制器。
  4. #interrupt-cells:该中断控制器用多少个cell(一个cell就是一个32-bit的单元)描述一个外设的interrupt request line。这个参数决定了使用这个中断控制器的设备的设备树的interrupts怎么写。

4.2 内核对设备树的处理

linux内核的设备树处理函数入口是drivers/of/platform.c文件的of_device_alloc函数,这个函数会在平台设备遍历设备树注册成平台设备的过程中被调用:

struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)
{struct platform_device *dev;int rc, i, num_reg = 0, num_irq;struct resource *res, temp_res;//分配平台设备dev = platform_device_alloc("", PLATFORM_DEVID_NONE);if (!dev)return NULL;/* count the io and irq resources */while (of_address_to_resource(np, num_reg, &temp_res) == 0)num_reg++;num_irq = of_irq_count(np);//统计节点使用irq的次数/* Populate the resource table */if (num_irq || num_reg) {res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);if (!res) {platform_device_put(dev);return NULL;}dev->num_resources = num_reg + num_irq;dev->resource = res;for (i = 0; i < num_reg; i++, res++) {//把设备树地址转换为资源rc = of_address_to_resource(np, i, res);WARN_ON(rc);}//根据设备节点中的中断信息, 构造中断资源if (of_irq_to_resource_table(np, res, num_irq) != num_irq)pr_debug("not all legacy IRQ resources mapped for %pOFn\n",np);}dev->dev.of_node = of_node_get(np);//把node节点保存到deb中dev->dev.fwnode = &np->fwnode;dev->dev.parent = parent ? : &platform_bus;//保存父节点if (bus_id)dev_set_name(&dev->dev, "%s", bus_id);elseof_device_make_bus_id(&dev->dev);return dev;
}

of_device_alloc主要做了以下一些工作:

  1. 调用函数platform_device_alloc分配平台设备
  2. 调用函数of_irq_count统计节点使用irq的次数
  3. 遍历reg资源,调用of_address_to_resource函数把设备树地址转换为资源
  4. 调用函数of_irq_to_resource_table根据设备节点中的中断信息, 构造中断资源
  5. 设置平台设备的父节点、节点和名字等信息

我们主要关注of_irq_to_resource_table函数:

int of_irq_to_resource_table(struct device_node *dev, struct resource *res,int nr_irqs)
{int i;for (i = 0; i < nr_irqs; i++, res++)if (of_irq_to_resource(dev, i, res) <= 0)break;return i;
}

of_irq_to_resource_table函数主要是遍历全部irq,然后调用of_irq_to_resource解析节点中的中断信息,和构造中断资源。我们看of_irq_to_resource这个函数:

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
{int irq = of_irq_get(dev, index);if (irq < 0)return irq;/* Only dereference the resource if both the* resource and the irq are valid. */if (r && irq) {const char *name = NULL;memset(r, 0, sizeof(*r));/** Get optional "interrupt-names" property to add a name* to the resource.*/of_property_read_string_index(dev, "interrupt-names", index,&name);r->start = r->end = irq;r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));r->name = name ? name : of_node_full_name(dev);}return irq;
}

of_irq_to_resource主要是调用of_irq_get函数找到对应节点的中断号,同时找到其父节点,创建映射关系,最后保存为resource。我们看看of_irq_get:

int of_irq_get(struct device_node *dev, int index)
{int rc;struct of_phandle_args oirq;struct irq_domain *domain;//解析设备树中的中断信息, 保存在of_phandle_args结构体中rc = of_irq_parse_one(dev, index, &oirq);if (rc)return rc;domain = irq_find_host(oirq.np);if (!domain)return -EPROBE_DEFER;return irq_create_of_mapping(&oirq);//创建中断映射
}

of_irq_get主要做了两件事:

  1. 调用函数of_irq_parse_one解析设备树中的中断信息, 保存在of_phandle_args结构体中
  2. 调用函数irq_create_of_mapping创建中断映射

我们继续看irq_create_of_mapping:

unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
{struct irq_fwspec fwspec;//根据irq_data的信息填充irq_fwspec结构体of_phandle_args_to_fwspec(irq_data->np, irq_data->args,irq_data->args_count, &fwspec);//创建从fwspec到IRQ号的映射关系return irq_create_fwspec_mapping(&fwspec);
}

irq_create_of_mapping函数首先根据irq_data的信息填充irq_fwspec结构体,然后调用函数irq_create_fwspec_mapping创建从fwspec到IRQ号的映射关系。irq_create_fwspec_mapping函数:

unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
{struct irq_domain *domain;struct irq_data *irq_data;irq_hw_number_t hwirq;unsigned int type = IRQ_TYPE_NONE;int virq;//根据fwspec找到对应的domianif (fwspec->fwnode) {domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);if (!domain)domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);} else {domain = irq_default_domain;}if (!domain) {pr_warn("no irq domain found for %s !\n",of_node_full_name(to_of_node(fwspec->fwnode)));return 0;}//调用irq_domain->ops的translate或xlate,把设备节点里的中断信息解析为hwirq, typeif (irq_domain_translate(domain, fwspec, &hwirq, &type))return 0;/** WARN if the irqchip returns a type with bits* outside the sense mask set and clear these bits.*/if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))type &= IRQ_TYPE_SENSE_MASK;//看看这个hwirq是否已经映射, 如果virq非0,说明找到了就直接返回virq = irq_find_mapping(domain, hwirq);if (virq) {/** If the trigger type is not specified or matches the* current trigger type then we are done so return the* interrupt number.*/if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))return virq;/** If the trigger type has not been set yet, then set* it now and return the interrupt number.*/if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {irq_data = irq_get_irq_data(virq);if (!irq_data)return 0;irqd_set_trigger_type(irq_data, type);return virq;}pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));return 0;}//来到这里说明没有找到,需要创建映射if (irq_domain_is_hierarchy(domain)) {//如果这个是级联中断//根据domian分配虚拟中断号virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);if (virq <= 0)return 0;} else {//否则就是链式中断//创建硬中断号和虚拟中断号的映射关系virq = irq_create_mapping(domain, hwirq);if (!virq)return virq;}//通过虚拟中断号查找irq_data,找不到就返回irq_data = irq_get_irq_data(virq);if (!irq_data) {if (irq_domain_is_hierarchy(domain))irq_domain_free_irqs(virq, 1);elseirq_dispose_mapping(virq);return 0;}//保存中断触发类型irqd_set_trigger_type(irq_data, type);return virq;
}

irq_create_fwspec_mapping函数主要做了一下几件事:

  1. 根据fwspec找到对应的domian
  2. 调用函数irq_domain_translate把设备节点里的中断信息解析出hwirq, type
  3. 调用函数irq_find_mapping查看这个hwirq是否已经映射,如果已经映射到某个虚拟中断号,则返回;否则往下走
  4. 调用函数irq_domain_is_hierarchy判断是否为级联中断,如果是则调用函数irq_domain_alloc_irqs根据domian分配虚拟中断号,
  5. 如果不是级联中断,说明是链式中断,则调用函数irq_create_mapping创建硬中断号和虚拟中断号的映射关系
  6. 调用函数irq_get_irq_data通过软件中断号查找irq_data,找不到就返回
  7. 调用函数irqd_set_trigger_type保存中断触发类型,返回虚拟中断号

下面我们分小结讲解irq_domain_translate、irq_domain_alloc_irqs和irq_create_mapping这几个函数

4.2.1 irq_domain_translate

static int irq_domain_translate(struct irq_domain *d,struct irq_fwspec *fwspec,irq_hw_number_t *hwirq, unsigned int *type)
{
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHYif (d->ops->translate)//如果translate方法集存在,就调用它return d->ops->translate(d, fwspec, hwirq, type);
#endifif (d->ops->xlate)//如果xlate方法集存在,就调用它return d->ops->xlate(d, to_of_node(fwspec->fwnode),fwspec->param, fwspec->param_count,hwirq, type);//如果转换方法都不存在,则假定中断号*hwirq = fwspec->param[0];return 0;
}

irq_domain_translate函数做了一下几件事:

  1. /如果translate方法集存在,就调用translate方法后返回
  2. 如果xlate方法集存在,就调用xlate方法后返回
  3. 如果转换方法都不存在,则假定中断号

translate和xlate都是中断控制器驱动注册的时候写好的,后面会详细说。

4.2.2 irq_domain_alloc_irqs

static inline int irq_domain_alloc_irqs(struct irq_domain *domain,unsigned int nr_irqs, int node, void *arg)
{return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false,NULL);
}int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,unsigned int nr_irqs, int node, void *arg,bool realloc, const struct irq_affinity_desc *affinity)
{int i, ret, virq;if (domain == NULL) {domain = irq_default_domain;if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n"))return -EINVAL;}if (realloc && irq_base >= 0) {virq = irq_base;} else {//分配和初始化irq_des内存virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node,affinity);if (virq < 0) {pr_debug("cannot allocate IRQ(base %d, count %d)\n",irq_base, nr_irqs);return virq;}}//最外层的irq_data被嵌入到结构体irq_desc中if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) {pr_debug("cannot allocate memory for IRQ%d\n", virq);ret = -ENOMEM;goto out_free_desc;}mutex_lock(&irq_domain_mutex);//调用domain->ops->alloc申请虚拟中断号ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg);if (ret < 0) {mutex_unlock(&irq_domain_mutex);goto out_free_irq_data;}for (i = 0; i < nr_irqs; i++) {//在IRQ域中修剪层次结构,并更新IRQ域的继承关系ret = irq_domain_trim_hierarchy(virq + i);if (ret) {mutex_unlock(&irq_domain_mutex);goto out_free_irq_data;}}for (i = 0; i < nr_irqs; i++)//将新的中断描述符加入到IRQ域中,以及将中断描述符与对应的中断控制器进行关联irq_domain_insert_irq(virq + i);mutex_unlock(&irq_domain_mutex);return virq;out_free_irq_data:irq_domain_free_irq_data(virq, nr_irqs);
out_free_desc:irq_free_descs(virq, nr_irqs);return ret;
}

irq_domain_alloc_irqs直接调用函数__irq_domain_alloc_irqs,__irq_domain_alloc_irqs函数做了以下几件事:

  1. 调用函数irq_domain_alloc_descs分配和初始化irq_des内存
  2. 调用函数irq_domain_alloc_irq_data把最外层的irq_data被嵌入到结构体irq_desc中
  3. 调用函数irq_domain_alloc_irqs_hierarchy调用domain->ops->alloc申请虚拟中断号
  4. 遍历每一个虚拟中断号,调用函数irq_domain_trim_hierarchy在IRQ域中修剪层次结构,并更新IRQ域的继承关系
  5. 遍历每一个虚拟中断号,调用函数irq_domain_insert_irq将新的中断描述符加入到IRQ域中,以及将中断描述符与对应的中断控制器进行关联

4.2.3 irq_create_mapping

static inline unsigned int irq_create_mapping(struct irq_domain *host,irq_hw_number_t hwirq)
{//创建映射return irq_create_mapping_affinity(host, hwirq, NULL);
}unsigned int irq_create_mapping_affinity(struct irq_domain *domain,irq_hw_number_t hwirq,const struct irq_affinity_desc *affinity)
{struct device_node *of_node;int virq;pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);/* Look for default domain if nececssary */if (domain == NULL)domain = irq_default_domain;if (domain == NULL) {WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);return 0;}pr_debug("-> using domain @%p\n", domain);of_node = irq_domain_get_of_node(domain);//通过硬中断号找软中断号virq = irq_find_mapping(domain, hwirq);if (virq) {pr_debug("-> existing mapping on virq %d\n", virq);return virq;}//申请软件中断号virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),affinity);if (virq <= 0) {pr_debug("-> virq allocation failed\n");return 0;}//建立软中断号和硬中断号的映射关系if (irq_domain_associate(domain, virq, hwirq)) {irq_free_desc(virq);return 0;}pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",hwirq, of_node_full_name(of_node), virq);return virq;
}

irq_create_mapping函数直接调用irq_create_mapping_affinity,irq_create_mapping_affinity做了以下几件事:

  1. 调用函数irq_find_mapping通过硬中断号找软中断号,找到了就返回
  2. 调用函数irq_domain_alloc_descs申请软件中断号
  3. 调用函数irq_domain_associate建立软中断号和硬中断号的映射关系

irq_domain_associate主要是通过线性映射或radix tree映射,以硬中断号为索引,建立硬中断号与软中断号的映射关系:

int irq_domain_associate(struct irq_domain *domain, unsigned int virq,irq_hw_number_t hwirq)
{struct irq_data *irq_data = irq_get_irq_data(virq);int ret;if (WARN(hwirq >= domain->hwirq_max,"error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))return -EINVAL;if (WARN(!irq_data, "error: virq%i is not allocated", virq))return -EINVAL;if (WARN(irq_data->domain, "error: virq%i is already associated", virq))return -EINVAL;mutex_lock(&irq_domain_mutex);//初始化硬中断号,软中断号在alloc_desc->desc_set_defaults中初始化irq_data->hwirq = hwirq;irq_data->domain = domain;//调用map方法进行映射if (domain->ops->map) {ret = domain->ops->map(domain, virq, hwirq);if (ret != 0) {/** If map() returns -EPERM, this interrupt is protected* by the firmware or some other service and shall not* be mapped. Don't bother telling the user about it.*/if (ret != -EPERM) {pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",domain->name, hwirq, virq, ret);}irq_data->domain = NULL;irq_data->hwirq = 0;mutex_unlock(&irq_domain_mutex);return ret;}/* If not already assigned, give the domain the chip's name */if (!domain->name && irq_data->chip)domain->name = irq_data->chip->name;}domain->mapcount++;//建立了软硬中断号之间的映射关系irq_domain_set_mapping(domain, hwirq, irq_data);mutex_unlock(&irq_domain_mutex);irq_clear_status_flags(virq, IRQ_NOREQUEST);return 0;
}

irq_domain_associate做了以下几件事:

  1. 初始化硬中断号和domian
  2. 调用调用map方法进行映射
  3. 调用函数irq_domain_set_mapping建立了软硬中断号之间的映射关系

http://www.ppmy.cn/news/481983.html

相关文章

word鼠标变箭头

word鼠标变箭头 按两次win键

显示、隐藏鼠标箭头

显示鼠标箭头&#xff1a; QWSServer::setCursorVisible(true); 隐藏鼠标箭头&#xff1a; QWSServer::setCursorVisible(false);

windows10 开机后鼠标失灵看不见光标

试了网上的好多方法&#xff0c;终于解决&#xff0c;感谢有人分享&#xff0c;还是记一下&#xff0c;能帮到需要的人。 按下“windows键r”运行“msconfig”&#xff0c;命令行打开运行也行。 点击左下方的check按钮&#xff0c;隐藏所有的windows服务&#xff0c;再选中所有…

mac鼠标箭头消失解决方法

在触控板使用三指手势&#xff0c;向上滑动即可出现

电脑无鼠标修复计算机怎么办,电脑屏幕只有鼠标箭头怎么解决_电脑开机后只显示鼠标修复方法...

现阶段&#xff0c;大部分人群都开始使用上电脑了&#xff0c;可是对于电脑中出现的一些故障&#xff0c;可能有些用户就不知道该怎么解决了&#xff0c;例如有的用户在启动完电脑之后&#xff0c;只有鼠标显示出来&#xff0c;这让许多用户都感到很疑惑&#xff0c;那么电脑屏…

计算机鼠标不显示桌面,电脑开机后不显示桌面只有鼠标箭头,怎么回事

这是由于Explorer资源管理器没有启动原因&#xff0c;您可以按【Ctrl】【Alt】【Delete】键&#xff0c;打开【任务管理器】窗口&#xff0c;选择【应用程序】这一项&#xff0c;选择【新建任务】&#xff0c;在编辑框中输入【explorer】&#xff0c;点击【确定】即可。以下是详…

为什么把鼠标箭头指向任务栏时箭头就没了,根本就不知道箭头在哪

为什么把鼠标箭头指向任务栏时箭头就没了&#xff0c;根本就不知道箭头在哪 首先去官网找你电脑对应系统的显卡驱动&#xff0c;你安装的是XP系统&#xff1f;很多新显卡都不支持XP&#xff0c;也不发行XP的驱动了。如果是就换成WIN7吧&#xff0c;然后用驱动精灵更新下驱动就可…

Win10系统电脑鼠标箭头不见了怎么办?

Win10系统电脑鼠标箭头不见了怎么办&#xff1f;有用户电脑开机了之后发现鼠标的指针箭头不见了&#xff0c;但是在进行点击操作的时候&#xff0c;鼠标是可以使用的。那么这个情况怎么去进行鼠标箭头的恢复呢&#xff1f;来看看以下 的解决方法吧。 解决方法&#xff1a; 1、首…