/* * tables.c - ACPI tables, chipset, and errata handling * * Copyright (C) 2000 Andrew Henroid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "acpi.h" #include "driver.h" struct acpi_facp acpi_facp; #define ACPI_DUMMY_CHECKSUM 9 #define ACPI_DUMMY_PBLK 51 static u8 acpi_dummy_dsdt[] = { 0x44, 0x53, 0x44, 0x54, // "DSDT" 0x38, 0x00, 0x00, 0x00, // length 0x01, // revision 0x00, // checksum 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x00, // "LINUX" 0x44, 0x55, 0x4d, 0x4d, 0x59, 0x00, 0x00, 0x00, // "DUMMY" 0x01, 0x00, 0x00, 0x00, // OEM rev 0x4c, 0x4e, 0x55, 0x58, // "LNUX" 0x01, 0x00, 0x00, 0x00, // creator rev 0x10, // Scope 0x13, // PkgLength 0x5c, 0x5f, 0x50, 0x52, 0x5f, // \_PR_ 0x5b, 0x83, // Processor 0x0b, // PkgLength 0x43, 0x50, 0x55, 0x30, // CPU0 0x00, // ID 0x00, 0x00, 0x00, 0x00, // PBLK 0x06 // PBLK size }; /* * Calculate and set ACPI table checksum */ static void acpi_set_checksum(u8 *table, int size) { int i, sum = 0; for (i = 0; i < size; i++) sum += (int) table[i]; sum = (0x100 - ((sum - table[ACPI_DUMMY_CHECKSUM]) & 0xff)); table[ACPI_DUMMY_CHECKSUM] = sum; } /* * Init PIIX4 device, create a fake FACP */ static int acpi_init_piix4(struct pci_dev *dev) { u32 base, pblk; u16 cmd; u8 pmregmisc; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_IO)) return -ENODEV; pci_read_config_byte(dev, ACPI_PIIX4_PMREGMISC, &pmregmisc); if (!(pmregmisc & ACPI_PIIX4_PMIOSE)) return -ENODEV; base = pci_resource_start (dev, PCI_BRIDGE_RESOURCES); if (!base) return -ENODEV; printk(KERN_INFO "ACPI: found \"%s\" at 0x%04x\n", dev->name, base); memset(&acpi_facp, 0, sizeof(acpi_facp)); acpi_facp.hdr.signature = ACPI_FACP_SIG; acpi_facp.hdr.length = sizeof(acpi_facp); acpi_facp.int_model = ACPI_PIIX4_INT_MODEL; acpi_facp.sci_int = ACPI_PIIX4_SCI_INT; acpi_facp.smi_cmd = ACPI_PIIX4_SMI_CMD; acpi_facp.acpi_enable = ACPI_PIIX4_ACPI_ENABLE; acpi_facp.acpi_disable = ACPI_PIIX4_ACPI_DISABLE; acpi_facp.s4bios_req = ACPI_PIIX4_S4BIOS_REQ; acpi_facp.pm1a_evt = base + ACPI_PIIX4_PM1_EVT; acpi_facp.pm1a_cnt = base + ACPI_PIIX4_PM1_CNT; acpi_facp.pm2_cnt = ACPI_PIIX4_PM2_CNT; acpi_facp.pm_tmr = base + ACPI_PIIX4_PM_TMR; acpi_facp.gpe0 = base + ACPI_PIIX4_GPE0; acpi_facp.pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN; acpi_facp.pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN; acpi_facp.pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN; acpi_facp.pm_tm_len = ACPI_PIIX4_PM_TM_LEN; acpi_facp.gpe0_len = ACPI_PIIX4_GPE0_LEN; acpi_facp.p_lvl2_lat = (__u16) ACPI_INFINITE_LAT; acpi_facp.p_lvl3_lat = (__u16) ACPI_INFINITE_LAT; acpi_set_checksum((u8*) &acpi_facp, sizeof(acpi_facp)); acpi_load_table((ACPI_TABLE_HEADER*) &acpi_facp); pblk = base + ACPI_PIIX4_P_BLK; memcpy(acpi_dummy_dsdt + ACPI_DUMMY_PBLK, &pblk, sizeof(pblk)); acpi_set_checksum(acpi_dummy_dsdt, sizeof(acpi_dummy_dsdt)); acpi_load_table((ACPI_TABLE_HEADER*) acpi_dummy_dsdt); return 0; } /* * Init VIA ACPI device and create a fake FACP */ static int acpi_init_via(struct pci_dev *dev) { u32 base, pblk; u8 tmp, irq; pci_read_config_byte(dev, 0x41, &tmp); if (!(tmp & 0x80)) return -ENODEV; base = pci_resource_start(dev, PCI_BRIDGE_RESOURCES); if (!base) { base = pci_resource_start(dev, PCI_BASE_ADDRESS_4); if (!base) return -ENODEV; } pci_read_config_byte(dev, 0x42, &irq); printk(KERN_INFO "ACPI: found \"%s\" at 0x%04x\n", dev->name, base); memset(&acpi_facp, 0, sizeof(acpi_facp)); acpi_facp.hdr.signature = ACPI_FACP_SIG; acpi_facp.hdr.length = sizeof(acpi_facp); acpi_facp.int_model = ACPI_VIA_INT_MODEL; acpi_facp.sci_int = irq; acpi_facp.smi_cmd = base + ACPI_VIA_SMI_CMD; acpi_facp.acpi_enable = ACPI_VIA_ACPI_ENABLE; acpi_facp.acpi_disable = ACPI_VIA_ACPI_DISABLE; acpi_facp.pm1a_evt = base + ACPI_VIA_PM1_EVT; acpi_facp.pm1a_cnt = base + ACPI_VIA_PM1_CNT; acpi_facp.pm_tmr = base + ACPI_VIA_PM_TMR; acpi_facp.gpe0 = base + ACPI_VIA_GPE0; acpi_facp.pm1_evt_len = ACPI_VIA_PM1_EVT_LEN; acpi_facp.pm1_cnt_len = ACPI_VIA_PM1_CNT_LEN; acpi_facp.pm_tm_len = ACPI_VIA_PM_TM_LEN; acpi_facp.gpe0_len = ACPI_VIA_GPE0_LEN; acpi_facp.p_lvl2_lat = (__u16) ACPI_INFINITE_LAT; acpi_facp.p_lvl3_lat = (__u16) ACPI_INFINITE_LAT; acpi_facp.duty_offset = ACPI_VIA_DUTY_OFFSET; acpi_facp.duty_width = ACPI_VIA_DUTY_WIDTH; acpi_facp.day_alarm = ACPI_VIA_DAY_ALARM; acpi_facp.mon_alarm = ACPI_VIA_MON_ALARM; acpi_facp.century = ACPI_VIA_CENTURY; acpi_set_checksum((u8*) &acpi_facp, sizeof(acpi_facp)); acpi_load_table((ACPI_TABLE_HEADER*) &acpi_facp); pblk = base + ACPI_VIA_P_BLK; memcpy(acpi_dummy_dsdt + ACPI_DUMMY_PBLK, &pblk, sizeof(pblk)); acpi_set_checksum(acpi_dummy_dsdt, sizeof(acpi_dummy_dsdt)); acpi_load_table((ACPI_TABLE_HEADER*) acpi_dummy_dsdt); return 0; } typedef enum { CH_UNKNOWN = 0, CH_INTEL_PIIX4, CH_VIA_586, CH_VIA_686A, } acpi_chip_t; /* indexed by value of each enum in acpi_chip_t */ const static struct { int (*chip_init)(struct pci_dev *dev); } acpi_chip_info[] = { {NULL,}, {acpi_init_piix4}, {acpi_init_via}, {acpi_init_via}, }; static struct pci_device_id acpi_pci_tbl[] = { {0x8086, 0x7113, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_INTEL_PIIX4}, {0x1106, 0x3040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_586}, {0x1106, 0x3057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_686A}, {0,} /* terminate list */ }; static int acpi_probe(struct pci_dev *dev, const struct pci_device_id *id) { return acpi_chip_info[id->driver_data].chip_init(dev); } static struct pci_driver acpi_driver = { name: "acpi", id_table: acpi_pci_tbl, probe: acpi_probe, }; static int acpi_driver_registered = 0; /* * Locate a known ACPI chipset */ static int acpi_find_chipset(void) { if (pci_register_driver(&acpi_driver) < 1) return -ENODEV; acpi_driver_registered = 1; return 0; } /* * Fetch the FACP information */ static int acpi_fetch_facp(void) { ACPI_BUFFER buffer; memset(&acpi_facp, 0, sizeof(acpi_facp)); buffer.pointer = &acpi_facp; buffer.length = sizeof(acpi_facp); if (!ACPI_SUCCESS(acpi_get_table(ACPI_TABLE_FACP, 1, &buffer))) { printk(KERN_ERR "ACPI: missing FACP\n"); return -ENODEV; } if (acpi_facp.p_lvl2_lat && acpi_facp.p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) { acpi_c2_exit_latency = ACPI_uS_TO_TMR_TICKS(acpi_facp.p_lvl2_lat); acpi_c2_enter_latency = ACPI_uS_TO_TMR_TICKS(ACPI_TMR_HZ / 1000); } if (acpi_facp.p_lvl3_lat && acpi_facp.p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) { acpi_c3_exit_latency = ACPI_uS_TO_TMR_TICKS(acpi_facp.p_lvl3_lat); acpi_c3_enter_latency = ACPI_uS_TO_TMR_TICKS(acpi_facp.p_lvl3_lat * 5); } return 0; } /* * Find and load ACPI tables */ int acpi_load_tables(void) { if (ACPI_SUCCESS(acpi_load_firmware_tables())) { printk(KERN_INFO "ACPI: support found\n"); } else if (acpi_find_chipset()) { acpi_terminate(); return -1; } if (acpi_fetch_facp()) { acpi_terminate(); return -1; } if (!ACPI_SUCCESS(acpi_load_namespace())) { printk(KERN_ERR "ACPI: namespace load failed\n"); acpi_terminate(); return -1; } return 0; }