Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * IRQ chip definitions for INTC IRQs.
0003  *
0004  * Copyright (C) 2007, 2008 Magnus Damm
0005  * Copyright (C) 2009 - 2012 Paul Mundt
0006  *
0007  * This file is subject to the terms and conditions of the GNU General Public
0008  * License.  See the file "COPYING" in the main directory of this archive
0009  * for more details.
0010  */
0011 #include <linux/cpumask.h>
0012 #include <linux/bsearch.h>
0013 #include <linux/io.h>
0014 #include "internals.h"
0015 
0016 void _intc_enable(struct irq_data *data, unsigned long handle)
0017 {
0018     unsigned int irq = data->irq;
0019     struct intc_desc_int *d = get_intc_desc(irq);
0020     unsigned long addr;
0021     unsigned int cpu;
0022 
0023     for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
0024 #ifdef CONFIG_SMP
0025         if (!cpumask_test_cpu(cpu, irq_data_get_affinity_mask(data)))
0026             continue;
0027 #endif
0028         addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
0029         intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
0030                             [_INTC_FN(handle)], irq);
0031     }
0032 
0033     intc_balancing_enable(irq);
0034 }
0035 
0036 static void intc_enable(struct irq_data *data)
0037 {
0038     _intc_enable(data, (unsigned long)irq_data_get_irq_chip_data(data));
0039 }
0040 
0041 static void intc_disable(struct irq_data *data)
0042 {
0043     unsigned int irq = data->irq;
0044     struct intc_desc_int *d = get_intc_desc(irq);
0045     unsigned long handle = (unsigned long)irq_data_get_irq_chip_data(data);
0046     unsigned long addr;
0047     unsigned int cpu;
0048 
0049     intc_balancing_disable(irq);
0050 
0051     for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
0052 #ifdef CONFIG_SMP
0053         if (!cpumask_test_cpu(cpu, irq_data_get_affinity_mask(data)))
0054             continue;
0055 #endif
0056         addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
0057         intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
0058                              [_INTC_FN(handle)], irq);
0059     }
0060 }
0061 
0062 #ifdef CONFIG_SMP
0063 /*
0064  * This is held with the irq desc lock held, so we don't require any
0065  * additional locking here at the intc desc level. The affinity mask is
0066  * later tested in the enable/disable paths.
0067  */
0068 static int intc_set_affinity(struct irq_data *data,
0069                  const struct cpumask *cpumask,
0070                  bool force)
0071 {
0072     if (!cpumask_intersects(cpumask, cpu_online_mask))
0073         return -1;
0074 
0075     irq_data_update_affinity(data, cpumask);
0076 
0077     return IRQ_SET_MASK_OK_NOCOPY;
0078 }
0079 #endif
0080 
0081 static void intc_mask_ack(struct irq_data *data)
0082 {
0083     unsigned int irq = data->irq;
0084     struct intc_desc_int *d = get_intc_desc(irq);
0085     unsigned long handle = intc_get_ack_handle(irq);
0086     void __iomem *addr;
0087 
0088     intc_disable(data);
0089 
0090     /* read register and write zero only to the associated bit */
0091     if (handle) {
0092         unsigned int value;
0093 
0094         addr = (void __iomem *)INTC_REG(d, _INTC_ADDR_D(handle), 0);
0095         value = intc_set_field_from_handle(0, 1, handle);
0096 
0097         switch (_INTC_FN(handle)) {
0098         case REG_FN_MODIFY_BASE + 0:    /* 8bit */
0099             __raw_readb(addr);
0100             __raw_writeb(0xff ^ value, addr);
0101             break;
0102         case REG_FN_MODIFY_BASE + 1:    /* 16bit */
0103             __raw_readw(addr);
0104             __raw_writew(0xffff ^ value, addr);
0105             break;
0106         case REG_FN_MODIFY_BASE + 3:    /* 32bit */
0107             __raw_readl(addr);
0108             __raw_writel(0xffffffff ^ value, addr);
0109             break;
0110         default:
0111             BUG();
0112             break;
0113         }
0114     }
0115 }
0116 
0117 static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
0118                          unsigned int nr_hp,
0119                          unsigned int irq)
0120 {
0121     struct intc_handle_int key;
0122 
0123     key.irq = irq;
0124     key.handle = 0;
0125 
0126     return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);
0127 }
0128 
0129 int intc_set_priority(unsigned int irq, unsigned int prio)
0130 {
0131     struct intc_desc_int *d = get_intc_desc(irq);
0132     struct irq_data *data = irq_get_irq_data(irq);
0133     struct intc_handle_int *ihp;
0134 
0135     if (!intc_get_prio_level(irq) || prio <= 1)
0136         return -EINVAL;
0137 
0138     ihp = intc_find_irq(d->prio, d->nr_prio, irq);
0139     if (ihp) {
0140         if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
0141             return -EINVAL;
0142 
0143         intc_set_prio_level(irq, prio);
0144 
0145         /*
0146          * only set secondary masking method directly
0147          * primary masking method is using intc_prio_level[irq]
0148          * priority level will be set during next enable()
0149          */
0150         if (_INTC_FN(ihp->handle) != REG_FN_ERR)
0151             _intc_enable(data, ihp->handle);
0152     }
0153     return 0;
0154 }
0155 
0156 #define SENSE_VALID_FLAG 0x80
0157 #define VALID(x) (x | SENSE_VALID_FLAG)
0158 
0159 static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
0160     [IRQ_TYPE_EDGE_FALLING] = VALID(0),
0161     [IRQ_TYPE_EDGE_RISING] = VALID(1),
0162     [IRQ_TYPE_LEVEL_LOW] = VALID(2),
0163     /* SH7706, SH7707 and SH7709 do not support high level triggered */
0164 #if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
0165     !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
0166     !defined(CONFIG_CPU_SUBTYPE_SH7709)
0167     [IRQ_TYPE_LEVEL_HIGH] = VALID(3),
0168 #endif
0169 #if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */
0170     [IRQ_TYPE_EDGE_BOTH] = VALID(4),
0171 #endif
0172 };
0173 
0174 static int intc_set_type(struct irq_data *data, unsigned int type)
0175 {
0176     unsigned int irq = data->irq;
0177     struct intc_desc_int *d = get_intc_desc(irq);
0178     unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
0179     struct intc_handle_int *ihp;
0180     unsigned long addr;
0181 
0182     if (!value)
0183         return -EINVAL;
0184 
0185     value &= ~SENSE_VALID_FLAG;
0186 
0187     ihp = intc_find_irq(d->sense, d->nr_sense, irq);
0188     if (ihp) {
0189         /* PINT has 2-bit sense registers, should fail on EDGE_BOTH */
0190         if (value >= (1 << _INTC_WIDTH(ihp->handle)))
0191             return -EINVAL;
0192 
0193         addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
0194         intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
0195     }
0196 
0197     return 0;
0198 }
0199 
0200 struct irq_chip intc_irq_chip   = {
0201     .irq_mask       = intc_disable,
0202     .irq_unmask     = intc_enable,
0203     .irq_mask_ack       = intc_mask_ack,
0204     .irq_enable     = intc_enable,
0205     .irq_disable        = intc_disable,
0206     .irq_set_type       = intc_set_type,
0207 #ifdef CONFIG_SMP
0208     .irq_set_affinity   = intc_set_affinity,
0209 #endif
0210     .flags          = IRQCHIP_SKIP_SET_WAKE,
0211 };