Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * SMC 37C93X initialization code |
| 3 | */ |
| 4 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5 | #include <linux/kernel.h> |
| 6 | |
| 7 | #include <linux/slab.h> |
| 8 | #include <linux/mm.h> |
| 9 | #include <linux/init.h> |
| 10 | #include <linux/delay.h> |
| 11 | |
| 12 | #include <asm/hwrpb.h> |
| 13 | #include <asm/io.h> |
| 14 | #include <asm/segment.h> |
| 15 | |
| 16 | #define SMC_DEBUG 0 |
| 17 | |
| 18 | #if SMC_DEBUG |
| 19 | # define DBG_DEVS(args) printk args |
| 20 | #else |
| 21 | # define DBG_DEVS(args) |
| 22 | #endif |
| 23 | |
| 24 | #define KB 1024 |
| 25 | #define MB (1024*KB) |
| 26 | #define GB (1024*MB) |
| 27 | |
| 28 | /* device "activate" register contents */ |
| 29 | #define DEVICE_ON 1 |
| 30 | #define DEVICE_OFF 0 |
| 31 | |
| 32 | /* configuration on/off keys */ |
| 33 | #define CONFIG_ON_KEY 0x55 |
| 34 | #define CONFIG_OFF_KEY 0xaa |
| 35 | |
| 36 | /* configuration space device definitions */ |
| 37 | #define FDC 0 |
| 38 | #define IDE1 1 |
| 39 | #define IDE2 2 |
| 40 | #define PARP 3 |
| 41 | #define SER1 4 |
| 42 | #define SER2 5 |
| 43 | #define RTCL 6 |
| 44 | #define KYBD 7 |
| 45 | #define AUXIO 8 |
| 46 | |
| 47 | /* Chip register offsets from base */ |
| 48 | #define CONFIG_CONTROL 0x02 |
| 49 | #define INDEX_ADDRESS 0x03 |
| 50 | #define LOGICAL_DEVICE_NUMBER 0x07 |
| 51 | #define DEVICE_ID 0x20 |
| 52 | #define DEVICE_REV 0x21 |
| 53 | #define POWER_CONTROL 0x22 |
| 54 | #define POWER_MGMT 0x23 |
| 55 | #define OSC 0x24 |
| 56 | |
| 57 | #define ACTIVATE 0x30 |
| 58 | #define ADDR_HI 0x60 |
| 59 | #define ADDR_LO 0x61 |
| 60 | #define INTERRUPT_SEL 0x70 |
| 61 | #define INTERRUPT_SEL_2 0x72 /* KYBD/MOUS only */ |
| 62 | #define DMA_CHANNEL_SEL 0x74 /* FDC/PARP only */ |
| 63 | |
| 64 | #define FDD_MODE_REGISTER 0x90 |
| 65 | #define FDD_OPTION_REGISTER 0x91 |
| 66 | |
| 67 | /* values that we read back that are expected ... */ |
| 68 | #define VALID_DEVICE_ID 2 |
| 69 | |
| 70 | /* default device addresses */ |
| 71 | #define KYBD_INTERRUPT 1 |
| 72 | #define MOUS_INTERRUPT 12 |
| 73 | #define COM2_BASE 0x2f8 |
| 74 | #define COM2_INTERRUPT 3 |
| 75 | #define COM1_BASE 0x3f8 |
| 76 | #define COM1_INTERRUPT 4 |
| 77 | #define PARP_BASE 0x3bc |
| 78 | #define PARP_INTERRUPT 7 |
| 79 | |
| 80 | static unsigned long __init SMCConfigState(unsigned long baseAddr) |
| 81 | { |
| 82 | unsigned char devId; |
| 83 | unsigned char devRev; |
| 84 | |
| 85 | unsigned long configPort; |
| 86 | unsigned long indexPort; |
| 87 | unsigned long dataPort; |
| 88 | |
| 89 | int i; |
| 90 | |
| 91 | configPort = indexPort = baseAddr; |
| 92 | dataPort = configPort + 1; |
| 93 | |
| 94 | #define NUM_RETRIES 5 |
| 95 | |
| 96 | for (i = 0; i < NUM_RETRIES; i++) |
| 97 | { |
| 98 | outb(CONFIG_ON_KEY, configPort); |
| 99 | outb(CONFIG_ON_KEY, configPort); |
| 100 | outb(DEVICE_ID, indexPort); |
| 101 | devId = inb(dataPort); |
| 102 | if (devId == VALID_DEVICE_ID) { |
| 103 | outb(DEVICE_REV, indexPort); |
| 104 | devRev = inb(dataPort); |
| 105 | break; |
| 106 | } |
| 107 | else |
| 108 | udelay(100); |
| 109 | } |
| 110 | return (i != NUM_RETRIES) ? baseAddr : 0L; |
| 111 | } |
| 112 | |
| 113 | static void __init SMCRunState(unsigned long baseAddr) |
| 114 | { |
| 115 | outb(CONFIG_OFF_KEY, baseAddr); |
| 116 | } |
| 117 | |
| 118 | static unsigned long __init SMCDetectUltraIO(void) |
| 119 | { |
| 120 | unsigned long baseAddr; |
| 121 | |
| 122 | baseAddr = 0x3F0; |
| 123 | if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { |
| 124 | return( baseAddr ); |
| 125 | } |
| 126 | baseAddr = 0x370; |
| 127 | if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { |
| 128 | return( baseAddr ); |
| 129 | } |
| 130 | return( ( unsigned long )0 ); |
| 131 | } |
| 132 | |
| 133 | static void __init SMCEnableDevice(unsigned long baseAddr, |
| 134 | unsigned long device, |
| 135 | unsigned long portaddr, |
| 136 | unsigned long interrupt) |
| 137 | { |
| 138 | unsigned long indexPort; |
| 139 | unsigned long dataPort; |
| 140 | |
| 141 | indexPort = baseAddr; |
| 142 | dataPort = baseAddr + 1; |
| 143 | |
| 144 | outb(LOGICAL_DEVICE_NUMBER, indexPort); |
| 145 | outb(device, dataPort); |
| 146 | |
| 147 | outb(ADDR_LO, indexPort); |
| 148 | outb(( portaddr & 0xFF ), dataPort); |
| 149 | |
| 150 | outb(ADDR_HI, indexPort); |
| 151 | outb((portaddr >> 8) & 0xFF, dataPort); |
| 152 | |
| 153 | outb(INTERRUPT_SEL, indexPort); |
| 154 | outb(interrupt, dataPort); |
| 155 | |
| 156 | outb(ACTIVATE, indexPort); |
| 157 | outb(DEVICE_ON, dataPort); |
| 158 | } |
| 159 | |
| 160 | static void __init SMCEnableKYBD(unsigned long baseAddr) |
| 161 | { |
| 162 | unsigned long indexPort; |
| 163 | unsigned long dataPort; |
| 164 | |
| 165 | indexPort = baseAddr; |
| 166 | dataPort = baseAddr + 1; |
| 167 | |
| 168 | outb(LOGICAL_DEVICE_NUMBER, indexPort); |
| 169 | outb(KYBD, dataPort); |
| 170 | |
| 171 | outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ |
| 172 | outb(KYBD_INTERRUPT, dataPort); |
| 173 | |
| 174 | outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */ |
| 175 | outb(MOUS_INTERRUPT, dataPort); |
| 176 | |
| 177 | outb(ACTIVATE, indexPort); |
| 178 | outb(DEVICE_ON, dataPort); |
| 179 | } |
| 180 | |
| 181 | static void __init SMCEnableFDC(unsigned long baseAddr) |
| 182 | { |
| 183 | unsigned long indexPort; |
| 184 | unsigned long dataPort; |
| 185 | |
| 186 | unsigned char oldValue; |
| 187 | |
| 188 | indexPort = baseAddr; |
| 189 | dataPort = baseAddr + 1; |
| 190 | |
| 191 | outb(LOGICAL_DEVICE_NUMBER, indexPort); |
| 192 | outb(FDC, dataPort); |
| 193 | |
| 194 | outb(FDD_MODE_REGISTER, indexPort); |
| 195 | oldValue = inb(dataPort); |
| 196 | |
| 197 | oldValue |= 0x0E; /* Enable burst mode */ |
| 198 | outb(oldValue, dataPort); |
| 199 | |
| 200 | outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ |
| 201 | outb(0x06, dataPort ); |
| 202 | |
| 203 | outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ |
| 204 | outb(0x02, dataPort); |
| 205 | |
| 206 | outb(ACTIVATE, indexPort); |
| 207 | outb(DEVICE_ON, dataPort); |
| 208 | } |
| 209 | |
| 210 | #if SMC_DEBUG |
| 211 | static void __init SMCReportDeviceStatus(unsigned long baseAddr) |
| 212 | { |
| 213 | unsigned long indexPort; |
| 214 | unsigned long dataPort; |
| 215 | unsigned char currentControl; |
| 216 | |
| 217 | indexPort = baseAddr; |
| 218 | dataPort = baseAddr + 1; |
| 219 | |
| 220 | outb(POWER_CONTROL, indexPort); |
| 221 | currentControl = inb(dataPort); |
| 222 | |
| 223 | printk(currentControl & (1 << FDC) |
| 224 | ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n"); |
| 225 | printk(currentControl & (1 << IDE1) |
| 226 | ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n"); |
| 227 | printk(currentControl & (1 << IDE2) |
| 228 | ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n"); |
| 229 | printk(currentControl & (1 << PARP) |
| 230 | ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n"); |
| 231 | printk(currentControl & (1 << SER1) |
| 232 | ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n"); |
| 233 | printk(currentControl & (1 << SER2) |
| 234 | ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n"); |
| 235 | |
| 236 | printk( "\n" ); |
| 237 | } |
| 238 | #endif |
| 239 | |
| 240 | int __init SMC93x_Init(void) |
| 241 | { |
| 242 | unsigned long SMCUltraBase; |
| 243 | unsigned long flags; |
| 244 | |
| 245 | local_irq_save(flags); |
| 246 | if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) { |
| 247 | #if SMC_DEBUG |
| 248 | SMCReportDeviceStatus(SMCUltraBase); |
| 249 | #endif |
| 250 | SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT); |
| 251 | DBG_DEVS(("SMC FDC37C93X: SER1 done\n")); |
| 252 | SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT); |
| 253 | DBG_DEVS(("SMC FDC37C93X: SER2 done\n")); |
| 254 | SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT); |
| 255 | DBG_DEVS(("SMC FDC37C93X: PARP done\n")); |
| 256 | /* On PC164, IDE on the SMC is not enabled; |
| 257 | CMD646 (PCI) on MB */ |
| 258 | SMCEnableKYBD(SMCUltraBase); |
| 259 | DBG_DEVS(("SMC FDC37C93X: KYB done\n")); |
| 260 | SMCEnableFDC(SMCUltraBase); |
| 261 | DBG_DEVS(("SMC FDC37C93X: FDC done\n")); |
| 262 | #if SMC_DEBUG |
| 263 | SMCReportDeviceStatus(SMCUltraBase); |
| 264 | #endif |
| 265 | SMCRunState(SMCUltraBase); |
| 266 | local_irq_restore(flags); |
| 267 | printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", |
| 268 | SMCUltraBase); |
| 269 | return 1; |
| 270 | } |
| 271 | else { |
| 272 | local_irq_restore(flags); |
| 273 | DBG_DEVS(("No SMC FDC37C93X Ultra I/O Controller found\n")); |
| 274 | return 0; |
| 275 | } |
| 276 | } |