Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
4342ec5f
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
4342ec5f
编写于
8月 21, 2017
作者:
L
Linus Walleij
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'irq/for-gpio' of
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
into devel
上级
5751d3dc
495c38d3
变更
9
显示空白变更内容
内联
并排
Showing
9 changed file
with
533 addition
and
30 deletion
+533
-30
Documentation/driver-model/devres.txt
Documentation/driver-model/devres.txt
+1
-0
include/linux/irq.h
include/linux/irq.h
+2
-0
include/linux/irq_sim.h
include/linux/irq_sim.h
+44
-0
include/linux/irqdomain.h
include/linux/irqdomain.h
+3
-0
kernel/irq/Kconfig
kernel/irq/Kconfig
+9
-0
kernel/irq/Makefile
kernel/irq/Makefile
+1
-0
kernel/irq/chip.c
kernel/irq/chip.c
+109
-0
kernel/irq/irq_sim.c
kernel/irq/irq_sim.c
+164
-0
kernel/irq/irqdomain.c
kernel/irq/irqdomain.c
+200
-30
未找到文件。
Documentation/driver-model/devres.txt
浏览文件 @
4342ec5f
...
...
@@ -312,6 +312,7 @@ IRQ
devm_irq_alloc_descs_from()
devm_irq_alloc_generic_chip()
devm_irq_setup_generic_chip()
devm_irq_sim_init()
LED
devm_led_classdev_register()
...
...
include/linux/irq.h
浏览文件 @
4342ec5f
...
...
@@ -568,6 +568,8 @@ extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
extern
int
irq_chip_pm_get
(
struct
irq_data
*
data
);
extern
int
irq_chip_pm_put
(
struct
irq_data
*
data
);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
extern
void
handle_fasteoi_ack_irq
(
struct
irq_desc
*
desc
);
extern
void
handle_fasteoi_mask_irq
(
struct
irq_desc
*
desc
);
extern
void
irq_chip_enable_parent
(
struct
irq_data
*
data
);
extern
void
irq_chip_disable_parent
(
struct
irq_data
*
data
);
extern
void
irq_chip_ack_parent
(
struct
irq_data
*
data
);
...
...
include/linux/irq_sim.h
0 → 100644
浏览文件 @
4342ec5f
#ifndef _LINUX_IRQ_SIM_H
#define _LINUX_IRQ_SIM_H
/*
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/irq_work.h>
#include <linux/device.h>
/*
* Provides a framework for allocating simulated interrupts which can be
* requested like normal irqs and enqueued from process context.
*/
struct
irq_sim_work_ctx
{
struct
irq_work
work
;
int
irq
;
};
struct
irq_sim_irq_ctx
{
int
irqnum
;
bool
enabled
;
};
struct
irq_sim
{
struct
irq_sim_work_ctx
work_ctx
;
int
irq_base
;
unsigned
int
irq_count
;
struct
irq_sim_irq_ctx
*
irqs
;
};
int
irq_sim_init
(
struct
irq_sim
*
sim
,
unsigned
int
num_irqs
);
int
devm_irq_sim_init
(
struct
device
*
dev
,
struct
irq_sim
*
sim
,
unsigned
int
num_irqs
);
void
irq_sim_fini
(
struct
irq_sim
*
sim
);
void
irq_sim_fire
(
struct
irq_sim
*
sim
,
unsigned
int
offset
);
int
irq_sim_irqnum
(
struct
irq_sim
*
sim
,
unsigned
int
offset
);
#endif
/* _LINUX_IRQ_SIM_H */
include/linux/irqdomain.h
浏览文件 @
4342ec5f
...
...
@@ -460,6 +460,9 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
extern
void
irq_domain_free_irqs_top
(
struct
irq_domain
*
domain
,
unsigned
int
virq
,
unsigned
int
nr_irqs
);
extern
int
irq_domain_push_irq
(
struct
irq_domain
*
domain
,
int
virq
,
void
*
arg
);
extern
int
irq_domain_pop_irq
(
struct
irq_domain
*
domain
,
int
virq
);
extern
int
irq_domain_alloc_irqs_parent
(
struct
irq_domain
*
domain
,
unsigned
int
irq_base
,
unsigned
int
nr_irqs
,
void
*
arg
);
...
...
kernel/irq/Kconfig
浏览文件 @
4342ec5f
...
...
@@ -63,11 +63,20 @@ config GENERIC_IRQ_CHIP
config IRQ_DOMAIN
bool
# Support for simulated interrupts
config IRQ_SIM
bool
select IRQ_WORK
# Support for hierarchical irq domains
config IRQ_DOMAIN_HIERARCHY
bool
select IRQ_DOMAIN
# Support for hierarchical fasteoi+edge and fasteoi+level handlers
config IRQ_FASTEOI_HIERARCHY_HANDLERS
bool
# Generic IRQ IPI support
config GENERIC_IRQ_IPI
bool
...
...
kernel/irq/Makefile
浏览文件 @
4342ec5f
...
...
@@ -4,6 +4,7 @@ obj-$(CONFIG_IRQ_TIMINGS) += timings.o
obj-$(CONFIG_GENERIC_IRQ_CHIP)
+=
generic-chip.o
obj-$(CONFIG_GENERIC_IRQ_PROBE)
+=
autoprobe.o
obj-$(CONFIG_IRQ_DOMAIN)
+=
irqdomain.o
obj-$(CONFIG_IRQ_SIM)
+=
irq_sim.o
obj-$(CONFIG_PROC_FS)
+=
proc.o
obj-$(CONFIG_GENERIC_PENDING_IRQ)
+=
migration.o
obj-$(CONFIG_GENERIC_IRQ_MIGRATION)
+=
cpuhotplug.o
...
...
kernel/irq/chip.c
浏览文件 @
4342ec5f
...
...
@@ -1092,6 +1092,112 @@ void irq_cpu_offline(void)
}
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
/**
* handle_fasteoi_ack_irq - irq handler for edge hierarchy
* stacked on transparent controllers
*
* @desc: the interrupt description structure for this irq
*
* Like handle_fasteoi_irq(), but for use with hierarchy where
* the irq_chip also needs to have its ->irq_ack() function
* called.
*/
void
handle_fasteoi_ack_irq
(
struct
irq_desc
*
desc
)
{
struct
irq_chip
*
chip
=
desc
->
irq_data
.
chip
;
raw_spin_lock
(
&
desc
->
lock
);
if
(
!
irq_may_run
(
desc
))
goto
out
;
desc
->
istate
&=
~
(
IRQS_REPLAY
|
IRQS_WAITING
);
/*
* If its disabled or no action available
* then mask it and get out of here:
*/
if
(
unlikely
(
!
desc
->
action
||
irqd_irq_disabled
(
&
desc
->
irq_data
)))
{
desc
->
istate
|=
IRQS_PENDING
;
mask_irq
(
desc
);
goto
out
;
}
kstat_incr_irqs_this_cpu
(
desc
);
if
(
desc
->
istate
&
IRQS_ONESHOT
)
mask_irq
(
desc
);
/* Start handling the irq */
desc
->
irq_data
.
chip
->
irq_ack
(
&
desc
->
irq_data
);
preflow_handler
(
desc
);
handle_irq_event
(
desc
);
cond_unmask_eoi_irq
(
desc
,
chip
);
raw_spin_unlock
(
&
desc
->
lock
);
return
;
out:
if
(
!
(
chip
->
flags
&
IRQCHIP_EOI_IF_HANDLED
))
chip
->
irq_eoi
(
&
desc
->
irq_data
);
raw_spin_unlock
(
&
desc
->
lock
);
}
EXPORT_SYMBOL_GPL
(
handle_fasteoi_ack_irq
);
/**
* handle_fasteoi_mask_irq - irq handler for level hierarchy
* stacked on transparent controllers
*
* @desc: the interrupt description structure for this irq
*
* Like handle_fasteoi_irq(), but for use with hierarchy where
* the irq_chip also needs to have its ->irq_mask_ack() function
* called.
*/
void
handle_fasteoi_mask_irq
(
struct
irq_desc
*
desc
)
{
struct
irq_chip
*
chip
=
desc
->
irq_data
.
chip
;
raw_spin_lock
(
&
desc
->
lock
);
mask_ack_irq
(
desc
);
if
(
!
irq_may_run
(
desc
))
goto
out
;
desc
->
istate
&=
~
(
IRQS_REPLAY
|
IRQS_WAITING
);
/*
* If its disabled or no action available
* then mask it and get out of here:
*/
if
(
unlikely
(
!
desc
->
action
||
irqd_irq_disabled
(
&
desc
->
irq_data
)))
{
desc
->
istate
|=
IRQS_PENDING
;
mask_irq
(
desc
);
goto
out
;
}
kstat_incr_irqs_this_cpu
(
desc
);
if
(
desc
->
istate
&
IRQS_ONESHOT
)
mask_irq
(
desc
);
preflow_handler
(
desc
);
handle_irq_event
(
desc
);
cond_unmask_eoi_irq
(
desc
,
chip
);
raw_spin_unlock
(
&
desc
->
lock
);
return
;
out:
if
(
!
(
chip
->
flags
&
IRQCHIP_EOI_IF_HANDLED
))
chip
->
irq_eoi
(
&
desc
->
irq_data
);
raw_spin_unlock
(
&
desc
->
lock
);
}
EXPORT_SYMBOL_GPL
(
handle_fasteoi_mask_irq
);
#endif
/* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
/**
* irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
* NULL)
...
...
@@ -1105,6 +1211,7 @@ void irq_chip_enable_parent(struct irq_data *data)
else
data
->
chip
->
irq_unmask
(
data
);
}
EXPORT_SYMBOL_GPL
(
irq_chip_enable_parent
);
/**
* irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if
...
...
@@ -1119,6 +1226,7 @@ void irq_chip_disable_parent(struct irq_data *data)
else
data
->
chip
->
irq_mask
(
data
);
}
EXPORT_SYMBOL_GPL
(
irq_chip_disable_parent
);
/**
* irq_chip_ack_parent - Acknowledge the parent interrupt
...
...
@@ -1181,6 +1289,7 @@ int irq_chip_set_affinity_parent(struct irq_data *data,
return
-
ENOSYS
;
}
EXPORT_SYMBOL_GPL
(
irq_chip_set_affinity_parent
);
/**
* irq_chip_set_type_parent - Set IRQ type on the parent interrupt
...
...
kernel/irq/irq_sim.c
0 → 100644
浏览文件 @
4342ec5f
/*
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/irq_sim.h>
#include <linux/irq.h>
struct
irq_sim_devres
{
struct
irq_sim
*
sim
;
};
static
void
irq_sim_irqmask
(
struct
irq_data
*
data
)
{
struct
irq_sim_irq_ctx
*
irq_ctx
=
irq_data_get_irq_chip_data
(
data
);
irq_ctx
->
enabled
=
false
;
}
static
void
irq_sim_irqunmask
(
struct
irq_data
*
data
)
{
struct
irq_sim_irq_ctx
*
irq_ctx
=
irq_data_get_irq_chip_data
(
data
);
irq_ctx
->
enabled
=
true
;
}
static
struct
irq_chip
irq_sim_irqchip
=
{
.
name
=
"irq_sim"
,
.
irq_mask
=
irq_sim_irqmask
,
.
irq_unmask
=
irq_sim_irqunmask
,
};
static
void
irq_sim_handle_irq
(
struct
irq_work
*
work
)
{
struct
irq_sim_work_ctx
*
work_ctx
;
work_ctx
=
container_of
(
work
,
struct
irq_sim_work_ctx
,
work
);
handle_simple_irq
(
irq_to_desc
(
work_ctx
->
irq
));
}
/**
* irq_sim_init - Initialize the interrupt simulator: allocate a range of
* dummy interrupts.
*
* @sim: The interrupt simulator object to initialize.
* @num_irqs: Number of interrupts to allocate
*
* Returns 0 on success and a negative error number on failure.
*/
int
irq_sim_init
(
struct
irq_sim
*
sim
,
unsigned
int
num_irqs
)
{
int
i
;
sim
->
irqs
=
kmalloc_array
(
num_irqs
,
sizeof
(
*
sim
->
irqs
),
GFP_KERNEL
);
if
(
!
sim
->
irqs
)
return
-
ENOMEM
;
sim
->
irq_base
=
irq_alloc_descs
(
-
1
,
0
,
num_irqs
,
0
);
if
(
sim
->
irq_base
<
0
)
{
kfree
(
sim
->
irqs
);
return
sim
->
irq_base
;
}
for
(
i
=
0
;
i
<
num_irqs
;
i
++
)
{
sim
->
irqs
[
i
].
irqnum
=
sim
->
irq_base
+
i
;
sim
->
irqs
[
i
].
enabled
=
false
;
irq_set_chip
(
sim
->
irq_base
+
i
,
&
irq_sim_irqchip
);
irq_set_chip_data
(
sim
->
irq_base
+
i
,
&
sim
->
irqs
[
i
]);
irq_set_handler
(
sim
->
irq_base
+
i
,
&
handle_simple_irq
);
irq_modify_status
(
sim
->
irq_base
+
i
,
IRQ_NOREQUEST
|
IRQ_NOAUTOEN
,
IRQ_NOPROBE
);
}
init_irq_work
(
&
sim
->
work_ctx
.
work
,
irq_sim_handle_irq
);
sim
->
irq_count
=
num_irqs
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
irq_sim_init
);
/**
* irq_sim_fini - Deinitialize the interrupt simulator: free the interrupt
* descriptors and allocated memory.
*
* @sim: The interrupt simulator to tear down.
*/
void
irq_sim_fini
(
struct
irq_sim
*
sim
)
{
irq_work_sync
(
&
sim
->
work_ctx
.
work
);
irq_free_descs
(
sim
->
irq_base
,
sim
->
irq_count
);
kfree
(
sim
->
irqs
);
}
EXPORT_SYMBOL_GPL
(
irq_sim_fini
);
static
void
devm_irq_sim_release
(
struct
device
*
dev
,
void
*
res
)
{
struct
irq_sim_devres
*
this
=
res
;
irq_sim_fini
(
this
->
sim
);
}
/**
* irq_sim_init - Initialize the interrupt simulator for a managed device.
*
* @dev: Device to initialize the simulator object for.
* @sim: The interrupt simulator object to initialize.
* @num_irqs: Number of interrupts to allocate
*
* Returns 0 on success and a negative error number on failure.
*/
int
devm_irq_sim_init
(
struct
device
*
dev
,
struct
irq_sim
*
sim
,
unsigned
int
num_irqs
)
{
struct
irq_sim_devres
*
dr
;
int
rv
;
dr
=
devres_alloc
(
devm_irq_sim_release
,
sizeof
(
*
dr
),
GFP_KERNEL
);
if
(
!
dr
)
return
-
ENOMEM
;
rv
=
irq_sim_init
(
sim
,
num_irqs
);
if
(
rv
)
{
devres_free
(
dr
);
return
rv
;
}
dr
->
sim
=
sim
;
devres_add
(
dev
,
dr
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
devm_irq_sim_init
);
/**
* irq_sim_fire - Enqueue an interrupt.
*
* @sim: The interrupt simulator object.
* @offset: Offset of the simulated interrupt which should be fired.
*/
void
irq_sim_fire
(
struct
irq_sim
*
sim
,
unsigned
int
offset
)
{
if
(
sim
->
irqs
[
offset
].
enabled
)
{
sim
->
work_ctx
.
irq
=
irq_sim_irqnum
(
sim
,
offset
);
irq_work_queue
(
&
sim
->
work_ctx
.
work
);
}
}
EXPORT_SYMBOL_GPL
(
irq_sim_fire
);
/**
* irq_sim_irqnum - Get the allocated number of a dummy interrupt.
*
* @sim: The interrupt simulator object.
* @offset: Offset of the simulated interrupt for which to retrieve
* the number.
*/
int
irq_sim_irqnum
(
struct
irq_sim
*
sim
,
unsigned
int
offset
)
{
return
sim
->
irqs
[
offset
].
irqnum
;
}
EXPORT_SYMBOL_GPL
(
irq_sim_irqnum
);
kernel/irq/irqdomain.c
浏览文件 @
4342ec5f
...
...
@@ -455,6 +455,31 @@ void irq_set_default_host(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL
(
irq_set_default_host
);
static
void
irq_domain_clear_mapping
(
struct
irq_domain
*
domain
,
irq_hw_number_t
hwirq
)
{
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
0
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_delete
(
&
domain
->
revmap_tree
,
hwirq
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
}
static
void
irq_domain_set_mapping
(
struct
irq_domain
*
domain
,
irq_hw_number_t
hwirq
,
struct
irq_data
*
irq_data
)
{
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
irq_data
->
irq
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_insert
(
&
domain
->
revmap_tree
,
hwirq
,
irq_data
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
}
void
irq_domain_disassociate
(
struct
irq_domain
*
domain
,
unsigned
int
irq
)
{
struct
irq_data
*
irq_data
=
irq_get_irq_data
(
irq
);
...
...
@@ -483,13 +508,7 @@ void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
domain
->
mapcount
--
;
/* Clear reverse map for this hwirq */
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
0
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_delete
(
&
domain
->
revmap_tree
,
hwirq
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
irq_domain_clear_mapping
(
domain
,
hwirq
);
}
int
irq_domain_associate
(
struct
irq_domain
*
domain
,
unsigned
int
virq
,
...
...
@@ -533,13 +552,7 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
}
domain
->
mapcount
++
;
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
virq
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_insert
(
&
domain
->
revmap_tree
,
hwirq
,
irq_data
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
irq_domain_set_mapping
(
domain
,
hwirq
,
irq_data
);
mutex_unlock
(
&
irq_domain_mutex
);
irq_clear_status_flags
(
virq
,
IRQ_NOREQUEST
);
...
...
@@ -1138,16 +1151,9 @@ static void irq_domain_insert_irq(int virq)
for
(
data
=
irq_get_irq_data
(
virq
);
data
;
data
=
data
->
parent_data
)
{
struct
irq_domain
*
domain
=
data
->
domain
;
irq_hw_number_t
hwirq
=
data
->
hwirq
;
domain
->
mapcount
++
;
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
virq
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_insert
(
&
domain
->
revmap_tree
,
hwirq
,
data
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
irq_domain_set_mapping
(
domain
,
data
->
hwirq
,
data
);
/* If not already assigned, give the domain the chip's name */
if
(
!
domain
->
name
&&
data
->
chip
)
...
...
@@ -1171,13 +1177,7 @@ static void irq_domain_remove_irq(int virq)
irq_hw_number_t
hwirq
=
data
->
hwirq
;
domain
->
mapcount
--
;
if
(
hwirq
<
domain
->
revmap_size
)
{
domain
->
linear_revmap
[
hwirq
]
=
0
;
}
else
{
mutex_lock
(
&
revmap_trees_mutex
);
radix_tree_delete
(
&
domain
->
revmap_tree
,
hwirq
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
irq_domain_clear_mapping
(
domain
,
hwirq
);
}
}
...
...
@@ -1362,6 +1362,7 @@ static void irq_domain_free_irqs_hierarchy(struct irq_domain *domain,
unsigned
int
irq_base
,
unsigned
int
nr_irqs
)
{
if
(
domain
->
ops
->
free
)
domain
->
ops
->
free
(
domain
,
irq_base
,
nr_irqs
);
}
...
...
@@ -1448,6 +1449,175 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
return
ret
;
}
/* The irq_data was moved, fix the revmap to refer to the new location */
static
void
irq_domain_fix_revmap
(
struct
irq_data
*
d
)
{
void
**
slot
;
if
(
d
->
hwirq
<
d
->
domain
->
revmap_size
)
return
;
/* Not using radix tree. */
/* Fix up the revmap. */
mutex_lock
(
&
revmap_trees_mutex
);
slot
=
radix_tree_lookup_slot
(
&
d
->
domain
->
revmap_tree
,
d
->
hwirq
);
if
(
slot
)
radix_tree_replace_slot
(
&
d
->
domain
->
revmap_tree
,
slot
,
d
);
mutex_unlock
(
&
revmap_trees_mutex
);
}
/**
* irq_domain_push_irq() - Push a domain in to the top of a hierarchy.
* @domain: Domain to push.
* @virq: Irq to push the domain in to.
* @arg: Passed to the irq_domain_ops alloc() function.
*
* For an already existing irqdomain hierarchy, as might be obtained
* via a call to pci_enable_msix(), add an additional domain to the
* head of the processing chain. Must be called before request_irq()
* has been called.
*/
int
irq_domain_push_irq
(
struct
irq_domain
*
domain
,
int
virq
,
void
*
arg
)
{
struct
irq_data
*
child_irq_data
;
struct
irq_data
*
root_irq_data
=
irq_get_irq_data
(
virq
);
struct
irq_desc
*
desc
;
int
rv
=
0
;
/*
* Check that no action has been set, which indicates the virq
* is in a state where this function doesn't have to deal with
* races between interrupt handling and maintaining the
* hierarchy. This will catch gross misuse. Attempting to
* make the check race free would require holding locks across
* calls to struct irq_domain_ops->alloc(), which could lead
* to deadlock, so we just do a simple check before starting.
*/
desc
=
irq_to_desc
(
virq
);
if
(
!
desc
)
return
-
EINVAL
;
if
(
WARN_ON
(
desc
->
action
))
return
-
EBUSY
;
if
(
domain
==
NULL
)
return
-
EINVAL
;
if
(
WARN_ON
(
!
irq_domain_is_hierarchy
(
domain
)))
return
-
EINVAL
;
if
(
domain
->
parent
!=
root_irq_data
->
domain
)
return
-
EINVAL
;
if
(
!
root_irq_data
)
return
-
EINVAL
;
child_irq_data
=
kzalloc_node
(
sizeof
(
*
child_irq_data
),
GFP_KERNEL
,
irq_data_get_node
(
root_irq_data
));
if
(
!
child_irq_data
)
return
-
ENOMEM
;
mutex_lock
(
&
irq_domain_mutex
);
/* Copy the original irq_data. */
*
child_irq_data
=
*
root_irq_data
;
/*
* Overwrite the root_irq_data, which is embedded in struct
* irq_desc, with values for this domain.
*/
root_irq_data
->
parent_data
=
child_irq_data
;
root_irq_data
->
domain
=
domain
;
root_irq_data
->
mask
=
0
;
root_irq_data
->
hwirq
=
0
;
root_irq_data
->
chip
=
NULL
;
root_irq_data
->
chip_data
=
NULL
;
/* May (probably does) set hwirq, chip, etc. */
rv
=
irq_domain_alloc_irqs_hierarchy
(
domain
,
virq
,
1
,
arg
);
if
(
rv
)
{
/* Restore the original irq_data. */
*
root_irq_data
=
*
child_irq_data
;
goto
error
;
}
irq_domain_fix_revmap
(
child_irq_data
);
irq_domain_set_mapping
(
domain
,
root_irq_data
->
hwirq
,
root_irq_data
);
error:
mutex_unlock
(
&
irq_domain_mutex
);
return
rv
;
}
EXPORT_SYMBOL_GPL
(
irq_domain_push_irq
);
/**
* irq_domain_pop_irq() - Remove a domain from the top of a hierarchy.
* @domain: Domain to remove.
* @virq: Irq to remove the domain from.
*
* Undo the effects of a call to irq_domain_push_irq(). Must be
* called either before request_irq() or after free_irq().
*/
int
irq_domain_pop_irq
(
struct
irq_domain
*
domain
,
int
virq
)
{
struct
irq_data
*
root_irq_data
=
irq_get_irq_data
(
virq
);
struct
irq_data
*
child_irq_data
;
struct
irq_data
*
tmp_irq_data
;
struct
irq_desc
*
desc
;
/*
* Check that no action is set, which indicates the virq is in
* a state where this function doesn't have to deal with races
* between interrupt handling and maintaining the hierarchy.
* This will catch gross misuse. Attempting to make the check
* race free would require holding locks across calls to
* struct irq_domain_ops->free(), which could lead to
* deadlock, so we just do a simple check before starting.
*/
desc
=
irq_to_desc
(
virq
);
if
(
!
desc
)
return
-
EINVAL
;
if
(
WARN_ON
(
desc
->
action
))
return
-
EBUSY
;
if
(
domain
==
NULL
)
return
-
EINVAL
;
if
(
!
root_irq_data
)
return
-
EINVAL
;
tmp_irq_data
=
irq_domain_get_irq_data
(
domain
,
virq
);
/* We can only "pop" if this domain is at the top of the list */
if
(
WARN_ON
(
root_irq_data
!=
tmp_irq_data
))
return
-
EINVAL
;
if
(
WARN_ON
(
root_irq_data
->
domain
!=
domain
))
return
-
EINVAL
;
child_irq_data
=
root_irq_data
->
parent_data
;
if
(
WARN_ON
(
!
child_irq_data
))
return
-
EINVAL
;
mutex_lock
(
&
irq_domain_mutex
);
root_irq_data
->
parent_data
=
NULL
;
irq_domain_clear_mapping
(
domain
,
root_irq_data
->
hwirq
);
irq_domain_free_irqs_hierarchy
(
domain
,
virq
,
1
);
/* Restore the original irq_data. */
*
root_irq_data
=
*
child_irq_data
;
irq_domain_fix_revmap
(
root_irq_data
);
mutex_unlock
(
&
irq_domain_mutex
);
kfree
(
child_irq_data
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
irq_domain_pop_irq
);
/**
* irq_domain_free_irqs - Free IRQ number and associated data structures
* @virq: base IRQ number
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录