Path: blob/master/arch/powerpc/platforms/cell/beat_interrupt.c
10818 views
/*1* Celleb/Beat Interrupt controller2*3* (C) Copyright 2006-2007 TOSHIBA CORPORATION4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License along16* with this program; if not, write to the Free Software Foundation, Inc.,17* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.18*/1920#include <linux/init.h>21#include <linux/interrupt.h>22#include <linux/irq.h>23#include <linux/percpu.h>24#include <linux/types.h>2526#include <asm/machdep.h>2728#include "beat_interrupt.h"29#include "beat_wrapper.h"3031#define MAX_IRQS NR_IRQS32static DEFINE_RAW_SPINLOCK(beatic_irq_mask_lock);33static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64];34static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64];3536static struct irq_host *beatic_host;3738/*39* In this implementation, "virq" == "IRQ plug number",40* "(irq_hw_number_t)hwirq" == "IRQ outlet number".41*/4243/* assumption: locked */44static inline void beatic_update_irq_mask(unsigned int irq_plug)45{46int off;47unsigned long masks[4];4849off = (irq_plug / 256) * 4;50masks[0] = beatic_irq_mask_enable[off + 0]51& beatic_irq_mask_ack[off + 0];52masks[1] = beatic_irq_mask_enable[off + 1]53& beatic_irq_mask_ack[off + 1];54masks[2] = beatic_irq_mask_enable[off + 2]55& beatic_irq_mask_ack[off + 2];56masks[3] = beatic_irq_mask_enable[off + 3]57& beatic_irq_mask_ack[off + 3];58if (beat_set_interrupt_mask(irq_plug&~255UL,59masks[0], masks[1], masks[2], masks[3]) != 0)60panic("Failed to set mask IRQ!");61}6263static void beatic_mask_irq(struct irq_data *d)64{65unsigned long flags;6667raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);68beatic_irq_mask_enable[d->irq/64] &= ~(1UL << (63 - (d->irq%64)));69beatic_update_irq_mask(d->irq);70raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);71}7273static void beatic_unmask_irq(struct irq_data *d)74{75unsigned long flags;7677raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);78beatic_irq_mask_enable[d->irq/64] |= 1UL << (63 - (d->irq%64));79beatic_update_irq_mask(d->irq);80raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);81}8283static void beatic_ack_irq(struct irq_data *d)84{85unsigned long flags;8687raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);88beatic_irq_mask_ack[d->irq/64] &= ~(1UL << (63 - (d->irq%64)));89beatic_update_irq_mask(d->irq);90raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);91}9293static void beatic_end_irq(struct irq_data *d)94{95s64 err;96unsigned long flags;9798err = beat_downcount_of_interrupt(d->irq);99if (err != 0) {100if ((err & 0xFFFFFFFF) != 0xFFFFFFF5) /* -11: wrong state */101panic("Failed to downcount IRQ! Error = %16llx", err);102103printk(KERN_ERR "IRQ over-downcounted, plug %d\n", d->irq);104}105raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags);106beatic_irq_mask_ack[d->irq/64] |= 1UL << (63 - (d->irq%64));107beatic_update_irq_mask(d->irq);108raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);109}110111static struct irq_chip beatic_pic = {112.name = "CELL-BEAT",113.irq_unmask = beatic_unmask_irq,114.irq_mask = beatic_mask_irq,115.irq_eoi = beatic_end_irq,116};117118/*119* Dispose binding hardware IRQ number (hw) and Virtuql IRQ number (virq),120* update flags.121*122* Note that the number (virq) is already assigned at upper layer.123*/124static void beatic_pic_host_unmap(struct irq_host *h, unsigned int virq)125{126beat_destruct_irq_plug(virq);127}128129/*130* Create or update binding hardware IRQ number (hw) and Virtuql131* IRQ number (virq). This is called only once for a given mapping.132*133* Note that the number (virq) is already assigned at upper layer.134*/135static int beatic_pic_host_map(struct irq_host *h, unsigned int virq,136irq_hw_number_t hw)137{138int64_t err;139140err = beat_construct_and_connect_irq_plug(virq, hw);141if (err < 0)142return -EIO;143144irq_set_status_flags(virq, IRQ_LEVEL);145irq_set_chip_and_handler(virq, &beatic_pic, handle_fasteoi_irq);146return 0;147}148149/*150* Translate device-tree interrupt spec to irq_hw_number_t style (ulong),151* to pass away to irq_create_mapping().152*153* Called from irq_create_of_mapping() only.154* Note: We have only 1 entry to translate.155*/156static int beatic_pic_host_xlate(struct irq_host *h, struct device_node *ct,157const u32 *intspec, unsigned int intsize,158irq_hw_number_t *out_hwirq,159unsigned int *out_flags)160{161const u64 *intspec2 = (const u64 *)intspec;162163*out_hwirq = *intspec2;164*out_flags |= IRQ_TYPE_LEVEL_LOW;165return 0;166}167168static int beatic_pic_host_match(struct irq_host *h, struct device_node *np)169{170/* Match all */171return 1;172}173174static struct irq_host_ops beatic_pic_host_ops = {175.map = beatic_pic_host_map,176.unmap = beatic_pic_host_unmap,177.xlate = beatic_pic_host_xlate,178.match = beatic_pic_host_match,179};180181/*182* Get an IRQ number183* Note: returns VIRQ184*/185static inline unsigned int beatic_get_irq_plug(void)186{187int i;188uint64_t pending[4], ub;189190for (i = 0; i < MAX_IRQS; i += 256) {191beat_detect_pending_interrupts(i, pending);192__asm__ ("cntlzd %0,%1":"=r"(ub):193"r"(pending[0] & beatic_irq_mask_enable[i/64+0]194& beatic_irq_mask_ack[i/64+0]));195if (ub != 64)196return i + ub + 0;197__asm__ ("cntlzd %0,%1":"=r"(ub):198"r"(pending[1] & beatic_irq_mask_enable[i/64+1]199& beatic_irq_mask_ack[i/64+1]));200if (ub != 64)201return i + ub + 64;202__asm__ ("cntlzd %0,%1":"=r"(ub):203"r"(pending[2] & beatic_irq_mask_enable[i/64+2]204& beatic_irq_mask_ack[i/64+2]));205if (ub != 64)206return i + ub + 128;207__asm__ ("cntlzd %0,%1":"=r"(ub):208"r"(pending[3] & beatic_irq_mask_enable[i/64+3]209& beatic_irq_mask_ack[i/64+3]));210if (ub != 64)211return i + ub + 192;212}213214return NO_IRQ;215}216unsigned int beatic_get_irq(void)217{218unsigned int ret;219220ret = beatic_get_irq_plug();221if (ret != NO_IRQ)222beatic_ack_irq(irq_get_irq_data(ret));223return ret;224}225226/*227*/228void __init beatic_init_IRQ(void)229{230int i;231232memset(beatic_irq_mask_enable, 0, sizeof(beatic_irq_mask_enable));233memset(beatic_irq_mask_ack, 255, sizeof(beatic_irq_mask_ack));234for (i = 0; i < MAX_IRQS; i += 256)235beat_set_interrupt_mask(i, 0L, 0L, 0L, 0L);236237/* Set out get_irq function */238ppc_md.get_irq = beatic_get_irq;239240/* Allocate an irq host */241beatic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,242&beatic_pic_host_ops,2430);244BUG_ON(beatic_host == NULL);245irq_set_default_host(beatic_host);246}247248void beatic_deinit_IRQ(void)249{250int i;251252for (i = 1; i < NR_IRQS; i++)253beat_destruct_irq_plug(i);254}255256257