Skip to contentSkip to conversation

ppc32: PowerMac PM rework

PATCH In review Benjamin Herrenschmidt 2 participants 3 messages May 28, 2005

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ppc32: PowerMac PM rework

Hi !

This new version of my PM rework fixes bits & pieces, most significantly, improves suspend to disk by properly saving/restoring MacIO ASIC state, and fixes issues with suspend to disk vs. AGP/DRI.

Also, the previous patch had the backward compatible ioctl backward, it would suspend to disk instead of to RAM :)

Applies on top of current git plus:

http://gate.crashing.org/~benh/ppc32-pmac-cpufreq-upd.diff
http://gate.crashing.org/~benh/ppc32-pmac-pmu-irq-pri.diff
http://gate.crashing.org/~benh/ppc32-pmu-wakeup.diff

Please test and report any issue.

arch/ppc/platforms/Makefile +3 -2
Index: linux-work/arch/ppc/platforms/Makefile
===================================================================
@@ -10,8 +10,9 @@
 obj-$(CONFIG_PCI)		+= apus_pci.o
 endif
 obj-$(CONFIG_PPC_PMAC)		+= pmac_pic.o pmac_setup.o pmac_time.o \
-					pmac_feature.o pmac_pci.o pmac_sleep.o \
-					pmac_low_i2c.o pmac_cache.o
+				   pmac_feature.o pmac_pci.o pmac_sleep.o \
+				   pmac_low_i2c.o pmac_cache.o pmac_pm.o
+
 obj-$(CONFIG_PPC_CHRP)		+= chrp_setup.o chrp_time.o chrp_pci.o \
 					chrp_pegasos_eth.o
 obj-$(CONFIG_PPC_PREP)		+= prep_pci.o prep_setup.o
arch/ppc/platforms/pmac_pm.c new file +802 -0

802 lines not on this page · raw message

arch/ppc/platforms/pmac_setup.c +1 -63

76 lines not on this page · raw message

drivers/base/power/suspend.c +12 -1
Index: linux-work/drivers/base/power/suspend.c
===================================================================
@@ -113,8 +113,19 @@
 		put_device(dev);
 	}
 	up(&dpm_list_sem);
-	if (error)
+	if (error) {
+		/* we failed... before resuming, bring back devices from
+		 * dpm_off_irq list back to main dpm_off list, we do want
+		 * to call resume() on them, in case they partially suspended
+		 * despite returning -EAGAIN
+		 */
+		while (!list_empty(&dpm_off_irq)) {
+			struct list_head * entry = dpm_off_irq.next;
+			list_del(entry);
+			list_add(entry, &dpm_off);
+		}
 		dpm_resume();
+	}
 	up(&dpm_sem);
 	return error;
 }
drivers/macintosh/via-pmu.c +41 -667

986 lines not on this page · raw message

include/asm-ppc/machdep.h +6 -0
Index: linux-work/include/asm-ppc/machdep.h
===================================================================
@@ -110,6 +110,12 @@
 	 */
 	long (*feature_call)(unsigned int feature, ...);
 
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	int (*arch_prepare_suspend)(void);
+	void (*save_processor_state)(void);
+	void (*restore_processor_state)(void);
+#endif /* CONFIG_SOFTWARE_SUSPEND */
+
 #ifdef CONFIG_SMP
 	/* functions for dealing with other cpus */
 	struct smp_ops_t *smp_ops;
include/asm-ppc/suspend.h +32 -0
Index: linux-work/include/asm-ppc/suspend.h
===================================================================
@@ -1,12 +1,44 @@
+#ifndef __PPC_SUSPEND_H
+#define __PPC_SUSPEND_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/machdep.h>
+
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
 static inline int arch_prepare_suspend(void)
 {
+	if (ppc_md.arch_prepare_suspend)
+		return ppc_md.arch_prepare_suspend();
 	return 0;
 }
 
 static inline void save_processor_state(void)
 {
+	if (ppc_md.save_processor_state)
+		return ppc_md.save_processor_state();
 }
 
 static inline void restore_processor_state(void)
 {
+	if (ppc_md.restore_processor_state)
+		return ppc_md.restore_processor_state();
 }
+#else
+static inline int arch_prepare_suspend(void)
+{
+	return 0;
+}
+
+static inline void save_processor_state(void)
+{
+}
+
+static inline void restore_processor_state(void)
+{
+}
+#endif
+
+
+#endif /* __PPC_SUSPEND_H */
include/linux/pm.h +30 -1
Index: linux-work/include/linux/pm.h
===================================================================
@@ -169,9 +169,38 @@
 
 struct pm_ops {
 	suspend_disk_method_t pm_disk_mode;
+	
+	/* Call before process freezing. If returns 0, then no freeze
+	 * should be done, if 1, freeze, negative -> error
+	 */
+	int (*pre_freeze)(suspend_state_t state);
+
+	/* called before devices are suspended */
 	int (*prepare)(suspend_state_t state);
+
+	/* called just before irqs are off and device second pass
+	 * and sysdevs are suspended. This function can on some archs
+	 * shut irqs off, in which case, they'll still be off when
+	 * finish_irqs() is called.
+	 */
+	int (*prepare_irqs)(suspend_state_t state);
+
+	/* called for entering the actual suspend state. Exits with
+	 * machine worken up and interrupts off
+	 */
 	int (*enter)(suspend_state_t state);
-	int (*finish)(suspend_state_t state);
+
+	/* called after sysdevs and "irq off" devices have been
+	 * worken up, irqs have just been restored to whatever state
+	 * prepare_irqs() left them in.
+	 */
+	void (*finish_irqs)(suspend_state_t state);
+
+	/* called after all devices are woken up, processes still frozen */
+	void (*finish)(suspend_state_t state);
+
+	/* called after unfreezing userland */
+	void (*post_freeze)(suspend_state_t state);
 };
 
 extern void pm_set_ops(struct pm_ops *);
include/linux/pmu.h +3 -0
Index: linux-work/include/linux/pmu.h
===================================================================
@@ -151,6 +151,9 @@
 extern void pmu_suspend(void);
 extern void pmu_resume(void);
 
+extern void pmu_save_via_state(void);
+extern void pmu_restore_via_state(void);
+
 extern void pmu_enable_irled(int on);
 
 extern void pmu_restart(void);
kernel/power/main.c +34 -4
Index: linux-work/kernel/power/main.c
===================================================================
@@ -23,6 +23,7 @@
 
 struct pm_ops * pm_ops = NULL;
 suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
+static int pm_process_frozen;
 
 /**
  *	pm_set_ops - Set the global power method table. 
@@ -49,32 +50,53 @@
 static int suspend_prepare(suspend_state_t state)
 {
 	int error = 0;
+	int freeze = 1;
 
 	if (!pm_ops || !pm_ops->enter)
 		return -EPERM;
 
 	pm_prepare_console();
 
-	if (freeze_processes()) {
+	if (pm_ops->pre_freeze)
+		freeze = pm_ops->pre_freeze(state);
+	if (freeze < 0)
+		goto Console;
+
+	if (freeze && freeze_processes()) {
 		error = -EAGAIN;
 		goto Thaw;
 	}
+	pm_process_frozen = freeze;
 
 	if (pm_ops->prepare) {
+		pr_debug("preparing arch...\n");
 		if ((error = pm_ops->prepare(state)))
 			goto Thaw;
 	}
 
+	pr_debug("suspending devices...\n");
 	if ((error = device_suspend(PMSG_SUSPEND))) {
 		printk(KERN_ERR "Some devices failed to suspend\n");
 		goto Finish;
 	}
+
+	if (pm_ops->prepare_irqs) {
+		pr_debug("preparing arch irqs...\n");
+		if ((error = pm_ops->prepare_irqs(state)))
+			goto Finish;
+	}
+
 	return 0;
  Finish:
 	if (pm_ops->finish)
 		pm_ops->finish(state);
  Thaw:
-	thaw_processes();
+	if (freeze)
+		thaw_processes();
+
+	if (pm_ops->post_freeze)
+		pm_ops->post_freeze(state);
+ Console:
 	pm_restore_console();
 	return error;
 }
@@ -109,10 +131,18 @@
 
 static void suspend_finish(suspend_state_t state)
 {
+	if (!pm_ops)
+		return;
+
+	if (pm_ops->finish_irqs)
+		pm_ops->finish_irqs(state);
 	device_resume();
-	if (pm_ops && pm_ops->finish)
+	if (pm_ops->finish)
 		pm_ops->finish(state);
-	thaw_processes();
+	if (pm_process_frozen)
+		thaw_processes();
+	if (pm_ops->post_freeze)
+		pm_ops->post_freeze(state);
 	pm_restore_console();
 }
 
arch/ppc/platforms/pmac_pic.c +4 -7
Index: linux-work/arch/ppc/platforms/pmac_pic.c
===================================================================
@@ -619,7 +619,7 @@
 	return viaint;
 }
 
-static int pmacpic_suspend(struct sys_device *sysdev, u32 state)
+void __pmac pmacpic_suspend(void)
 {
 	int viaint = pmacpic_find_viaint();
 
@@ -636,11 +636,9 @@
 	/* make sure mask gets to controller before we return to caller */
 	mb();
         (void)in_le32(&pmac_irq_hw[0]->enable);
-
-        return 0;
 }
 
-static int pmacpic_resume(struct sys_device *sysdev)
+void __pmac pmacpic_resume(void)
 {
 	int i;
 
@@ -651,12 +649,11 @@
 	for (i = 0; i < max_real_irqs; ++i)
 		if (test_bit(i, sleep_save_mask))
 			pmac_unmask_irq(i);
-
-	return 0;
 }
 
 #endif /* CONFIG_PM */
 
+#if 0
 static struct sysdev_class pmacpic_sysclass = {
 	set_kset_name("pmac_pic"),
 };
@@ -686,4 +683,4 @@
 }
 
 subsys_initcall(init_pmacpic_sysfs);
-
+#endif
arch/ppc/syslib/open_pic.c +13 -19
Index: linux-work/arch/ppc/syslib/open_pic.c
===================================================================
@@ -946,11 +946,7 @@
 	save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK;
 }
 
-/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
- * we need something better to deal with that... Maybe switch to S1 for
- * cpufreq changes
- */
-int openpic_suspend(struct sys_device *sysdev, u32 state)
+void openpic_suspend(void)
 {
 	int	i;
 	unsigned long flags;
@@ -959,11 +955,9 @@
 
 	if (openpic_suspend_count++ > 0) {
 		spin_unlock_irqrestore(&openpic_setup_lock, flags);
-		return 0;
+		return;
 	}
 
- 	openpic_set_priority(0xf);
-
 	open_pic.enable = openpic_cached_enable_irq;
 	open_pic.disable = openpic_cached_disable_irq;
 
@@ -973,6 +967,8 @@
 				   OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
 	}
 
+ 	openpic_set_priority(0xf);
+
 	for (i=0; i<OPENPIC_NUM_IPI; i++)
 		save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i));
 	for (i=0; i<NumSources; i++) {
@@ -983,15 +979,13 @@
 	}
 
 	spin_unlock_irqrestore(&openpic_setup_lock, flags);
-
-	return 0;
 }
 
 /* WARNING: Can be called directly by the cpufreq code with NULL parameter,
  * we need something better to deal with that... Maybe switch to S1 for
  * cpufreq changes
  */
-int openpic_resume(struct sys_device *sysdev)
+void openpic_resume(void)
 {
 	int		i;
 	unsigned long	flags;
@@ -1003,9 +997,11 @@
 
 	if ((--openpic_suspend_count) > 0) {
 		spin_unlock_irqrestore(&openpic_setup_lock, flags);
-		return 0;
+		return;
 	}
 
+ 	openpic_set_priority(0xf);
+
 	/* OpenPIC sometimes seem to need some time to be fully back up... */
 	do {
 		openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
@@ -1028,22 +1024,19 @@
 		} while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask)
 			 != (save_irq_src_vp[i] & vppmask));
 	}
-	for (i=0; i<NumProcessors; i++)
-		openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
-			      save_cpu_task_pri[i]);
-
 	open_pic.enable = openpic_enable_irq;
 	open_pic.disable = openpic_disable_irq;
 
- 	openpic_set_priority(0);
+	for (i=0; i<NumProcessors; i++)
+		openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
+			      save_cpu_task_pri[i]);
 
 	spin_unlock_irqrestore(&openpic_setup_lock, flags);
-
-	return 0;
 }
 
 #endif /* CONFIG_PM */
 
+#if 0
 static struct sysdev_class openpic_sysclass = {
 	set_kset_name("openpic"),
 };
@@ -1087,3 +1080,4 @@
 
 subsys_initcall(init_openpic_sysfs);
 
+#endif
include/asm-ppc/open_pic.h +3 -0
Index: linux-work/include/asm-ppc/open_pic.h
===================================================================
@@ -64,6 +64,9 @@
 extern void openpic_set_priority(u_int pri);
 extern u_int openpic_get_priority(void);
 
+extern void openpic_suspend(void);
+extern void openpic_resume(void);
+
 extern inline int openpic_to_irq(int irq)
 {
 	/* IRQ 0 usually means 'disabled'.. don't mess with it
arch/ppc/kernel/time.c +5 -1
Index: linux-work/arch/ppc/kernel/time.c
===================================================================
@@ -86,6 +86,7 @@
 unsigned tb_to_us;
 unsigned tb_last_stamp;
 unsigned long tb_to_ns_scale;
+int ppc_force_interrupt;
 
 extern unsigned long wall_jiffies;
 
@@ -131,8 +132,11 @@
 	unsigned jiffy_stamp = last_jiffy_stamp(cpu);
 	extern void do_IRQ(struct pt_regs *);
 
-	if (atomic_read(&ppc_n_lost_interrupts) != 0)
+	if ((atomic_read(&ppc_n_lost_interrupts) != 0) ||
+	    ppc_force_interrupt) {
+		ppc_force_interrupt = 0;
 		do_IRQ(regs);
+	}
 
 	irq_enter();
 
arch/ppc/platforms/pmac_cpufreq.c +4 -1
Index: linux-work/arch/ppc/platforms/pmac_cpufreq.c
===================================================================
@@ -112,11 +112,11 @@
 
 static inline void wakeup_decrementer(void)
 {
-	set_dec(tb_ticks_per_jiffy);
 	/* No currently-supported powerbook has a 601,
 	 * so use get_tbl, not native
 	 */
 	last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+	set_dec(1);
 }
 
 #ifdef DEBUG_FREQ
@@ -340,6 +340,9 @@
 	/* Restore interrupts */
  	openpic_set_priority(pic_prio);
 
+	/* Force a fetch from the PIC on the first DEC interrupt. */
+	ppc_force_interrupt = 1;
+
 	/* Let interrupts flow again ... */
 	local_irq_restore(flags);
 
arch/ppc/platforms/pmac_feature.c +65 -46

167 lines not on this page · raw message

include/asm-ppc/pmac_feature.h +1 -0
Index: linux-work/include/asm-ppc/pmac_feature.h
===================================================================
@@ -245,6 +245,7 @@
  * set the sleep state of the motherboard.
  *
  * Pass -1 as value to query for sleep capability
+ * Pass 2 to save state & not sleep
  * Pass 1 to set IOs to sleep
  * Pass 0 to set IOs to wake
  */
include/asm-ppc/irq.h +1 -0
Index: linux-work/include/asm-ppc/irq.h
===================================================================
@@ -391,6 +391,7 @@
 extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
 extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
 extern atomic_t ppc_n_lost_interrupts;
+extern int ppc_force_interrupt;
 
 struct irqaction;
 struct pt_regs;
drivers/ide/ide-io.c +1 -1
Index: linux-work/drivers/ide/ide-io.c
===================================================================
@@ -150,7 +150,7 @@
 
 	switch (rq->pm->pm_step) {
 	case ide_pm_flush_cache:	/* Suspend step 1 (flush cache) complete */
-		if (rq->pm->pm_state == 4)
+		if (rq->pm->pm_state == PM_SUSPEND_DISK)
 			rq->pm->pm_step = ide_pm_state_completed;
 		else
 			rq->pm->pm_step = idedisk_pm_standby;
drivers/video/aty/aty128fb.c +8 -16
Index: linux-work/drivers/video/aty/aty128fb.c
===================================================================
@@ -2337,21 +2337,15 @@
 	 * can properly take care of D3 ? Also, with swsusp, we
 	 * know we'll be rebooted, ...
 	 */
-#ifdef CONFIG_PPC_PMAC
-	/* HACK ALERT ! Once I find a proper way to say to each driver
-	 * individually what will happen with it's PCI slot, I'll change
-	 * that. On laptops, the AGP slot is just unclocked, so D2 is
-	 * expected, while on desktops, the card is powered off
-	 */
-	if (state >= 3)
-		state = 2;
-#endif /* CONFIG_PPC_PMAC */
 	 
-	if (state != 2 || state == pdev->dev.power.power_state)
+	if (pdev->dev.power.power_state)
 		return 0;
 
 	printk(KERN_DEBUG "aty128fb: suspending...\n");
 	
+	if (state == PM_SUSPEND_DISK)
+		goto skip;
+
 	acquire_console_sem();
 
 	fb_set_suspend(info, 1);
@@ -2381,12 +2375,11 @@
 	 * used dummy fb ops, 2.5 need proper support for this at the
 	 * fbdev level
 	 */
-	if (state == 2)
-		aty128_set_suspend(par, 1);
+	aty128_set_suspend(par, 1);
 
 	release_console_sem();
-
-	pdev->dev.power.power_state = state;
+ skip:
+	pdev->dev.power.power_state = 1;
 
 	return 0;
 }
@@ -2400,8 +2393,7 @@
 		return 0;
 
 	/* Wakeup chip */
-	if (pdev->dev.power.power_state == 2)
-		aty128_set_suspend(par, 0);
+	aty128_set_suspend(par, 0);
 	par->asleep = 0;
 
 	/* Restore display & engine */
drivers/char/agp/uninorth-agp.c +2 -0
Index: linux-work/drivers/char/agp/uninorth-agp.c
===================================================================
@@ -347,6 +347,8 @@
 	struct agp_bridge_data *bridge;
 	u32 command;
 
+	uninorth_configure();
+
 	bridge = agp_find_bridge(pdev);
 	if (bridge == NULL)
 		return -ENODEV;
drivers/video/aty/radeon_base.c +2 -2
Index: linux-work/drivers/video/aty/radeon_base.c
===================================================================
@@ -2036,7 +2036,7 @@
  */
 #ifdef CONFIG_PPC_OF
 #undef SET_MC_FB_FROM_APERTURE
-static void fixup_memory_mappings(struct radeonfb_info *rinfo)
+void radeon_fixup_memory_mappings(struct radeonfb_info *rinfo)
 {
 	u32 save_crtc_gen_cntl, save_crtc2_gen_cntl = 0;
 	u32 save_crtc_ext_cntl;
@@ -2360,7 +2360,7 @@
 	 * to cause lockups when enabling the engine. We reconfigure
 	 * the card internal memory mappings properly
 	 */
-	fixup_memory_mappings(rinfo);
+	radeon_fixup_memory_mappings(rinfo);
 #endif /* CONFIG_PPC_OF */
 
 	/* Get VRAM size and type */
drivers/video/aty/radeon_pm.c +3 -1
Index: linux-work/drivers/video/aty/radeon_pm.c
===================================================================
@@ -2659,8 +2659,10 @@
 			radeon_set_suspend(rinfo, 0);
 
 		rinfo->asleep = 0;
-	} else
+	} else {
+		radeon_fixup_memory_mappings(rinfo);
 		radeon_engine_idle();
+	}
 
 	/* Restore display & engine */
 	radeon_write_mode (rinfo, &rinfo->state, 1);
drivers/video/aty/radeonfb.h +1 -0
Index: linux-work/drivers/video/aty/radeonfb.h
===================================================================
@@ -618,6 +618,7 @@
 extern void radeonfb_engine_reset(struct radeonfb_info *rinfo);
 
 /* Other functions */
+extern void radeon_fixup_memory_mappings(struct radeonfb_info *rinfo);
 extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch);
 extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 			       int reg_only);
#1 lore raw
Quoted 1 line · Benjamin Herrenschmidt

Please test and report any issue.

With this patch my iBook G3 no longer wakes up by opening the lid. I have to touch the keyboard to wake it up.

Andreas.

Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."
#2 lore raw
Quoted 6 lines · Andreas Schwab
Quoted 1 line · Benjamin Herrenschmidt

Please test and report any issue.

With this patch my iBook G3 no longer wakes up by opening the lid. I have to touch the keyboard to wake it up.

Yup, that's a minor issue, the lid wakeup option lost it's default value :) Will be fixed in the next version of the patch.

Ben.