aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/processor_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/processor_core.c')
-rw-r--r--drivers/acpi/processor_core.c989
1 files changed, 989 insertions, 0 deletions
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
new file mode 100644
index 000000000000..f4778747e889
--- /dev/null
+++ b/drivers/acpi/processor_core.c
@@ -0,0 +1,989 @@
+/*
+ * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * TBD:
+ * 1. Make # power states dynamic.
+ * 2. Support duty_cycle values that span bit 4.
+ * 3. Optimize by having scheduler determine business instead of
+ * having us try to calculate it here.
+ * 4. Need C1 timing -- must modify kernel (IRQ handler) to get this.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/dmi.h>
+#include <linux/moduleparam.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/cpu.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/smp.h>
+#include <asm/acpi.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/processor.h>
+
+
+#define ACPI_PROCESSOR_COMPONENT 0x01000000
+#define ACPI_PROCESSOR_CLASS "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver"
+#define ACPI_PROCESSOR_DEVICE_NAME "Processor"
+#define ACPI_PROCESSOR_FILE_INFO "info"
+#define ACPI_PROCESSOR_FILE_THROTTLING "throttling"
+#define ACPI_PROCESSOR_FILE_LIMIT "limit"
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
+
+#define ACPI_PROCESSOR_LIMIT_USER 0
+#define ACPI_PROCESSOR_LIMIT_THERMAL 1
+
+#define ACPI_STA_PRESENT 0x00000001
+
+#define _COMPONENT ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME ("acpi_processor")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+
+static int acpi_processor_add (struct acpi_device *device);
+static int acpi_processor_start (struct acpi_device *device);
+static int acpi_processor_remove (struct acpi_device *device, int type);
+static int acpi_processor_info_open_fs(struct inode *inode, struct file *file);
+static void acpi_processor_notify ( acpi_handle handle, u32 event, void *data);
+static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu);
+static int acpi_processor_handle_eject(struct acpi_processor *pr);
+
+static struct acpi_driver acpi_processor_driver = {
+ .name = ACPI_PROCESSOR_DRIVER_NAME,
+ .class = ACPI_PROCESSOR_CLASS,
+ .ids = ACPI_PROCESSOR_HID,
+ .ops = {
+ .add = acpi_processor_add,
+ .remove = acpi_processor_remove,
+ .start = acpi_processor_start,
+ },
+};
+
+#define INSTALL_NOTIFY_HANDLER 1
+#define UNINSTALL_NOTIFY_HANDLER 2
+
+
+static struct file_operations acpi_processor_info_fops = {
+ .open = acpi_processor_info_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+struct acpi_processor *processors[NR_CPUS];
+struct acpi_processor_errata errata;
+
+
+/* --------------------------------------------------------------------------
+ Errata Handling
+ -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_errata_piix4 (
+ struct pci_dev *dev)
+{
+ u8 rev = 0;
+ u8 value1 = 0;
+ u8 value2 = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_errata_piix4");
+
+ if (!dev)
+ return_VALUE(-EINVAL);
+
+ /*
+ * Note that 'dev' references the PIIX4 ACPI Controller.
+ */
+
+ pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+
+ switch (rev) {
+ case 0:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n"));
+ break;
+ case 1:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n"));
+ break;
+ case 2:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n"));
+ break;
+ case 3:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n"));
+ break;
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n"));
+ break;
+ }
+
+ switch (rev) {
+
+ case 0: /* PIIX4 A-step */
+ case 1: /* PIIX4 B-step */
+ /*
+ * See specification changes #13 ("Manual Throttle Duty Cycle")
+ * and #14 ("Enabling and Disabling Manual Throttle"), plus
+ * erratum #5 ("STPCLK# Deassertion Time") from the January
+ * 2002 PIIX4 specification update. Applies to only older
+ * PIIX4 models.
+ */
+ errata.piix4.throttle = 1;
+
+ case 2: /* PIIX4E */
+ case 3: /* PIIX4M */
+ /*
+ * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA
+ * Livelock") from the January 2002 PIIX4 specification update.
+ * Applies to all PIIX4 models.
+ */
+
+ /*
+ * BM-IDE
+ * ------
+ * Find the PIIX4 IDE Controller and get the Bus Master IDE
+ * Status register address. We'll use this later to read
+ * each IDE controller's DMA status to make sure we catch all
+ * DMA activity.
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ errata.piix4.bmisx = pci_resource_start(dev, 4);
+ pci_dev_put(dev);
+ }
+
+ /*
+ * Type-F DMA
+ * ----------
+ * Find the PIIX4 ISA Controller and read the Motherboard
+ * DMA controller's status to see if Type-F (Fast) DMA mode
+ * is enabled (bit 7) on either channel. Note that we'll
+ * disable C3 support if this is enabled, as some legacy
+ * devices won't operate well if fast DMA is disabled.
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_0,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ pci_read_config_byte(dev, 0x76, &value1);
+ pci_read_config_byte(dev, 0x77, &value2);
+ if ((value1 & 0x80) || (value2 & 0x80))
+ errata.piix4.fdma = 1;
+ pci_dev_put(dev);
+ }
+
+ break;
+ }
+
+ if (errata.piix4.bmisx)
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Bus master activity detection (BM-IDE) erratum enabled\n"));
+ if (errata.piix4.fdma)
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Type-F DMA livelock erratum (C3 disabled)\n"));
+
+ return_VALUE(0);
+}
+
+
+int
+acpi_processor_errata (
+ struct acpi_processor *pr)
+{
+ int result = 0;
+ struct pci_dev *dev = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_errata");
+
+ if (!pr)
+ return_VALUE(-EINVAL);
+
+ /*
+ * PIIX4
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ result = acpi_processor_errata_piix4(dev);
+ pci_dev_put(dev);
+ }
+
+ return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+ FS Interface (/proc)
+ -------------------------------------------------------------------------- */
+
+static struct proc_dir_entry *acpi_processor_dir = NULL;
+
+static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset)
+{
+ struct acpi_processor *pr = (struct acpi_processor *)seq->private;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_info_seq_show");
+
+ if (!pr)
+ goto end;
+
+ seq_printf(seq, "processor id: %d\n"
+ "acpi id: %d\n"
+ "bus mastering control: %s\n"
+ "power management: %s\n"
+ "throttling control: %s\n"
+ "limit interface: %s\n",
+ pr->id,
+ pr->acpi_id,
+ pr->flags.bm_control ? "yes" : "no",
+ pr->flags.power ? "yes" : "no",
+ pr->flags.throttling ? "yes" : "no",
+ pr->flags.limit ? "yes" : "no");
+
+end:
+ return_VALUE(0);
+}
+
+static int acpi_processor_info_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, acpi_processor_info_seq_show,
+ PDE(inode)->data);
+}
+
+
+static int
+acpi_processor_add_fs (
+ struct acpi_device *device)
+{
+ struct proc_dir_entry *entry = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_add_fs");
+
+ if (!acpi_device_dir(device)) {
+ acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+ acpi_processor_dir);
+ if (!acpi_device_dir(device))
+ return_VALUE(-ENODEV);
+ }
+ acpi_device_dir(device)->owner = THIS_MODULE;
+
+ /* 'info' [R] */
+ entry = create_proc_entry(ACPI_PROCESSOR_FILE_INFO,
+ S_IRUGO, acpi_device_dir(device));
+ if (!entry)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to create '%s' fs entry\n",
+ ACPI_PROCESSOR_FILE_INFO));
+ else {
+ entry->proc_fops = &acpi_processor_info_fops;
+ entry->data = acpi_driver_data(device);
+ entry->owner = THIS_MODULE;
+ }
+
+ /* 'throttling' [R/W] */
+ entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
+ S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+ if (!entry)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to create '%s' fs entry\n",
+ ACPI_PROCESSOR_FILE_THROTTLING));
+ else {
+ entry->proc_fops = &acpi_processor_throttling_fops;
+ entry->proc_fops->write = acpi_processor_write_throttling;
+ entry->data = acpi_driver_data(device);
+ entry->owner = THIS_MODULE;
+ }
+
+ /* 'limit' [R/W] */
+ entry = create_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,
+ S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+ if (!entry)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to create '%s' fs entry\n",
+ ACPI_PROCESSOR_FILE_LIMIT));
+ else {
+ entry->proc_fops = &acpi_processor_limit_fops;
+ entry->proc_fops->write = acpi_processor_write_limit;
+ entry->data = acpi_driver_data(device);
+ entry->owner = THIS_MODULE;
+ }
+
+ return_VALUE(0);
+}
+
+
+static int
+acpi_processor_remove_fs (
+ struct acpi_device *device)
+{
+ ACPI_FUNCTION_TRACE("acpi_processor_remove_fs");
+
+ if (acpi_device_dir(device)) {
+ remove_proc_entry(ACPI_PROCESSOR_FILE_INFO,acpi_device_dir(device));
+ remove_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
+ acpi_device_dir(device));
+ remove_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,acpi_device_dir(device));
+ remove_proc_entry(acpi_device_bid(device), acpi_processor_dir);
+ acpi_device_dir(device) = NULL;
+ }
+
+ return_VALUE(0);
+}
+
+/* Use the acpiid in MADT to map cpus in case of SMP */
+#ifndef CONFIG_SMP
+#define convert_acpiid_to_cpu(acpi_id) (0xff)
+#else
+
+#ifdef CONFIG_IA64
+#define arch_acpiid_to_apicid ia64_acpiid_to_sapicid
+#define arch_cpu_to_apicid ia64_cpu_to_sapicid
+#define ARCH_BAD_APICID (0xffff)
+#else
+#define arch_acpiid_to_apicid x86_acpiid_to_apicid
+#define arch_cpu_to_apicid x86_cpu_to_apicid
+#define ARCH_BAD_APICID (0xff)
+#endif
+
+static u8 convert_acpiid_to_cpu(u8 acpi_id)
+{
+ u16 apic_id;
+ int i;
+
+ apic_id = arch_acpiid_to_apicid[acpi_id];
+ if (apic_id == ARCH_BAD_APICID)
+ return -1;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (arch_cpu_to_apicid[i] == apic_id)
+ return i;
+ }
+ return -1;
+}
+#endif
+
+/* --------------------------------------------------------------------------
+ Driver Interface
+ -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_info (
+ struct acpi_processor *pr)
+{
+ acpi_status status = 0;
+ union acpi_object object = {0};
+ struct acpi_buffer buffer = {sizeof(union acpi_object), &object};
+ u8 cpu_index;
+ static int cpu0_initialized;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_get_info");
+
+ if (!pr)
+ return_VALUE(-EINVAL);
+
+ if (num_online_cpus() > 1)
+ errata.smp = TRUE;
+
+ acpi_processor_errata(pr);
+
+ /*
+ * Check to see if we have bus mastering arbitration control. This
+ * is required for proper C3 usage (to maintain cache coherency).
+ */
+ if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len) {
+ pr->flags.bm_control = 1;
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Bus mastering arbitration control present\n"));
+ }
+ else
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "No bus mastering arbitration control\n"));
+
+ /*
+ * Evalute the processor object. Note that it is common on SMP to
+ * have the first (boot) processor with a valid PBLK address while
+ * all others have a NULL address.
+ */
+ status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error evaluating processor object\n"));
+ return_VALUE(-ENODEV);
+ }
+
+ /*
+ * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
+ * >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c
+ */
+ pr->acpi_id = object.processor.proc_id;
+
+ cpu_index = convert_acpiid_to_cpu(pr->acpi_id);
+
+ /* Handle UP system running SMP kernel, with no LAPIC in MADT */
+ if ( !cpu0_initialized && (cpu_index == 0xff) &&
+ (num_online_cpus() == 1)) {
+ cpu_index = 0;
+ }
+
+ cpu0_initialized = 1;
+
+ pr->id = cpu_index;
+
+ /*
+ * Extra Processor objects may be enumerated on MP systems with
+ * less than the max # of CPUs. They should be ignored _iff
+ * they are physically not present.
+ */
+ if (cpu_index >= NR_CPUS) {
+ if (ACPI_FAILURE(acpi_processor_hotadd_init(pr->handle, &pr->id))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error getting cpuindex for acpiid 0x%x\n",
+ pr->acpi_id));
+ return_VALUE(-ENODEV);
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
+ pr->acpi_id));
+
+ if (!object.processor.pblk_address)
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n"));
+ else if (object.processor.pblk_length != 6)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid PBLK length [%d]\n",
+ object.processor.pblk_length));
+ else {
+ pr->throttling.address = object.processor.pblk_address;
+ pr->throttling.duty_offset = acpi_fadt.duty_offset;
+ pr->throttling.duty_width = acpi_fadt.duty_width;
+
+ pr->pblk = object.processor.pblk_address;
+
+ /*
+ * We don't care about error returns - we just try to mark
+ * these reserved so that nobody else is confused into thinking
+ * that this region might be unused..
+ *
+ * (In particular, allocating the IO range for Cardbus)
+ */
+ request_region(pr->throttling.address, 6, "ACPI CPU throttle");
+ }
+
+#ifdef CONFIG_CPU_FREQ
+ acpi_processor_ppc_has_changed(pr);
+#endif
+ acpi_processor_get_throttling_info(pr);
+ acpi_processor_get_limit_info(pr);
+
+ return_VALUE(0);
+}
+
+static int
+acpi_processor_start(
+ struct acpi_device *device)
+{
+ int result = 0;
+ acpi_status status = AE_OK;
+ struct acpi_processor *pr;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_start");
+
+ pr = acpi_driver_data(device);
+
+ result = acpi_processor_get_info(pr);
+ if (result) {
+ /* Processor is physically not present */
+ return_VALUE(0);
+ }
+
+ BUG_ON((pr->id >= NR_CPUS) || (pr->id < 0));
+
+ processors[pr->id] = pr;
+
+ result = acpi_processor_add_fs(device);
+ if (result)
+ goto end;
+
+ status = acpi_install_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
+ acpi_processor_notify, pr);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error installing device notify handler\n"));
+ }
+
+ acpi_processor_power_init(pr, device);
+
+ if (pr->flags.throttling) {
+ printk(KERN_INFO PREFIX "%s [%s] (supports",
+ acpi_device_name(device), acpi_device_bid(device));
+ printk(" %d throttling states", pr->throttling.state_count);
+ printk(")\n");
+ }
+
+end:
+
+ return_VALUE(result);
+}
+
+
+
+static void
+acpi_processor_notify (
+ acpi_handle handle,
+ u32 event,
+ void *data)
+{
+ struct acpi_processor *pr = (struct acpi_processor *) data;
+ struct acpi_device *device = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_notify");
+
+ if (!pr)
+ return_VOID;
+
+ if (acpi_bus_get_device(pr->handle, &device))
+ return_VOID;
+
+ switch (event) {
+ case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
+ acpi_processor_ppc_has_changed(pr);
+ acpi_bus_generate_event(device, event,
+ pr->performance_platform_limit);
+ break;
+ case ACPI_PROCESSOR_NOTIFY_POWER:
+ acpi_processor_cst_has_changed(pr);
+ acpi_bus_generate_event(device, event, 0);
+ break;
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Unsupported event [0x%x]\n", event));
+ break;
+ }
+
+ return_VOID;
+}
+
+
+static int
+acpi_processor_add (
+ struct acpi_device *device)
+{
+ struct acpi_processor *pr = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_add");
+
+ if (!device)
+ return_VALUE(-EINVAL);
+
+ pr = kmalloc(sizeof(struct acpi_processor), GFP_KERNEL);
+ if (!pr)
+ return_VALUE(-ENOMEM);
+ memset(pr, 0, sizeof(struct acpi_processor));
+
+ pr->handle = device->handle;
+ strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
+ acpi_driver_data(device) = pr;
+
+ return_VALUE(0);
+}
+
+
+static int
+acpi_processor_remove (
+ struct acpi_device *device,
+ int type)
+{
+ acpi_status status = AE_OK;
+ struct acpi_processor *pr = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_remove");
+
+ if (!device || !acpi_driver_data(device))
+ return_VALUE(-EINVAL);
+
+ pr = (struct acpi_processor *) acpi_driver_data(device);
+
+ if (pr->id >= NR_CPUS) {
+ kfree(pr);
+ return_VALUE(0);
+ }
+
+ if (type == ACPI_BUS_REMOVAL_EJECT) {
+ if (acpi_processor_handle_eject(pr))
+ return_VALUE(-EINVAL);
+ }
+
+ acpi_processor_power_exit(pr, device);
+
+ status = acpi_remove_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
+ acpi_processor_notify);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error removing notify handler\n"));
+ }
+
+ acpi_processor_remove_fs(device);
+
+ processors[pr->id] = NULL;
+
+ kfree(pr);
+
+ return_VALUE(0);
+}
+
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+/****************************************************************************
+ * Acpi processor hotplug support *
+ ****************************************************************************/
+
+static int is_processor_present(acpi_handle handle);
+
+static int
+is_processor_present(
+ acpi_handle handle)
+{
+ acpi_status status;
+ unsigned long sta = 0;
+
+ ACPI_FUNCTION_TRACE("is_processor_present");
+
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Processor Device is not present\n"));
+ return_VALUE(0);
+ }
+ return_VALUE(1);
+}
+
+
+static
+int acpi_processor_device_add(
+ acpi_handle handle,
+ struct acpi_device **device)
+{
+ acpi_handle phandle;
+ struct acpi_device *pdev;
+ struct acpi_processor *pr;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_device_add");
+
+ if (acpi_get_parent(handle, &phandle)) {
+ return_VALUE(-ENODEV);
+ }
+
+ if (acpi_bus_get_device(phandle, &pdev)) {
+ return_VALUE(-ENODEV);
+ }
+
+ if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) {
+ return_VALUE(-ENODEV);
+ }
+
+ acpi_bus_scan(*device);
+
+ pr = acpi_driver_data(*device);
+ if (!pr)
+ return_VALUE(-ENODEV);
+
+ if ((pr->id >=0) && (pr->id < NR_CPUS)) {
+ kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE);
+ }
+ return_VALUE(0);
+}
+
+
+static void
+acpi_processor_hotplug_notify (
+ acpi_handle handle,
+ u32 event,
+ void *data)
+{
+ struct acpi_processor *pr;
+ struct acpi_device *device = NULL;
+ int result;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_hotplug_notify");
+
+ switch (event) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ printk("Processor driver received %s event\n",
+ (event==ACPI_NOTIFY_BUS_CHECK)?
+ "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK");
+
+ if (!is_processor_present(handle))
+ break;
+
+ if (acpi_bus_get_device(handle, &device)) {
+ result = acpi_processor_device_add(handle, &device);
+ if (result)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to add the device\n"));
+ break;
+ }
+
+ pr = acpi_driver_data(device);
+ if (!pr) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Driver data is NULL\n"));
+ break;
+ }
+
+ if (pr->id >= 0 && (pr->id < NR_CPUS)) {
+ kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+ break;
+ }
+
+ result = acpi_processor_start(device);
+ if ((!result) && ((pr->id >=0) && (pr->id < NR_CPUS))) {
+ kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Device [%s] failed to start\n",
+ acpi_device_bid(device)));
+ }
+ break;
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,"received ACPI_NOTIFY_EJECT_REQUEST\n"));
+
+ if (acpi_bus_get_device(handle, &device)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Device don't exist, dropping EJECT\n"));
+ break;
+ }
+ pr = acpi_driver_data(device);
+ if (!pr) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Driver data is NULL, dropping EJECT\n"));
+ return_VOID;
+ }
+
+ if ((pr->id < NR_CPUS) && (cpu_present(pr->id)))
+ kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+ break;
+ default:
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Unsupported event [0x%x]\n", event));
+ break;
+ }
+
+ return_VOID;
+}
+
+static acpi_status
+processor_walk_namespace_cb(acpi_handle handle,
+ u32 lvl,
+ void *context,
+ void **rv)
+{
+ acpi_status status;
+ int *action = context;
+ acpi_object_type type = 0;
+
+ status = acpi_get_type(handle, &type);
+ if (ACPI_FAILURE(status))
+ return(AE_OK);
+
+ if (type != ACPI_TYPE_PROCESSOR)
+ return(AE_OK);
+
+ switch(*action) {
+ case INSTALL_NOTIFY_HANDLER:
+ acpi_install_notify_handler(handle,
+ ACPI_SYSTEM_NOTIFY,
+ acpi_processor_hotplug_notify,
+ NULL);
+ break;
+ case UNINSTALL_NOTIFY_HANDLER:
+ acpi_remove_notify_handler(handle,
+ ACPI_SYSTEM_NOTIFY,
+ acpi_processor_hotplug_notify);
+ break;
+ default:
+ break;
+ }
+
+ return(AE_OK);
+}
+
+
+static acpi_status
+acpi_processor_hotadd_init(
+ acpi_handle handle,
+ int *p_cpu)
+{
+ ACPI_FUNCTION_TRACE("acpi_processor_hotadd_init");
+
+ if (!is_processor_present(handle)) {
+ return_VALUE(AE_ERROR);
+ }
+
+ if (acpi_map_lsapic(handle, p_cpu))
+ return_VALUE(AE_ERROR);
+
+ if (arch_register_cpu(*p_cpu)) {
+ acpi_unmap_lsapic(*p_cpu);
+ return_VALUE(AE_ERROR);
+ }
+
+ return_VALUE(AE_OK);
+}
+
+
+static int
+acpi_processor_handle_eject(struct acpi_processor *pr)
+{
+ if (cpu_online(pr->id)) {
+ return(-EINVAL);
+ }
+ arch_unregister_cpu(pr->id);
+ acpi_unmap_lsapic(pr->id);
+ return(0);
+}
+#else
+static acpi_status
+acpi_processor_hotadd_init(
+ acpi_handle handle,
+ int *p_cpu)
+{
+ return AE_ERROR;
+}
+static int
+acpi_processor_handle_eject(struct acpi_processor *pr)
+{
+ return(-EINVAL);
+}
+#endif
+
+
+static
+void acpi_processor_install_hotplug_notify(void)
+{
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+ int action = INSTALL_NOTIFY_HANDLER;
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ processor_walk_namespace_cb,
+ &action, NULL);
+#endif
+}
+
+
+static
+void acpi_processor_uninstall_hotplug_notify(void)
+{
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+ int action = UNINSTALL_NOTIFY_HANDLER;
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ processor_walk_namespace_cb,
+ &action, NULL);
+#endif
+}
+
+/*
+ * We keep the driver loaded even when ACPI is not running.
+ * This is needed for the powernow-k8 driver, that works even without
+ * ACPI, but needs symbols from this driver
+ */
+
+static int __init
+acpi_processor_init (void)
+{
+ int result = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_processor_init");
+
+ memset(&processors, 0, sizeof(processors));
+ memset(&errata, 0, sizeof(errata));
+
+ acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+ if (!acpi_processor_dir)
+ return_VALUE(0);
+ acpi_processor_dir->owner = THIS_MODULE;
+
+ result = acpi_bus_register_driver(&acpi_processor_driver);
+ if (result < 0) {
+ remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+ return_VALUE(0);
+ }
+
+ acpi_processor_install_hotplug_notify();
+
+ acpi_thermal_cpufreq_init();
+
+ acpi_processor_ppc_init();
+
+ return_VALUE(0);
+}
+
+
+static void __exit
+acpi_processor_exit (void)
+{
+ ACPI_FUNCTION_TRACE("acpi_processor_exit");
+
+ acpi_processor_ppc_exit();
+
+ acpi_thermal_cpufreq_exit();
+
+ acpi_processor_uninstall_hotplug_notify();
+
+ acpi_bus_unregister_driver(&acpi_processor_driver);
+
+ remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+
+ return_VOID;
+}
+
+
+module_init(acpi_processor_init);
+module_exit(acpi_processor_exit);
+
+EXPORT_SYMBOL(acpi_processor_set_thermal_limit);
+
+MODULE_ALIAS("processor");