Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/alpha/kernel/core_lca.c |
| 3 | * |
| 4 | * Written by David Mosberger (davidm@cs.arizona.edu) with some code |
| 5 | * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit |
| 6 | * bios code. |
| 7 | * |
| 8 | * Code common to all LCA core logic chips. |
| 9 | */ |
| 10 | |
| 11 | #define __EXTERN_INLINE inline |
| 12 | #include <asm/io.h> |
| 13 | #include <asm/core_lca.h> |
| 14 | #undef __EXTERN_INLINE |
| 15 | |
| 16 | #include <linux/types.h> |
| 17 | #include <linux/pci.h> |
| 18 | #include <linux/init.h> |
| 19 | #include <linux/tty.h> |
| 20 | |
| 21 | #include <asm/ptrace.h> |
| 22 | #include <asm/smp.h> |
| 23 | |
| 24 | #include "proto.h" |
| 25 | #include "pci_impl.h" |
| 26 | |
| 27 | |
| 28 | /* |
| 29 | * BIOS32-style PCI interface: |
| 30 | */ |
| 31 | |
| 32 | /* |
| 33 | * Machine check reasons. Defined according to PALcode sources |
| 34 | * (osf.h and platform.h). |
| 35 | */ |
| 36 | #define MCHK_K_TPERR 0x0080 |
| 37 | #define MCHK_K_TCPERR 0x0082 |
| 38 | #define MCHK_K_HERR 0x0084 |
| 39 | #define MCHK_K_ECC_C 0x0086 |
| 40 | #define MCHK_K_ECC_NC 0x0088 |
| 41 | #define MCHK_K_UNKNOWN 0x008A |
| 42 | #define MCHK_K_CACKSOFT 0x008C |
| 43 | #define MCHK_K_BUGCHECK 0x008E |
| 44 | #define MCHK_K_OS_BUGCHECK 0x0090 |
| 45 | #define MCHK_K_DCPERR 0x0092 |
| 46 | #define MCHK_K_ICPERR 0x0094 |
| 47 | |
| 48 | |
| 49 | /* |
| 50 | * Platform-specific machine-check reasons: |
| 51 | */ |
| 52 | #define MCHK_K_SIO_SERR 0x204 /* all platforms so far */ |
| 53 | #define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */ |
| 54 | #define MCHK_K_DCSR 0x208 /* all but Noname */ |
| 55 | |
| 56 | |
| 57 | /* |
| 58 | * Given a bus, device, and function number, compute resulting |
| 59 | * configuration space address and setup the LCA_IOC_CONF register |
| 60 | * accordingly. It is therefore not safe to have concurrent |
| 61 | * invocations to configuration space access routines, but there |
| 62 | * really shouldn't be any need for this. |
| 63 | * |
| 64 | * Type 0: |
| 65 | * |
| 66 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 |
| 67 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 |
| 68 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 69 | * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| |
| 70 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 71 | * |
| 72 | * 31:11 Device select bit. |
| 73 | * 10:8 Function number |
| 74 | * 7:2 Register number |
| 75 | * |
| 76 | * Type 1: |
| 77 | * |
| 78 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 |
| 79 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 |
| 80 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 81 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| |
| 82 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 83 | * |
| 84 | * 31:24 reserved |
| 85 | * 23:16 bus number (8 bits = 128 possible buses) |
| 86 | * 15:11 Device number (5 bits) |
| 87 | * 10:8 function number |
| 88 | * 7:2 register number |
| 89 | * |
| 90 | * Notes: |
| 91 | * The function number selects which function of a multi-function device |
| 92 | * (e.g., SCSI and Ethernet). |
| 93 | * |
| 94 | * The register selects a DWORD (32 bit) register offset. Hence it |
| 95 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two |
| 96 | * bits. |
| 97 | */ |
| 98 | |
| 99 | static int |
| 100 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, |
| 101 | unsigned long *pci_addr) |
| 102 | { |
| 103 | unsigned long addr; |
| 104 | u8 bus = pbus->number; |
| 105 | |
| 106 | if (bus == 0) { |
| 107 | int device = device_fn >> 3; |
| 108 | int func = device_fn & 0x7; |
| 109 | |
| 110 | /* Type 0 configuration cycle. */ |
| 111 | |
| 112 | if (device > 12) { |
| 113 | return -1; |
| 114 | } |
| 115 | |
| 116 | *(vulp)LCA_IOC_CONF = 0; |
| 117 | addr = (1 << (11 + device)) | (func << 8) | where; |
| 118 | } else { |
| 119 | /* Type 1 configuration cycle. */ |
| 120 | *(vulp)LCA_IOC_CONF = 1; |
| 121 | addr = (bus << 16) | (device_fn << 8) | where; |
| 122 | } |
| 123 | *pci_addr = addr; |
| 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | static unsigned int |
| 128 | conf_read(unsigned long addr) |
| 129 | { |
| 130 | unsigned long flags, code, stat0; |
| 131 | unsigned int value; |
| 132 | |
| 133 | local_irq_save(flags); |
| 134 | |
| 135 | /* Reset status register to avoid loosing errors. */ |
| 136 | stat0 = *(vulp)LCA_IOC_STAT0; |
| 137 | *(vulp)LCA_IOC_STAT0 = stat0; |
| 138 | mb(); |
| 139 | |
| 140 | /* Access configuration space. */ |
| 141 | value = *(vuip)addr; |
| 142 | draina(); |
| 143 | |
| 144 | stat0 = *(vulp)LCA_IOC_STAT0; |
| 145 | if (stat0 & LCA_IOC_STAT0_ERR) { |
| 146 | code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) |
| 147 | & LCA_IOC_STAT0_CODE_MASK); |
| 148 | if (code != 1) { |
| 149 | printk("lca.c:conf_read: got stat0=%lx\n", stat0); |
| 150 | } |
| 151 | |
| 152 | /* Reset error status. */ |
| 153 | *(vulp)LCA_IOC_STAT0 = stat0; |
| 154 | mb(); |
| 155 | |
| 156 | /* Reset machine check. */ |
| 157 | wrmces(0x7); |
| 158 | |
| 159 | value = 0xffffffff; |
| 160 | } |
| 161 | local_irq_restore(flags); |
| 162 | return value; |
| 163 | } |
| 164 | |
| 165 | static void |
| 166 | conf_write(unsigned long addr, unsigned int value) |
| 167 | { |
| 168 | unsigned long flags, code, stat0; |
| 169 | |
| 170 | local_irq_save(flags); /* avoid getting hit by machine check */ |
| 171 | |
| 172 | /* Reset status register to avoid loosing errors. */ |
| 173 | stat0 = *(vulp)LCA_IOC_STAT0; |
| 174 | *(vulp)LCA_IOC_STAT0 = stat0; |
| 175 | mb(); |
| 176 | |
| 177 | /* Access configuration space. */ |
| 178 | *(vuip)addr = value; |
| 179 | draina(); |
| 180 | |
| 181 | stat0 = *(vulp)LCA_IOC_STAT0; |
| 182 | if (stat0 & LCA_IOC_STAT0_ERR) { |
| 183 | code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) |
| 184 | & LCA_IOC_STAT0_CODE_MASK); |
| 185 | if (code != 1) { |
| 186 | printk("lca.c:conf_write: got stat0=%lx\n", stat0); |
| 187 | } |
| 188 | |
| 189 | /* Reset error status. */ |
| 190 | *(vulp)LCA_IOC_STAT0 = stat0; |
| 191 | mb(); |
| 192 | |
| 193 | /* Reset machine check. */ |
| 194 | wrmces(0x7); |
| 195 | } |
| 196 | local_irq_restore(flags); |
| 197 | } |
| 198 | |
| 199 | static int |
| 200 | lca_read_config(struct pci_bus *bus, unsigned int devfn, int where, |
| 201 | int size, u32 *value) |
| 202 | { |
| 203 | unsigned long addr, pci_addr; |
| 204 | long mask; |
| 205 | int shift; |
| 206 | |
| 207 | if (mk_conf_addr(bus, devfn, where, &pci_addr)) |
| 208 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 209 | |
| 210 | shift = (where & 3) * 8; |
| 211 | mask = (size - 1) * 8; |
| 212 | addr = (pci_addr << 5) + mask + LCA_CONF; |
| 213 | *value = conf_read(addr) >> (shift); |
| 214 | return PCIBIOS_SUCCESSFUL; |
| 215 | } |
| 216 | |
| 217 | static int |
| 218 | lca_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, |
| 219 | u32 value) |
| 220 | { |
| 221 | unsigned long addr, pci_addr; |
| 222 | long mask; |
| 223 | |
| 224 | if (mk_conf_addr(bus, devfn, where, &pci_addr)) |
| 225 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 226 | |
| 227 | mask = (size - 1) * 8; |
| 228 | addr = (pci_addr << 5) + mask + LCA_CONF; |
| 229 | conf_write(addr, value << ((where & 3) * 8)); |
| 230 | return PCIBIOS_SUCCESSFUL; |
| 231 | } |
| 232 | |
| 233 | struct pci_ops lca_pci_ops = |
| 234 | { |
| 235 | .read = lca_read_config, |
| 236 | .write = lca_write_config, |
| 237 | }; |
| 238 | |
| 239 | void |
| 240 | lca_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) |
| 241 | { |
| 242 | wmb(); |
| 243 | *(vulp)LCA_IOC_TBIA = 0; |
| 244 | mb(); |
| 245 | } |
| 246 | |
| 247 | void __init |
| 248 | lca_init_arch(void) |
| 249 | { |
| 250 | struct pci_controller *hose; |
| 251 | |
| 252 | /* |
| 253 | * Create our single hose. |
| 254 | */ |
| 255 | |
| 256 | pci_isa_hose = hose = alloc_pci_controller(); |
| 257 | hose->io_space = &ioport_resource; |
| 258 | hose->mem_space = &iomem_resource; |
| 259 | hose->index = 0; |
| 260 | |
| 261 | hose->sparse_mem_base = LCA_SPARSE_MEM - IDENT_ADDR; |
| 262 | hose->dense_mem_base = LCA_DENSE_MEM - IDENT_ADDR; |
| 263 | hose->sparse_io_base = LCA_IO - IDENT_ADDR; |
| 264 | hose->dense_io_base = 0; |
| 265 | |
| 266 | /* |
| 267 | * Set up the PCI to main memory translation windows. |
| 268 | * |
| 269 | * Mimic the SRM settings for the direct-map window. |
| 270 | * Window 0 is scatter-gather 8MB at 8MB (for isa). |
| 271 | * Window 1 is direct access 1GB at 1GB. |
| 272 | * |
| 273 | * Note that we do not try to save any of the DMA window CSRs |
| 274 | * before setting them, since we cannot read those CSRs on LCA. |
| 275 | */ |
| 276 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); |
| 277 | hose->sg_pci = NULL; |
| 278 | __direct_map_base = 0x40000000; |
| 279 | __direct_map_size = 0x40000000; |
| 280 | |
| 281 | *(vulp)LCA_IOC_W_BASE0 = hose->sg_isa->dma_base | (3UL << 32); |
| 282 | *(vulp)LCA_IOC_W_MASK0 = (hose->sg_isa->size - 1) & 0xfff00000; |
| 283 | *(vulp)LCA_IOC_T_BASE0 = virt_to_phys(hose->sg_isa->ptes); |
| 284 | |
| 285 | *(vulp)LCA_IOC_W_BASE1 = __direct_map_base | (2UL << 32); |
| 286 | *(vulp)LCA_IOC_W_MASK1 = (__direct_map_size - 1) & 0xfff00000; |
| 287 | *(vulp)LCA_IOC_T_BASE1 = 0; |
| 288 | |
| 289 | *(vulp)LCA_IOC_TB_ENA = 0x80; |
| 290 | |
| 291 | lca_pci_tbi(hose, 0, -1); |
| 292 | |
| 293 | /* |
| 294 | * Disable PCI parity for now. The NCR53c810 chip has |
| 295 | * troubles meeting the PCI spec which results in |
| 296 | * data parity errors. |
| 297 | */ |
| 298 | *(vulp)LCA_IOC_PAR_DIS = 1UL<<5; |
| 299 | |
| 300 | /* |
| 301 | * Finally, set up for restoring the correct HAE if using SRM. |
| 302 | * Again, since we cannot read many of the CSRs on the LCA, |
| 303 | * one of which happens to be the HAE, we save the value that |
| 304 | * the SRM will expect... |
| 305 | */ |
| 306 | if (alpha_using_srm) |
| 307 | srm_hae = 0x80000000UL; |
| 308 | } |
| 309 | |
| 310 | /* |
| 311 | * Constants used during machine-check handling. I suppose these |
| 312 | * could be moved into lca.h but I don't see much reason why anybody |
| 313 | * else would want to use them. |
| 314 | */ |
| 315 | |
| 316 | #define ESR_EAV (1UL<< 0) /* error address valid */ |
| 317 | #define ESR_CEE (1UL<< 1) /* correctable error */ |
| 318 | #define ESR_UEE (1UL<< 2) /* uncorrectable error */ |
| 319 | #define ESR_WRE (1UL<< 3) /* write-error */ |
| 320 | #define ESR_SOR (1UL<< 4) /* error source */ |
| 321 | #define ESR_CTE (1UL<< 7) /* cache-tag error */ |
| 322 | #define ESR_MSE (1UL<< 9) /* multiple soft errors */ |
| 323 | #define ESR_MHE (1UL<<10) /* multiple hard errors */ |
| 324 | #define ESR_NXM (1UL<<12) /* non-existent memory */ |
| 325 | |
| 326 | #define IOC_ERR ( 1<<4) /* ioc logs an error */ |
| 327 | #define IOC_CMD_SHIFT 0 |
| 328 | #define IOC_CMD (0xf<<IOC_CMD_SHIFT) |
| 329 | #define IOC_CODE_SHIFT 8 |
| 330 | #define IOC_CODE (0xf<<IOC_CODE_SHIFT) |
| 331 | #define IOC_LOST ( 1<<5) |
| 332 | #define IOC_P_NBR ((__u32) ~((1<<13) - 1)) |
| 333 | |
| 334 | static void |
| 335 | mem_error(unsigned long esr, unsigned long ear) |
| 336 | { |
| 337 | printk(" %s %s error to %s occurred at address %x\n", |
| 338 | ((esr & ESR_CEE) ? "Correctable" : |
| 339 | (esr & ESR_UEE) ? "Uncorrectable" : "A"), |
| 340 | (esr & ESR_WRE) ? "write" : "read", |
| 341 | (esr & ESR_SOR) ? "memory" : "b-cache", |
| 342 | (unsigned) (ear & 0x1ffffff8)); |
| 343 | if (esr & ESR_CTE) { |
| 344 | printk(" A b-cache tag parity error was detected.\n"); |
| 345 | } |
| 346 | if (esr & ESR_MSE) { |
| 347 | printk(" Several other correctable errors occurred.\n"); |
| 348 | } |
| 349 | if (esr & ESR_MHE) { |
| 350 | printk(" Several other uncorrectable errors occurred.\n"); |
| 351 | } |
| 352 | if (esr & ESR_NXM) { |
| 353 | printk(" Attempted to access non-existent memory.\n"); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | static void |
| 358 | ioc_error(__u32 stat0, __u32 stat1) |
| 359 | { |
| 360 | static const char * const pci_cmd[] = { |
| 361 | "Interrupt Acknowledge", "Special", "I/O Read", "I/O Write", |
| 362 | "Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", |
| 363 | "Rsvd4", "Configuration Read", "Configuration Write", |
| 364 | "Memory Read Multiple", "Dual Address", "Memory Read Line", |
| 365 | "Memory Write and Invalidate" |
| 366 | }; |
| 367 | static const char * const err_name[] = { |
| 368 | "exceeded retry limit", "no device", "bad data parity", |
| 369 | "target abort", "bad address parity", "page table read error", |
| 370 | "invalid page", "data error" |
| 371 | }; |
| 372 | unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT; |
| 373 | unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT; |
| 374 | |
| 375 | printk(" %s initiated PCI %s cycle to address %x" |
| 376 | " failed due to %s.\n", |
| 377 | code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]); |
| 378 | |
| 379 | if (code == 5 || code == 6) { |
| 380 | printk(" (Error occurred at PCI memory address %x.)\n", |
| 381 | (stat0 & ~IOC_P_NBR)); |
| 382 | } |
| 383 | if (stat0 & IOC_LOST) { |
| 384 | printk(" Other PCI errors occurred simultaneously.\n"); |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | void |
| 389 | lca_machine_check(unsigned long vector, unsigned long la_ptr, |
| 390 | struct pt_regs *regs) |
| 391 | { |
| 392 | const char * reason; |
| 393 | union el_lca el; |
| 394 | |
| 395 | el.c = (struct el_common *) la_ptr; |
| 396 | |
| 397 | wrmces(rdmces()); /* reset machine check pending flag */ |
| 398 | |
| 399 | printk(KERN_CRIT "LCA machine check: vector=%#lx pc=%#lx code=%#x\n", |
| 400 | vector, regs->pc, (unsigned int) el.c->code); |
| 401 | |
| 402 | /* |
| 403 | * The first quadword after the common header always seems to |
| 404 | * be the machine check reason---don't know why this isn't |
| 405 | * part of the common header instead. In the case of a long |
| 406 | * logout frame, the upper 32 bits is the machine check |
| 407 | * revision level, which we ignore for now. |
| 408 | */ |
| 409 | switch ((unsigned int) el.c->code) { |
| 410 | case MCHK_K_TPERR: reason = "tag parity error"; break; |
| 411 | case MCHK_K_TCPERR: reason = "tag control parity error"; break; |
| 412 | case MCHK_K_HERR: reason = "access to non-existent memory"; break; |
| 413 | case MCHK_K_ECC_C: reason = "correctable ECC error"; break; |
| 414 | case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; |
| 415 | case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; |
| 416 | case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; |
| 417 | case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; |
| 418 | case MCHK_K_DCPERR: reason = "d-cache parity error"; break; |
| 419 | case MCHK_K_ICPERR: reason = "i-cache parity error"; break; |
| 420 | case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on PCI bus"; break; |
| 421 | case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; |
| 422 | case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; |
| 423 | case MCHK_K_UNKNOWN: |
| 424 | default: reason = "unknown"; break; |
| 425 | } |
| 426 | |
| 427 | switch (el.c->size) { |
| 428 | case sizeof(struct el_lca_mcheck_short): |
| 429 | printk(KERN_CRIT |
| 430 | " Reason: %s (short frame%s, dc_stat=%#lx):\n", |
| 431 | reason, el.c->retry ? ", retryable" : "", |
| 432 | el.s->dc_stat); |
| 433 | if (el.s->esr & ESR_EAV) { |
| 434 | mem_error(el.s->esr, el.s->ear); |
| 435 | } |
| 436 | if (el.s->ioc_stat0 & IOC_ERR) { |
| 437 | ioc_error(el.s->ioc_stat0, el.s->ioc_stat1); |
| 438 | } |
| 439 | break; |
| 440 | |
| 441 | case sizeof(struct el_lca_mcheck_long): |
| 442 | printk(KERN_CRIT " Reason: %s (long frame%s):\n", |
| 443 | reason, el.c->retry ? ", retryable" : ""); |
| 444 | printk(KERN_CRIT |
| 445 | " reason: %#lx exc_addr: %#lx dc_stat: %#lx\n", |
| 446 | el.l->pt[0], el.l->exc_addr, el.l->dc_stat); |
| 447 | printk(KERN_CRIT " car: %#lx\n", el.l->car); |
| 448 | if (el.l->esr & ESR_EAV) { |
| 449 | mem_error(el.l->esr, el.l->ear); |
| 450 | } |
| 451 | if (el.l->ioc_stat0 & IOC_ERR) { |
| 452 | ioc_error(el.l->ioc_stat0, el.l->ioc_stat1); |
| 453 | } |
| 454 | break; |
| 455 | |
| 456 | default: |
| 457 | printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size); |
| 458 | } |
| 459 | |
| 460 | /* Dump the logout area to give all info. */ |
| 461 | #ifdef CONFIG_VERBOSE_MCHECK |
| 462 | if (alpha_verbose_mcheck > 1) { |
| 463 | unsigned long * ptr = (unsigned long *) la_ptr; |
| 464 | long i; |
| 465 | for (i = 0; i < el.c->size / sizeof(long); i += 2) { |
| 466 | printk(KERN_CRIT " +%8lx %016lx %016lx\n", |
| 467 | i*sizeof(long), ptr[i], ptr[i+1]); |
| 468 | } |
| 469 | } |
| 470 | #endif /* CONFIG_VERBOSE_MCHECK */ |
| 471 | } |
| 472 | |
| 473 | /* |
| 474 | * The following routines are needed to support the SPEED changing |
| 475 | * necessary to successfully manage the thermal problem on the AlphaBook1. |
| 476 | */ |
| 477 | |
| 478 | void |
| 479 | lca_clock_print(void) |
| 480 | { |
| 481 | long pmr_reg; |
| 482 | |
| 483 | pmr_reg = LCA_READ_PMR; |
| 484 | |
| 485 | printk("Status of clock control:\n"); |
| 486 | printk("\tPrimary clock divisor\t0x%lx\n", LCA_GET_PRIMARY(pmr_reg)); |
| 487 | printk("\tOverride clock divisor\t0x%lx\n", LCA_GET_OVERRIDE(pmr_reg)); |
| 488 | printk("\tInterrupt override is %s\n", |
| 489 | (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); |
| 490 | printk("\tDMA override is %s\n", |
| 491 | (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); |
| 492 | |
| 493 | } |
| 494 | |
| 495 | int |
| 496 | lca_get_clock(void) |
| 497 | { |
| 498 | long pmr_reg; |
| 499 | |
| 500 | pmr_reg = LCA_READ_PMR; |
| 501 | return(LCA_GET_PRIMARY(pmr_reg)); |
| 502 | |
| 503 | } |
| 504 | |
| 505 | void |
| 506 | lca_clock_fiddle(int divisor) |
| 507 | { |
| 508 | long pmr_reg; |
| 509 | |
| 510 | pmr_reg = LCA_READ_PMR; |
| 511 | LCA_SET_PRIMARY_CLOCK(pmr_reg, divisor); |
| 512 | /* lca_norm_clock = divisor; */ |
| 513 | LCA_WRITE_PMR(pmr_reg); |
| 514 | mb(); |
| 515 | } |