]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Convert APIC to driver model
authorJohn Levon <levon@movementarian.org>
Wed, 2 Apr 2003 02:30:35 +0000 (18:30 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Wed, 2 Apr 2003 02:30:35 +0000 (18:30 -0800)
This is Mikael's version of Pavel's patch, fixed to let CONFIG_PM=n
compile. It works for me on my 2-way using oprofile.

arch/i386/kernel/apic.c
arch/i386/kernel/apm.c
arch/i386/kernel/i386_ksyms.c
arch/i386/kernel/nmi.c
arch/i386/oprofile/nmi_int.c
include/asm-i386/apic.h
include/asm-i386/nmi.h

index af9ec6bbf5ba8901c278a0e52178ecdf9f79a32a..dd9b25424f0c628cfe36d2f4a8d9654f05184f8d 100644 (file)
@@ -10,6 +10,8 @@
  *                                     for testing these extensively.
  *     Maciej W. Rozycki       :       Various updates and fixes.
  *     Mikael Pettersson       :       Power Management for UP-APIC.
+ *     Pavel Machek and
+ *     Mikael Pettersson       :       PM converted to driver model.
  */
 
 #include <linux/config.h>
@@ -451,17 +453,14 @@ void __init setup_local_APIC (void)
 
 #ifdef CONFIG_PM
 
-#include <linux/slab.h>
-#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/module.h>
 
 static struct {
        /* 'active' is true if the local APIC was enabled by us and
           not the BIOS; this signifies that we are also responsible
           for disabling it before entering apm/acpi suspend */
        int active;
-       /* 'perfctr_pmdev' is here because the current (2.4.1) PM
-          callback system doesn't handle hierarchical dependencies */
-       struct pm_dev *perfctr_pmdev;
        /* r/w apic fields */
        unsigned int apic_id;
        unsigned int apic_taskpri;
@@ -478,13 +477,16 @@ static struct {
        unsigned int apic_thmr;
 } apic_pm_state;
 
-static void apic_pm_suspend(void *data)
+static int lapic_suspend(struct device *dev, u32 state, u32 level)
 {
        unsigned int l, h;
        unsigned long flags;
 
-       if (apic_pm_state.perfctr_pmdev)
-               pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data);
+       if (level != SUSPEND_POWER_DOWN)
+               return 0;
+       if (!apic_pm_state.active)
+               return 0;
+
        apic_pm_state.apic_id = apic_read(APIC_ID);
        apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI);
        apic_pm_state.apic_ldr = apic_read(APIC_LDR);
@@ -505,13 +507,22 @@ static void apic_pm_suspend(void *data)
        l &= ~MSR_IA32_APICBASE_ENABLE;
        wrmsr(MSR_IA32_APICBASE, l, h);
        local_irq_restore(flags);
+       return 0;
 }
 
-static void apic_pm_resume(void *data)
+static int lapic_resume(struct device *dev, u32 level)
 {
        unsigned int l, h;
        unsigned long flags;
 
+       if (level != RESUME_POWER_ON)
+               return 0;
+       if (!apic_pm_state.active)
+               return 0;
+
+       /* XXX: Pavel needs this for S3 resume, but can't explain why */
+       set_fixmap_nocache(FIX_APIC_BASE, APIC_DEFAULT_PHYS_BASE);
+
        local_irq_save(flags);
        rdmsr(MSR_IA32_APICBASE, l, h);
        l &= ~MSR_IA32_APICBASE_BASE;
@@ -536,73 +547,45 @@ static void apic_pm_resume(void *data)
        apic_write(APIC_ESR, 0);
        apic_read(APIC_ESR);
        local_irq_restore(flags);
-       if (apic_pm_state.perfctr_pmdev)
-               pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data);
-}
-
-static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
-{
-       switch (rqst) {
-       case PM_SUSPEND:
-               apic_pm_suspend(data);
-               break;
-       case PM_RESUME:
-               apic_pm_resume(data);
-               break;
-       }
        return 0;
 }
 
-/* perfctr driver should call this instead of pm_register() */
-struct pm_dev *apic_pm_register(pm_dev_t type,
-                               unsigned long id,
-                               pm_callback callback)
-{
-       struct pm_dev *dev;
-
-       if (!apic_pm_state.active)
-               return pm_register(type, id, callback);
-       if (apic_pm_state.perfctr_pmdev)
-               return NULL;    /* we're busy */
-       dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);
-       if (dev) {
-               memset(dev, 0, sizeof(*dev));
-               dev->type = type;
-               dev->id = id;
-               dev->callback = callback;
-               apic_pm_state.perfctr_pmdev = dev;
-       }
-       return dev;
-}
-
-/* perfctr driver should call this instead of pm_unregister() */
-void apic_pm_unregister(struct pm_dev *dev)
+static struct device_driver lapic_driver = {
+       .name           = "lapic",
+       .bus            = &system_bus_type,
+       .resume         = lapic_resume,
+       .suspend        = lapic_suspend,
+};
+
+/* not static, needed by child devices */
+struct sys_device device_lapic = {
+       .name           = "lapic",
+       .id             = 0,
+       .dev            = {
+               .name   = "lapic",
+               .driver = &lapic_driver,
+       },
+};
+EXPORT_SYMBOL(device_lapic);
+
+static void __init apic_pm_activate(void)
 {
-       if (!apic_pm_state.active) {
-               pm_unregister(dev);
-       } else if (dev == apic_pm_state.perfctr_pmdev) {
-               apic_pm_state.perfctr_pmdev = NULL;
-               kfree(dev);
-       }
-}
-
-static void __init apic_pm_init1(void)
-{
-       /* can't pm_register() at this early stage in the boot process
-          (causes an immediate reboot), so just set the flag */
        apic_pm_state.active = 1;
 }
 
-static void __init apic_pm_init2(void)
+static int __init init_lapic_devicefs(void)
 {
-       if (apic_pm_state.active)
-               pm_register(PM_SYS_DEV, 0, apic_pm_callback);
+       if (!cpu_has_apic)
+               return 0;
+       /* XXX: remove suspend/resume procs if !apic_pm_state.active? */
+       driver_register(&lapic_driver);
+       return sys_device_register(&device_lapic);
 }
+device_initcall(init_lapic_devicefs);
 
 #else  /* CONFIG_PM */
 
-static inline void apic_pm_init1(void) { }
-static inline void apic_pm_init2(void) { }
+static inline void apic_pm_activate(void) { }
 
 #endif /* CONFIG_PM */
 
@@ -670,7 +653,7 @@ static int __init detect_init_APIC (void)
 
        printk("Found and enabled local APIC!\n");
 
-       apic_pm_init1();
+       apic_pm_activate();
 
        return 0;
 
@@ -1155,8 +1138,6 @@ int __init APIC_init_uniprocessor (void)
 
        phys_cpu_present_map = 1 << boot_cpu_physical_apicid;
 
-       apic_pm_init2();
-
        setup_local_APIC();
 
        if (nmi_watchdog == NMI_LOCAL_APIC)
index 67e9015e64004e1a95853b8e3d432e9a321a2f8b..d92802445be759dd6caec18c0ee4a7284593c63f 100644 (file)
 #include <linux/time.h>
 #include <linux/sched.h>
 #include <linux/pm.h>
+#include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -1237,6 +1238,9 @@ static int suspend(int vetoable)
                }
                printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
        }
+
+       device_suspend(3, SUSPEND_POWER_DOWN);
+
        /* serialize with the timer interrupt */
        write_seqlock_irq(&xtime_lock);
 
@@ -1257,6 +1261,7 @@ static int suspend(int vetoable)
        if (err != APM_SUCCESS)
                apm_error("suspend", err);
        err = (err == APM_SUCCESS) ? 0 : -EIO;
+       device_resume(RESUME_POWER_ON);
        pm_send_all(PM_RESUME, (void *)0);
        queue_event(APM_NORMAL_RESUME, NULL);
  out:
@@ -1370,6 +1375,7 @@ static void check_events(void)
                                write_seqlock_irq(&xtime_lock);
                                set_time();
                                write_sequnlock_irq(&xtime_lock);
+                               device_resume(RESUME_POWER_ON);
                                pm_send_all(PM_RESUME, (void *)0);
                                queue_event(event, NULL);
                        }
index ff8eb575cd3436c4137d5af823f62781d0a032fe..fc1e4ca0c4a2fa43eed4a1a9aecbce92cfcb62d2 100644 (file)
@@ -163,10 +163,6 @@ EXPORT_SYMBOL(smp_call_function);
 EXPORT_SYMBOL(flush_tlb_page);
 #endif
 
-#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PM)
-EXPORT_SYMBOL_GPL(set_nmi_pm_callback);
-EXPORT_SYMBOL_GPL(unset_nmi_pm_callback);
-#endif
 #ifdef CONFIG_X86_IO_APIC
 EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
 #endif
index ed31b7804c06d8c5581b13492a640739f2a39504..b74137219ce73347f59fddcc1c2b34fa8100fad4 100644 (file)
@@ -9,6 +9,8 @@
  *  Mikael Pettersson  : AMD K7 support for local APIC NMI watchdog.
  *  Mikael Pettersson  : Power Management for local APIC NMI watchdog.
  *  Mikael Pettersson  : Pentium 4 support for local APIC NMI watchdog.
+ *  Pavel Machek and
+ *  Mikael Pettersson  : PM converted to driver model. Disable/enable API.
  */
 
 #include <linux/config.h>
@@ -20,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/mc146818rtc.h>
 #include <linux/kernel_stat.h>
+#include <linux/module.h>
 
 #include <asm/smp.h>
 #include <asm/mtrr.h>
@@ -134,14 +137,18 @@ static int __init setup_nmi_watchdog(char *str)
 
 __setup("nmi_watchdog=", setup_nmi_watchdog);
 
-#ifdef CONFIG_PM
-
-#include <linux/pm.h>
-
-struct pm_dev *nmi_pmdev;
+/* nmi_active:
+ * +1: the lapic NMI watchdog is active, but can be disabled
+ *  0: the lapic NMI watchdog has not been set up, and cannot
+ *     be enabled
+ * -1: the lapic NMI watchdog is disabled, but can be enabled
+ */
+static int nmi_active;
 
-static void disable_apic_nmi_watchdog(void)
+void disable_lapic_nmi_watchdog(void)
 {
+       if (nmi_active <= 0)
+               return;
        switch (boot_cpu_data.x86_vendor) {
        case X86_VENDOR_AMD:
                wrmsr(MSR_K7_EVNTSEL0, 0, 0);
@@ -158,46 +165,65 @@ static void disable_apic_nmi_watchdog(void)
                }
                break;
        }
+       nmi_active = -1;
+       /* tell do_nmi() and others that we're not active any more */
+       nmi_watchdog = 0;
 }
 
-static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+void enable_lapic_nmi_watchdog(void)
 {
-       switch (rqst) {
-       case PM_SUSPEND:
-               disable_apic_nmi_watchdog();
-               break;
-       case PM_RESUME:
+       if (nmi_active < 0) {
+               nmi_watchdog = NMI_LOCAL_APIC;
                setup_apic_nmi_watchdog();
-               break;
        }
-       return 0;
 }
 
-struct pm_dev * set_nmi_pm_callback(pm_callback callback)
+#ifdef CONFIG_PM
+
+#include <linux/device.h>
+
+static int lapic_nmi_suspend(struct device *dev, u32 state, u32 level)
 {
-       apic_pm_unregister(nmi_pmdev);
-       return apic_pm_register(PM_SYS_DEV, 0, callback);
+       if (level != SUSPEND_POWER_DOWN)
+               return 0;
+       disable_lapic_nmi_watchdog();
+       return 0;
 }
 
-void unset_nmi_pm_callback(struct pm_dev * dev)
+static int lapic_nmi_resume(struct device *dev, u32 level)
 {
-       apic_pm_unregister(dev);
-       nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback);
+       if (level != RESUME_POWER_ON)
+               return 0;
+       enable_lapic_nmi_watchdog();
+       return 0;
 }
- 
-static void nmi_pm_init(void)
+
+static struct device_driver lapic_nmi_driver = {
+       .name           = "lapic_nmi",
+       .bus            = &system_bus_type,
+       .resume         = lapic_nmi_resume,
+       .suspend        = lapic_nmi_suspend,
+};
+
+static struct sys_device device_lapic_nmi = {
+       .name           = "lapic_nmi",
+       .id             = 0,
+       .dev            = {
+               .name   = "lapic_nmi",
+               .driver = &lapic_nmi_driver,
+               .parent = &device_lapic.dev,
+       },
+};
+
+static int __init init_lapic_nmi_devicefs(void)
 {
-       if (!nmi_pmdev)
-               nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback);
+       if (nmi_active == 0)
+               return 0;
+       driver_register(&lapic_nmi_driver);
+       return sys_device_register(&device_lapic_nmi);
 }
-
-#define __pminit       /*empty*/
-
-#else  /* CONFIG_PM */
-
-static inline void nmi_pm_init(void) { }
-
-#define __pminit       __init
+/* must come after the local APIC's device_initcall() */
+late_initcall(init_lapic_nmi_devicefs);
 
 #endif /* CONFIG_PM */
 
@@ -206,7 +232,7 @@ static inline void nmi_pm_init(void) { }
  * Original code written by Keith Owens.
  */
 
-static void __pminit clear_msr_range(unsigned int base, unsigned int n)
+static void clear_msr_range(unsigned int base, unsigned int n)
 {
        unsigned int i;
 
@@ -214,7 +240,7 @@ static void __pminit clear_msr_range(unsigned int base, unsigned int n)
                wrmsr(base+i, 0, 0);
 }
 
-static void __pminit setup_k7_watchdog(void)
+static void setup_k7_watchdog(void)
 {
        unsigned int evntsel;
 
@@ -236,7 +262,7 @@ static void __pminit setup_k7_watchdog(void)
        wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
 }
 
-static void __pminit setup_p6_watchdog(void)
+static void setup_p6_watchdog(void)
 {
        unsigned int evntsel;
 
@@ -258,7 +284,7 @@ static void __pminit setup_p6_watchdog(void)
        wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
 }
 
-static int __pminit setup_p4_watchdog(void)
+static int setup_p4_watchdog(void)
 {
        unsigned int misc_enable, dummy;
 
@@ -288,7 +314,7 @@ static int __pminit setup_p4_watchdog(void)
        return 1;
 }
 
-void __pminit setup_apic_nmi_watchdog (void)
+void setup_apic_nmi_watchdog (void)
 {
        switch (boot_cpu_data.x86_vendor) {
        case X86_VENDOR_AMD:
@@ -312,7 +338,7 @@ void __pminit setup_apic_nmi_watchdog (void)
        default:
                return;
        }
-       nmi_pm_init();
+       nmi_active = 1;
 }
 
 static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED;
@@ -400,3 +426,7 @@ void nmi_watchdog_tick (struct pt_regs * regs)
                wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
        }
 }
+
+EXPORT_SYMBOL(nmi_watchdog);
+EXPORT_SYMBOL(disable_lapic_nmi_watchdog);
+EXPORT_SYMBOL(enable_lapic_nmi_watchdog);
index a6463c2e2a7590772cdcbd6c58babc89d2d4f863..c158cc89591628042972f3e7da43b98be41a240d 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/notifier.h>
 #include <linux/smp.h>
 #include <linux/oprofile.h>
+#include <linux/device.h>
 #include <asm/nmi.h>
 #include <asm/msr.h>
 #include <asm/apic.h>
@@ -25,27 +26,59 @@ static unsigned long saved_lvtpc[NR_CPUS];
 static int nmi_start(void);
 static void nmi_stop(void);
 
-static struct pm_dev * oprofile_pmdev;
- 
-/* We're at risk of causing big trouble unless we
- * make sure to not cause any NMI interrupts when
- * suspended.
- */
-static int oprofile_pm_callback(struct pm_dev * dev,
-               pm_request_t rqst, void * data)
+/* 0 == registered but off, 1 == registered and on */
+static int nmi_enabled = 0;
+
+#ifdef CONFIG_PM
+
+static int nmi_suspend(struct device *dev, u32 state, u32 level)
 {
-       switch (rqst) {
-               case PM_SUSPEND:
-                       nmi_stop();
-                       break;
-               case PM_RESUME:
-                       nmi_start();
-                       break;
-       }
+       if (level != SUSPEND_POWER_DOWN)
+               return 0;
+       if (nmi_enabled == 1)
+               nmi_stop();
        return 0;
 }
- 
- 
+
+
+static int nmi_resume(struct device *dev, u32 level)
+{
+       if (level != RESUME_POWER_ON)
+               return 0;
+       if (nmi_enabled == 1)
+               nmi_start();
+       return 0;
+}
+
+
+static struct device_driver nmi_driver = {
+       .name           = "oprofile",
+       .bus            = &system_bus_type,
+       .resume         = nmi_resume,
+       .suspend        = nmi_suspend,
+};
+
+
+static struct device device_nmi = {
+       .name   = "oprofile",
+       .bus_id = "oprofile",
+       .driver = &nmi_driver,
+       .parent = &device_lapic.dev,
+};
+
+
+static int __init init_nmi_driverfs(void)
+{
+       driver_register(&nmi_driver);
+       return device_register(&device_nmi);
+}
+
+
+late_initcall(init_nmi_driverfs);
+
+#endif /* CONFIG_PM */
+
+
 static int nmi_callback(struct pt_regs * regs, int cpu)
 {
        return model->check_ctrs(cpu, &cpu_msrs[cpu], regs);
@@ -86,7 +119,7 @@ static void nmi_cpu_setup(void * dummy)
        saved_lvtpc[cpu] = apic_read(APIC_LVTPC);
        apic_write(APIC_LVTPC, APIC_DM_NMI);
 }
- 
+
 
 static int nmi_setup(void)
 {
@@ -95,9 +128,10 @@ static int nmi_setup(void)
         * without actually triggering any NMIs as this will
         * break the core code horrifically.
         */
+       disable_lapic_nmi_watchdog();
        on_each_cpu(nmi_cpu_setup, NULL, 0, 1);
        set_nmi_callback(nmi_callback);
-       oprofile_pmdev = set_nmi_pm_callback(oprofile_pm_callback);
+       nmi_enabled = 1;
        return 0;
 }
 
@@ -145,9 +179,10 @@ static void nmi_cpu_shutdown(void * dummy)
  
 static void nmi_shutdown(void)
 {
-       unset_nmi_pm_callback(oprofile_pmdev);
+       nmi_enabled = 0;
        unset_nmi_callback();
        on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1);
+       enable_lapic_nmi_watchdog();
 }
 
  
index ec8ef4832b82e0a70917c0937b1fa8051b77bc83..2a4bfcd38033dfd4d181291b1595e174b0164824 100644 (file)
@@ -79,13 +79,16 @@ extern void smp_local_timer_interrupt (struct pt_regs * regs);
 extern void setup_boot_APIC_clock (void);
 extern void setup_secondary_APIC_clock (void);
 extern void setup_apic_nmi_watchdog (void);
+extern void disable_lapic_nmi_watchdog(void);
+extern void enable_lapic_nmi_watchdog(void);
 extern inline void nmi_watchdog_tick (struct pt_regs * regs);
 extern int APIC_init_uniprocessor (void);
 extern void disable_APIC_timer(void);
 extern void enable_APIC_timer(void);
 
-extern struct pm_dev *apic_pm_register(pm_dev_t, unsigned long, pm_callback);
-extern void apic_pm_unregister(struct pm_dev*);
+#ifdef CONFIG_PM
+extern struct sys_device device_lapic;
+#endif
 
 extern int check_nmi_watchdog (void);
 extern void enable_NMI_through_LVT0 (void * dummy);
index d20f0fb9ad2b7d5590211560c2b1003a579f5a46..21f16638fc6146575963cd19ec9697cf3ade2c4e 100644 (file)
@@ -25,25 +25,4 @@ void set_nmi_callback(nmi_callback_t callback);
  */
 void unset_nmi_callback(void);
  
-#ifdef CONFIG_PM
- 
-/** Replace the PM callback routine for NMI. */
-struct pm_dev * set_nmi_pm_callback(pm_callback callback);
-
-/** Unset the PM callback routine back to the default. */
-void unset_nmi_pm_callback(struct pm_dev * dev);
-
-#else
-
-static inline struct pm_dev * set_nmi_pm_callback(pm_callback callback)
-{
-       return 0;
-} 
- 
-static inline void unset_nmi_pm_callback(struct pm_dev * dev)
-{
-}
-
-#endif /* CONFIG_PM */
- 
 #endif /* ASM_NMI_H */