|  | /* Copyright (c) 2014 The Regents of the University of California | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> */ | 
|  |  | 
|  | #include <arch/x86.h> | 
|  | #include <arch/pci.h> | 
|  | #include <trap.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <assert.h> | 
|  | #include <kmalloc.h> | 
|  | #include <time.h> | 
|  | #include <mm.h> | 
|  |  | 
|  | /* super-shoddy LPC chip initialization. | 
|  | * | 
|  | * the PCH (old southbridge) is a c602, TBDF x:1f:00 LPC controller | 
|  | * - PMBASE is the mnemonic for "ACPI Base Address" | 
|  | * - TCO is 0x60 off in the PMBASE space | 
|  | * - PMBASE + 0x30 is an SMI control reg | 
|  | * | 
|  | * TODO: | 
|  | * - why don't we find the other functions (1f.2 and 1f.3, in linux)? | 
|  | */ | 
|  | static void lpc_init_pci(struct pci_device *pcidev) | 
|  | { | 
|  | uint32_t pmbase = pcidev_read32(pcidev, 0x40); | 
|  |  | 
|  | pmbase &= ~1; /* clear bit 0 */ | 
|  | uint32_t smi_ctl = inl(pmbase + 0x30); | 
|  | #if 0 | 
|  | /* halt the tco timer: this busts things, and won't work with the lock | 
|  | * on */ | 
|  | uint16_t tco1 = inw(pmbase + 0x60 + 0x08); | 
|  | if (tco1 & (1 << 12)) { | 
|  | printk("\t\tTCO_LOCK is on!\n"); | 
|  | } else { | 
|  | outw(pmbase + 0x60 + 0x08, tco1 & ~(1 << 11)); | 
|  | tco1 = inw(pmbase + 0x60 + 0x08); | 
|  | } | 
|  | #endif | 
|  | /* bit 6 is another timer, one that messes up c89. */ | 
|  | outl(pmbase + 0x30, smi_ctl & ~(1 << 6)); | 
|  | smi_ctl = inl(pmbase + 0x30); | 
|  | } | 
|  |  | 
|  | void intel_lpc_init(void) | 
|  | { | 
|  | struct pci_device *i; | 
|  |  | 
|  | STAILQ_FOREACH(i, &pci_devices, all_dev) { | 
|  | if ((i->dev == 0x1f) && (i->func == 0x00)) | 
|  | lpc_init_pci(i); | 
|  | } | 
|  | } |