linux/arch/alpha/kernel/sys_cabriolet.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *      linux/arch/alpha/kernel/sys_cabriolet.c
   4 *
   5 *      Copyright (C) 1995 David A Rusling
   6 *      Copyright (C) 1996 Jay A Estabrook
   7 *      Copyright (C) 1998, 1999, 2000 Richard Henderson
   8 *
   9 * Code supporting the Cabriolet (AlphaPC64), EB66+, and EB164,
  10 * PC164 and LX164.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/types.h>
  15#include <linux/mm.h>
  16#include <linux/sched.h>
  17#include <linux/pci.h>
  18#include <linux/init.h>
  19#include <linux/bitops.h>
  20
  21#include <asm/ptrace.h>
  22#include <asm/dma.h>
  23#include <asm/irq.h>
  24#include <asm/mmu_context.h>
  25#include <asm/io.h>
  26#include <asm/pgtable.h>
  27#include <asm/core_apecs.h>
  28#include <asm/core_cia.h>
  29#include <asm/core_lca.h>
  30#include <asm/tlbflush.h>
  31
  32#include "proto.h"
  33#include "irq_impl.h"
  34#include "pci_impl.h"
  35#include "machvec_impl.h"
  36#include "pc873xx.h"
  37
  38/* Note mask bit is true for DISABLED irqs.  */
  39static unsigned long cached_irq_mask = ~0UL;
  40
  41static inline void
  42cabriolet_update_irq_hw(unsigned int irq, unsigned long mask)
  43{
  44        int ofs = (irq - 16) / 8;
  45        outb(mask >> (16 + ofs * 8), 0x804 + ofs);
  46}
  47
  48static inline void
  49cabriolet_enable_irq(struct irq_data *d)
  50{
  51        cabriolet_update_irq_hw(d->irq, cached_irq_mask &= ~(1UL << d->irq));
  52}
  53
  54static void
  55cabriolet_disable_irq(struct irq_data *d)
  56{
  57        cabriolet_update_irq_hw(d->irq, cached_irq_mask |= 1UL << d->irq);
  58}
  59
  60static struct irq_chip cabriolet_irq_type = {
  61        .name           = "CABRIOLET",
  62        .irq_unmask     = cabriolet_enable_irq,
  63        .irq_mask       = cabriolet_disable_irq,
  64        .irq_mask_ack   = cabriolet_disable_irq,
  65};
  66
  67static void 
  68cabriolet_device_interrupt(unsigned long v)
  69{
  70        unsigned long pld;
  71        unsigned int i;
  72
  73        /* Read the interrupt summary registers */
  74        pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16);
  75
  76        /*
  77         * Now for every possible bit set, work through them and call
  78         * the appropriate interrupt handler.
  79         */
  80        while (pld) {
  81                i = ffz(~pld);
  82                pld &= pld - 1; /* clear least bit set */
  83                if (i == 4) {
  84                        isa_device_interrupt(v);
  85                } else {
  86                        handle_irq(16 + i);
  87                }
  88        }
  89}
  90
  91static void __init
  92common_init_irq(void (*srm_dev_int)(unsigned long v))
  93{
  94        init_i8259a_irqs();
  95
  96        if (alpha_using_srm) {
  97                alpha_mv.device_interrupt = srm_dev_int;
  98                init_srm_irqs(35, 0);
  99        }
 100        else {
 101                long i;
 102
 103                outb(0xff, 0x804);
 104                outb(0xff, 0x805);
 105                outb(0xff, 0x806);
 106
 107                for (i = 16; i < 35; ++i) {
 108                        irq_set_chip_and_handler(i, &cabriolet_irq_type,
 109                                                 handle_level_irq);
 110                        irq_set_status_flags(i, IRQ_LEVEL);
 111                }
 112        }
 113
 114        common_init_isa_dma();
 115        setup_irq(16+4, &isa_cascade_irqaction);
 116}
 117
 118#ifndef CONFIG_ALPHA_PC164
 119static void __init
 120cabriolet_init_irq(void)
 121{
 122        common_init_irq(srm_device_interrupt);
 123}
 124#endif
 125
 126#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PC164)
 127/* In theory, the PC164 has the same interrupt hardware as the other
 128   Cabriolet based systems.  However, something got screwed up late
 129   in the development cycle which broke the interrupt masking hardware.
 130   Repeat, it is not possible to mask and ack interrupts.  At all.
 131
 132   In an attempt to work around this, while processing interrupts,
 133   we do not allow the IPL to drop below what it is currently.  This
 134   prevents the possibility of recursion.  
 135
 136   ??? Another option might be to force all PCI devices to use edge
 137   triggered rather than level triggered interrupts.  That might be
 138   too invasive though.  */
 139
 140static void
 141pc164_srm_device_interrupt(unsigned long v)
 142{
 143        __min_ipl = getipl();
 144        srm_device_interrupt(v);
 145        __min_ipl = 0;
 146}
 147
 148static void
 149pc164_device_interrupt(unsigned long v)
 150{
 151        __min_ipl = getipl();
 152        cabriolet_device_interrupt(v);
 153        __min_ipl = 0;
 154}
 155
 156static void __init
 157pc164_init_irq(void)
 158{
 159        common_init_irq(pc164_srm_device_interrupt);
 160}
 161#endif
 162
 163/*
 164 * The EB66+ is very similar to the EB66 except that it does not have
 165 * the on-board NCR and Tulip chips.  In the code below, I have used
 166 * slot number to refer to the id select line and *not* the slot
 167 * number used in the EB66+ documentation.  However, in the table,
 168 * I've given the slot number, the id select line and the Jxx number
 169 * that's printed on the board.  The interrupt pins from the PCI slots
 170 * are wired into 3 interrupt summary registers at 0x804, 0x805 and
 171 * 0x806 ISA.
 172 *
 173 * In the table, -1 means don't assign an IRQ number.  This is usually
 174 * because it is the Saturn IO (SIO) PCI/ISA Bridge Chip.
 175 */
 176
 177static inline int
 178eb66p_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 179{
 180        static char irq_tab[5][5] = {
 181                /*INT  INTA  INTB  INTC   INTD */
 182                {16+0, 16+0, 16+5,  16+9, 16+13},  /* IdSel 6,  slot 0, J25 */
 183                {16+1, 16+1, 16+6, 16+10, 16+14},  /* IdSel 7,  slot 1, J26 */
 184                {  -1,   -1,   -1,    -1,    -1},  /* IdSel 8,  SIO         */
 185                {16+2, 16+2, 16+7, 16+11, 16+15},  /* IdSel 9,  slot 2, J27 */
 186                {16+3, 16+3, 16+8, 16+12,  16+6}   /* IdSel 10, slot 3, J28 */
 187        };
 188        const long min_idsel = 6, max_idsel = 10, irqs_per_slot = 5;
 189        return COMMON_TABLE_LOOKUP;
 190}
 191
 192
 193/*
 194 * The AlphaPC64 is very similar to the EB66+ except that its slots
 195 * are numbered differently.  In the code below, I have used slot
 196 * number to refer to the id select line and *not* the slot number
 197 * used in the AlphaPC64 documentation.  However, in the table, I've
 198 * given the slot number, the id select line and the Jxx number that's
 199 * printed on the board.  The interrupt pins from the PCI slots are
 200 * wired into 3 interrupt summary registers at 0x804, 0x805 and 0x806
 201 * ISA.
 202 *
 203 * In the table, -1 means don't assign an IRQ number.  This is usually
 204 * because it is the Saturn IO (SIO) PCI/ISA Bridge Chip.
 205 */
 206
 207static inline int
 208cabriolet_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 209{
 210        static char irq_tab[5][5] = {
 211                /*INT   INTA  INTB  INTC   INTD */
 212                { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5,  slot 2, J21 */
 213                { 16+0, 16+0, 16+5,  16+9, 16+13}, /* IdSel 6,  slot 0, J19 */
 214                { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7,  slot 1, J20 */
 215                {   -1,   -1,   -1,    -1,    -1}, /* IdSel 8,  SIO         */
 216                { 16+3, 16+3, 16+8, 16+12, 16+16}  /* IdSel 9,  slot 3, J22 */
 217        };
 218        const long min_idsel = 5, max_idsel = 9, irqs_per_slot = 5;
 219        return COMMON_TABLE_LOOKUP;
 220}
 221
 222static inline void __init
 223cabriolet_enable_ide(void)
 224{
 225        if (pc873xx_probe() == -1) {
 226                printk(KERN_ERR "Probing for PC873xx Super IO chip failed.\n");
 227         } else {
 228                printk(KERN_INFO "Found %s Super IO chip at 0x%x\n",
 229                        pc873xx_get_model(), pc873xx_get_base());
 230
 231                pc873xx_enable_ide();
 232        }
 233}
 234
 235static inline void __init
 236cabriolet_init_pci(void)
 237{
 238        common_init_pci();
 239        cabriolet_enable_ide();
 240}
 241
 242static inline void __init
 243cia_cab_init_pci(void)
 244{
 245        cia_init_pci();
 246        cabriolet_enable_ide();
 247}
 248
 249/*
 250 * The PC164 and LX164 have 19 PCI interrupts, four from each of the four
 251 * PCI slots, the SIO, PCI/IDE, and USB.
 252 * 
 253 * Each of the interrupts can be individually masked. This is
 254 * accomplished by setting the appropriate bit in the mask register.
 255 * A bit is set by writing a "1" to the desired position in the mask
 256 * register and cleared by writing a "0". There are 3 mask registers
 257 * located at ISA address 804h, 805h and 806h.
 258 * 
 259 * An I/O read at ISA address 804h, 805h, 806h will return the
 260 * state of the 11 PCI interrupts and not the state of the MASKED
 261 * interrupts.
 262 * 
 263 * Note: A write to I/O 804h, 805h, and 806h the mask register will be
 264 * updated.
 265 * 
 266 * 
 267 *                              ISA DATA<7:0>
 268 * ISA     +--------------------------------------------------------------+
 269 * ADDRESS |   7   |   6   |   5   |   4   |   3   |   2  |   1   |   0   |
 270 *         +==============================================================+
 271 * 0x804   | INTB0 |  USB  |  IDE  |  SIO  | INTA3 |INTA2 | INTA1 | INTA0 |
 272 *         +--------------------------------------------------------------+
 273 * 0x805   | INTD0 | INTC3 | INTC2 | INTC1 | INTC0 |INTB3 | INTB2 | INTB1 |
 274 *         +--------------------------------------------------------------+
 275 * 0x806   | Rsrv  | Rsrv  | Rsrv  | Rsrv  | Rsrv  |INTD3 | INTD2 | INTD1 |
 276 *         +--------------------------------------------------------------+
 277 *         * Rsrv = reserved bits
 278 *         Note: The mask register is write-only.
 279 * 
 280 * IdSel        
 281 *   5   32 bit PCI option slot 2
 282 *   6   64 bit PCI option slot 0
 283 *   7   64 bit PCI option slot 1
 284 *   8   Saturn I/O
 285 *   9   32 bit PCI option slot 3
 286 *  10   USB
 287 *  11   IDE
 288 * 
 289 */
 290
 291static inline int
 292alphapc164_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 293{
 294        static char irq_tab[7][5] = {
 295                /*INT   INTA  INTB   INTC   INTD */
 296                { 16+2, 16+2, 16+9,  16+13, 16+17}, /* IdSel  5, slot 2, J20 */
 297                { 16+0, 16+0, 16+7,  16+11, 16+15}, /* IdSel  6, slot 0, J29 */
 298                { 16+1, 16+1, 16+8,  16+12, 16+16}, /* IdSel  7, slot 1, J26 */
 299                {   -1,   -1,   -1,    -1,    -1},  /* IdSel  8, SIO */
 300                { 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel  9, slot 3, J19 */
 301                { 16+6, 16+6, 16+6,  16+6,  16+6},  /* IdSel 10, USB */
 302                { 16+5, 16+5, 16+5,  16+5,  16+5}   /* IdSel 11, IDE */
 303        };
 304        const long min_idsel = 5, max_idsel = 11, irqs_per_slot = 5;
 305        return COMMON_TABLE_LOOKUP;
 306}
 307
 308static inline void __init
 309alphapc164_init_pci(void)
 310{
 311        cia_init_pci();
 312        SMC93x_Init();
 313}
 314
 315
 316/*
 317 * The System Vector
 318 */
 319
 320#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_CABRIOLET)
 321struct alpha_machine_vector cabriolet_mv __initmv = {
 322        .vector_name            = "Cabriolet",
 323        DO_EV4_MMU,
 324        DO_DEFAULT_RTC,
 325        DO_APECS_IO,
 326        .machine_check          = apecs_machine_check,
 327        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
 328        .min_io_address         = DEFAULT_IO_BASE,
 329        .min_mem_address        = APECS_AND_LCA_DEFAULT_MEM_BASE,
 330
 331        .nr_irqs                = 35,
 332        .device_interrupt       = cabriolet_device_interrupt,
 333
 334        .init_arch              = apecs_init_arch,
 335        .init_irq               = cabriolet_init_irq,
 336        .init_rtc               = common_init_rtc,
 337        .init_pci               = cabriolet_init_pci,
 338        .pci_map_irq            = cabriolet_map_irq,
 339        .pci_swizzle            = common_swizzle,
 340};
 341#ifndef CONFIG_ALPHA_EB64P
 342ALIAS_MV(cabriolet)
 343#endif
 344#endif
 345
 346#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB164)
 347struct alpha_machine_vector eb164_mv __initmv = {
 348        .vector_name            = "EB164",
 349        DO_EV5_MMU,
 350        DO_DEFAULT_RTC,
 351        DO_CIA_IO,
 352        .machine_check          = cia_machine_check,
 353        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
 354        .min_io_address         = DEFAULT_IO_BASE,
 355        .min_mem_address        = CIA_DEFAULT_MEM_BASE,
 356
 357        .nr_irqs                = 35,
 358        .device_interrupt       = cabriolet_device_interrupt,
 359
 360        .init_arch              = cia_init_arch,
 361        .init_irq               = cabriolet_init_irq,
 362        .init_rtc               = common_init_rtc,
 363        .init_pci               = cia_cab_init_pci,
 364        .kill_arch              = cia_kill_arch,
 365        .pci_map_irq            = cabriolet_map_irq,
 366        .pci_swizzle            = common_swizzle,
 367};
 368ALIAS_MV(eb164)
 369#endif
 370
 371#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB66P)
 372struct alpha_machine_vector eb66p_mv __initmv = {
 373        .vector_name            = "EB66+",
 374        DO_EV4_MMU,
 375        DO_DEFAULT_RTC,
 376        DO_LCA_IO,
 377        .machine_check          = lca_machine_check,
 378        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
 379        .min_io_address         = DEFAULT_IO_BASE,
 380        .min_mem_address        = APECS_AND_LCA_DEFAULT_MEM_BASE,
 381
 382        .nr_irqs                = 35,
 383        .device_interrupt       = cabriolet_device_interrupt,
 384
 385        .init_arch              = lca_init_arch,
 386        .init_irq               = cabriolet_init_irq,
 387        .init_rtc               = common_init_rtc,
 388        .init_pci               = cabriolet_init_pci,
 389        .pci_map_irq            = eb66p_map_irq,
 390        .pci_swizzle            = common_swizzle,
 391};
 392ALIAS_MV(eb66p)
 393#endif
 394
 395#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_LX164)
 396struct alpha_machine_vector lx164_mv __initmv = {
 397        .vector_name            = "LX164",
 398        DO_EV5_MMU,
 399        DO_DEFAULT_RTC,
 400        DO_PYXIS_IO,
 401        .machine_check          = cia_machine_check,
 402        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
 403        .min_io_address         = DEFAULT_IO_BASE,
 404        .min_mem_address        = DEFAULT_MEM_BASE,
 405        .pci_dac_offset         = PYXIS_DAC_OFFSET,
 406
 407        .nr_irqs                = 35,
 408        .device_interrupt       = cabriolet_device_interrupt,
 409
 410        .init_arch              = pyxis_init_arch,
 411        .init_irq               = cabriolet_init_irq,
 412        .init_rtc               = common_init_rtc,
 413        .init_pci               = alphapc164_init_pci,
 414        .kill_arch              = cia_kill_arch,
 415        .pci_map_irq            = alphapc164_map_irq,
 416        .pci_swizzle            = common_swizzle,
 417};
 418ALIAS_MV(lx164)
 419#endif
 420
 421#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PC164)
 422struct alpha_machine_vector pc164_mv __initmv = {
 423        .vector_name            = "PC164",
 424        DO_EV5_MMU,
 425        DO_DEFAULT_RTC,
 426        DO_CIA_IO,
 427        .machine_check          = cia_machine_check,
 428        .max_isa_dma_address    = ALPHA_MAX_ISA_DMA_ADDRESS,
 429        .min_io_address         = DEFAULT_IO_BASE,
 430        .min_mem_address        = CIA_DEFAULT_MEM_BASE,
 431
 432        .nr_irqs                = 35,
 433        .device_interrupt       = pc164_device_interrupt,
 434
 435        .init_arch              = cia_init_arch,
 436        .init_irq               = pc164_init_irq,
 437        .init_rtc               = common_init_rtc,
 438        .init_pci               = alphapc164_init_pci,
 439        .kill_arch              = cia_kill_arch,
 440        .pci_map_irq            = alphapc164_map_irq,
 441        .pci_swizzle            = common_swizzle,
 442};
 443ALIAS_MV(pc164)
 444#endif
 445