bcm1480_irq.c

来自「一个很好的嵌入式linux平台下的bootloader」· C语言 代码 · 共 462 行

C
462
字号
/*  *********************************************************************    *  Broadcom Common Firmware Environment (CFE)    *      *  Polled interrupt dispatch routines	File: bcm1480_irq.c    *      *********************************************************************      *    *  Copyright 2000,2001,2002,2003    *  Broadcom Corporation. All rights reserved.    *      *  This software is furnished under license and may be used and     *  copied only in accordance with the following terms and     *  conditions.  Subject to these conditions, you may download,     *  copy, install, use, modify and distribute modified or unmodified     *  copies of this software in source and/or binary form.  No title     *  or ownership is transferred hereby.    *      *  1) Any source code used, modified or distributed must reproduce     *     and retain this copyright notice and list of conditions     *     as they appear in the source file.    *      *  2) No right is granted to use any trade name, trademark, or     *     logo of Broadcom Corporation.  The "Broadcom Corporation"     *     name may not be used to endorse or promote products derived     *     from this software without the prior written permission of     *     Broadcom Corporation.    *      *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR     *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT     *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN     *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES     *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF     *     THE POSSIBILITY OF SUCH DAMAGE.    ********************************************************************* *//*    *********************************************************************    *  This module provides an interface for associating service    *  routines with SB-1250 system and LDT interrupt sources.  The    *  various interrupt mapper registers are periodically polled    *  and the requested service routine is invoked when a    *  corresponding interrupt request is active and enabled.    *    *  The interface is loosely based on irq.c from Linux.    *    *  This is not a full-fledged interrupt handler.    *    *  If CFG_INTERRUPTS == 0, it operates synchronously with the    *  main polling loop and is never invoked directly by the    *  hardware exception handler.  If CFG_INTERRUPTS == 1, certain    *  interrupt sources can be handled asynchronously as exceptions.    *    *  For now, all interrupts are directed to CPU 0, via its    *  interrupt mapper, but there are some hooks for handlers    *  with CPU affinity.    *    ********************************************************************* */#include "cfe.h"#include "sbmips.h"         /* XXX updated IP and IM definitions needed. */#include "bcm1480_regs.h"#include "bcm1480_scd.h"#include "bcm1480_int.h"extern void bcm1480_update_sr(uint32_t clear, uint32_t set);#include "exception.h"#include "cfe_irq.h"extern void bcm1480_irq_install(void);extern void bcm1480_irq_arm(void);/* First level dispatching (MIPS IP level). */#define IP_LEVELS 8/* Shared variables that must be protected in non-interrupt code. */static ip_handler_t ip_handler[IP_LEVELS] = {NULL};voidcfe_irq_setvector(int index, ip_handler_t handler){    if (index >= 0 && index < IP_LEVELS) {	uint32_t set, clear;	ip_handler[index] = NULL;   /* disable: see demux */	if (handler == NULL) {	    clear = _MM_MAKEMASK1(S_SR_IMMASK + index);	    set = 0;	    }	else {	    clear = 0;	    set = _MM_MAKEMASK1(S_SR_IMMASK + index);	    }	bcm1480_update_sr(clear, set);	ip_handler[index] = handler;    }}/* * Dispatch function called from the exception handler for * asynchronous (non-polled) interrupts. * info is a pointer to the saved register block. * * At entry, interrupts will be masked. */static voidbcm1480_irq_demux(int code, mips_reg_t *info){    uint32_t pending;    pending = (uint32_t)(info[XCP0_CAUSE] & info[XCP0_SR]);    /* For now, we handle IP7 (internal timers) and IP2 (mapper) only */    if (pending & M_CAUSE_IP7) {	if (ip_handler[7] != NULL) {	    (*(ip_handler[7]))(7);	    }	else {	    /* mask off IP7, else we're caught here forever */	    bcm1480_update_sr(M_SR_IM7, 0);	    }	}    if (pending & M_CAUSE_IP2) {	if (ip_handler[2] != NULL) {	    (*(ip_handler[2]))(2);	    }	else {	    /* mask off IP2, else we're caught here forever */	    bcm1480_update_sr(M_SR_IM2, 0);	    }	}}/* * Initialize the MIPS level dispatch vector. * This function should be called with interrupts disabled. */static voidbcm1480_irq_vectorinit(void){    int  i;    for (i = 0; i < IP_LEVELS; i++) {	ip_handler[i] = NULL;	}    _exc_setvector(XTYPE_INTERRUPT, (void *) bcm1480_irq_demux);    bcm1480_irq_arm();}/* Second level dispatching (BCM1480 interrupt source level). */#define NR_IRQS K_INT_SOURCES#define IMR_POINTER(cpu,reg) \    ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg))))typedef struct irq_action_s irq_action_t;struct irq_action_s {    void (*handler)(void *arg);    void *arg;    unsigned long flags;    int device;              /* to distinguish actions for shared interrupts */    irq_action_t *next;};typedef struct irq_desc_s {    irq_action_t *actions;    int depth;    int status;} irq_desc_t;/* status flags */#define IRQ_DISABLED  0x0001/* Shared variables that must be protected in non-interrupt code. */static irq_desc_t irq_desc[NR_IRQS];/* *  cfe_irq_init is called early in the boot sequence.  It is *  responsible for setting up the interrupt mappesr and initializing *  the handler table that will be used for dispatching pending *  interrupts.  If hard interrupts are used, it then enables hardware *  interrupts (initially all masked). * *  This function should be called before interrupts are enabled. */voidcfe_irq_init(void){    int i, j;    irq_desc_t *p;    int ncpus;    for (i = 0, p = irq_desc; i < NR_IRQS; i++, p++) {        p->actions = NULL;	p->depth = 0;	p->status = IRQ_DISABLED;	}	    /* Get the number of CPUs from the part number */    ncpus = (G_SYS_PART(SBREADCSR(A_SCD_SYSTEM_REVISION)) >> 8) & 0xF;    for (i = 0; i < ncpus; i++) {	/* initially, all interrupts are masked */	*IMR_POINTER(i, R_IMR_INTERRUPT_MASK_H) = ~((uint64_t)0);	*IMR_POINTER(i, R_IMR_INTERRUPT_MASK_L) = ~((uint64_t)0);	*IMR_POINTER(i, R_IMR_INTERRUPT_DIAG_H) = 0;	*IMR_POINTER(i, R_IMR_INTERRUPT_DIAG_L) = 0;	for (j = 0; j < IMR_INTERRUPT_MAP_COUNT; j++) {	    *IMR_POINTER(i, R_IMR_INTERRUPT_MAP_BASE_H + 8*j) = 0;	    *IMR_POINTER(i, R_IMR_INTERRUPT_MAP_BASE_L + 8*j) = 0;	    }	}    bcm1480_irq_install();    bcm1480_irq_vectorinit();#if CFG_INTERRUPTS    cfe_irq_setvector(2, (ip_handler_t)cfe_irq_poll);#endif}/* cfe_mask_irq() is called to mask an interrupt at the hw level */voidcfe_mask_irq(int cpu, unsigned int irq){    int sr;    if (irq >= NR_IRQS)	return;    sr = cfe_irq_disable();    if (_INT_OFFSET(irq) == 0)	*IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK_H) |= _INT_MASK1(irq);    else	*IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK_L) |= _INT_MASK1(irq);    __asm__ __volatile__ ("sync" : : : "memory");    cfe_irq_enable(sr);}/* cfe_unmask_irq() is called to unmask an interrupt at the hw level */voidcfe_unmask_irq(int cpu, unsigned int irq){    int sr;    if (irq >= NR_IRQS)	return;    sr = cfe_irq_disable();    if (_INT_OFFSET(irq) == 0)	*IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK_H) &=~ _INT_MASK1(irq);    else	*IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK_L) &=~ _INT_MASK1(irq);    cfe_irq_enable(sr);}/* If depth is 0, unmask the interrupt. Increment depth. */voidcfe_enable_irq(unsigned int irq){    int sr;    irq_desc_t *desc;    if (irq >= NR_IRQS)	return;    desc = &irq_desc[irq];    /* The following code must be atomic */    sr = cfe_irq_disable();    if (desc->depth == 0) {	if (_INT_OFFSET(irq) == 0)	    *IMR_POINTER(0, R_IMR_INTERRUPT_MASK_H) &=~ _INT_MASK1(irq);	else	    *IMR_POINTER(0, R_IMR_INTERRUPT_MASK_L) &=~ _INT_MASK1(irq);	desc->status &=~ IRQ_DISABLED;	}    desc->depth++;    cfe_irq_enable(sr);}/* Decrement depth. If depth is 0, mask the interrupt. */voidcfe_disable_irq(unsigned int irq){    int sr;    irq_desc_t *desc;    if (irq >= NR_IRQS)	return;    desc = &irq_desc[irq];    /* The following code must be atomic */    sr = cfe_irq_disable();    desc->depth--;    if (desc->depth == 0) {	if (_INT_OFFSET(irq) == 0)	    *IMR_POINTER(0, R_IMR_INTERRUPT_MASK_H) |= _INT_MASK1(irq);	else	    *IMR_POINTER(0, R_IMR_INTERRUPT_MASK_L) |= _INT_MASK1(irq);	desc->status |= IRQ_DISABLED;	__asm__ __volatile__ ("sync" : : : "memory");	}    cfe_irq_enable(sr);}/* *  cfe_request_irq() is called by drivers to request addition to the *  chain of handlers called for a given interrupt.   */intcfe_request_irq(unsigned int irq, 		void (*handler)(void *), void *arg,		unsigned long irqflags, int device){    int sr;    irq_action_t *action;    irq_desc_t *desc;    irq_action_t *p;    if (irq >= NR_IRQS) {        return -1;	}    if (handler == NULL) {        return -1;	}    action = (irq_action_t *) KMALLOC(sizeof(irq_action_t), 0);    if (action == NULL) {        return -1;	}    action->handler = handler;    action->arg = arg;    action->flags = irqflags;    action->device = device;    action->next = NULL;    desc = &irq_desc[irq];    /* The following block of code has to be executed atomically */    sr = cfe_irq_disable();    p = desc->actions;    if (p == NULL) {        desc->actions = action;        desc->depth = 0;        desc->status |= IRQ_DISABLED;	 /* always direct to i0 (IP[2]) for now */	if (_INT_OFFSET(irq) == 0)	    *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE_H + 8*irq) = K_INT_MAP_I0;	else	    *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE_L + 8*irq) = K_INT_MAP_I0;	__asm__ __volatile__ ("sync" : : : "memory");	}    else {        /* Can't share interrupts unless both agree to */        if ((p->flags & action->flags & CFE_IRQ_FLAGS_SHARED) == 0) {	    cfe_enable_irq(irq);	    KFREE(action);	    xprintf("cfe_request_irq: conflicting unsharable interrupts.\n");            return -1;	    }	while (p->next != NULL) {	    p = p->next;	    }	p->next = action;	}    cfe_enable_irq(irq);    cfe_irq_enable(sr);    return 0;}/* *  free_irq() releases a handler set up by request_irq() */voidcfe_free_irq(unsigned int irq, int device){    int sr;    irq_desc_t *desc;    irq_action_t *p, *q;    if (irq >= NR_IRQS)        return;    desc = &irq_desc[irq];    /* The following block of code has to be executed atomically */    sr = cfe_irq_disable();    p = desc->actions;    q = NULL;    while (p != NULL) {	if (p->device == device) {	    cfe_disable_irq(irq);	    if (q == NULL) {		desc->actions = p->next;		if (desc->actions == NULL)		    desc->status |= IRQ_DISABLED;		}	    else		q->next = p->next;	    break;	    }	else {	    q = p;	    p = p->next;	    }	}    cfe_irq_enable(sr);    if (p != NULL)	KFREE(p);}/* The interrupt polling code calls this routine to dispatch each   pending, unmasked interrupt.   For asynchronous interrupt processing, it is entered with   interrupts disabled. */void bcm1480_dispatch_irq(unsigned int irq);voidbcm1480_dispatch_irq(unsigned int irq){    irq_action_t *action;    irq_desc_t *desc = &irq_desc[irq];    for (action = desc->actions; action != NULL; action = action->next) {	if (action->handler != NULL) {	    (*action->handler)(action->arg);	    }	}}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?