LCOV - code coverage report
Current view: top level - arch/x86/kernel - i8259.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 84 160 52.5 %
Date: 2022-03-28 16:04:14 Functions: 8 20 40.0 %
Branches: 13 24 54.2 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : #include <linux/linkage.h>
       3                 :            : #include <linux/errno.h>
       4                 :            : #include <linux/signal.h>
       5                 :            : #include <linux/sched.h>
       6                 :            : #include <linux/ioport.h>
       7                 :            : #include <linux/interrupt.h>
       8                 :            : #include <linux/irq.h>
       9                 :            : #include <linux/timex.h>
      10                 :            : #include <linux/random.h>
      11                 :            : #include <linux/init.h>
      12                 :            : #include <linux/kernel_stat.h>
      13                 :            : #include <linux/syscore_ops.h>
      14                 :            : #include <linux/bitops.h>
      15                 :            : #include <linux/acpi.h>
      16                 :            : #include <linux/io.h>
      17                 :            : #include <linux/delay.h>
      18                 :            : 
      19                 :            : #include <linux/atomic.h>
      20                 :            : #include <asm/timer.h>
      21                 :            : #include <asm/hw_irq.h>
      22                 :            : #include <asm/pgtable.h>
      23                 :            : #include <asm/desc.h>
      24                 :            : #include <asm/apic.h>
      25                 :            : #include <asm/i8259.h>
      26                 :            : 
      27                 :            : /*
      28                 :            :  * This is the 'legacy' 8259A Programmable Interrupt Controller,
      29                 :            :  * present in the majority of PC/AT boxes.
      30                 :            :  * plus some generic x86 specific things if generic specifics makes
      31                 :            :  * any sense at all.
      32                 :            :  */
      33                 :            : static void init_8259A(int auto_eoi);
      34                 :            : 
      35                 :            : static int i8259A_auto_eoi;
      36                 :            : DEFINE_RAW_SPINLOCK(i8259A_lock);
      37                 :            : 
      38                 :            : /*
      39                 :            :  * 8259A PIC functions to handle ISA devices:
      40                 :            :  */
      41                 :            : 
      42                 :            : /*
      43                 :            :  * This contains the irq mask for both 8259A irq controllers,
      44                 :            :  */
      45                 :            : unsigned int cached_irq_mask = 0xffff;
      46                 :            : 
      47                 :            : /*
      48                 :            :  * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
      49                 :            :  * boards the timer interrupt is not really connected to any IO-APIC pin,
      50                 :            :  * it's fed to the master 8259A's IR0 line only.
      51                 :            :  *
      52                 :            :  * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
      53                 :            :  * this 'mixed mode' IRQ handling costs nothing because it's only used
      54                 :            :  * at IRQ setup time.
      55                 :            :  */
      56                 :            : unsigned long io_apic_irqs;
      57                 :            : 
      58                 :        312 : static void mask_8259A_irq(unsigned int irq)
      59                 :            : {
      60                 :        312 :         unsigned int mask = 1 << irq;
      61                 :        312 :         unsigned long flags;
      62                 :            : 
      63                 :        312 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
      64                 :        312 :         cached_irq_mask |= mask;
      65         [ +  + ]:        312 :         if (irq & 8)
      66                 :        182 :                 outb(cached_slave_mask, PIC_SLAVE_IMR);
      67                 :            :         else
      68                 :        130 :                 outb(cached_master_mask, PIC_MASTER_IMR);
      69                 :        312 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
      70                 :        312 : }
      71                 :            : 
      72                 :          0 : static void disable_8259A_irq(struct irq_data *data)
      73                 :            : {
      74                 :          0 :         mask_8259A_irq(data->irq);
      75                 :          0 : }
      76                 :            : 
      77                 :         27 : static void unmask_8259A_irq(unsigned int irq)
      78                 :            : {
      79                 :         27 :         unsigned int mask = ~(1 << irq);
      80                 :         27 :         unsigned long flags;
      81                 :            : 
      82                 :         27 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
      83                 :         27 :         cached_irq_mask &= mask;
      84         [ -  + ]:         27 :         if (irq & 8)
      85                 :          0 :                 outb(cached_slave_mask, PIC_SLAVE_IMR);
      86                 :            :         else
      87                 :         27 :                 outb(cached_master_mask, PIC_MASTER_IMR);
      88                 :         27 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
      89                 :         27 : }
      90                 :            : 
      91                 :         27 : static void enable_8259A_irq(struct irq_data *data)
      92                 :            : {
      93                 :         27 :         unmask_8259A_irq(data->irq);
      94                 :         27 : }
      95                 :            : 
      96                 :        104 : static int i8259A_irq_pending(unsigned int irq)
      97                 :            : {
      98                 :        104 :         unsigned int mask = 1<<irq;
      99                 :        104 :         unsigned long flags;
     100                 :        104 :         int ret;
     101                 :            : 
     102                 :        104 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     103         [ +  + ]:        104 :         if (irq < 8)
     104                 :         26 :                 ret = inb(PIC_MASTER_CMD) & mask;
     105                 :            :         else
     106                 :         78 :                 ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
     107                 :        104 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     108                 :            : 
     109                 :        104 :         return ret;
     110                 :            : }
     111                 :            : 
     112                 :          0 : static void make_8259A_irq(unsigned int irq)
     113                 :            : {
     114                 :          0 :         disable_irq_nosync(irq);
     115                 :          0 :         io_apic_irqs &= ~(1<<irq);
     116                 :          0 :         irq_set_chip_and_handler(irq, &i8259A_chip, handle_level_irq);
     117                 :          0 :         enable_irq(irq);
     118                 :          0 :         lapic_assign_legacy_vector(irq, true);
     119                 :          0 : }
     120                 :            : 
     121                 :            : /*
     122                 :            :  * This function assumes to be called rarely. Switching between
     123                 :            :  * 8259A registers is slow.
     124                 :            :  * This has to be protected by the irq controller spinlock
     125                 :            :  * before being called.
     126                 :            :  */
     127                 :          0 : static inline int i8259A_irq_real(unsigned int irq)
     128                 :            : {
     129                 :          0 :         int value;
     130                 :          0 :         int irqmask = 1<<irq;
     131                 :            : 
     132                 :          0 :         if (irq < 8) {
     133                 :          0 :                 outb(0x0B, PIC_MASTER_CMD);     /* ISR register */
     134                 :          0 :                 value = inb(PIC_MASTER_CMD) & irqmask;
     135                 :          0 :                 outb(0x0A, PIC_MASTER_CMD);     /* back to the IRR register */
     136                 :          0 :                 return value;
     137                 :            :         }
     138                 :          0 :         outb(0x0B, PIC_SLAVE_CMD);      /* ISR register */
     139                 :          0 :         value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
     140                 :          0 :         outb(0x0A, PIC_SLAVE_CMD);      /* back to the IRR register */
     141                 :          0 :         return value;
     142                 :            : }
     143                 :            : 
     144                 :            : /*
     145                 :            :  * Careful! The 8259A is a fragile beast, it pretty
     146                 :            :  * much _has_ to be done exactly like this (mask it
     147                 :            :  * first, _then_ send the EOI, and the order of EOI
     148                 :            :  * to the two 8259s is important!
     149                 :            :  */
     150                 :         14 : static void mask_and_ack_8259A(struct irq_data *data)
     151                 :            : {
     152                 :         14 :         unsigned int irq = data->irq;
     153                 :         14 :         unsigned int irqmask = 1 << irq;
     154                 :         14 :         unsigned long flags;
     155                 :            : 
     156                 :         14 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     157                 :            :         /*
     158                 :            :          * Lightweight spurious IRQ detection. We do not want
     159                 :            :          * to overdo spurious IRQ handling - it's usually a sign
     160                 :            :          * of hardware problems, so we only do the checks we can
     161                 :            :          * do without slowing down good hardware unnecessarily.
     162                 :            :          *
     163                 :            :          * Note that IRQ7 and IRQ15 (the two spurious IRQs
     164                 :            :          * usually resulting from the 8259A-1|2 PICs) occur
     165                 :            :          * even if the IRQ is masked in the 8259A. Thus we
     166                 :            :          * can check spurious 8259A IRQs without doing the
     167                 :            :          * quite slow i8259A_irq_real() call for every IRQ.
     168                 :            :          * This does not cover 100% of spurious interrupts,
     169                 :            :          * but should be enough to warn the user that there
     170                 :            :          * is something bad going on ...
     171                 :            :          */
     172         [ -  + ]:         14 :         if (cached_irq_mask & irqmask)
     173                 :          0 :                 goto spurious_8259A_irq;
     174                 :         14 :         cached_irq_mask |= irqmask;
     175                 :            : 
     176                 :         14 : handle_real_irq:
     177         [ -  + ]:         14 :         if (irq & 8) {
     178                 :          0 :                 inb(PIC_SLAVE_IMR);     /* DUMMY - (do we need this?) */
     179                 :          0 :                 outb(cached_slave_mask, PIC_SLAVE_IMR);
     180                 :            :                 /* 'Specific EOI' to slave */
     181                 :          0 :                 outb(0x60+(irq&7), PIC_SLAVE_CMD);
     182                 :            :                  /* 'Specific EOI' to master-IRQ2 */
     183                 :          0 :                 outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD);
     184                 :            :         } else {
     185                 :         14 :                 inb(PIC_MASTER_IMR);    /* DUMMY - (do we need this?) */
     186                 :         14 :                 outb(cached_master_mask, PIC_MASTER_IMR);
     187                 :         14 :                 outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
     188                 :            :         }
     189                 :         14 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     190                 :         14 :         return;
     191                 :            : 
     192                 :            : spurious_8259A_irq:
     193                 :            :         /*
     194                 :            :          * this is the slow path - should happen rarely.
     195                 :            :          */
     196   [ #  #  #  # ]:          0 :         if (i8259A_irq_real(irq))
     197                 :            :                 /*
     198                 :            :                  * oops, the IRQ _is_ in service according to the
     199                 :            :                  * 8259A - not spurious, go handle it.
     200                 :            :                  */
     201                 :          0 :                 goto handle_real_irq;
     202                 :            : 
     203                 :            :         {
     204                 :          0 :                 static int spurious_irq_mask;
     205                 :            :                 /*
     206                 :            :                  * At this point we can be sure the IRQ is spurious,
     207                 :            :                  * lets ACK and report it. [once per IRQ]
     208                 :            :                  */
     209         [ #  # ]:          0 :                 if (!(spurious_irq_mask & irqmask)) {
     210                 :          0 :                         printk(KERN_DEBUG
     211                 :            :                                "spurious 8259A interrupt: IRQ%d.\n", irq);
     212                 :          0 :                         spurious_irq_mask |= irqmask;
     213                 :            :                 }
     214                 :          0 :                 atomic_inc(&irq_err_count);
     215                 :            :                 /*
     216                 :            :                  * Theoretically we do not have to handle this IRQ,
     217                 :            :                  * but in Linux this does not cause problems and is
     218                 :            :                  * simpler for us.
     219                 :            :                  */
     220                 :          0 :                 goto handle_real_irq;
     221                 :            :         }
     222                 :            : }
     223                 :            : 
     224                 :            : struct irq_chip i8259A_chip = {
     225                 :            :         .name           = "XT-PIC",
     226                 :            :         .irq_mask       = disable_8259A_irq,
     227                 :            :         .irq_disable    = disable_8259A_irq,
     228                 :            :         .irq_unmask     = enable_8259A_irq,
     229                 :            :         .irq_mask_ack   = mask_and_ack_8259A,
     230                 :            : };
     231                 :            : 
     232                 :            : static char irq_trigger[2];
     233                 :            : /**
     234                 :            :  * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ
     235                 :            :  */
     236                 :          0 : static void restore_ELCR(char *trigger)
     237                 :            : {
     238                 :          0 :         outb(trigger[0], 0x4d0);
     239                 :          0 :         outb(trigger[1], 0x4d1);
     240                 :            : }
     241                 :            : 
     242                 :          0 : static void save_ELCR(char *trigger)
     243                 :            : {
     244                 :            :         /* IRQ 0,1,2,8,13 are marked as reserved */
     245                 :          0 :         trigger[0] = inb(0x4d0) & 0xF8;
     246                 :          0 :         trigger[1] = inb(0x4d1) & 0xDE;
     247                 :            : }
     248                 :            : 
     249                 :          0 : static void i8259A_resume(void)
     250                 :            : {
     251                 :          0 :         init_8259A(i8259A_auto_eoi);
     252                 :          0 :         restore_ELCR(irq_trigger);
     253                 :          0 : }
     254                 :            : 
     255                 :          0 : static int i8259A_suspend(void)
     256                 :            : {
     257                 :          0 :         save_ELCR(irq_trigger);
     258                 :          0 :         return 0;
     259                 :            : }
     260                 :            : 
     261                 :          0 : static void i8259A_shutdown(void)
     262                 :            : {
     263                 :            :         /* Put the i8259A into a quiescent state that
     264                 :            :          * the kernel initialization code can get it
     265                 :            :          * out of.
     266                 :            :          */
     267                 :          0 :         outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
     268                 :          0 :         outb(0xff, PIC_SLAVE_IMR);      /* mask all of 8259A-2 */
     269                 :          0 : }
     270                 :            : 
     271                 :            : static struct syscore_ops i8259_syscore_ops = {
     272                 :            :         .suspend = i8259A_suspend,
     273                 :            :         .resume = i8259A_resume,
     274                 :            :         .shutdown = i8259A_shutdown,
     275                 :            : };
     276                 :            : 
     277                 :          0 : static void mask_8259A(void)
     278                 :            : {
     279                 :          0 :         unsigned long flags;
     280                 :            : 
     281                 :          0 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     282                 :            : 
     283                 :          0 :         outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
     284                 :          0 :         outb(0xff, PIC_SLAVE_IMR);      /* mask all of 8259A-2 */
     285                 :            : 
     286                 :          0 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     287                 :          0 : }
     288                 :            : 
     289                 :          0 : static void unmask_8259A(void)
     290                 :            : {
     291                 :          0 :         unsigned long flags;
     292                 :            : 
     293                 :          0 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     294                 :            : 
     295                 :          0 :         outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
     296                 :          0 :         outb(cached_slave_mask, PIC_SLAVE_IMR);   /* restore slave IRQ mask */
     297                 :            : 
     298                 :          0 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     299                 :          0 : }
     300                 :            : 
     301                 :         13 : static int probe_8259A(void)
     302                 :            : {
     303                 :         13 :         unsigned long flags;
     304                 :         13 :         unsigned char probe_val = ~(1 << PIC_CASCADE_IR);
     305                 :         13 :         unsigned char new_val;
     306                 :            :         /*
     307                 :            :          * Check to see if we have a PIC.
     308                 :            :          * Mask all except the cascade and read
     309                 :            :          * back the value we just wrote. If we don't
     310                 :            :          * have a PIC, we will read 0xff as opposed to the
     311                 :            :          * value we wrote.
     312                 :            :          */
     313                 :         13 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     314                 :            : 
     315                 :         13 :         outb(0xff, PIC_SLAVE_IMR);      /* mask all of 8259A-2 */
     316                 :         13 :         outb(probe_val, PIC_MASTER_IMR);
     317                 :         13 :         new_val = inb(PIC_MASTER_IMR);
     318         [ -  + ]:         13 :         if (new_val != probe_val) {
     319                 :          0 :                 printk(KERN_INFO "Using NULL legacy PIC\n");
     320                 :          0 :                 legacy_pic = &null_legacy_pic;
     321                 :            :         }
     322                 :            : 
     323                 :         13 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     324                 :         13 :         return nr_legacy_irqs();
     325                 :            : }
     326                 :            : 
     327                 :         26 : static void init_8259A(int auto_eoi)
     328                 :            : {
     329                 :         26 :         unsigned long flags;
     330                 :            : 
     331                 :         26 :         i8259A_auto_eoi = auto_eoi;
     332                 :            : 
     333                 :         26 :         raw_spin_lock_irqsave(&i8259A_lock, flags);
     334                 :            : 
     335                 :         26 :         outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
     336                 :            : 
     337                 :            :         /*
     338                 :            :          * outb_pic - this has to work on a wide range of PC hardware.
     339                 :            :          */
     340                 :         26 :         outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
     341                 :            : 
     342                 :            :         /* ICW2: 8259A-1 IR0-7 mapped to ISA_IRQ_VECTOR(0) */
     343                 :         26 :         outb_pic(ISA_IRQ_VECTOR(0), PIC_MASTER_IMR);
     344                 :            : 
     345                 :            :         /* 8259A-1 (the master) has a slave on IR2 */
     346                 :         26 :         outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);
     347                 :            : 
     348         [ +  + ]:         26 :         if (auto_eoi)   /* master does Auto EOI */
     349                 :         13 :                 outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
     350                 :            :         else            /* master expects normal EOI */
     351                 :         13 :                 outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
     352                 :            : 
     353                 :         26 :         outb_pic(0x11, PIC_SLAVE_CMD);  /* ICW1: select 8259A-2 init */
     354                 :            : 
     355                 :            :         /* ICW2: 8259A-2 IR0-7 mapped to ISA_IRQ_VECTOR(8) */
     356                 :         26 :         outb_pic(ISA_IRQ_VECTOR(8), PIC_SLAVE_IMR);
     357                 :            :         /* 8259A-2 is a slave on master's IR2 */
     358                 :         26 :         outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
     359                 :            :         /* (slave's support for AEOI in flat mode is to be investigated) */
     360                 :         26 :         outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
     361                 :            : 
     362         [ +  + ]:         26 :         if (auto_eoi)
     363                 :            :                 /*
     364                 :            :                  * In AEOI mode we just have to mask the interrupt
     365                 :            :                  * when acking.
     366                 :            :                  */
     367                 :         13 :                 i8259A_chip.irq_mask_ack = disable_8259A_irq;
     368                 :            :         else
     369                 :         13 :                 i8259A_chip.irq_mask_ack = mask_and_ack_8259A;
     370                 :            : 
     371                 :         26 :         udelay(100);            /* wait for 8259A to initialize */
     372                 :            : 
     373                 :         26 :         outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
     374                 :         26 :         outb(cached_slave_mask, PIC_SLAVE_IMR);   /* restore slave IRQ mask */
     375                 :            : 
     376                 :         26 :         raw_spin_unlock_irqrestore(&i8259A_lock, flags);
     377                 :         26 : }
     378                 :            : 
     379                 :            : /*
     380                 :            :  * make i8259 a driver so that we can select pic functions at run time. the goal
     381                 :            :  * is to make x86 binary compatible among pc compatible and non-pc compatible
     382                 :            :  * platforms, such as x86 MID.
     383                 :            :  */
     384                 :            : 
     385                 :          0 : static void legacy_pic_noop(void) { };
     386                 :          0 : static void legacy_pic_uint_noop(unsigned int unused) { };
     387                 :          0 : static void legacy_pic_int_noop(int unused) { };
     388                 :          0 : static int legacy_pic_irq_pending_noop(unsigned int irq)
     389                 :            : {
     390                 :          0 :         return 0;
     391                 :            : }
     392                 :          0 : static int legacy_pic_probe(void)
     393                 :            : {
     394                 :          0 :         return 0;
     395                 :            : }
     396                 :            : 
     397                 :            : struct legacy_pic null_legacy_pic = {
     398                 :            :         .nr_legacy_irqs = 0,
     399                 :            :         .chip = &dummy_irq_chip,
     400                 :            :         .mask = legacy_pic_uint_noop,
     401                 :            :         .unmask = legacy_pic_uint_noop,
     402                 :            :         .mask_all = legacy_pic_noop,
     403                 :            :         .restore_mask = legacy_pic_noop,
     404                 :            :         .init = legacy_pic_int_noop,
     405                 :            :         .probe = legacy_pic_probe,
     406                 :            :         .irq_pending = legacy_pic_irq_pending_noop,
     407                 :            :         .make_irq = legacy_pic_uint_noop,
     408                 :            : };
     409                 :            : 
     410                 :            : struct legacy_pic default_legacy_pic = {
     411                 :            :         .nr_legacy_irqs = NR_IRQS_LEGACY,
     412                 :            :         .chip  = &i8259A_chip,
     413                 :            :         .mask = mask_8259A_irq,
     414                 :            :         .unmask = unmask_8259A_irq,
     415                 :            :         .mask_all = mask_8259A,
     416                 :            :         .restore_mask = unmask_8259A,
     417                 :            :         .init = init_8259A,
     418                 :            :         .probe = probe_8259A,
     419                 :            :         .irq_pending = i8259A_irq_pending,
     420                 :            :         .make_irq = make_8259A_irq,
     421                 :            : };
     422                 :            : 
     423                 :            : struct legacy_pic *legacy_pic = &default_legacy_pic;
     424                 :            : EXPORT_SYMBOL(legacy_pic);
     425                 :            : 
     426                 :         13 : static int __init i8259A_init_ops(void)
     427                 :            : {
     428         [ +  - ]:         13 :         if (legacy_pic == &default_legacy_pic)
     429                 :         13 :                 register_syscore_ops(&i8259_syscore_ops);
     430                 :            : 
     431                 :         13 :         return 0;
     432                 :            : }
     433                 :            : 
     434                 :            : device_initcall(i8259A_init_ops);

Generated by: LCOV version 1.14