Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
0004  *
0005  * This file contains the interrupt probing code and driver APIs.
0006  */
0007 
0008 #include <linux/irq.h>
0009 #include <linux/module.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/delay.h>
0012 #include <linux/async.h>
0013 
0014 #include "internals.h"
0015 
0016 /*
0017  * Autodetection depends on the fact that any interrupt that
0018  * comes in on to an unassigned handler will get stuck with
0019  * "IRQS_WAITING" cleared and the interrupt disabled.
0020  */
0021 static DEFINE_MUTEX(probing_active);
0022 
0023 /**
0024  *  probe_irq_on    - begin an interrupt autodetect
0025  *
0026  *  Commence probing for an interrupt. The interrupts are scanned
0027  *  and a mask of potential interrupt lines is returned.
0028  *
0029  */
0030 unsigned long probe_irq_on(void)
0031 {
0032     struct irq_desc *desc;
0033     unsigned long mask = 0;
0034     int i;
0035 
0036     /*
0037      * quiesce the kernel, or at least the asynchronous portion
0038      */
0039     async_synchronize_full();
0040     mutex_lock(&probing_active);
0041     /*
0042      * something may have generated an irq long ago and we want to
0043      * flush such a longstanding irq before considering it as spurious.
0044      */
0045     for_each_irq_desc_reverse(i, desc) {
0046         raw_spin_lock_irq(&desc->lock);
0047         if (!desc->action && irq_settings_can_probe(desc)) {
0048             /*
0049              * Some chips need to know about probing in
0050              * progress:
0051              */
0052             if (desc->irq_data.chip->irq_set_type)
0053                 desc->irq_data.chip->irq_set_type(&desc->irq_data,
0054                              IRQ_TYPE_PROBE);
0055             irq_activate_and_startup(desc, IRQ_NORESEND);
0056         }
0057         raw_spin_unlock_irq(&desc->lock);
0058     }
0059 
0060     /* Wait for longstanding interrupts to trigger. */
0061     msleep(20);
0062 
0063     /*
0064      * enable any unassigned irqs
0065      * (we must startup again here because if a longstanding irq
0066      * happened in the previous stage, it may have masked itself)
0067      */
0068     for_each_irq_desc_reverse(i, desc) {
0069         raw_spin_lock_irq(&desc->lock);
0070         if (!desc->action && irq_settings_can_probe(desc)) {
0071             desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;
0072             if (irq_activate_and_startup(desc, IRQ_NORESEND))
0073                 desc->istate |= IRQS_PENDING;
0074         }
0075         raw_spin_unlock_irq(&desc->lock);
0076     }
0077 
0078     /*
0079      * Wait for spurious interrupts to trigger
0080      */
0081     msleep(100);
0082 
0083     /*
0084      * Now filter out any obviously spurious interrupts
0085      */
0086     for_each_irq_desc(i, desc) {
0087         raw_spin_lock_irq(&desc->lock);
0088 
0089         if (desc->istate & IRQS_AUTODETECT) {
0090             /* It triggered already - consider it spurious. */
0091             if (!(desc->istate & IRQS_WAITING)) {
0092                 desc->istate &= ~IRQS_AUTODETECT;
0093                 irq_shutdown_and_deactivate(desc);
0094             } else
0095                 if (i < 32)
0096                     mask |= 1 << i;
0097         }
0098         raw_spin_unlock_irq(&desc->lock);
0099     }
0100 
0101     return mask;
0102 }
0103 EXPORT_SYMBOL(probe_irq_on);
0104 
0105 /**
0106  *  probe_irq_mask - scan a bitmap of interrupt lines
0107  *  @val:   mask of interrupts to consider
0108  *
0109  *  Scan the interrupt lines and return a bitmap of active
0110  *  autodetect interrupts. The interrupt probe logic state
0111  *  is then returned to its previous value.
0112  *
0113  *  Note: we need to scan all the irq's even though we will
0114  *  only return autodetect irq numbers - just so that we reset
0115  *  them all to a known state.
0116  */
0117 unsigned int probe_irq_mask(unsigned long val)
0118 {
0119     unsigned int mask = 0;
0120     struct irq_desc *desc;
0121     int i;
0122 
0123     for_each_irq_desc(i, desc) {
0124         raw_spin_lock_irq(&desc->lock);
0125         if (desc->istate & IRQS_AUTODETECT) {
0126             if (i < 16 && !(desc->istate & IRQS_WAITING))
0127                 mask |= 1 << i;
0128 
0129             desc->istate &= ~IRQS_AUTODETECT;
0130             irq_shutdown_and_deactivate(desc);
0131         }
0132         raw_spin_unlock_irq(&desc->lock);
0133     }
0134     mutex_unlock(&probing_active);
0135 
0136     return mask & val;
0137 }
0138 EXPORT_SYMBOL(probe_irq_mask);
0139 
0140 /**
0141  *  probe_irq_off   - end an interrupt autodetect
0142  *  @val: mask of potential interrupts (unused)
0143  *
0144  *  Scans the unused interrupt lines and returns the line which
0145  *  appears to have triggered the interrupt. If no interrupt was
0146  *  found then zero is returned. If more than one interrupt is
0147  *  found then minus the first candidate is returned to indicate
0148  *  their is doubt.
0149  *
0150  *  The interrupt probe logic state is returned to its previous
0151  *  value.
0152  *
0153  *  BUGS: When used in a module (which arguably shouldn't happen)
0154  *  nothing prevents two IRQ probe callers from overlapping. The
0155  *  results of this are non-optimal.
0156  */
0157 int probe_irq_off(unsigned long val)
0158 {
0159     int i, irq_found = 0, nr_of_irqs = 0;
0160     struct irq_desc *desc;
0161 
0162     for_each_irq_desc(i, desc) {
0163         raw_spin_lock_irq(&desc->lock);
0164 
0165         if (desc->istate & IRQS_AUTODETECT) {
0166             if (!(desc->istate & IRQS_WAITING)) {
0167                 if (!nr_of_irqs)
0168                     irq_found = i;
0169                 nr_of_irqs++;
0170             }
0171             desc->istate &= ~IRQS_AUTODETECT;
0172             irq_shutdown_and_deactivate(desc);
0173         }
0174         raw_spin_unlock_irq(&desc->lock);
0175     }
0176     mutex_unlock(&probing_active);
0177 
0178     if (nr_of_irqs > 1)
0179         irq_found = -irq_found;
0180 
0181     return irq_found;
0182 }
0183 EXPORT_SYMBOL(probe_irq_off);
0184