/* * SMC 37C669 initialization code */ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <asm/hwrpb.h> #include <asm/io.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( int ); 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. * *-- */ #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; int irq; 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 */ }; /* ** The following definition is for the MONET (XP1000) IRQ ** translation table. */ static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_monet_irq_table[] __initdata = { { SMC37c669_DEVICE_IRQ_A, -1 }, { SMC37c669_DEVICE_IRQ_B, -1 }, { SMC37c669_DEVICE_IRQ_C, 6 }, { SMC37c669_DEVICE_IRQ_D, 7 }, { SMC37c669_DEVICE_IRQ_E, 4 }, { SMC37c669_DEVICE_IRQ_F, 3 }, { SMC37c669_DEVICE_IRQ_H, -1 }, { -1, -1 } /* End of table */ }; static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_tables[] __initdata = { SMC37c669_default_irq_table, SMC37c669_monet_irq_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( int irq ); static int SMC37c669_xlate_drq( int drq ); static __cacheline_aligned DEFINE_SPINLOCK(smc_lock); /* **++ ** 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( int index ) { 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_irq_tables[ index ]; 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 it 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. */ spin_lock(&smc_lock); wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); spin_unlock(&smc_lock); } 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 ) { wb(&SMC37c669->index_port, index); return rb(&SMC37c669->data_port); } /* **++ ** 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 ( 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 ( 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 ( int index ) { SMC37c669_CONFIG_REGS *SMC_base; unsigned long flags; local_irq_save(flags); if ( ( SMC_base = SMC37c669_detect( index ) ) != NULL ) { #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 ); /* Wake up sometimes forgotten floppy, especially on DP264. */ outb(0xc, 0x3f2); SMC37c669_disable_device( IDE_0 ); #if SMC_DEBUG SMC37c669_config_mode( TRUE ); SMC37c669_dump_registers( ); SMC37c669_config_mode( FALSE ); SMC37c669_display_device_info( ); #endif local_irq_restore(flags); printk( "SMC37c669 Super I/O Controller found @ 0x%p\n", SMC_base ); } else { local_irq_restore(flags); #if SMC_DEBUG printk( "No SMC37c669 Super I/O Controller found\n" ); #endif } }