linux/drivers/parisc/gsc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Interrupt management for most GSC and related devices.
   4 *
   5 * (c) Copyright 1999 Alex deVries for The Puffin Group
   6 * (c) Copyright 1999 Grant Grundler for Hewlett-Packard
   7 * (c) Copyright 1999 Matthew Wilcox
   8 * (c) Copyright 2000 Helge Deller
   9 * (c) Copyright 2001 Matthew Wilcox for Hewlett-Packard
  10 */
  11
  12#include <linux/bitops.h>
  13#include <linux/errno.h>
  14#include <linux/init.h>
  15#include <linux/interrupt.h>
  16#include <linux/ioport.h>
  17#include <linux/module.h>
  18#include <linux/types.h>
  19
  20#include <asm/hardware.h>
  21#include <asm/io.h>
  22
  23#include "gsc.h"
  24
  25#undef DEBUG
  26
  27#ifdef DEBUG
  28#define DEBPRINTK printk
  29#else
  30#define DEBPRINTK(x,...)
  31#endif
  32
  33int gsc_alloc_irq(struct gsc_irq *i)
  34{
  35        int irq = txn_alloc_irq(GSC_EIM_WIDTH);
  36        if (irq < 0) {
  37                printk("cannot get irq\n");
  38                return irq;
  39        }
  40
  41        i->txn_addr = txn_alloc_addr(irq);
  42        i->txn_data = txn_alloc_data(irq);
  43        i->irq = irq;
  44
  45        return irq;
  46}
  47
  48int gsc_claim_irq(struct gsc_irq *i, int irq)
  49{
  50        int c = irq;
  51
  52        irq += CPU_IRQ_BASE; /* virtualize the IRQ first */
  53
  54        irq = txn_claim_irq(irq);
  55        if (irq < 0) {
  56                printk("cannot claim irq %d\n", c);
  57                return irq;
  58        }
  59
  60        i->txn_addr = txn_alloc_addr(irq);
  61        i->txn_data = txn_alloc_data(irq);
  62        i->irq = irq;
  63
  64        return irq;
  65}
  66
  67EXPORT_SYMBOL(gsc_alloc_irq);
  68EXPORT_SYMBOL(gsc_claim_irq);
  69
  70/* Common interrupt demultiplexer used by Asp, Lasi & Wax.  */
  71irqreturn_t gsc_asic_intr(int gsc_asic_irq, void *dev)
  72{
  73        unsigned long irr;
  74        struct gsc_asic *gsc_asic = dev;
  75
  76        irr = gsc_readl(gsc_asic->hpa + OFFSET_IRR);
  77        if (irr == 0)
  78                return IRQ_NONE;
  79
  80        DEBPRINTK("%s intr, mask=0x%x\n", gsc_asic->name, irr);
  81
  82        do {
  83                int local_irq = __ffs(irr);
  84                unsigned int irq = gsc_asic->global_irq[local_irq];
  85                generic_handle_irq(irq);
  86                irr &= ~(1 << local_irq);
  87        } while (irr);
  88
  89        return IRQ_HANDLED;
  90}
  91
  92int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit)
  93{
  94        int local_irq;
  95
  96        for (local_irq = 0; local_irq < limit; local_irq++) {
  97                if (global_irqs[local_irq] == irq)
  98                        return local_irq;
  99        }
 100
 101        return NO_IRQ;
 102}
 103
 104static void gsc_asic_mask_irq(struct irq_data *d)
 105{
 106        struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d);
 107        int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32);
 108        u32 imr;
 109
 110        DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq,
 111                        irq_dev->name, imr);
 112
 113        /* Disable the IRQ line by clearing the bit in the IMR */
 114        imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
 115        imr &= ~(1 << local_irq);
 116        gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
 117}
 118
 119static void gsc_asic_unmask_irq(struct irq_data *d)
 120{
 121        struct gsc_asic *irq_dev = irq_data_get_irq_chip_data(d);
 122        int local_irq = gsc_find_local_irq(d->irq, irq_dev->global_irq, 32);
 123        u32 imr;
 124
 125        DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __func__, d->irq,
 126                        irq_dev->name, imr);
 127
 128        /* Enable the IRQ line by setting the bit in the IMR */
 129        imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
 130        imr |= 1 << local_irq;
 131        gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
 132        /*
 133         * FIXME: read IPR to make sure the IRQ isn't already pending.
 134         *   If so, we need to read IRR and manually call do_irq().
 135         */
 136}
 137
 138static struct irq_chip gsc_asic_interrupt_type = {
 139        .name           =       "GSC-ASIC",
 140        .irq_unmask     =       gsc_asic_unmask_irq,
 141        .irq_mask       =       gsc_asic_mask_irq,
 142};
 143
 144int gsc_assign_irq(struct irq_chip *type, void *data)
 145{
 146        static int irq = GSC_IRQ_BASE;
 147
 148        if (irq > GSC_IRQ_MAX)
 149                return NO_IRQ;
 150
 151        irq_set_chip_and_handler(irq, type, handle_simple_irq);
 152        irq_set_chip_data(irq, data);
 153
 154        return irq++;
 155}
 156
 157void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp)
 158{
 159        int irq = asic->global_irq[local_irq];
 160        
 161        if (irq <= 0) {
 162                irq = gsc_assign_irq(&gsc_asic_interrupt_type, asic);
 163                if (irq == NO_IRQ)
 164                        return;
 165
 166                asic->global_irq[local_irq] = irq;
 167        }
 168        *irqp = irq;
 169}
 170
 171struct gsc_fixup_struct {
 172        void (*choose_irq)(struct parisc_device *, void *);
 173        void *ctrl;
 174};
 175
 176static int gsc_fixup_irqs_callback(struct device *dev, void *data)
 177{
 178        struct parisc_device *padev = to_parisc_device(dev);
 179        struct gsc_fixup_struct *gf = data;
 180
 181        /* work-around for 715/64 and others which have parent
 182           at path [5] and children at path [5/0/x] */
 183        if (padev->id.hw_type == HPHW_FAULTY)
 184                gsc_fixup_irqs(padev, gf->ctrl, gf->choose_irq);
 185        gf->choose_irq(padev, gf->ctrl);
 186
 187        return 0;
 188}
 189
 190void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl,
 191                        void (*choose_irq)(struct parisc_device *, void *))
 192{
 193        struct gsc_fixup_struct data = {
 194                .choose_irq     = choose_irq,
 195                .ctrl           = ctrl,
 196        };
 197
 198        device_for_each_child(&parent->dev, &data, gsc_fixup_irqs_callback);
 199}
 200
 201int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic)
 202{
 203        struct resource *res;
 204        int i;
 205
 206        gsc_asic->gsc = parent;
 207
 208        /* Initialise local irq -> global irq mapping */
 209        for (i = 0; i < 32; i++) {
 210                gsc_asic->global_irq[i] = NO_IRQ;
 211        }
 212
 213        /* allocate resource region */
 214        res = request_mem_region(gsc_asic->hpa, 0x100000, gsc_asic->name);
 215        if (res) {
 216                res->flags = IORESOURCE_MEM;    /* do not mark it busy ! */
 217        }
 218
 219#if 0
 220        printk(KERN_WARNING "%s IRQ %d EIM 0x%x", gsc_asic->name,
 221                        parent->irq, gsc_asic->eim);
 222        if (gsc_readl(gsc_asic->hpa + OFFSET_IMR))
 223                printk("  IMR is non-zero! (0x%x)",
 224                                gsc_readl(gsc_asic->hpa + OFFSET_IMR));
 225        printk("\n");
 226#endif
 227
 228        return 0;
 229}
 230
 231extern struct parisc_driver lasi_driver;
 232extern struct parisc_driver asp_driver;
 233extern struct parisc_driver wax_driver;
 234
 235void __init gsc_init(void)
 236{
 237#ifdef CONFIG_GSC_LASI
 238        register_parisc_driver(&lasi_driver);
 239        register_parisc_driver(&asp_driver);
 240#endif
 241#ifdef CONFIG_GSC_WAX
 242        register_parisc_driver(&wax_driver);
 243#endif
 244}
 245