1. MIPS中断
1.1. 中断ebase寄存器
2. octeon CIU
中央中断控制器
下图中, A是SUM0, B是SUM1;
SUM0共有9个地址, 每个core2个. 4(core)*2+1(pcie RC); 也有9个相应的en地址
SUM1只有1个地址, 所有core公用. 但有9个对应的en地址, 目的是每个core都能单独使能中断.
还有个SUM4(0..3), 每个core一个, 对应IP4.
3. kernel代码
octeon-irq.c
static __read_mostly u8 octeon_irq_ciu_to_irq[8][64];
kernel/irq.c
- CIU: 8个mips中断
octeon_irq_init_core(); octeon_irq_ip2 = octeon_irq_ip2_ciu octeon_irq_ip3 = octeon_irq_ip3_ciu; octeon_irq_ip4 = octeon_irq_ip4_ciu ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, dd); irq_set_default_host(ciu_domain);
- GPIO:
gpiod->base_hwirq = base_hwirq irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod)
- CIB:
cib_domain = irq_domain_add_linear(of_node, host_data->max_bits,&octeon_irq_domain_cib_ops,host_data); r = request_irq(parent_irq, octeon_irq_cib_handler, 0,"cib", cib_domain); request_threaded_irq(irq, handler, NULL, flags, name, dev)
cib中断上下文的处理函数
octeon_irq_cib_handler()
找出中断bit
//应答相应位
cvmx_write_csr(host_data->raw_reg, 1ull << i)
//调用通用处理函数
generic_handle_irq_desc(irq, desc)
3.1. generic_handle_irq_desc(irq, desc)
基本上, 每个中断线都有一个handle, 在初始化时确定. 一般handle有以下几种:
- handle_level_irq(unsigned int irq, struct irq_desc *desc)
- handle_edge_irq(unsigned int irq, struct irq_desc *desc)
- handle_nested_irq(unsigned int irq, struct irq_desc *desc)
- handle_simple_irq(unsigned int irq, struct irq_desc *desc)
比如
handle_simple_irq(unsigned int irq, struct irq_desc *desc)
handle_irq_event(desc);
handle_irq_event_percpu(desc, action)
//先调用request_irq()注册的handler, 注意action是个链表
//对每个链表action
res = action->handler(irq, action->dev_id);
根据返回值,
IRQ_WAKE_THREAD:则调用action->thread_fn()
irq_wake_thread(desc, action);
IRQ_HANDLED:不调用thread_fn()
3.2. 中断初始化
init_IRQ()
for (i = 0; i < NR_IRQS; i++) //#define NR_IRQS OCTEON_IRQ_LAST
irq_set_noprobe(i)
arch_init_irq(ciu_types)
of_irq_init(ciu_types);
while 所有的compatible
//调用每个compatible的init
irq_init_cb = (of_irq_init_cb_t)match->data
ret = irq_init_cb(desc->dev, desc->interrupt_parent)
octeon_irq_init_ciu(struct device_node *ciu_node, struct device_node *parent)
3.3. irq号
有硬件irq号和linux irq号之分:
- 硬件irq号 hw: irq_hw_number_t hw
- 根据硬件irq号算出line和bit
unsigned int line = hw >> 6; unsigned int bit = hw & 63;
- line和bit用于算出linux irq号
例如对lmc中断来说, line是1, bit是52, 就能查表得出linux irq号linux_irq = octeon_irq_ciu_to_irq[line][bit]
- 每个domain都有一个map函数, 用于hw irq和虚拟irq之间转换
3.4. 中断注册方法
知道linux irq号, 用request_irq()
或request_threaded_irq()
就能注册中断了.
参考octeon_setup_debug_uart()
但linux irq号怎么得到?
3.4.1. 从hw irq号得到
用下面这个函数来map一个hw irq, 返回一个linux irq. 必须先有一个虚拟irq才能开始用中断
unsigned int irq_create_mapping(struct irq_domain *domain, irq_hw_number_t hwirq)
这里面的关键是理解到底那个hwirq是你要注册的中断, 这要看这个domain的map函数
得到
3.4.2. 从platform_device得到
irq = platform_get_irq(pdev, 0);
result = devm_request_irq(&pdev->dev, i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c);
3.4.3. 从of_node得到
parent_irq = irq_of_parse_and_map(ciu_node, 0);
of_irq_map_one(dev, index, &oirq)
irq_create_of_mapping(oirq.controller, oirq.specifier,oirq.size);
r = request_irq(parent_irq, octeon_irq_cib_handler,IRQF_NO_THREAD, "cib", cib_domain);
3.5. 中断触发方式的判断
if (irqd_get_trigger_type(irq_data) & IRQ_TYPE_EDGE_BOTH)
//设置中断类型
irq_set_irq_type(virq, type);
3.6. 中断处理过程
asmlinkage void plat_irq_dispatch(void)
//重点是这个循环
while (1):
读出cop0_cause和cop0_status
//处理ip2
octeon_irq_ip2
//处理ip3
octeon_irq_ip3
读SUM1寄存器
//找到最后一位1
int bit = fls64(ciu_sum) - 1;
//转换为linux irq
int irq = octeon_irq_ciu_to_irq[1][bit]
do_IRQ(irq)
generic_handle_irq(irq)
generic_handle_irq_desc(irq, desc)
desc->handle_irq(irq, desc)
//处理ip4
octeon_irq_ip4
do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE)
上面这个函数会被锁到cache里, 在
cavium-octeon/setup.c
里面cvmx_l2c_lock_mem_region(__pa_symbol(handle_int), len); cvmx_l2c_lock_mem_region(__pa_symbol(plat_irq_dispatch), len2);
在
arch/mips/kernel/genex.S
中会调用这个函数
更详细代码见arch/mips/kernel/entry.S
和arch/mips/kernel/process.c
汇编里面的handle_int()
函数://这步很牛, 直接锁定返回地址是ret_from_irq PTR_LA ra, ret_from_irq //这是个c函数, 所以最后返回指令肯定是 jr ra. 所以就跳到ret_from_irq()里面去了 PTR_LA v0, plat_irq_dispatch //直接跳过去. 这是个任意地址跳转指令 jr v0
3.6.1. 哪里调用了handle_int()? --直接写进中断向量表0号位置
extern asmlinkage void handle_int(void)
@init/main.c
asmlinkage void __init start_kernel(void)
@arch/mips/kernel/traps.c
trap_init()
ebase=0xffffffff80000000
if (cpu_has_mips_r2)
ebase += (read_c0_ebase() & 0x3ffff000)
set_handler(0x180, &except_vec3_generic, 0x80)
for (i = 0; i <= 31; i++)
set_except_vector(i, handle_reserved)
set_except_vector(23, handle_watch)
set_handler(0x200, &except_vec4, 0x8)
set_except_vector(0, using_rollback_handler() ? rollback_handle_int: handle_int);
set_except_vector(1, handle_tlbm);
set_except_vector(2, handle_tlbl);
set_except_vector(3, handle_tlbs);
set_except_vector(4, handle_adel);
set_except_vector(5, handle_ades);
set_except_vector(6, handle_ibe);
set_except_vector(7, handle_dbe);
set_except_vector(8, handle_sys);
set_except_vector(9, handle_bp);
set_except_vector(10, rdhwr_noopt ? handle_ri :(cpu_has_vtag_icache ?handle_ri_rdhwr_vivt : handle_ri_rdhwr));
set_except_vector(11, handle_cpu)
set_except_vector(12, handle_ov);
set_except_vector(13, handle_tr);
board_nmi_handler_setup()
set_except_vector(15, handle_fpe)
set_except_vector(22, handle_mdmx)
set_except_vector(24, handle_mcheck)
set_except_vector(25, handle_mt)
set_except_vector(26, handle_dsp)
board_cache_error_setup()
set_handler(0x080, &except_vec3_generic, 0x80)
以上这个表和mips异常表能对起来
上述的handle_int
, handle_sys
, handle_adel
, handle_ades
, handle_ibe
, handle_dbe
... 由下面的宏生成:
[arch/mips/kernel/genex.S]
BUILD_HANDLER adel ade ade silent /* #4 */
BUILD_HANDLER ades ade ade silent /* #5 */
BUILD_HANDLER ibe be cli silent /* #6 */
BUILD_HANDLER dbe be cli silent /* #7 */
BUILD_HANDLER bp bp sti silent /* #9 */
BUILD_HANDLER ri ri sti silent /* #10 */
BUILD_HANDLER cpu cpu sti silent /* #11 */
BUILD_HANDLER ov ov sti silent /* #12 */
BUILD_HANDLER tr tr sti silent /* #13 */
BUILD_HANDLER fpe fpe fpe silent /* #15 */
BUILD_HANDLER mdmx mdmx sti silent /* #22 */
#ifdef CONFIG_HARDWARE_WATCHPOINTS
/*
* For watch, interrupts will be enabled after the watch
* registers are read.
*/
BUILD_HANDLER watch watch cli silent /* #23 */
#else
BUILD_HANDLER watch watch sti verbose /* #23 */
#endif
BUILD_HANDLER mcheck mcheck cli verbose /* #24 */
BUILD_HANDLER mt mt sti silent /* #25 */
BUILD_HANDLER dsp dsp sti silent /* #26 */
BUILD_HANDLER reserved reserved sti verbose /* others */
BUILD_HANDLER 定义为:
.macro BUILD_HANDLER exception handler clear verbose
__BUILD_HANDLER \exception \handler \clear \verbose _int
.endm
.macro __BUILD_HANDLER exception handler clear verbose ext
.align 5
NESTED(handle_\exception, PT_SIZE, sp)
.set noat
SAVE_ALL
FEXPORT(handle_\exception\ext)
__BUILD_clear_\clear
.set at
__BUILD_\verbose \exception
move a0, sp
PTR_LA ra, ret_from_exception
j do_\handler //这里就是调用traps.c里面的do_*函数, 如do_bp, do_tr, do_ri
END(handle_\exception)
.endm
4. gpio中断
4.1. 问题现象
两块板子 发包到cpu 半小时内 GPIO2 不停中断(低电平)
CN71xx core0
SE-S from uboot
PHY(拔出,插入) --(直连, 外上拉电阻)--> GPIO2中断(低电平触发) --> WQE
4.2. 参考代码
GPIO中断配置~/repo/hg/OCTEON-SDK-3.1.0/linux/kernel/linux/arch/mips/cavium-octeon/octeon-irq.c
static void octeon_irq_gpio_setup(struct irq_data *data)
{
union cvmx_gpio_bit_cfgx cfg;
struct octeon_ciu_chip_data *cd;
u32 t = irqd_get_trigger_type(data);
cd = irq_data_get_irq_chip_data(data);
cfg.u64 = 0;
cfg.s.int_en = 1;
cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0;
cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
/* 140 nS glitch filter*/
cfg.s.fil_cnt = 7;
cfg.s.fil_sel = 3;
cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd->gpio_line), cfg.u64);
}
linux/kernel/linux/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt
Example:
gpio-controller@1070000000800 {
#gpio-cells = <2>;
compatible = "cavium,octeon-3860-gpio";
reg = <0x10700 0x00000800 0x0 0x100>;
gpio-controller;
/* Interrupts are specified by two parts:
* 1) GPIO pin number (0..15)
* 2) Triggering (1 - edge rising
* 2 - edge falling
* 4 - level active high
* 8 - level active low)
*/
interrupt-controller;
#interrupt-cells = <2>;
/* The GPIO pin connect to 16 consecutive CUI bits */
interrupts = <0 16>, <0 17>, <0 18>, <0 19>,
<0 20>, <0 21>, <0 22>, <0 23>,
<0 24>, <0 25>, <0 26>, <0 27>,
<0 28>, <0 29>, <0 30>, <0 31>;
};
GPIO中断应答
static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
{
struct octeon_ciu_chip_data *cd;
u64 mask;
cd = irq_data_get_irq_chip_data(data);
mask = 1ull << (cd->gpio_line);
cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
}