Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
cec9694a
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看板
提交
cec9694a
编写于
9月 14, 2014
作者:
J
Jason Cooper
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'irqchip/hip04' into irqchip/core
上级
1fc9d96e
8e4bebe0
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
425 addition
and
0 deletion
+425
-0
drivers/irqchip/Makefile
drivers/irqchip/Makefile
+1
-0
drivers/irqchip/irq-hip04.c
drivers/irqchip/irq-hip04.c
+424
-0
未找到文件。
drivers/irqchip/Makefile
浏览文件 @
cec9694a
...
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
...
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835)
+=
irq-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835)
+=
irq-bcm2835.o
obj-$(CONFIG_ARCH_EXYNOS)
+=
exynos-combiner.o
obj-$(CONFIG_ARCH_EXYNOS)
+=
exynos-combiner.o
obj-$(CONFIG_ARCH_HIP04)
+=
irq-hip04.o
obj-$(CONFIG_ARCH_MMP)
+=
irq-mmp.o
obj-$(CONFIG_ARCH_MMP)
+=
irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU)
+=
irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MVEBU)
+=
irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS)
+=
irq-mxs.o
obj-$(CONFIG_ARCH_MXS)
+=
irq-mxs.o
...
...
drivers/irqchip/irq-hip04.c
0 → 100644
浏览文件 @
cec9694a
/*
* Hisilicon HiP04 INTC
*
* Copyright (C) 2002-2014 ARM Limited.
* Copyright (c) 2013-2014 Hisilicon Ltd.
* Copyright (c) 2013-2014 Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Interrupt architecture for the HIP04 INTC:
*
* o There is one Interrupt Distributor, which receives interrupts
* from system devices and sends them to the Interrupt Controllers.
*
* o There is one CPU Interface per CPU, which sends interrupts sent
* by the Distributor, and interrupts generated locally, to the
* associated CPU. The base address of the CPU interface is usually
* aliased so that the same address points to different chips depending
* on the CPU it is accessed from.
*
* Note that IRQs 0-31 are special - they are local to each CPU.
* As such, the enable set/clear, pending set/clear and active bit
* registers are banked per-cpu for these sources.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/cpumask.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irqchip/arm-gic.h>
#include <asm/irq.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>
#include "irq-gic-common.h"
#include "irqchip.h"
#define HIP04_MAX_IRQS 510
struct
hip04_irq_data
{
void
__iomem
*
dist_base
;
void
__iomem
*
cpu_base
;
struct
irq_domain
*
domain
;
unsigned
int
nr_irqs
;
};
static
DEFINE_RAW_SPINLOCK
(
irq_controller_lock
);
/*
* The GIC mapping of CPU interfaces does not necessarily match
* the logical CPU numbering. Let's use a mapping as returned
* by the GIC itself.
*/
#define NR_HIP04_CPU_IF 16
static
u16
hip04_cpu_map
[
NR_HIP04_CPU_IF
]
__read_mostly
;
static
struct
hip04_irq_data
hip04_data
__read_mostly
;
static
inline
void
__iomem
*
hip04_dist_base
(
struct
irq_data
*
d
)
{
struct
hip04_irq_data
*
hip04_data
=
irq_data_get_irq_chip_data
(
d
);
return
hip04_data
->
dist_base
;
}
static
inline
void
__iomem
*
hip04_cpu_base
(
struct
irq_data
*
d
)
{
struct
hip04_irq_data
*
hip04_data
=
irq_data_get_irq_chip_data
(
d
);
return
hip04_data
->
cpu_base
;
}
static
inline
unsigned
int
hip04_irq
(
struct
irq_data
*
d
)
{
return
d
->
hwirq
;
}
/*
* Routines to acknowledge, disable and enable interrupts
*/
static
void
hip04_mask_irq
(
struct
irq_data
*
d
)
{
u32
mask
=
1
<<
(
hip04_irq
(
d
)
%
32
);
raw_spin_lock
(
&
irq_controller_lock
);
writel_relaxed
(
mask
,
hip04_dist_base
(
d
)
+
GIC_DIST_ENABLE_CLEAR
+
(
hip04_irq
(
d
)
/
32
)
*
4
);
raw_spin_unlock
(
&
irq_controller_lock
);
}
static
void
hip04_unmask_irq
(
struct
irq_data
*
d
)
{
u32
mask
=
1
<<
(
hip04_irq
(
d
)
%
32
);
raw_spin_lock
(
&
irq_controller_lock
);
writel_relaxed
(
mask
,
hip04_dist_base
(
d
)
+
GIC_DIST_ENABLE_SET
+
(
hip04_irq
(
d
)
/
32
)
*
4
);
raw_spin_unlock
(
&
irq_controller_lock
);
}
static
void
hip04_eoi_irq
(
struct
irq_data
*
d
)
{
writel_relaxed
(
hip04_irq
(
d
),
hip04_cpu_base
(
d
)
+
GIC_CPU_EOI
);
}
static
int
hip04_irq_set_type
(
struct
irq_data
*
d
,
unsigned
int
type
)
{
void
__iomem
*
base
=
hip04_dist_base
(
d
);
unsigned
int
irq
=
hip04_irq
(
d
);
/* Interrupt configuration for SGIs can't be changed */
if
(
irq
<
16
)
return
-
EINVAL
;
if
(
type
!=
IRQ_TYPE_LEVEL_HIGH
&&
type
!=
IRQ_TYPE_EDGE_RISING
)
return
-
EINVAL
;
raw_spin_lock
(
&
irq_controller_lock
);
gic_configure_irq
(
irq
,
type
,
base
,
NULL
);
raw_spin_unlock
(
&
irq_controller_lock
);
return
0
;
}
#ifdef CONFIG_SMP
static
int
hip04_irq_set_affinity
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask_val
,
bool
force
)
{
void
__iomem
*
reg
;
unsigned
int
cpu
,
shift
=
(
hip04_irq
(
d
)
%
2
)
*
16
;
u32
val
,
mask
,
bit
;
if
(
!
force
)
cpu
=
cpumask_any_and
(
mask_val
,
cpu_online_mask
);
else
cpu
=
cpumask_first
(
mask_val
);
if
(
cpu
>=
NR_HIP04_CPU_IF
||
cpu
>=
nr_cpu_ids
)
return
-
EINVAL
;
raw_spin_lock
(
&
irq_controller_lock
);
reg
=
hip04_dist_base
(
d
)
+
GIC_DIST_TARGET
+
((
hip04_irq
(
d
)
*
2
)
&
~
3
);
mask
=
0xffff
<<
shift
;
bit
=
hip04_cpu_map
[
cpu
]
<<
shift
;
val
=
readl_relaxed
(
reg
)
&
~
mask
;
writel_relaxed
(
val
|
bit
,
reg
);
raw_spin_unlock
(
&
irq_controller_lock
);
return
IRQ_SET_MASK_OK
;
}
#endif
static
void
__exception_irq_entry
hip04_handle_irq
(
struct
pt_regs
*
regs
)
{
u32
irqstat
,
irqnr
;
void
__iomem
*
cpu_base
=
hip04_data
.
cpu_base
;
do
{
irqstat
=
readl_relaxed
(
cpu_base
+
GIC_CPU_INTACK
);
irqnr
=
irqstat
&
GICC_IAR_INT_ID_MASK
;
if
(
likely
(
irqnr
>
15
&&
irqnr
<=
HIP04_MAX_IRQS
))
{
irqnr
=
irq_find_mapping
(
hip04_data
.
domain
,
irqnr
);
handle_IRQ
(
irqnr
,
regs
);
continue
;
}
if
(
irqnr
<
16
)
{
writel_relaxed
(
irqstat
,
cpu_base
+
GIC_CPU_EOI
);
#ifdef CONFIG_SMP
handle_IPI
(
irqnr
,
regs
);
#endif
continue
;
}
break
;
}
while
(
1
);
}
static
struct
irq_chip
hip04_irq_chip
=
{
.
name
=
"HIP04 INTC"
,
.
irq_mask
=
hip04_mask_irq
,
.
irq_unmask
=
hip04_unmask_irq
,
.
irq_eoi
=
hip04_eoi_irq
,
.
irq_set_type
=
hip04_irq_set_type
,
#ifdef CONFIG_SMP
.
irq_set_affinity
=
hip04_irq_set_affinity
,
#endif
};
static
u16
hip04_get_cpumask
(
struct
hip04_irq_data
*
intc
)
{
void
__iomem
*
base
=
intc
->
dist_base
;
u32
mask
,
i
;
for
(
i
=
mask
=
0
;
i
<
32
;
i
+=
2
)
{
mask
=
readl_relaxed
(
base
+
GIC_DIST_TARGET
+
i
*
2
);
mask
|=
mask
>>
16
;
if
(
mask
)
break
;
}
if
(
!
mask
)
pr_crit
(
"GIC CPU mask not found - kernel will fail to boot.
\n
"
);
return
mask
;
}
static
void
__init
hip04_irq_dist_init
(
struct
hip04_irq_data
*
intc
)
{
unsigned
int
i
;
u32
cpumask
;
unsigned
int
nr_irqs
=
intc
->
nr_irqs
;
void
__iomem
*
base
=
intc
->
dist_base
;
writel_relaxed
(
0
,
base
+
GIC_DIST_CTRL
);
/*
* Set all global interrupts to this CPU only.
*/
cpumask
=
hip04_get_cpumask
(
intc
);
cpumask
|=
cpumask
<<
16
;
for
(
i
=
32
;
i
<
nr_irqs
;
i
+=
2
)
writel_relaxed
(
cpumask
,
base
+
GIC_DIST_TARGET
+
((
i
*
2
)
&
~
3
));
gic_dist_config
(
base
,
nr_irqs
,
NULL
);
writel_relaxed
(
1
,
base
+
GIC_DIST_CTRL
);
}
static
void
hip04_irq_cpu_init
(
struct
hip04_irq_data
*
intc
)
{
void
__iomem
*
dist_base
=
intc
->
dist_base
;
void
__iomem
*
base
=
intc
->
cpu_base
;
unsigned
int
cpu_mask
,
cpu
=
smp_processor_id
();
int
i
;
/*
* Get what the GIC says our CPU mask is.
*/
BUG_ON
(
cpu
>=
NR_HIP04_CPU_IF
);
cpu_mask
=
hip04_get_cpumask
(
intc
);
hip04_cpu_map
[
cpu
]
=
cpu_mask
;
/*
* Clear our mask from the other map entries in case they're
* still undefined.
*/
for
(
i
=
0
;
i
<
NR_HIP04_CPU_IF
;
i
++
)
if
(
i
!=
cpu
)
hip04_cpu_map
[
i
]
&=
~
cpu_mask
;
gic_cpu_config
(
dist_base
,
NULL
);
writel_relaxed
(
0xf0
,
base
+
GIC_CPU_PRIMASK
);
writel_relaxed
(
1
,
base
+
GIC_CPU_CTRL
);
}
#ifdef CONFIG_SMP
static
void
hip04_raise_softirq
(
const
struct
cpumask
*
mask
,
unsigned
int
irq
)
{
int
cpu
;
unsigned
long
flags
,
map
=
0
;
raw_spin_lock_irqsave
(
&
irq_controller_lock
,
flags
);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu
(
cpu
,
mask
)
map
|=
hip04_cpu_map
[
cpu
];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb
(
ishst
);
/* this always happens on GIC0 */
writel_relaxed
(
map
<<
8
|
irq
,
hip04_data
.
dist_base
+
GIC_DIST_SOFTINT
);
raw_spin_unlock_irqrestore
(
&
irq_controller_lock
,
flags
);
}
#endif
static
int
hip04_irq_domain_map
(
struct
irq_domain
*
d
,
unsigned
int
irq
,
irq_hw_number_t
hw
)
{
if
(
hw
<
32
)
{
irq_set_percpu_devid
(
irq
);
irq_set_chip_and_handler
(
irq
,
&
hip04_irq_chip
,
handle_percpu_devid_irq
);
set_irq_flags
(
irq
,
IRQF_VALID
|
IRQF_NOAUTOEN
);
}
else
{
irq_set_chip_and_handler
(
irq
,
&
hip04_irq_chip
,
handle_fasteoi_irq
);
set_irq_flags
(
irq
,
IRQF_VALID
|
IRQF_PROBE
);
}
irq_set_chip_data
(
irq
,
d
->
host_data
);
return
0
;
}
static
int
hip04_irq_domain_xlate
(
struct
irq_domain
*
d
,
struct
device_node
*
controller
,
const
u32
*
intspec
,
unsigned
int
intsize
,
unsigned
long
*
out_hwirq
,
unsigned
int
*
out_type
)
{
unsigned
long
ret
=
0
;
if
(
d
->
of_node
!=
controller
)
return
-
EINVAL
;
if
(
intsize
<
3
)
return
-
EINVAL
;
/* Get the interrupt number and add 16 to skip over SGIs */
*
out_hwirq
=
intspec
[
1
]
+
16
;
/* For SPIs, we need to add 16 more to get the irq ID number */
if
(
!
intspec
[
0
])
*
out_hwirq
+=
16
;
*
out_type
=
intspec
[
2
]
&
IRQ_TYPE_SENSE_MASK
;
return
ret
;
}
#ifdef CONFIG_SMP
static
int
hip04_irq_secondary_init
(
struct
notifier_block
*
nfb
,
unsigned
long
action
,
void
*
hcpu
)
{
if
(
action
==
CPU_STARTING
||
action
==
CPU_STARTING_FROZEN
)
hip04_irq_cpu_init
(
&
hip04_data
);
return
NOTIFY_OK
;
}
/*
* Notifier for enabling the INTC CPU interface. Set an arbitrarily high
* priority because the GIC needs to be up before the ARM generic timers.
*/
static
struct
notifier_block
hip04_irq_cpu_notifier
=
{
.
notifier_call
=
hip04_irq_secondary_init
,
.
priority
=
100
,
};
#endif
static
const
struct
irq_domain_ops
hip04_irq_domain_ops
=
{
.
map
=
hip04_irq_domain_map
,
.
xlate
=
hip04_irq_domain_xlate
,
};
static
int
__init
hip04_of_init
(
struct
device_node
*
node
,
struct
device_node
*
parent
)
{
irq_hw_number_t
hwirq_base
=
16
;
int
nr_irqs
,
irq_base
,
i
;
if
(
WARN_ON
(
!
node
))
return
-
ENODEV
;
hip04_data
.
dist_base
=
of_iomap
(
node
,
0
);
WARN
(
!
hip04_data
.
dist_base
,
"fail to map hip04 intc dist registers
\n
"
);
hip04_data
.
cpu_base
=
of_iomap
(
node
,
1
);
WARN
(
!
hip04_data
.
cpu_base
,
"unable to map hip04 intc cpu registers
\n
"
);
/*
* Initialize the CPU interface map to all CPUs.
* It will be refined as each CPU probes its ID.
*/
for
(
i
=
0
;
i
<
NR_HIP04_CPU_IF
;
i
++
)
hip04_cpu_map
[
i
]
=
0xff
;
/*
* Find out how many interrupts are supported.
* The HIP04 INTC only supports up to 510 interrupt sources.
*/
nr_irqs
=
readl_relaxed
(
hip04_data
.
dist_base
+
GIC_DIST_CTR
)
&
0x1f
;
nr_irqs
=
(
nr_irqs
+
1
)
*
32
;
if
(
nr_irqs
>
HIP04_MAX_IRQS
)
nr_irqs
=
HIP04_MAX_IRQS
;
hip04_data
.
nr_irqs
=
nr_irqs
;
nr_irqs
-=
hwirq_base
;
/* calculate # of irqs to allocate */
irq_base
=
irq_alloc_descs
(
-
1
,
hwirq_base
,
nr_irqs
,
numa_node_id
());
if
(
IS_ERR_VALUE
(
irq_base
))
{
pr_err
(
"failed to allocate IRQ numbers
\n
"
);
return
-
EINVAL
;
}
hip04_data
.
domain
=
irq_domain_add_legacy
(
node
,
nr_irqs
,
irq_base
,
hwirq_base
,
&
hip04_irq_domain_ops
,
&
hip04_data
);
if
(
WARN_ON
(
!
hip04_data
.
domain
))
return
-
EINVAL
;
#ifdef CONFIG_SMP
set_smp_cross_call
(
hip04_raise_softirq
);
register_cpu_notifier
(
&
hip04_irq_cpu_notifier
);
#endif
set_handle_irq
(
hip04_handle_irq
);
hip04_irq_dist_init
(
&
hip04_data
);
hip04_irq_cpu_init
(
&
hip04_data
);
return
0
;
}
IRQCHIP_DECLARE
(
hip04_intc
,
"hisilicon,hip04-intc"
,
hip04_of_init
);
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录