linux/drivers/sh/intc/handle.c
<<
>>
Prefs
   1/*
   2 * Shared interrupt handling code for IPR and INTC2 types of IRQs.
   3 *
   4 * Copyright (C) 2007, 2008 Magnus Damm
   5 * Copyright (C) 2009, 2010 Paul Mundt
   6 *
   7 * This file is subject to the terms and conditions of the GNU General Public
   8 * License.  See the file "COPYING" in the main directory of this archive
   9 * for more details.
  10 */
  11#include <linux/init.h>
  12#include <linux/irq.h>
  13#include <linux/spinlock.h>
  14#include "internals.h"
  15
  16static unsigned long ack_handle[INTC_NR_IRQS];
  17
  18static intc_enum __init intc_grp_id(struct intc_desc *desc,
  19                                    intc_enum enum_id)
  20{
  21        struct intc_group *g = desc->hw.groups;
  22        unsigned int i, j;
  23
  24        for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
  25                g = desc->hw.groups + i;
  26
  27                for (j = 0; g->enum_ids[j]; j++) {
  28                        if (g->enum_ids[j] != enum_id)
  29                                continue;
  30
  31                        return g->enum_id;
  32                }
  33        }
  34
  35        return 0;
  36}
  37
  38static unsigned int __init _intc_mask_data(struct intc_desc *desc,
  39                                           struct intc_desc_int *d,
  40                                           intc_enum enum_id,
  41                                           unsigned int *reg_idx,
  42                                           unsigned int *fld_idx)
  43{
  44        struct intc_mask_reg *mr = desc->hw.mask_regs;
  45        unsigned int fn, mode;
  46        unsigned long reg_e, reg_d;
  47
  48        while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
  49                mr = desc->hw.mask_regs + *reg_idx;
  50
  51                for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
  52                        if (mr->enum_ids[*fld_idx] != enum_id)
  53                                continue;
  54
  55                        if (mr->set_reg && mr->clr_reg) {
  56                                fn = REG_FN_WRITE_BASE;
  57                                mode = MODE_DUAL_REG;
  58                                reg_e = mr->clr_reg;
  59                                reg_d = mr->set_reg;
  60                        } else {
  61                                fn = REG_FN_MODIFY_BASE;
  62                                if (mr->set_reg) {
  63                                        mode = MODE_ENABLE_REG;
  64                                        reg_e = mr->set_reg;
  65                                        reg_d = mr->set_reg;
  66                                } else {
  67                                        mode = MODE_MASK_REG;
  68                                        reg_e = mr->clr_reg;
  69                                        reg_d = mr->clr_reg;
  70                                }
  71                        }
  72
  73                        fn += (mr->reg_width >> 3) - 1;
  74                        return _INTC_MK(fn, mode,
  75                                        intc_get_reg(d, reg_e),
  76                                        intc_get_reg(d, reg_d),
  77                                        1,
  78                                        (mr->reg_width - 1) - *fld_idx);
  79                }
  80
  81                *fld_idx = 0;
  82                (*reg_idx)++;
  83        }
  84
  85        return 0;
  86}
  87
  88unsigned int __init
  89intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d,
  90                     intc_enum enum_id, int do_grps)
  91{
  92        unsigned int i = 0;
  93        unsigned int j = 0;
  94        unsigned int ret;
  95
  96        ret = _intc_mask_data(desc, d, enum_id, &i, &j);
  97        if (ret)
  98                return ret;
  99
 100        if (do_grps)
 101                return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0);
 102
 103        return 0;
 104}
 105
 106static unsigned int __init _intc_prio_data(struct intc_desc *desc,
 107                                           struct intc_desc_int *d,
 108                                           intc_enum enum_id,
 109                                           unsigned int *reg_idx,
 110                                           unsigned int *fld_idx)
 111{
 112        struct intc_prio_reg *pr = desc->hw.prio_regs;
 113        unsigned int fn, n, mode, bit;
 114        unsigned long reg_e, reg_d;
 115
 116        while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
 117                pr = desc->hw.prio_regs + *reg_idx;
 118
 119                for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
 120                        if (pr->enum_ids[*fld_idx] != enum_id)
 121                                continue;
 122
 123                        if (pr->set_reg && pr->clr_reg) {
 124                                fn = REG_FN_WRITE_BASE;
 125                                mode = MODE_PCLR_REG;
 126                                reg_e = pr->set_reg;
 127                                reg_d = pr->clr_reg;
 128                        } else {
 129                                fn = REG_FN_MODIFY_BASE;
 130                                mode = MODE_PRIO_REG;
 131                                if (!pr->set_reg)
 132                                        BUG();
 133                                reg_e = pr->set_reg;
 134                                reg_d = pr->set_reg;
 135                        }
 136
 137                        fn += (pr->reg_width >> 3) - 1;
 138                        n = *fld_idx + 1;
 139
 140                        BUG_ON(n * pr->field_width > pr->reg_width);
 141
 142                        bit = pr->reg_width - (n * pr->field_width);
 143
 144                        return _INTC_MK(fn, mode,
 145                                        intc_get_reg(d, reg_e),
 146                                        intc_get_reg(d, reg_d),
 147                                        pr->field_width, bit);
 148                }
 149
 150                *fld_idx = 0;
 151                (*reg_idx)++;
 152        }
 153
 154        return 0;
 155}
 156
 157unsigned int __init
 158intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
 159                     intc_enum enum_id, int do_grps)
 160{
 161        unsigned int i = 0;
 162        unsigned int j = 0;
 163        unsigned int ret;
 164
 165        ret = _intc_prio_data(desc, d, enum_id, &i, &j);
 166        if (ret)
 167                return ret;
 168
 169        if (do_grps)
 170                return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0);
 171
 172        return 0;
 173}
 174
 175static unsigned int intc_ack_data(struct intc_desc *desc,
 176                                  struct intc_desc_int *d, intc_enum enum_id)
 177{
 178        struct intc_mask_reg *mr = desc->hw.ack_regs;
 179        unsigned int i, j, fn, mode;
 180        unsigned long reg_e, reg_d;
 181
 182        for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
 183                mr = desc->hw.ack_regs + i;
 184
 185                for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
 186                        if (mr->enum_ids[j] != enum_id)
 187                                continue;
 188
 189                        fn = REG_FN_MODIFY_BASE;
 190                        mode = MODE_ENABLE_REG;
 191                        reg_e = mr->set_reg;
 192                        reg_d = mr->set_reg;
 193
 194                        fn += (mr->reg_width >> 3) - 1;
 195                        return _INTC_MK(fn, mode,
 196                                        intc_get_reg(d, reg_e),
 197                                        intc_get_reg(d, reg_d),
 198                                        1,
 199                                        (mr->reg_width - 1) - j);
 200                }
 201        }
 202
 203        return 0;
 204}
 205
 206static void intc_enable_disable(struct intc_desc_int *d,
 207                                unsigned long handle, int do_enable)
 208{
 209        unsigned long addr;
 210        unsigned int cpu;
 211        unsigned long (*fn)(unsigned long, unsigned long,
 212                   unsigned long (*)(unsigned long, unsigned long,
 213                                     unsigned long),
 214                   unsigned int);
 215
 216        if (do_enable) {
 217                for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
 218                        addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
 219                        fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
 220                        fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
 221                }
 222        } else {
 223                for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
 224                        addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
 225                        fn = intc_disable_fns[_INTC_MODE(handle)];
 226                        fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
 227                }
 228        }
 229}
 230
 231void __init intc_enable_disable_enum(struct intc_desc *desc,
 232                                     struct intc_desc_int *d,
 233                                     intc_enum enum_id, int enable)
 234{
 235        unsigned int i, j, data;
 236
 237        /* go through and enable/disable all mask bits */
 238        i = j = 0;
 239        do {
 240                data = _intc_mask_data(desc, d, enum_id, &i, &j);
 241                if (data)
 242                        intc_enable_disable(d, data, enable);
 243                j++;
 244        } while (data);
 245
 246        /* go through and enable/disable all priority fields */
 247        i = j = 0;
 248        do {
 249                data = _intc_prio_data(desc, d, enum_id, &i, &j);
 250                if (data)
 251                        intc_enable_disable(d, data, enable);
 252
 253                j++;
 254        } while (data);
 255}
 256
 257unsigned int __init
 258intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d,
 259                      intc_enum enum_id)
 260{
 261        struct intc_sense_reg *sr = desc->hw.sense_regs;
 262        unsigned int i, j, fn, bit;
 263
 264        for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
 265                sr = desc->hw.sense_regs + i;
 266
 267                for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
 268                        if (sr->enum_ids[j] != enum_id)
 269                                continue;
 270
 271                        fn = REG_FN_MODIFY_BASE;
 272                        fn += (sr->reg_width >> 3) - 1;
 273
 274                        BUG_ON((j + 1) * sr->field_width > sr->reg_width);
 275
 276                        bit = sr->reg_width - ((j + 1) * sr->field_width);
 277
 278                        return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
 279                                        0, sr->field_width, bit);
 280                }
 281        }
 282
 283        return 0;
 284}
 285
 286
 287void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc,
 288                         struct intc_desc_int *d, intc_enum id)
 289{
 290        unsigned long flags;
 291
 292        /*
 293         * Nothing to do for this IRQ.
 294         */
 295        if (!desc->hw.ack_regs)
 296                return;
 297
 298        raw_spin_lock_irqsave(&intc_big_lock, flags);
 299        ack_handle[irq] = intc_ack_data(desc, d, id);
 300        raw_spin_unlock_irqrestore(&intc_big_lock, flags);
 301}
 302
 303unsigned long intc_get_ack_handle(unsigned int irq)
 304{
 305        return ack_handle[irq];
 306}
 307