uboot/board/mpl/common/isa.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2001
   3 * Denis Peter, MPL AG Switzerland
   4 *
   5 * See file CREDITS for list of people who contributed to this
   6 * project.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of
  11 * the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21 * MA 02111-1307 USA
  22 *
  23 *
  24 * TODO: clean-up
  25 */
  26
  27#include <common.h>
  28#include <asm/processor.h>
  29#include <stdio_dev.h>
  30#include "isa.h"
  31#include "piix4_pci.h"
  32#include "kbd.h"
  33#include "video.h"
  34
  35
  36#undef  ISA_DEBUG
  37
  38#ifdef  ISA_DEBUG
  39#define PRINTF(fmt,args...)     printf (fmt ,##args)
  40#else
  41#define PRINTF(fmt,args...)
  42#endif
  43
  44#ifndef TRUE
  45#define TRUE            1
  46#endif
  47#ifndef FALSE
  48#define FALSE           0
  49#endif
  50
  51#if defined(CONFIG_PIP405)
  52
  53extern int drv_isa_kbd_init (void);
  54
  55/* fdc (logical device 0) */
  56const SIO_LOGDEV_TABLE sio_fdc[] = {
  57        {0x60, 3},                      /* set IO to FDPort (3F0) */
  58        {0x61, 0xF0},           /* set IO to FDPort (3F0) */
  59        {0x70, 06},                     /* set IRQ 6 for FDPort */
  60        {0x74, 02},                     /* set DMA 2 for FDPort */
  61        {0xF0, 0x05},           /* set to PS2 type */
  62        {0xF1, 0x00},     /* default value */
  63        {0x30, 1},                      /* and activate the device */
  64        {0xFF, 0}                               /* end of device table */
  65};
  66/* paralell port (logical device 3) */
  67const SIO_LOGDEV_TABLE sio_pport[] = {
  68        {0x60, 3},                      /* set IO to PPort (378) */
  69        {0x61, 0x78},           /* set IO to PPort (378) */
  70        {0x70, 07},                     /* set IRQ 7 for PPort */
  71        {0xF1, 00},                     /* set PPort to normal */
  72        {0x30, 1},                      /* and activate the device */
  73        {0xFF, 0}                               /* end of device table */
  74};
  75/* paralell port (logical device 3) Floppy assigned to lpt */
  76const SIO_LOGDEV_TABLE sio_pport_fdc[] = {
  77        {0x60, 3},                      /* set IO to PPort (378) */
  78        {0x61, 0x78},           /* set IO to PPort (378) */
  79        {0x70, 07},                     /* set IRQ 7 for PPort */
  80        {0xF1, 02},                     /* set PPort to Floppy */
  81        {0x30, 1},                      /* and activate the device */
  82        {0xFF, 0}                               /* end of device table */
  83};
  84/* uart 1 (logical device 4) */
  85const SIO_LOGDEV_TABLE sio_com1[] = {
  86        {0x60, 3},                      /* set IO to COM1 (3F8) */
  87        {0x61, 0xF8},           /* set IO to COM1 (3F8) */
  88        {0x70, 04},                     /* set IRQ 4 for COM1 */
  89        {0x30, 1},                      /* and activate the device */
  90        {0xFF, 0}                               /* end of device table */
  91};
  92/* uart 2 (logical device 5) */
  93const SIO_LOGDEV_TABLE sio_com2[] = {
  94        {0x60, 2},                      /* set IO to COM2 (2F8) */
  95        {0x61, 0xF8},           /* set IO to COM2 (2F8) */
  96        {0x70, 03},                     /* set IRQ 3 for COM2 */
  97        {0x30, 1},                      /* and activate the device */
  98        {0xFF, 0}                               /* end of device table */
  99};
 100
 101/* keyboard controller (logical device 7) */
 102const SIO_LOGDEV_TABLE sio_keyboard[] = {
 103        {0x70, 1},                      /* set IRQ 1 for keyboard */
 104        {0x72, 12},                     /* set IRQ 12 for mouse */
 105        {0xF0, 0},                      /* disable Port92 (this is a PowerPC!!) */
 106        {0x30, 1},                      /* and activate the device */
 107        {0xFF, 0}                               /* end of device table */
 108};
 109
 110
 111/*******************************************************************************
 112* Config SuperIO FDC37C672
 113********************************************************************************/
 114unsigned char open_cfg_super_IO(int address)
 115{
 116        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,0x55); /* open config */
 117        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,0x20); /* set address to DEV ID */
 118        if(in8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address | 0x1)==0x40) /* ok Device ID is correct */
 119                return TRUE;
 120        else
 121                return FALSE;
 122}
 123
 124void close_cfg_super_IO(int address)
 125{
 126        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,0xAA); /* close config */
 127}
 128
 129
 130unsigned char read_cfg_super_IO(int address, unsigned char function, unsigned char regaddr)
 131{
 132        /* assuming config reg is open */
 133        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,0x7); /* points to the function reg */
 134        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address | 1,function); /* set the function no */
 135        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,regaddr); /* sets the address in the function */
 136        return in8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address | 1);
 137}
 138
 139void write_cfg_super_IO(int address, unsigned char function, unsigned char regaddr, unsigned char data)
 140{
 141        /* assuming config reg is open */
 142        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,0x7); /* points to the function reg */
 143        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address | 1,function); /* set the function no */
 144        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address,regaddr); /* sets the address in the function */
 145        out8(CONFIG_SYS_ISA_IO_BASE_ADDRESS | address | 1,data); /* writes the data */
 146}
 147
 148void isa_write_table(SIO_LOGDEV_TABLE *ldt,unsigned char ldev)
 149{
 150        while (ldt->index != 0xFF) {
 151                write_cfg_super_IO(SIO_CFG_PORT, ldev, ldt->index, ldt->val);
 152                ldt++;
 153        } /* endwhile */
 154}
 155
 156void isa_sio_loadtable(void)
 157{
 158        char *s = getenv("floppy");
 159        /* setup Floppy device 0*/
 160        isa_write_table((SIO_LOGDEV_TABLE *)&sio_fdc,0);
 161        /* setup parallel port device 3 */
 162        if(s && !strncmp(s, "lpt", 3)) {
 163                printf("SIO:   Floppy assigned to LPT\n");
 164                /* floppy is assigned to the LPT */
 165                isa_write_table((SIO_LOGDEV_TABLE *)&sio_pport_fdc,3);
 166        }
 167        else {
 168                /*printf("Floppy assigned to internal port\n");*/
 169                isa_write_table((SIO_LOGDEV_TABLE *)&sio_pport,3);
 170        }
 171        /* setup Com1 port device 4 */
 172        isa_write_table((SIO_LOGDEV_TABLE *)&sio_com1,4);
 173        /* setup Com2 port device 5 */
 174        isa_write_table((SIO_LOGDEV_TABLE *)&sio_com2,5);
 175        /* setup keyboards device 7 */
 176        isa_write_table((SIO_LOGDEV_TABLE *)&sio_keyboard,7);
 177}
 178
 179
 180void isa_sio_setup(void)
 181{
 182        if(open_cfg_super_IO(SIO_CFG_PORT)==TRUE)
 183        {
 184                isa_sio_loadtable();
 185                close_cfg_super_IO(0x3F0);
 186        }
 187}
 188#endif
 189
 190/******************************************************************************
 191 * IRQ Controller
 192 * we use the Vector mode
 193 */
 194
 195struct  isa_irq_action {
 196         interrupt_handler_t *handler;
 197         void *arg;
 198         int count;
 199};
 200
 201static struct isa_irq_action isa_irqs[16];
 202
 203
 204/*
 205 * This contains the irq mask for both 8259A irq controllers,
 206 */
 207static unsigned int cached_irq_mask = 0xfff9;
 208
 209#define cached_imr1     (unsigned char)cached_irq_mask
 210#define cached_imr2     (unsigned char)(cached_irq_mask>>8)
 211#define IMR_1           CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_OCW1
 212#define IMR_2           CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_OCW1
 213#define ICW1_1  CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_ICW1
 214#define ICW1_2  CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_ICW1
 215#define ICW2_1  CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_ICW2
 216#define ICW2_2  CONFIG_SYS_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_ICW2
 217#define ICW3_1  ICW2_1
 218#define ICW3_2  ICW2_2
 219#define ICW4_1  ICW2_1
 220#define ICW4_2  ICW2_2
 221#define ISR_1           ICW1_1
 222#define ISR_2           ICW1_2
 223
 224
 225void disable_8259A_irq(unsigned int irq)
 226{
 227        unsigned int mask = 1 << irq;
 228
 229        cached_irq_mask |= mask;
 230        if (irq & 8)
 231                out8(IMR_2,cached_imr2);
 232        else
 233                out8(IMR_1,cached_imr1);
 234}
 235
 236void enable_8259A_irq(unsigned int irq)
 237{
 238        unsigned int mask = ~(1 << irq);
 239
 240        cached_irq_mask &= mask;
 241        if (irq & 8)
 242                out8(IMR_2,cached_imr2);
 243        else
 244                out8(IMR_1,cached_imr1);
 245}
 246/*
 247int i8259A_irq_pending(unsigned int irq)
 248{
 249        unsigned int mask = 1<<irq;
 250        int ret;
 251
 252        if (irq < 8)
 253                ret = inb(0x20) & mask;
 254        else
 255                ret = inb(0xA0) & (mask >> 8);
 256        spin_unlock_irqrestore(&i8259A_lock, flags);
 257
 258        return ret;
 259}
 260*/
 261
 262/*
 263 * This function assumes to be called rarely. Switching between
 264 * 8259A registers is slow.
 265 */
 266int i8259A_irq_real(unsigned int irq)
 267{
 268        int value;
 269        int irqmask = 1<<irq;
 270
 271        if (irq < 8) {
 272                out8(ISR_1,0x0B);               /* ISR register */
 273                value = in8(ISR_1) & irqmask;
 274                out8(ISR_1,0x0A);               /* back to the IRR register */
 275                return value;
 276        }
 277        out8(ISR_2,0x0B);               /* ISR register */
 278        value = in8(ISR_2) & (irqmask >> 8);
 279        out8(ISR_2,0x0A);               /* back to the IRR register */
 280        return value;
 281}
 282
 283/*
 284 * Careful! The 8259A is a fragile beast, it pretty
 285 * much _has_ to be done exactly like this (mask it
 286 * first, _then_ send the EOI, and the order of EOI
 287 * to the two 8259s is important!
 288 */
 289void mask_and_ack_8259A(unsigned int irq)
 290{
 291        unsigned int irqmask = 1 << irq;
 292        unsigned int temp_irqmask = cached_irq_mask;
 293        /*
 294         * Lightweight spurious IRQ detection. We do not want
 295         * to overdo spurious IRQ handling - it's usually a sign
 296         * of hardware problems, so we only do the checks we can
 297         * do without slowing down good hardware unnecesserily.
 298         *
 299         * Note that IRQ7 and IRQ15 (the two spurious IRQs
 300         * usually resulting from the 8259A-1|2 PICs) occur
 301         * even if the IRQ is masked in the 8259A. Thus we
 302         * can check spurious 8259A IRQs without doing the
 303         * quite slow i8259A_irq_real() call for every IRQ.
 304         * This does not cover 100% of spurious interrupts,
 305         * but should be enough to warn the user that there
 306         * is something bad going on ...
 307         */
 308        if (temp_irqmask & irqmask)
 309                goto spurious_8259A_irq;
 310        temp_irqmask |= irqmask;
 311
 312handle_real_irq:
 313        if (irq & 8) {
 314                in8(IMR_2);             /* DUMMY - (do we need this?) */
 315                out8(IMR_2,(unsigned char)(temp_irqmask>>8));
 316                out8(ISR_2,0x60+(irq&7));/* 'Specific EOI' to slave */
 317                out8(ISR_1,0x62);       /* 'Specific EOI' to master-IRQ2 */
 318                out8(IMR_2,cached_imr2); /* turn it on again */
 319        } else {
 320                in8(IMR_1);             /* DUMMY - (do we need this?) */
 321                out8(IMR_1,(unsigned char)temp_irqmask);
 322                out8(ISR_1,0x60+irq);   /* 'Specific EOI' to master */
 323                out8(IMR_1,cached_imr1); /* turn it on again */
 324        }
 325
 326        return;
 327
 328spurious_8259A_irq:
 329        /*
 330         * this is the slow path - should happen rarely.
 331         */
 332        if (i8259A_irq_real(irq))
 333                /*
 334                 * oops, the IRQ _is_ in service according to the
 335                 * 8259A - not spurious, go handle it.
 336                 */
 337                goto handle_real_irq;
 338
 339        {
 340                static int spurious_irq_mask;
 341                /*
 342                 * At this point we can be sure the IRQ is spurious,
 343                 * lets ACK and report it. [once per IRQ]
 344                 */
 345                if (!(spurious_irq_mask & irqmask)) {
 346                        PRINTF("spurious 8259A interrupt: IRQ%d.\n", irq);
 347                        spurious_irq_mask |= irqmask;
 348                }
 349                /* irq_err_count++; */
 350                /*
 351                 * Theoretically we do not have to handle this IRQ,
 352                 * but in Linux this does not cause problems and is
 353                 * simpler for us.
 354                 */
 355                goto handle_real_irq;
 356        }
 357}
 358
 359void init_8259A(void)
 360{
 361        out8(IMR_1,0xff);       /* mask all of 8259A-1 */
 362        out8(IMR_2,0xff);       /* mask all of 8259A-2 */
 363
 364        out8(ICW1_1,0x11);      /* ICW1: select 8259A-1 init */
 365        out8(ICW2_1,0x20 + 0);  /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
 366        out8(ICW3_1,0x04);      /* 8259A-1 (the master) has a slave on IR2 */
 367        out8(ICW4_1,0x01);      /* master expects normal EOI */
 368        out8(ICW1_2,0x11);      /* ICW2: select 8259A-2 init */
 369        out8(ICW2_2,0x20 + 8);  /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
 370        out8(ICW3_2,0x02);      /* 8259A-2 is a slave on master's IR2 */
 371        out8(ICW4_2,0x01);      /* (slave's support for AEOI in flat mode
 372                                    is to be investigated) */
 373        udelay(10000);          /* wait for 8259A to initialize */
 374        out8(IMR_1,cached_imr1);        /* restore master IRQ mask */
 375        udelay(10000);          /* wait for 8259A to initialize */
 376        out8(IMR_2,cached_imr2);        /* restore slave IRQ mask */
 377}
 378
 379
 380#define PCI_INT_ACK_ADDR 0xEED00000
 381
 382int handle_isa_int(void)
 383{
 384        unsigned long irqack;
 385        unsigned char isr1,isr2,irq;
 386        /* first we acknokledge the int via the PCI bus */
 387        irqack=in32(PCI_INT_ACK_ADDR);
 388        /* now we get the ISRs */
 389        isr2=in8(ISR_2);
 390        isr1=in8(ISR_1);
 391        irq=(unsigned char)irqack;
 392        irq-=32;
 393/*      if((irq==7)&&((isr1&0x80)==0)) {
 394                PRINTF("IRQ7 detected but not in ISR\n");
 395        }
 396        else {
 397*/              /* we should handle cascaded interrupts here also */
 398        {
 399/*              printf("ISA Irq %d\n",irq); */
 400                isa_irqs[irq].count++;
 401                if(irq!=2) { /* just swallow the cascade irq 2 */
 402                        if (isa_irqs[irq].handler != NULL)
 403                                (*isa_irqs[irq].handler)(isa_irqs[irq].arg);      /* call isr */
 404                        else {
 405                                PRINTF ("bogus interrupt vector 0x%x\n", irq);
 406                        }
 407                }
 408        }
 409        /* issue EOI instruction to clear the IRQ */
 410        mask_and_ack_8259A(irq);
 411        return 0;
 412}
 413
 414
 415/******************************************************************
 416 * Install and free an ISA interrupt handler.
 417 */
 418
 419void isa_irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
 420{
 421        if (isa_irqs[vec].handler != NULL) {
 422                printf ("ISA Interrupt vector %d: handler 0x%x replacing 0x%x\n",
 423                        vec, (uint)handler, (uint)isa_irqs[vec].handler);
 424        }
 425        isa_irqs[vec].handler = handler;
 426        isa_irqs[vec].arg     = arg;
 427        enable_8259A_irq(vec);
 428        PRINTF ("Install ISA IRQ %d ==> %p, @ %p mask=%04x\n", vec, handler, &isa_irqs[vec].handler,cached_irq_mask);
 429
 430}
 431
 432void isa_irq_free_handler(int vec)
 433{
 434        disable_8259A_irq(vec);
 435        isa_irqs[vec].handler = NULL;
 436        isa_irqs[vec].arg     = NULL;
 437        PRINTF ("Free ISA IRQ %d mask=%04x\n", vec, cached_irq_mask);
 438
 439}
 440
 441/****************************************************************************/
 442void isa_init_irq_contr(void)
 443{
 444        int i;
 445        /* disable all Interrupts */
 446        /* first write icws controller 1 */
 447        for(i=0;i<16;i++)
 448        {
 449                isa_irqs[i].handler=NULL;
 450                isa_irqs[i].arg=NULL;
 451                isa_irqs[i].count=0;
 452        }
 453        init_8259A();
 454        out8(IMR_2,0xFF);
 455}
 456/*************************************************************************/
 457
 458void isa_show_irq(void)
 459{
 460        int vec;
 461
 462        printf ("\nISA Interrupt-Information:\n");
 463        printf ("Nr  Routine   Arg       Count\n");
 464
 465        for (vec=0; vec<16; vec++) {
 466                if (isa_irqs[vec].handler != NULL) {
 467                        printf ("%02d  %08lx  %08lx  %d\n",
 468                                vec,
 469                                (ulong)isa_irqs[vec].handler,
 470                                (ulong)isa_irqs[vec].arg,
 471                                isa_irqs[vec].count);
 472                }
 473        }
 474}
 475
 476int isa_irq_get_count(int vec)
 477{
 478        return(isa_irqs[vec].count);
 479}
 480
 481/******************************************************************
 482 * Init the ISA bus and devices.
 483 */
 484
 485#if defined(CONFIG_PIP405)
 486
 487int isa_init(void)
 488{
 489        isa_sio_setup();
 490        isa_init_irq_contr();
 491        drv_isa_kbd_init();
 492        return 0;
 493}
 494#endif
 495