patch-2.1.89 linux/arch/alpha/kernel/smc37c669.c

Next file: linux/arch/alpha/kernel/smc37c93x.c
Previous file: linux/arch/alpha/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.88/linux/arch/alpha/kernel/smc37c669.c linux/arch/alpha/kernel/smc37c669.c
@@ -0,0 +1,2583 @@
+/*
+ * SMC 37C669 initialization code
+ */
+#include <linux/kernel.h>
+
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#if 0
+# define DBG_DEVS(args)         printk args
+#else
+# define DBG_DEVS(args)
+#endif
+
+#define KB              1024
+#define MB              (1024*KB)
+#define GB              (1024*MB)
+
+#define SMC_DEBUG   0
+
+/* File:	smcc669_def.h
+ *
+ * Copyright (C) 1997 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ * All rights reserved.
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  of  the  terms  of  such  license  and with the
+ * inclusion of the above copyright notice. This software or  any  other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ * The information in this software is  subject to change without notice
+ * and  should  not  be  construed  as a commitment by Digital Equipment
+ * Corporation.
+ *
+ * Digital assumes no responsibility for the use  or  reliability of its
+ * software on equipment which is not supplied by Digital.
+ *
+ *
+ * Abstract:	
+ *
+ *	This file contains header definitions for the SMC37c669 
+ *	Super I/O controller. 
+ *
+ * Author:	
+ *
+ *	Eric Rasmussen
+ *
+ * Modification History:
+ *
+ *	er	28-Jan-1997	Initial Entry
+ */
+
+#ifndef __SMC37c669_H
+#define __SMC37c669_H
+
+/*
+** Macros for handling device IRQs
+**
+** The mask acts as a flag used in mapping actual ISA IRQs (0 - 15) 
+** to device IRQs (A - H).
+*/
+#define SMC37c669_DEVICE_IRQ_MASK	0x80000000
+#define SMC37c669_DEVICE_IRQ( __i )	\
+	((SMC37c669_DEVICE_IRQ_MASK) | (__i))
+#define SMC37c669_IS_DEVICE_IRQ(__i)	\
+	(((__i) & (SMC37c669_DEVICE_IRQ_MASK)) == (SMC37c669_DEVICE_IRQ_MASK))
+#define SMC37c669_RAW_DEVICE_IRQ(__i)	\
+	((__i) & ~(SMC37c669_DEVICE_IRQ_MASK))
+
+/*
+** Macros for handling device DRQs
+**
+** The mask acts as a flag used in mapping actual ISA DMA
+** channels to device DMA channels (A - C).
+*/
+#define SMC37c669_DEVICE_DRQ_MASK	0x80000000
+#define SMC37c669_DEVICE_DRQ(__d)	\
+	((SMC37c669_DEVICE_DRQ_MASK) | (__d))
+#define SMC37c669_IS_DEVICE_DRQ(__d)	\
+	(((__d) & (SMC37c669_DEVICE_DRQ_MASK)) == (SMC37c669_DEVICE_DRQ_MASK))
+#define SMC37c669_RAW_DEVICE_DRQ(__d)	\
+	((__d) & ~(SMC37c669_DEVICE_DRQ_MASK))
+
+#define SMC37c669_DEVICE_ID	0x3
+
+/*
+** SMC37c669 Device Function Definitions
+*/
+#define SERIAL_0	0
+#define SERIAL_1	1
+#define PARALLEL_0	2
+#define FLOPPY_0	3
+#define IDE_0		4
+#define NUM_FUNCS	5
+
+/*
+** Default Device Function Mappings
+*/
+#define COM1_BASE	0x3F8
+#define COM1_IRQ	4
+#define COM2_BASE	0x2F8
+#define COM2_IRQ	3
+#define PARP_BASE	0x3BC
+#define PARP_IRQ	7
+#define PARP_DRQ	3
+#define FDC_BASE	0x3F0
+#define FDC_IRQ		6
+#define FDC_DRQ		2
+
+/*
+** Configuration On/Off Key Definitions
+*/
+#define SMC37c669_CONFIG_ON_KEY		0x55
+#define SMC37c669_CONFIG_OFF_KEY	0xAA
+
+/*
+** SMC 37c669 Device IRQs
+*/
+#define SMC37c669_DEVICE_IRQ_A	    ( SMC37c669_DEVICE_IRQ( 0x01 ) )
+#define SMC37c669_DEVICE_IRQ_B	    ( SMC37c669_DEVICE_IRQ( 0x02 ) )
+#define SMC37c669_DEVICE_IRQ_C	    ( SMC37c669_DEVICE_IRQ( 0x03 ) )
+#define SMC37c669_DEVICE_IRQ_D	    ( SMC37c669_DEVICE_IRQ( 0x04 ) )
+#define SMC37c669_DEVICE_IRQ_E	    ( SMC37c669_DEVICE_IRQ( 0x05 ) )
+#define SMC37c669_DEVICE_IRQ_F	    ( SMC37c669_DEVICE_IRQ( 0x06 ) )
+/*      SMC37c669_DEVICE_IRQ_G	    *** RESERVED ***/
+#define SMC37c669_DEVICE_IRQ_H	    ( SMC37c669_DEVICE_IRQ( 0x08 ) )
+
+/*
+** SMC 37c669 Device DMA Channel Definitions
+*/
+#define SMC37c669_DEVICE_DRQ_A		    ( SMC37c669_DEVICE_DRQ( 0x01 ) )
+#define SMC37c669_DEVICE_DRQ_B		    ( SMC37c669_DEVICE_DRQ( 0x02 ) )
+#define SMC37c669_DEVICE_DRQ_C		    ( SMC37c669_DEVICE_DRQ( 0x03 ) )
+
+/*
+** Configuration Register Index Definitions
+*/
+#define SMC37c669_CR00_INDEX	    0x00
+#define SMC37c669_CR01_INDEX	    0x01
+#define SMC37c669_CR02_INDEX	    0x02
+#define SMC37c669_CR03_INDEX	    0x03
+#define SMC37c669_CR04_INDEX	    0x04
+#define SMC37c669_CR05_INDEX	    0x05
+#define SMC37c669_CR06_INDEX	    0x06
+#define SMC37c669_CR07_INDEX	    0x07
+#define SMC37c669_CR08_INDEX	    0x08
+#define SMC37c669_CR09_INDEX	    0x09
+#define SMC37c669_CR0A_INDEX	    0x0A
+#define SMC37c669_CR0B_INDEX	    0x0B
+#define SMC37c669_CR0C_INDEX	    0x0C
+#define SMC37c669_CR0D_INDEX	    0x0D
+#define SMC37c669_CR0E_INDEX	    0x0E
+#define SMC37c669_CR0F_INDEX	    0x0F
+#define SMC37c669_CR10_INDEX	    0x10
+#define SMC37c669_CR11_INDEX	    0x11
+#define SMC37c669_CR12_INDEX	    0x12
+#define SMC37c669_CR13_INDEX	    0x13
+#define SMC37c669_CR14_INDEX	    0x14
+#define SMC37c669_CR15_INDEX	    0x15
+#define SMC37c669_CR16_INDEX	    0x16
+#define SMC37c669_CR17_INDEX	    0x17
+#define SMC37c669_CR18_INDEX	    0x18
+#define SMC37c669_CR19_INDEX	    0x19
+#define SMC37c669_CR1A_INDEX	    0x1A
+#define SMC37c669_CR1B_INDEX	    0x1B
+#define SMC37c669_CR1C_INDEX	    0x1C
+#define SMC37c669_CR1D_INDEX	    0x1D
+#define SMC37c669_CR1E_INDEX	    0x1E
+#define SMC37c669_CR1F_INDEX	    0x1F
+#define SMC37c669_CR20_INDEX	    0x20
+#define SMC37c669_CR21_INDEX	    0x21
+#define SMC37c669_CR22_INDEX	    0x22
+#define SMC37c669_CR23_INDEX	    0x23
+#define SMC37c669_CR24_INDEX	    0x24
+#define SMC37c669_CR25_INDEX	    0x25
+#define SMC37c669_CR26_INDEX	    0x26
+#define SMC37c669_CR27_INDEX	    0x27
+#define SMC37c669_CR28_INDEX	    0x28
+#define SMC37c669_CR29_INDEX	    0x29
+
+/*
+** Configuration Register Alias Definitions
+*/
+#define SMC37c669_DEVICE_ID_INDEX		    SMC37c669_CR0D_INDEX
+#define SMC37c669_DEVICE_REVISION_INDEX		    SMC37c669_CR0E_INDEX
+#define SMC37c669_FDC_BASE_ADDRESS_INDEX	    SMC37c669_CR20_INDEX
+#define SMC37c669_IDE_BASE_ADDRESS_INDEX	    SMC37c669_CR21_INDEX
+#define SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX	    SMC37c669_CR22_INDEX
+#define SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX	    SMC37c669_CR23_INDEX
+#define SMC37c669_SERIAL0_BASE_ADDRESS_INDEX	    SMC37c669_CR24_INDEX
+#define SMC37c669_SERIAL1_BASE_ADDRESS_INDEX	    SMC37c669_CR25_INDEX
+#define SMC37c669_PARALLEL_FDC_DRQ_INDEX	    SMC37c669_CR26_INDEX
+#define SMC37c669_PARALLEL_FDC_IRQ_INDEX	    SMC37c669_CR27_INDEX
+#define SMC37c669_SERIAL_IRQ_INDEX		    SMC37c669_CR28_INDEX
+
+/*
+** Configuration Register Definitions
+**
+** The INDEX (write only) and DATA (read/write) ports are effective 
+** only when the chip is in the Configuration State.
+*/
+typedef struct _SMC37c669_CONFIG_REGS {
+    unsigned char index_port;
+    unsigned char data_port;
+} SMC37c669_CONFIG_REGS;
+
+/*
+** CR00 - default value 0x28
+**
+**  IDE_EN (CR00<1:0>):
+**	0x - 30ua pull-ups on nIDEEN, nHDCS0, NHDCS1
+**	11 - IRQ_H available as IRQ output,
+**	     IRRX2, IRTX2 available as alternate IR pins
+**	10 - nIDEEN, nHDCS0, nHDCS1 used to control IDE
+**
+**  VALID (CR00<7>):
+**	A high level on this software controlled bit can
+**	be used to indicate that a valid configuration
+**	cycle has occurred.  The control software must
+**	take care to set this bit at the appropriate times.
+**	Set to zero after power up.  This bit has no
+**	effect on any other hardware in the chip.
+**
+*/
+typedef union _SMC37c669_CR00 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ide_en : 2;	    /* See note above		*/
+	unsigned reserved1 : 1;	    /* RAZ			*/
+	unsigned fdc_pwr : 1;	    /* 1 = supply power to FDC  */
+	unsigned reserved2 : 3;	    /* Read as 010b		*/
+	unsigned valid : 1;	    /* See note above		*/
+    }	by_field;
+} SMC37c669_CR00;
+
+/*
+** CR01 - default value 0x9C
+*/
+typedef union _SMC37c669_CR01 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 2;	    /* RAZ			    */
+	unsigned ppt_pwr : 1;	    /* 1 = supply power to PPT	    */
+	unsigned ppt_mode : 1;	    /* 1 = Printer mode, 0 = EPP    */
+	unsigned reserved2 : 1;	    /* Read as 1		    */
+	unsigned reserved3 : 2;	    /* RAZ			    */
+	unsigned lock_crx: 1;	    /* Lock CR00 - CR18		    */
+    }	by_field;
+} SMC37c669_CR01;
+
+/*
+** CR02 - default value 0x88
+*/
+typedef union _SMC37c669_CR02 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 3;	    /* RAZ			    */
+	unsigned uart1_pwr : 1;	    /* 1 = supply power to UART1    */
+	unsigned reserved2 : 3;	    /* RAZ			    */
+	unsigned uart2_pwr : 1;	    /* 1 = supply power to UART2    */
+    }	by_field;
+} SMC37c669_CR02;
+
+/*
+** CR03 - default value 0x78
+**
+**  CR03<7>	CR03<2>	    Pin 94
+**  -------	-------	    ------
+**     0	   X	    DRV2 (input)
+**     1	   0	    ADRX
+**     1	   1	    IRQ_B
+**
+**  CR03<6>	CR03<5>	    Op Mode
+**  -------	-------	    -------
+**     0	   0	    Model 30
+**     0	   1	    PS/2
+**     1	   0	    Reserved
+**     1	   1	    AT Mode
+*/
+typedef union _SMC37c669_CR03 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned pwrgd_gamecs : 1;  /* 1 = PWRGD, 0 = GAMECS	    */
+	unsigned fdc_mode2 : 1;	    /* 1 = Enhanced Mode 2	    */
+	unsigned pin94_0 : 1;	    /* See note above		    */
+	unsigned reserved1 : 1;	    /* RAZ			    */
+	unsigned drvden : 1;	    /* 1 = high, 0 - output	    */
+	unsigned op_mode : 2;	    /* See note above		    */
+	unsigned pin94_1 : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR03;
+
+/*
+** CR04 - default value 0x00
+**
+**  PP_EXT_MODE:
+**	If CR01<PP_MODE> = 0 and PP_EXT_MODE =
+**	    00 - Standard and Bidirectional
+**	    01 - EPP mode and SPP
+**	    10 - ECP mode
+**		 In this mode, 2 drives can be supported
+**		 directly, 3 or 4 drives must use external
+**		 4 drive support.  SPP can be selected
+**		 through the ECR register of ECP as mode 000.
+**	    11 - ECP mode and EPP mode
+**		 In this mode, 2 drives can be supported
+**		 directly, 3 or 4 drives must use external
+**		 4 drive support.  SPP can be selected
+**		 through the ECR register of ECP as mode 000.
+**		 In this mode, EPP can be selected through
+**		 the ECR register of ECP as mode 100.
+**
+**  PP_FDC:
+**	00 - Normal
+**	01 - PPFD1
+**	10 - PPFD2
+**	11 - Reserved
+**
+**  MIDI1:
+**	Serial Clock Select: 
+**	    A low level on this bit disables MIDI support,
+**	    clock = divide by 13.  A high level on this 
+**	    bit enables MIDI support, clock = divide by 12.
+**
+**	MIDI operates at 31.25 Kbps which can be derived 
+**	from 125 KHz (24 MHz / 12 = 2 MHz, 2 MHz / 16 = 125 KHz)
+**
+**  ALT_IO:
+**	0 - Use pins IRRX, IRTX
+**	1 - Use pins IRRX2, IRTX2
+**
+**	If this bit is set, the IR receive and transmit
+**	functions will not be available on pins 25 and 26
+**	unless CR00<IDE_EN> = 11.
+*/
+typedef union _SMC37c669_CR04 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_ext_mode : 2;  /* See note above		    */
+	unsigned ppt_fdc : 2;	    /* See note above		    */
+	unsigned midi1 : 1;	    /* See note above		    */
+	unsigned midi2 : 1;	    /* See note above		    */
+	unsigned epp_type : 1;	    /* 0 = EPP 1.9, 1 = EPP 1.7	    */
+	unsigned alt_io : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR04;
+
+/*
+** CR05 - default value 0x00
+**
+**  DEN_SEL:
+**	00 - Densel output normal
+**	01 - Reserved
+**	10 - Densel output 1
+**	11 - Densel output 0
+**
+*/
+typedef union _SMC37c669_CR05 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 2;	    /* RAZ					*/
+	unsigned fdc_dma_mode : 1;  /* 0 = burst, 1 = non-burst			*/
+	unsigned den_sel : 2;	    /* See note above				*/
+	unsigned swap_drv : 1;	    /* Swap the FDC motor selects		*/
+	unsigned extx4 : 1;	    /* 0 = 2 drive, 1 = external 4 drive decode	*/
+	unsigned reserved2 : 1;	    /* RAZ					*/
+    }	by_field;
+} SMC37c669_CR05;
+
+/*
+** CR06 - default value 0xFF
+*/
+typedef union _SMC37c669_CR06 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned floppy_a : 2;	    /* Type of floppy drive A	    */
+	unsigned floppy_b : 2;	    /* Type of floppy drive B	    */
+	unsigned floppy_c : 2;	    /* Type of floppy drive C	    */
+	unsigned floppy_d : 2;	    /* Type of floppy drive D	    */
+    }	by_field;
+} SMC37c669_CR06;
+
+/*
+** CR07 - default value 0x00
+**
+**  Auto Power Management CR07<7:4>:
+**	0 - Auto Powerdown disabled (default)
+**	1 - Auto Powerdown enabled
+**
+**	This bit is reset to the default state by POR or
+**	a hardware reset.
+**
+*/
+typedef union _SMC37c669_CR07 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned floppy_boot : 2;   /* 0 = A:, 1 = B:		    */
+	unsigned reserved1 : 2;	    /* RAZ			    */
+	unsigned ppt_en : 1;	    /* See note above		    */
+	unsigned uart1_en : 1;	    /* See note above		    */
+	unsigned uart2_en : 1;	    /* See note above		    */
+	unsigned fdc_en : 1;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR07;
+
+/*
+** CR08 - default value 0x00
+*/
+typedef union _SMC37c669_CR08 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 4;	    /* 0			    */
+	unsigned addrx7_4 : 4;	    /* ADR<7:3> for ADRx decode	    */
+    }	by_field;
+} SMC37c669_CR08;
+
+/*
+** CR09 - default value 0x00
+**
+**  ADRx_CONFIG:
+**	00 - ADRx disabled
+**	01 - 1 byte decode A<3:0> = 0000b
+**	10 - 8 byte block decode A<3:0> = 0XXXb
+**	11 - 16 byte block decode A<3:0> = XXXXb
+**
+*/
+typedef union _SMC37c669_CR09 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned adra8 : 3;	    /* ADR<10:8> for ADRx decode    */
+	unsigned reserved1 : 3;
+	unsigned adrx_config : 2;   /* See note above		    */
+    }	by_field;
+} SMC37c669_CR09;
+
+/*
+** CR0A - default value 0x00
+*/
+typedef union _SMC37c669_CR0A {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ecp_fifo_threshold : 4;
+	unsigned reserved1 : 4;
+    }	by_field;
+} SMC37c669_CR0A;
+
+/*
+** CR0B - default value 0x00
+*/
+typedef union _SMC37c669_CR0B {
+    unsigned char as_uchar;
+    struct {
+    	unsigned fdd0_drtx : 2;	    /* FDD0 Data Rate Table	    */
+	unsigned fdd1_drtx : 2;	    /* FDD1 Data Rate Table	    */
+	unsigned fdd2_drtx : 2;	    /* FDD2 Data Rate Table	    */
+	unsigned fdd3_drtx : 2;	    /* FDD3 Data Rate Table	    */
+    }	by_field;
+} SMC37c669_CR0B;
+
+/*
+** CR0C - default value 0x00
+**
+**  UART2_MODE:
+**	000 - Standard (default)
+**	001 - IrDA (HPSIR)
+**	010 - Amplitude Shift Keyed IR @500 KHz
+**	011 - Reserved
+**	1xx - Reserved
+**
+*/
+typedef union _SMC37c669_CR0C {
+    unsigned char as_uchar;
+    struct {
+    	unsigned uart2_rcv_polarity : 1;    /* 1 = invert RX		*/
+	unsigned uart2_xmit_polarity : 1;   /* 1 = invert TX		*/
+	unsigned uart2_duplex : 1;	    /* 1 = full, 0 = half	*/
+	unsigned uart2_mode : 3;	    /* See note above		*/
+	unsigned uart1_speed : 1;	    /* 1 = high speed enabled	*/
+	unsigned uart2_speed : 1;	    /* 1 = high speed enabled	*/
+    }	by_field;
+} SMC37c669_CR0C;
+
+/*
+** CR0D - default value 0x03
+**
+**  Device ID Register - read only
+*/
+typedef union _SMC37c669_CR0D {
+    unsigned char as_uchar;
+    struct {
+    	unsigned device_id : 8;	    /* Returns 0x3 in this field    */
+    }	by_field;
+} SMC37c669_CR0D;
+
+/*
+** CR0E - default value 0x02
+**
+**  Device Revision Register - read only
+*/
+typedef union _SMC37c669_CR0E {
+    unsigned char as_uchar;
+    struct {
+    	unsigned device_rev : 8;    /* Returns 0x2 in this field    */
+    }	by_field;
+} SMC37c669_CR0E;
+
+/*
+** CR0F - default value 0x00
+*/
+typedef union _SMC37c669_CR0F {
+    unsigned char as_uchar;
+    struct {
+    	unsigned test0 : 1;	    /* Reserved - set to 0	    */
+	unsigned test1 : 1;	    /* Reserved - set to 0	    */
+	unsigned test2 : 1;	    /* Reserved - set to 0	    */
+	unsigned test3 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test4 : 1;	    /* Reserved - set to 0	    */
+	unsigned test5 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test6 : 1;	    /* Reserved - set t0 0	    */
+	unsigned test7 : 1;	    /* Reserved - set to 0	    */
+    }	by_field;
+} SMC37c669_CR0F;
+
+/*
+** CR10 - default value 0x00
+*/
+typedef union _SMC37c669_CR10 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned reserved1 : 3;	     /* RAZ			    */
+	unsigned pll_gain : 1;	     /* 1 = 3V, 2 = 5V operation    */
+	unsigned pll_stop : 1;	     /* 1 = stop PLLs		    */
+	unsigned ace_stop : 1;	     /* 1 = stop UART clocks	    */
+	unsigned pll_clock_ctrl : 1; /* 0 = 14.318 MHz, 1 = 24 MHz  */
+	unsigned ir_test : 1;	     /* Enable IR test mode	    */
+    }	by_field;
+} SMC37c669_CR10;
+
+/*
+** CR11 - default value 0x00
+*/
+typedef union _SMC37c669_CR11 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ir_loopback : 1;   /* Internal IR loop back		    */
+	unsigned test_10ms : 1;	    /* Test 10ms autopowerdown FDC timeout  */
+	unsigned reserved1 : 6;	    /* RAZ				    */
+    }	by_field;
+} SMC37c669_CR11;
+
+/*
+** CR12 - CR1D are reserved registers
+*/
+
+/*
+** CR1E - default value 0x80
+**
+**  GAMECS:
+**	00 - GAMECS disabled
+**	01 - 1 byte decode ADR<3:0> = 0001b
+**	10 - 8 byte block decode ADR<3:0> = 0XXXb
+**	11 - 16 byte block decode ADR<3:0> = XXXXb
+**
+*/
+typedef union _SMC37c66_CR1E {
+    unsigned char as_uchar;
+    struct {
+    	unsigned gamecs_config: 2;   /* See note above		    */
+	unsigned gamecs_addr9_4 : 6; /* GAMECS Addr<9:4>	    */
+    }	by_field;
+} SMC37c669_CR1E;
+
+/*
+** CR1F - default value 0x00
+**
+**  DT0 DT1 DRVDEN0 DRVDEN1 Drive Type
+**  --- --- ------- ------- ----------
+**   0   0  DENSEL  DRATE0  4/2/1 MB 3.5"
+**                          2/1 MB 5.25"
+**                          2/1.6/1 MB 3.5" (3-mode)
+**   0   1  DRATE1  DRATE0
+**   1   0  nDENSEL DRATE0  PS/2
+**   1   1  DRATE0  DRATE1
+**
+**  Note: DENSEL, DRATE1, and DRATE0 map onto two output
+**	  pins - DRVDEN0 and DRVDEN1.
+**
+*/
+typedef union _SMC37c669_CR1F {
+    unsigned char as_uchar;
+    struct {
+    	unsigned fdd0_drive_type : 2;	/* FDD0 drive type	    */
+	unsigned fdd1_drive_type : 2;	/* FDD1 drive type	    */
+	unsigned fdd2_drive_type : 2;	/* FDD2 drive type	    */
+	unsigned fdd3_drive_type : 2;	/* FDD3 drive type	    */
+    }	by_field;
+} SMC37c669_CR1F;
+
+/*
+** CR20 - default value 0x3C
+**
+**  FDC Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR20 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* FDC Addr<9:4>		    */
+    }	by_field;
+} SMC37c669_CR20;
+
+/*
+** CR21 - default value 0x3C
+**
+**  IDE Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR21 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* IDE Addr<9:4>		    */
+    }	by_field;
+} SMC37c669_CR21;
+
+/*
+** CR22 - default value 0x3D
+**
+**  IDE Alternate Status Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<3:0> = 0110b to access.
+**
+*/
+typedef union _SMC37c669_CR22 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 2;	    /* 0			    */
+	unsigned addr9_4 : 6;	    /* IDE Alt Status Addr<9:4>	    */
+    }	by_field;
+} SMC37c669_CR22;
+
+/*
+** CR23 - default value 0x00
+**
+**  Parallel Port Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0 to access.
+**	- If EPP is enabled, A<2:0> = XXXb to access.
+**	  If EPP is NOT enabled, A<1:0> = XXb to access
+**
+*/
+typedef union _SMC37c669_CR23 {
+    unsigned char as_uchar;
+    struct {
+	unsigned addr9_2 : 8;	    /* Parallel Port Addr<9:2>	    */
+    }	by_field;
+} SMC37c669_CR23;
+
+/*
+** CR24 - default value 0x00
+**
+**  UART1 Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<2:0> = XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR24 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 1;	    /* 0			    */
+	unsigned addr9_3 : 7;	    /* UART1 Addr<9:3>		    */
+    }	by_field;
+} SMC37c669_CR24;
+
+/*
+** CR25 - default value 0x00
+**
+**  UART2 Base Address Register
+**	- To disable this decode set Addr<9:8> = 0
+**	- A<10> = 0, A<2:0> = XXXb to access.
+**
+*/
+typedef union _SMC37c669_CR25 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned zero : 1;	    /* 0			    */
+	unsigned addr9_3 : 7;	    /* UART2 Addr<9:3>		    */
+    }	by_field;
+} SMC37c669_CR25;
+
+/*
+** CR26 - default value 0x00
+**
+**  Parallel Port / FDC DMA Select Register
+**
+**  D3 - D0	  DMA
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 DMA_A
+**   0010	 DMA_B
+**   0011	 DMA_C
+**
+*/
+typedef union _SMC37c669_CR26 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_drq : 4;	    /* See note above		    */
+	unsigned fdc_drq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR26;
+
+/*
+** CR27 - default value 0x00
+**
+**  Parallel Port / FDC IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**
+**  Any unselected IRQ REQ is in tristate
+**
+*/
+typedef union _SMC37c669_CR27 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned ppt_irq : 4;	    /* See note above		    */
+	unsigned fdc_irq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR27;
+
+/*
+** CR28 - default value 0x00
+**
+**  UART IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**   1111	 share with UART1 (only for UART2)
+**
+**  Any unselected IRQ REQ is in tristate
+**
+**  To share an IRQ between UART1 and UART2, set
+**  UART1 to use the desired IRQ and set UART2 to
+**  0xF to enable sharing mechanism.
+**
+*/
+typedef union _SMC37c669_CR28 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned uart2_irq : 4;	    /* See note above		    */
+	unsigned uart1_irq : 4;	    /* See note above		    */
+    }	by_field;
+} SMC37c669_CR28;
+
+/*
+** CR29 - default value 0x00
+**
+**  IRQIN IRQ Select Register
+**
+**  D3 - D0	  IRQ
+**  D7 - D4	Selected
+**  -------	--------
+**   0000	 None
+**   0001	 IRQ_A
+**   0010	 IRQ_B
+**   0011	 IRQ_C
+**   0100	 IRQ_D
+**   0101	 IRQ_E
+**   0110	 IRQ_F
+**   0111	 Reserved
+**   1000	 IRQ_H
+**
+**  Any unselected IRQ REQ is in tristate
+**
+*/
+typedef union _SMC37c669_CR29 {
+    unsigned char as_uchar;
+    struct {
+    	unsigned irqin_irq : 4;	    /* See note above		    */
+	unsigned reserved1 : 4;	    /* RAZ			    */
+    }	by_field;
+} SMC37c669_CR29;
+
+/*
+** Aliases of Configuration Register formats (should match
+** the set of index aliases).
+**
+** Note that CR24 and CR25 have the same format and are the
+** base address registers for UART1 and UART2.  Because of
+** this we only define 1 alias here - for CR24 - as the serial
+** base address register.
+**
+** Note that CR21 and CR22 have the same format and are the
+** base address and alternate status address registers for
+** the IDE controller.  Because of this we only define 1 alias
+** here - for CR21 - as the IDE address register.
+**
+*/
+typedef SMC37c669_CR0D SMC37c669_DEVICE_ID_REGISTER;
+typedef SMC37c669_CR0E SMC37c669_DEVICE_REVISION_REGISTER;
+typedef SMC37c669_CR20 SMC37c669_FDC_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR21 SMC37c669_IDE_ADDRESS_REGISTER;
+typedef SMC37c669_CR23 SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR24 SMC37c669_SERIAL_BASE_ADDRESS_REGISTER;
+typedef SMC37c669_CR26 SMC37c669_PARALLEL_FDC_DRQ_REGISTER;
+typedef SMC37c669_CR27 SMC37c669_PARALLEL_FDC_IRQ_REGISTER;
+typedef SMC37c669_CR28 SMC37c669_SERIAL_IRQ_REGISTER;
+
+/*
+** ISA/Device IRQ Translation Table Entry Definition
+*/
+typedef struct _SMC37c669_IRQ_TRANSLATION_ENTRY {
+    int device_irq;
+    int isa_irq;
+} SMC37c669_IRQ_TRANSLATION_ENTRY;
+
+/*
+** ISA/Device DMA Translation Table Entry Definition
+*/
+typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY {
+    int device_drq;
+    int isa_drq;
+} SMC37c669_DRQ_TRANSLATION_ENTRY;
+
+/*
+** External Interface Function Prototype Declarations
+*/
+
+SMC37c669_CONFIG_REGS *SMC37c669_detect( 
+    void 
+);
+
+unsigned int SMC37c669_enable_device( 
+    unsigned int func 
+);
+
+unsigned int SMC37c669_disable_device( 
+    unsigned int func 
+);
+
+unsigned int SMC37c669_configure_device( 
+    unsigned int func, 
+    int port, 
+    int irq, 
+    int drq 
+);
+
+void SMC37c669_display_device_info( 
+    void 
+);
+
+#endif	/* __SMC37c669_H */
+
+/* file:	smcc669.c
+ *
+ * Copyright (C) 1997 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ * All rights reserved.
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  of  the  terms  of  such  license  and with the
+ * inclusion of the above copyright notice. This software or  any  other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ * The information in this software is  subject to change without notice
+ * and  should  not  be  construed  as a commitment by digital equipment
+ * corporation.
+ *
+ * Digital assumes no responsibility for the use  or  reliability of its
+ * software on equipment which is not supplied by digital.
+ */
+
+/*
+ *++
+ *  FACILITY:
+ *
+ *      Alpha SRM Console Firmware
+ *
+ *  MODULE DESCRIPTION:
+ *
+ *	SMC37c669 Super I/O controller configuration routines.
+ *
+ *  AUTHORS:
+ *
+ *	Eric Rasmussen
+ *
+ *  CREATION DATE:
+ *  
+ *	28-Jan-1997
+ *
+ *  MODIFICATION HISTORY:
+ *	
+ *	er	01-May-1997	Fixed pointer conversion errors in 
+ *				SMC37c669_get_device_config().
+ *      er	28-Jan-1997	Initial version.
+ *
+ *--
+ */
+#if 0
+/* $INCLUDE_OPTIONS$ */
+#include    "cp$inc:platform_io.h"
+/* $INCLUDE_OPTIONS_END$ */
+#include    "cp$src:common.h"
+#include    "cp$inc:prototypes.h"
+#include    "cp$src:kernel_def.h"
+#include    "cp$src:msg_def.h"
+#include    "cp$src:smcc669_def.h"
+/* Platform-specific includes */
+#include    "cp$src:platform.h"
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define wb( _x_, _y_ )	outb( _y_, (unsigned int)((unsigned long)_x_) )
+#define rb( _x_ )	inb( (unsigned int)((unsigned long)_x_) )
+
+/*
+** Local storage for device configuration information.
+**
+** Since the SMC37c669 does not provide an explicit
+** mechanism for enabling/disabling individual device 
+** functions, other than unmapping the device, local 
+** storage for device configuration information is 
+** allocated here for use in implementing our own 
+** function enable/disable scheme.
+*/
+static struct DEVICE_CONFIG {
+    unsigned int port1;
+    unsigned int port2;
+    unsigned int irq;
+    unsigned int drq;
+} local_config [NUM_FUNCS];
+
+/*
+** List of all possible addresses for the Super I/O chip
+*/
+static unsigned long SMC37c669_Addresses[] __initdata =
+    {
+	0x3F0UL,	    /* Primary address	    */
+	0x370UL,	    /* Secondary address    */
+	0UL		    /* End of list	    */
+    };
+
+/*
+** Global Pointer to the Super I/O device
+*/
+static SMC37c669_CONFIG_REGS *SMC37c669 __initdata = NULL;
+
+/*
+** IRQ Translation Table
+**
+** The IRQ translation table is a list of SMC37c669 device 
+** and standard ISA IRQs.
+**
+*/
+static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata; 
+
+/*
+** The following definition is for the default IRQ 
+** translation table.
+*/
+static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_default_irq_table[]
+__initdata = 
+    { 
+	{ SMC37c669_DEVICE_IRQ_A, -1 }, 
+	{ SMC37c669_DEVICE_IRQ_B, -1 }, 
+	{ SMC37c669_DEVICE_IRQ_C, 7 }, 
+	{ SMC37c669_DEVICE_IRQ_D, 6 }, 
+	{ SMC37c669_DEVICE_IRQ_E, 4 }, 
+	{ SMC37c669_DEVICE_IRQ_F, 3 }, 
+	{ SMC37c669_DEVICE_IRQ_H, -1 }, 
+	{ -1, -1 } /* End of table */
+    };
+
+/*
+** DRQ Translation Table
+**
+** The DRQ translation table is a list of SMC37c669 device and
+** ISA DMA channels.
+**
+*/
+static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata; 
+
+/*
+** The following definition is the default DRQ
+** translation table.
+*/
+static SMC37c669_DRQ_TRANSLATION_ENTRY SMC37c669_default_drq_table[]
+__initdata = 
+    { 
+	{ SMC37c669_DEVICE_DRQ_A, 2 }, 
+	{ SMC37c669_DEVICE_DRQ_B, 3 }, 
+	{ SMC37c669_DEVICE_DRQ_C, -1 }, 
+	{ -1, -1 } /* End of table */
+    };
+
+/*
+** Local Function Prototype Declarations
+*/
+
+static unsigned int SMC37c669_is_device_enabled( 
+    unsigned int func 
+);
+
+#if 0
+static unsigned int SMC37c669_get_device_config( 
+    unsigned int func, 
+    int *port, 
+    int *irq, 
+    int *drq 
+);
+#endif
+
+static void SMC37c669_config_mode( 
+    unsigned int enable 
+);
+
+static unsigned char SMC37c669_read_config( 
+    unsigned char index 
+);
+
+static void SMC37c669_write_config( 
+    unsigned char index, 
+    unsigned char data 
+);
+
+static void SMC37c669_init_local_config( void );
+
+static struct DEVICE_CONFIG *SMC37c669_get_config(
+    unsigned int func
+);
+
+static int SMC37c669_xlate_irq(
+    unsigned int irq 
+);
+
+static int SMC37c669_xlate_drq(
+    unsigned int drq 
+);
+
+#if 0
+/*
+** External Data Declarations
+*/
+
+extern struct LOCK spl_atomic;
+
+/*
+** External Function Prototype Declarations
+*/
+
+/* From kernel_alpha.mar */
+extern spinlock( 
+    struct LOCK *spl 
+);
+
+extern spinunlock( 
+    struct LOCK *spl 
+);
+
+/* From filesys.c */
+int allocinode(
+    char *name, 
+    int can_create, 
+    struct INODE **ipp
+);
+
+extern int null_procedure( void );
+
+int smcc669_init( void );
+int smcc669_open( struct FILE *fp, char *info, char *next, char *mode );
+int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf );
+int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf );
+int smcc669_close( struct FILE *fp );
+
+struct DDB smc_ddb = {
+	"smc",			/* how this routine wants to be called	*/
+	smcc669_read,		/* read routine				*/
+	smcc669_write,		/* write routine			*/
+	smcc669_open,		/* open routine				*/
+	smcc669_close,		/* close routine			*/
+	null_procedure,		/* name expansion routine		*/
+	null_procedure,		/* delete routine			*/
+	null_procedure,		/* create routine			*/
+	null_procedure,		/* setmode				*/
+	null_procedure,		/* validation routine			*/
+	0,			/* class specific use			*/
+	1,			/* allows information			*/
+	0,			/* must be stacked			*/
+	0,			/* is a flash update driver		*/
+	0,			/* is a block device			*/
+	0,			/* not seekable				*/
+	0,			/* is an ethernet device		*/
+	0,			/* is a filesystem driver		*/
+};
+#endif
+
+#define spinlock(x)
+#define spinunlock(x)
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function detects the presence of an SMC37c669 Super I/O
+**	controller.
+**
+**  FORMAL PARAMETERS:
+**
+**	None
+**
+**  RETURN VALUE:
+**
+**      Returns a pointer to the device if found, otherwise,
+**	the NULL pointer is returned.
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+SMC37c669_CONFIG_REGS * __init SMC37c669_detect( void )
+{
+    int i;
+    SMC37c669_DEVICE_ID_REGISTER id;
+
+    for ( i = 0;  SMC37c669_Addresses[i] != 0;  i++ ) {
+/*
+** Initialize the device pointer even though we don't yet know if
+** the controller is at this address.  The support functions access
+** the controller through this device pointer so we need to set it
+** even when we are looking ...
+*/
+    	SMC37c669 = ( SMC37c669_CONFIG_REGS * )SMC37c669_Addresses[i];
+/*
+** Enter configuration mode
+*/
+	SMC37c669_config_mode( TRUE );
+/*
+** Read the device id
+*/
+	id.as_uchar = SMC37c669_read_config( SMC37c669_DEVICE_ID_INDEX );
+/*
+** Exit configuration mode
+*/
+	SMC37c669_config_mode( FALSE );
+/*
+** Does the device id match?  If so, assume we have found an
+** SMC37c669 controller at this address.
+*/
+	if ( id.by_field.device_id == SMC37c669_DEVICE_ID ) {
+/*
+** Initialize the IRQ and DRQ translation tables.
+*/
+    	    SMC37c669_irq_table = SMC37c669_default_irq_table;
+	    SMC37c669_drq_table = SMC37c669_default_drq_table;
+/*
+** erfix
+**
+** If the platform can't use the IRQ and DRQ defaults set up in this 
+** file, it should call a platform-specific external routine at this 
+** point to reset the IRQ and DRQ translation table pointers to point 
+** at the appropriate tables for the platform.  If the defaults are 
+** acceptable, then the external routine should do nothing.
+*/
+
+/*
+** Put the chip back into configuration mode
+*/
+	    SMC37c669_config_mode( TRUE );
+/*
+** Initialize local storage for configuration information
+*/
+	    SMC37c669_init_local_config( );
+/*
+** Exit configuration mode
+*/
+	    SMC37c669_config_mode( FALSE );
+/*
+** SMC37c669 controller found, break out of search loop
+*/
+	    break;
+	}
+	else {
+/*
+** Otherwise, we did not find an SMC37c669 controller at this
+** address so set the device pointer to NULL.
+*/
+	    SMC37c669 = NULL;
+	}
+    }
+    return SMC37c669;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function enables an SMC37c669 device function.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function to enable
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE is the device function was enabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      Enabling a device function in the SMC37c669 controller involves
+**	setting all of its mappings (port, irq, drq ...).  A local 
+**	"shadow" copy of the device configuration is kept so we can
+**	just set each mapping to what the local copy says.
+**
+**	This function ALWAYS updates the local shadow configuration of
+**	the device function being enabled, even if the device is always
+**	enabled.  To avoid replication of code, functions such as
+**	configure_device set up the local copy and then call this 
+**	function to the update the real device.
+**
+**--
+*/
+unsigned int __init SMC37c669_enable_device ( unsigned int func )
+{
+    unsigned int ret_val = FALSE;
+/*
+** Put the device into configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+    switch ( func ) {
+    	case SERIAL_0:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Enable the serial 1 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart1_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Enable the serial 1 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case SERIAL_1:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Enable the serial 2 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart2_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Enable the serial 2 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case PARALLEL_0:
+	    {
+	    	SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Enable the parallel port DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+
+		drq.by_field.ppt_drq = 
+		    SMC37c669_RAW_DEVICE_DRQ(
+			SMC37c669_xlate_drq( local_config[ func ].drq )
+		    );
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Enable the parallel port IRQ mapping
+*/
+		irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+
+		irq.by_field.ppt_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Enable the parallel port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_2 = local_config[ func ].port1 >> 2;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case FLOPPY_0:
+	    {
+	    	SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Enable the floppy controller DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+		 
+		drq.by_field.fdc_drq =
+		    SMC37c669_RAW_DEVICE_DRQ(
+			SMC37c669_xlate_drq( local_config[ func ].drq )
+		    );
+		 
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Enable the floppy controller IRQ mapping
+*/
+		irq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+		 
+		irq.by_field.fdc_irq =
+		    SMC37c669_RAW_DEVICE_IRQ(
+			SMC37c669_xlate_irq( local_config[ func ].irq )
+		    );
+		 
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Enable the floppy controller base address mapping
+*/
+		base_addr.as_uchar = 0;
+		base_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_FDC_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case IDE_0:
+	    {
+	    	SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
+/*
+** Enable the IDE alternate status base address mapping
+*/
+	    	ide_addr.as_uchar = 0;
+		ide_addr.by_field.addr9_4 = local_config[ func ].port2 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+/*
+** Enable the IDE controller base address mapping
+*/
+		ide_addr.as_uchar = 0;
+		ide_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_BASE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+    }
+/*
+** Exit configuration mode and return
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function disables a device function within the
+**	SMC37c669 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which function to disable
+**
+**  RETURN VALUE:
+**
+**      Return TRUE if the device function was disabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      Disabling a function in the SMC37c669 device involves
+**	disabling all the function's mappings (port, irq, drq ...).
+**	A shadow copy of the device configuration is maintained
+**	in local storage so we won't worry aboving saving the
+**	current configuration information.
+**
+**--
+*/
+unsigned int __init SMC37c669_disable_device ( unsigned int func )
+{
+    unsigned int ret_val = FALSE;
+
+/*
+** Put the device into configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+    switch ( func ) {
+    	case SERIAL_0:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Disable the serial 1 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart1_irq = 0;
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Disable the serial 1 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case SERIAL_1:
+	    {
+	    	SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_SERIAL_IRQ_REGISTER irq;
+/*
+** Disable the serial 2 IRQ mapping
+*/
+	    	irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+
+		irq.by_field.uart2_irq = 0;
+
+		SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar );
+/*
+** Disable the serial 2 port base address mapping
+*/
+		base_addr.as_uchar = 0;
+
+		SMC37c669_write_config( 
+		    SMC37c669_SERIAL1_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case PARALLEL_0:
+	    {
+	    	SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Disable the parallel port DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+
+		drq.by_field.ppt_drq = 0;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Disable the parallel port IRQ mapping
+*/
+		irq.as_uchar = 
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+
+		irq.by_field.ppt_irq = 0;
+
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Disable the parallel port base address mapping
+*/
+		base_addr.as_uchar = 0;
+
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case FLOPPY_0:
+	    {
+	    	SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr;
+		SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq;
+		SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq;
+/*
+** Disable the floppy controller DMA channel mapping
+*/
+	    	drq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+		 
+		drq.by_field.fdc_drq = 0;
+		 
+		SMC37c669_write_config( 
+		    SMC37c669_PARALLEL_FDC_DRQ_INDEX,
+		    drq.as_uchar
+		);
+/*
+** Disable the floppy controller IRQ mapping
+*/
+		irq.as_uchar =
+		    SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+		 
+		irq.by_field.fdc_irq = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_PARALLEL_FDC_IRQ_INDEX,
+		    irq.as_uchar
+		);
+/*
+** Disable the floppy controller base address mapping
+*/
+		base_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_FDC_BASE_ADDRESS_INDEX,
+		    base_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+	case IDE_0:
+	    {
+	    	SMC37c669_IDE_ADDRESS_REGISTER ide_addr;
+/*
+** Disable the IDE alternate status base address mapping
+*/
+	    	ide_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+/*
+** Disable the IDE controller base address mapping
+*/
+		ide_addr.as_uchar = 0;
+		 
+		SMC37c669_write_config(
+		    SMC37c669_IDE_BASE_ADDRESS_INDEX,
+		    ide_addr.as_uchar
+		);
+		ret_val = TRUE;
+		break;
+	    }
+    }
+/*
+** Exit configuration mode and return
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function configures a device function within the 
+**	SMC37c669 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**       
+**      port:
+**          I/O port for the function to use
+**	 
+**      irq:
+**          IRQ for the device function to use
+**	 
+**      drq:
+**          DMA channel for the device function to use
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device function was configured, 
+**	otherwise, FALSE.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**	If this function returns TRUE, the local shadow copy of
+**	the configuration is also updated.  If the device function
+**	is currently disabled, only the local shadow copy is 
+**	updated and the actual device function will be updated
+**	if/when is is enabled.
+**
+**--
+*/
+unsigned int __init SMC37c669_configure_device (
+    unsigned int func,
+    int port,
+    int irq,
+    int drq )
+{
+    struct DEVICE_CONFIG *cp;
+
+/*
+** Check for a valid configuration
+*/
+    if ( ( cp = SMC37c669_get_config ( func ) ) != NULL ) {
+/*
+** Configuration is valid, update the local shadow copy
+*/
+    	if ( ( drq & ~0xFF ) == 0 ) {
+	    cp->drq = drq;
+	}
+	if ( ( irq & ~0xFF ) == 0 ) {
+	    cp->irq = irq;
+	}
+	if ( ( port & ~0xFFFF ) == 0 ) {
+	    cp->port1 = port;
+	}
+/*
+** If the device function is enabled, update the actual
+** device configuration.
+*/
+	if ( SMC37c669_is_device_enabled( func ) ) {
+	    SMC37c669_enable_device( func );
+	}
+	return TRUE;
+    }
+    return FALSE;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function determines whether a device function
+**	within the SMC37c669 controller is enabled.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device function is enabled, otherwise, FALSE
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**  DESIGN:
+**
+**      To check whether a device is enabled we will only look at 
+**	the port base address mapping.  According to the SMC37c669
+**	specification, all of the port base address mappings are
+**	disabled if the addr<9:8> (bits <7:6> of the register) are
+**	zero.
+**
+**--
+*/
+static unsigned int __init SMC37c669_is_device_enabled ( unsigned int func )
+{
+    unsigned char base_addr = 0;
+    unsigned int dev_ok = FALSE;
+    unsigned int ret_val = FALSE;
+/*
+** Enter configuration mode
+*/
+    SMC37c669_config_mode( TRUE );
+     
+    switch ( func ) {
+    	case SERIAL_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case SERIAL_1:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case PARALLEL_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case FLOPPY_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+	case IDE_0:
+	    base_addr =
+		SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
+	    dev_ok = TRUE;
+	    break;
+    }
+/*
+** If we have a valid device, check base_addr<7:6> to see if the
+** device is enabled (mapped).
+*/
+    if ( ( dev_ok ) && ( ( base_addr & 0xC0 ) != 0 ) ) {
+/*
+** The mapping is not disabled, so assume that the function is 
+** enabled.
+*/
+    	ret_val = TRUE;
+    }
+/*
+** Exit configuration mode 
+*/
+    SMC37c669_config_mode( FALSE );
+
+    return ret_val;
+}
+
+
+#if 0
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function retrieves the configuration information of a 
+**	device function within the SMC37c699 Super I/O controller.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**       
+**      port:
+**          I/O port returned
+**	 
+**      irq:
+**          IRQ returned
+**	 
+**      drq:
+**          DMA channel returned
+**
+**  RETURN VALUE:
+**
+**      Returns TRUE if the device configuration was successfully
+**	retrieved, otherwise, FALSE.
+**
+**  SIDE EFFECTS:
+**
+**      The data pointed to by the port, irq, and drq parameters
+**	my be modified even if the configuration is not successfully
+**	retrieved.
+**
+**  DESIGN:
+**
+**      The device configuration is fetched from the local shadow
+**	copy.  Any unused parameters will be set to -1.  Any
+**	parameter which is not desired can specify the NULL
+**	pointer.
+**
+**--
+*/
+static unsigned int __init SMC37c669_get_device_config (
+    unsigned int func,
+    int *port,
+    int *irq,
+    int *drq )
+{
+    struct DEVICE_CONFIG *cp;
+    unsigned int ret_val = FALSE;
+/*
+** Check for a valid device configuration
+*/
+    if ( ( cp = SMC37c669_get_config( func ) ) != NULL ) {
+    	if ( drq != NULL ) {
+	    *drq = cp->drq;
+	    ret_val = TRUE;
+	}
+	if ( irq != NULL ) {
+	    *irq = cp->irq;
+	    ret_val = TRUE;
+	}
+	if ( port != NULL ) {
+	    *port = cp->port1;
+	    ret_val = TRUE;
+	}
+    }
+    return ret_val;
+}
+#endif
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function displays the current state of the SMC37c699
+**	Super I/O controller's device functions.
+**
+**  FORMAL PARAMETERS:
+**
+**      None
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+void __init SMC37c669_display_device_info ( void )
+{
+    if ( SMC37c669_is_device_enabled( SERIAL_0 ) ) {
+    	printk( "  Serial 0:    Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ SERIAL_0 ].port1,
+		 local_config[ SERIAL_0 ].irq
+	);
+    }
+    else {
+    	printk( "  Serial 0:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( SERIAL_1 ) ) {
+    	printk( "  Serial 1:    Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ SERIAL_1 ].port1,
+		 local_config[ SERIAL_1 ].irq
+	);
+    }
+    else {
+    	printk( "  Serial 1:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( PARALLEL_0 ) ) {
+    	printk( "  Parallel:    Enabled [ Port 0x%x, IRQ %d/%d ]\n",
+		 local_config[ PARALLEL_0 ].port1,
+		 local_config[ PARALLEL_0 ].irq,
+		 local_config[ PARALLEL_0 ].drq
+	);
+    }
+    else {
+    	printk( "  Parallel:    Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( FLOPPY_0 ) ) {
+    	printk( "  Floppy Ctrl: Enabled [ Port 0x%x, IRQ %d/%d ]\n",
+		 local_config[ FLOPPY_0 ].port1,
+		 local_config[ FLOPPY_0 ].irq,
+		 local_config[ FLOPPY_0 ].drq
+	);
+    }
+    else {
+    	printk( "  Floppy Ctrl: Disabled\n" );
+    }
+
+    if ( SMC37c669_is_device_enabled( IDE_0 ) ) {
+    	printk( "  IDE 0:       Enabled [ Port 0x%x, IRQ %d ]\n",
+		 local_config[ IDE_0 ].port1,
+		 local_config[ IDE_0 ].irq
+	);
+    }
+    else {
+    	printk( "  IDE 0:       Disabled\n" );
+    }
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function puts the SMC37c669 Super I/O controller into,
+**	and takes it out of, configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      enable:
+**          TRUE to enter configuration mode, FALSE to exit.
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      The SMC37c669 controller may be left in configuration mode.
+**
+**--
+*/
+static void __init SMC37c669_config_mode( 
+    unsigned int enable )
+{
+    if ( enable ) {
+/*
+** To enter configuration mode, two writes in succession to the index
+** port are required.  If a write to another address or port occurs
+** between these two writes, the chip does not enter configuration
+** mode.  Therefore, a spinlock is placed around the two writes to 
+** guarantee that they complete uninterrupted.
+*/
+	spinlock( &spl_atomic );
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY );
+	spinunlock( &spl_atomic );
+    }
+    else {
+    	wb( &SMC37c669->index_port, SMC37c669_CONFIG_OFF_KEY );
+    }
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function reads an SMC37c669 Super I/O controller
+**	configuration register.  This function assumes that the
+**	device is already in configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      index:
+**          Index value of configuration register to read
+**
+**  RETURN VALUE:
+**
+**      Data read from configuration register
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+static unsigned char __init SMC37c669_read_config( 
+    unsigned char index )
+{
+    unsigned char data;
+
+    wb( &SMC37c669->index_port, index );
+    data = rb( &SMC37c669->data_port );
+    return data;
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function writes an SMC37c669 Super I/O controller
+**	configuration register.  This function assumes that the
+**	device is already in configuration mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      index:
+**          Index of configuration register to write
+**       
+**      data:
+**          Data to be written
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      None
+**
+**--
+*/
+static void __init SMC37c669_write_config( 
+    unsigned char index, 
+    unsigned char data )
+{
+    wb( &SMC37c669->index_port, index );
+    wb( &SMC37c669->data_port, data );
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function initializes the local device
+**	configuration storage.  This function assumes
+**	that the device is already in configuration
+**	mode.
+**
+**  FORMAL PARAMETERS:
+**
+**      None
+**
+**  RETURN VALUE:
+**
+**      None
+**
+**  SIDE EFFECTS:
+**
+**      Local storage for device configuration information
+**	is initialized.
+**
+**--
+*/
+static void __init SMC37c669_init_local_config ( void )
+{
+    SMC37c669_SERIAL_BASE_ADDRESS_REGISTER uart_base;
+    SMC37c669_SERIAL_IRQ_REGISTER uart_irqs;
+    SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER ppt_base;
+    SMC37c669_PARALLEL_FDC_IRQ_REGISTER ppt_fdc_irqs;
+    SMC37c669_PARALLEL_FDC_DRQ_REGISTER ppt_fdc_drqs;
+    SMC37c669_FDC_BASE_ADDRESS_REGISTER fdc_base;
+    SMC37c669_IDE_ADDRESS_REGISTER ide_base;
+    SMC37c669_IDE_ADDRESS_REGISTER ide_alt;
+
+/*
+** Get serial port 1 base address 
+*/
+    uart_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX );
+/*
+** Get IRQs for serial ports 1 & 2
+*/
+    uart_irqs.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX );
+/*
+** Store local configuration information for serial port 1
+*/
+    local_config[SERIAL_0].port1 = uart_base.by_field.addr9_3 << 3;
+    local_config[SERIAL_0].irq = 
+	SMC37c669_xlate_irq( 
+	    SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart1_irq ) 
+	);
+/*
+** Get serial port 2 base address
+*/
+    uart_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX );
+/*
+** Store local configuration information for serial port 2
+*/
+    local_config[SERIAL_1].port1 = uart_base.by_field.addr9_3 << 3;
+    local_config[SERIAL_1].irq = 
+	SMC37c669_xlate_irq( 
+	    SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart2_irq ) 
+	);
+/*
+** Get parallel port base address
+*/
+    ppt_base.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX );
+/*
+** Get IRQs for parallel port and floppy controller
+*/
+    ppt_fdc_irqs.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX );
+/*
+** Get DRQs for parallel port and floppy controller
+*/
+    ppt_fdc_drqs.as_uchar =
+	SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX );
+/*
+** Store local configuration information for parallel port
+*/
+    local_config[PARALLEL_0].port1 = ppt_base.by_field.addr9_2 << 2;
+    local_config[PARALLEL_0].irq =
+	SMC37c669_xlate_irq(
+	    SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.ppt_irq )
+	);
+    local_config[PARALLEL_0].drq =
+	SMC37c669_xlate_drq(
+	    SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.ppt_drq )
+	);
+/*
+** Get floppy controller base address
+*/
+    fdc_base.as_uchar = 
+	SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX );
+/*
+** Store local configuration information for floppy controller
+*/
+    local_config[FLOPPY_0].port1 = fdc_base.by_field.addr9_4 << 4;
+    local_config[FLOPPY_0].irq =
+	SMC37c669_xlate_irq(
+	    SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.fdc_irq )
+	);
+    local_config[FLOPPY_0].drq =
+	SMC37c669_xlate_drq(
+	    SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.fdc_drq )
+	);
+/*
+** Get IDE controller base address
+*/
+    ide_base.as_uchar =
+	SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX );
+/*
+** Get IDE alternate status base address
+*/
+    ide_alt.as_uchar =
+	SMC37c669_read_config( SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX );
+/*
+** Store local configuration information for IDE controller
+*/
+    local_config[IDE_0].port1 = ide_base.by_field.addr9_4 << 4;
+    local_config[IDE_0].port2 = ide_alt.by_field.addr9_4 << 4;
+    local_config[IDE_0].irq = 14;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function returns a pointer to the local shadow
+**	configuration of the requested device function.
+**
+**  FORMAL PARAMETERS:
+**
+**      func:
+**          Which device function
+**
+**  RETURN VALUE:
+**
+**      Returns a pointer to the DEVICE_CONFIG structure for the
+**	requested function, otherwise, NULL.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static struct DEVICE_CONFIG * __init SMC37c669_get_config( unsigned int func )
+{
+    struct DEVICE_CONFIG *cp = NULL;
+
+    switch ( func ) {
+    	case SERIAL_0:
+	    cp = &local_config[ SERIAL_0 ];
+	    break;
+	case SERIAL_1:
+	    cp = &local_config[ SERIAL_1 ];
+	    break;
+	case PARALLEL_0:
+	    cp = &local_config[ PARALLEL_0 ];
+	    break;
+	case FLOPPY_0:
+	    cp = &local_config[ FLOPPY_0 ];
+	    break;
+	case IDE_0:
+	    cp = &local_config[ IDE_0 ];
+	    break;
+    }
+    return cp;
+}
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function translates IRQs back and forth between ISA
+**	IRQs and SMC37c669 device IRQs.
+**
+**  FORMAL PARAMETERS:
+**
+**      irq:
+**          The IRQ to translate
+**
+**  RETURN VALUE:
+**
+**      Returns the translated IRQ, otherwise, returns -1.
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static int __init SMC37c669_xlate_irq ( unsigned int irq )
+{
+    int i, translated_irq = -1;
+
+    if ( SMC37c669_IS_DEVICE_IRQ( irq ) ) {
+/*
+** We are translating a device IRQ to an ISA IRQ
+*/
+    	for ( i = 0; ( SMC37c669_irq_table[i].device_irq != -1 ) || ( SMC37c669_irq_table[i].isa_irq != -1 ); i++ ) {
+	    if ( irq == SMC37c669_irq_table[i].device_irq ) {
+	    	translated_irq = SMC37c669_irq_table[i].isa_irq;
+		break;
+	    }
+	}
+    }
+    else {
+/*
+** We are translating an ISA IRQ to a device IRQ
+*/
+    	for ( i = 0; ( SMC37c669_irq_table[i].isa_irq != -1 ) || ( SMC37c669_irq_table[i].device_irq != -1 ); i++ ) {
+	    if ( irq == SMC37c669_irq_table[i].isa_irq ) {
+	    	translated_irq = SMC37c669_irq_table[i].device_irq;
+		break;
+	    }
+	}
+    }
+    return translated_irq;
+}
+
+
+/*
+**++
+**  FUNCTIONAL DESCRIPTION:
+**
+**      This function translates DMA channels back and forth between
+**	ISA DMA channels and SMC37c669 device DMA channels.
+**
+**  FORMAL PARAMETERS:
+**
+**      drq:
+**          The DMA channel to translate
+**
+**  RETURN VALUE:
+**
+**      Returns the translated DMA channel, otherwise, returns -1
+**
+**  SIDE EFFECTS:
+**
+**      {@description or none@}
+**
+**--
+*/
+static int __init SMC37c669_xlate_drq ( unsigned int drq )
+{
+    int i, translated_drq = -1;
+
+    if ( SMC37c669_IS_DEVICE_DRQ( drq ) ) {
+/*
+** We are translating a device DMA channel to an ISA DMA channel
+*/
+    	for ( i = 0; ( SMC37c669_drq_table[i].device_drq != -1 ) || ( SMC37c669_drq_table[i].isa_drq != -1 ); i++ ) {
+	    if ( drq == SMC37c669_drq_table[i].device_drq ) {
+	    	translated_drq = SMC37c669_drq_table[i].isa_drq;
+		break;
+	    }
+	}
+    }
+    else {
+/*
+** We are translating an ISA DMA channel to a device DMA channel
+*/
+    	for ( i = 0; ( SMC37c669_drq_table[i].isa_drq != -1 ) || ( SMC37c669_drq_table[i].device_drq != -1 ); i++ ) {
+	    if ( drq == SMC37c669_drq_table[i].isa_drq ) {
+	    	translated_drq = SMC37c669_drq_table[i].device_drq;
+		break;
+	    }
+	}
+    }
+    return translated_drq;
+}
+
+#if 0
+int __init smcc669_init ( void )
+{
+    struct INODE *ip;
+
+    allocinode( smc_ddb.name, 1, &ip );
+    ip->dva = &smc_ddb;
+    ip->attr = ATTR$M_WRITE | ATTR$M_READ;
+    ip->len[0] = 0x30;
+    ip->misc = 0;
+    INODE_UNLOCK( ip );
+
+    return msg_success;
+}
+
+int __init smcc669_open( struct FILE *fp, char *info, char *next, char *mode )
+{
+    struct INODE *ip;
+/*
+** Allow multiple readers but only one writer.  ip->misc keeps track
+** of the number of writers
+*/
+    ip = fp->ip;
+    INODE_LOCK( ip );
+    if ( fp->mode & ATTR$M_WRITE ) {
+	if ( ip->misc ) {
+	    INODE_UNLOCK( ip );
+	    return msg_failure;	    /* too many writers */
+	}
+	ip->misc++;
+    }
+/*
+** Treat the information field as a byte offset
+*/
+    *fp->offset = xtoi( info );
+    INODE_UNLOCK( ip );
+
+    return msg_success;
+}
+
+int __init smcc669_close( struct FILE *fp )
+{
+    struct INODE *ip;
+
+    ip = fp->ip;
+    if ( fp->mode & ATTR$M_WRITE ) {
+	INODE_LOCK( ip );
+	ip->misc--;
+	INODE_UNLOCK( ip );
+    }
+    return msg_success;
+}
+
+int __init smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf )
+{
+    int i;
+    int length;
+    int nbytes;
+    struct INODE *ip;
+
+/*
+** Always access a byte at a time
+*/
+    ip = fp->ip;
+    length = size * number;
+    nbytes = 0;
+
+    SMC37c669_config_mode( TRUE );
+    for ( i = 0; i < length; i++ ) {
+	if ( !inrange( *fp->offset, 0, ip->len[0] ) ) 
+	    break;
+	*buf++ = SMC37c669_read_config( *fp->offset );
+	*fp->offset += 1;
+	nbytes++;
+    }
+    SMC37c669_config_mode( FALSE );
+    return nbytes;
+}
+
+int __init smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf )
+{
+    int i;
+    int length;
+    int nbytes;
+    struct INODE *ip;
+/*
+** Always access a byte at a time
+*/
+    ip = fp->ip;
+    length = size * number;
+    nbytes = 0;
+
+    SMC37c669_config_mode( TRUE );
+    for ( i = 0; i < length; i++ ) {
+	if ( !inrange( *fp->offset, 0, ip->len[0] ) ) 
+	    break;
+	SMC37c669_write_config( *fp->offset, *buf );
+	*fp->offset += 1;
+	buf++;
+	nbytes++;
+    }
+    SMC37c669_config_mode( FALSE );
+    return nbytes;
+}
+#endif
+
+void __init
+SMC37c669_dump_registers(void)
+{
+  int i;
+  for (i = 0; i <= 0x29; i++)
+    printk("-- CR%02x : %02x\n", i, SMC37c669_read_config(i));
+}
+/*+
+ * ============================================================================
+ * = SMC_init - SMC37c669 Super I/O controller initialization                 =
+ * ============================================================================
+ *
+ * OVERVIEW:
+ *
+ *      This routine configures and enables device functions on the
+ *      SMC37c669 Super I/O controller.
+ *
+ * FORM OF CALL:
+ *
+ *      SMC_init( );
+ *
+ * RETURNS:
+ *
+ *      Nothing
+ *
+ * ARGUMENTS:
+ *
+ *      None
+ *
+ * SIDE EFFECTS:
+ *
+ *      None
+ *
+ */
+void __init SMC669_Init ( void )
+{
+    SMC37c669_CONFIG_REGS *SMC_base;
+
+    if ( ( SMC_base = SMC37c669_detect( ) ) != NULL ) {
+        printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n",
+		(unsigned long) SMC_base );
+#if SMC_DEBUG
+	SMC37c669_config_mode( TRUE );
+	SMC37c669_dump_registers( );
+	SMC37c669_config_mode( FALSE );
+        SMC37c669_display_device_info( );
+#endif
+        SMC37c669_disable_device( SERIAL_0 );
+        SMC37c669_configure_device(
+            SERIAL_0,
+            COM1_BASE,
+            COM1_IRQ,
+            -1
+        );
+        SMC37c669_enable_device( SERIAL_0 );
+
+        SMC37c669_disable_device( SERIAL_1 );
+        SMC37c669_configure_device(
+            SERIAL_1,
+            COM2_BASE,
+            COM2_IRQ,
+            -1
+        );
+        SMC37c669_enable_device( SERIAL_1 );
+
+        SMC37c669_disable_device( PARALLEL_0 );
+        SMC37c669_configure_device(
+            PARALLEL_0,
+            PARP_BASE,
+            PARP_IRQ,
+            PARP_DRQ
+        );
+        SMC37c669_enable_device( PARALLEL_0 );
+
+        SMC37c669_disable_device( FLOPPY_0 );
+        SMC37c669_configure_device(
+            FLOPPY_0,
+            FDC_BASE,
+            FDC_IRQ,
+            FDC_DRQ
+        );
+        SMC37c669_enable_device( FLOPPY_0 );
+          
+        SMC37c669_disable_device( IDE_0 );
+
+#if SMC_DEBUG
+	SMC37c669_config_mode( TRUE );
+	SMC37c669_dump_registers( );
+	SMC37c669_config_mode( FALSE );
+        SMC37c669_display_device_info( );
+#endif
+    }
+    else {
+#if SMC_DEBUG
+        printk( "No SMC37c669 Super I/O Controller found\n" );
+#endif
+    }
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov