Re: [PATCH] Longhaul - call suspend(PMSG_FREEZE) before and resume() after

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here is simplier patch using PCI bus master disabling code by Dave Jones.
Still using block subsystem, but this time it isn't exported to modules.

1. Call suspend(PMSG_FREEZE) for each block device.
2. Change frequency - notice that in this patch at least we give a chance to BCR2 MSR CPU's.
3. Call resume() for each block device.

Tested on Epia M10000. 22MB/s while changing from 997MHz to 532MHz at 1s interval.

EXPERIMENTAL - Please!
(HZ_100 || HZ_250) - because VIA CPU's with BCR2 MSR need >=1ms to sync PLL.
!SMP - original code and patch aren't SMP compatible.
!X86_UP_APIC - original code and patch aren't APIC compatible.


diff -uprN -X linux-2.6.16.18-vanilla/Documentation/dontdiff linux-2.6.16.18-vanilla/arch/i386/kernel/cpu/cpufreq/Kconfig linux-2.6.16.18/arch/i386/kernel/cpu/cpufreq/Kconfig
--- linux-2.6.16.18-vanilla/arch/i386/kernel/cpu/cpufreq/Kconfig	2006-05-28 15:04:34.000000000 +0200
+++ linux-2.6.16.18/arch/i386/kernel/cpu/cpufreq/Kconfig	2006-05-28 15:36:54.000000000 +0200
@@ -201,14 +201,17 @@ config X86_LONGRUN
 	  If in doubt, say N.
 
 config X86_LONGHAUL
-	tristate "VIA Cyrix III Longhaul"
+	bool "VIA Cyrix III Longhaul (EXPERIMENTAL)"
+	default n
 	select CPU_FREQ_TABLE
-	depends on BROKEN
+	depends on EXPERIMENTAL && !SMP && !X86_UP_APIC
+	depends on (HZ_100 || HZ_250) && (PREEMPT_NONE || PREEMPT_VOLUNTARY)
 	help
 	  This adds the CPUFreq driver for VIA Samuel/CyrixIII, 
 	  VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T 
 	  processors.
 
+	  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
 	  For details, take a look at <file:Documentation/cpu-freq/>.
 
 	  If in doubt, say N.
diff -uprN -X linux-2.6.16.18-vanilla/Documentation/dontdiff linux-2.6.16.18-vanilla/arch/i386/kernel/cpu/cpufreq/longhaul.c linux-2.6.16.18/arch/i386/kernel/cpu/cpufreq/longhaul.c
--- linux-2.6.16.18-vanilla/arch/i386/kernel/cpu/cpufreq/longhaul.c	2006-03-20 06:53:29.000000000 +0100
+++ linux-2.6.16.18/arch/i386/kernel/cpu/cpufreq/longhaul.c	2006-05-28 15:57:58.000000000 +0200
@@ -30,6 +30,11 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/kobject.h>
+#include <linux/sysdev.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
 
 #include <asm/msr.h>
 #include <asm/timex.h>
@@ -115,36 +120,40 @@ static int longhaul_get_cpu_mult(void)
 }
 
 
-static void do_powersaver(union msr_longhaul *longhaul,
-			unsigned int clock_ratio_index)
-{
-	struct pci_dev *dev;
-	unsigned long flags;
-	unsigned int tmp_mask;
-	int version;
-	int i;
-	u16 pci_cmd;
-	u16 cmd_state[64];
+extern struct subsystem block_subsys;
+extern struct semaphore block_subsys_sem;
 
-	switch (cpu_model) {
-	case CPU_EZRA_T:
-		version = 3;
-		break;
-	case CPU_NEHEMIAH:
-		version = 0xf;
-		break;
-	default:
-		return;
+static struct pm_message pmsg_freeze = { .event = PM_EVENT_FREEZE, };
+
+void block_freeze_resume(int freeze)
+{
+	struct gendisk *disk;
+	struct list_head *p;
+	struct bus_type *bus;
+
+	list_for_each(p, &block_subsys.kset.list) {
+		disk = list_entry(p, struct gendisk, kobj.entry);
+		if (disk->driverfs_dev && disk->driverfs_dev->bus) {
+			bus = disk->driverfs_dev->bus;
+			if (bus->suspend && freeze) {
+				printk(KERN_INFO PFX "%.32s (%.32s) -> suspend(PMSG_FREEZE)\n", &disk->disk_name[0], &disk->driverfs_dev->bus_id[0]);
+				bus->suspend(disk->driverfs_dev, pmsg_freeze); 
+			} else if (bus->resume && !freeze) {
+				printk(KERN_INFO PFX "%.32s (%.32s) -> resume()\n", &disk->disk_name[0], &disk->driverfs_dev->bus_id[0]);
+				bus->resume(disk->driverfs_dev); 
+			}
+		}
 	}
+}
 
-	rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
-	longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf;
-	longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
-	longhaul->bits.EnableSoftBusRatio = 1;
-	longhaul->bits.RevisionKey = 0;
 
-	preempt_disable();
-	local_irq_save(flags);
+static u16 cmd_state[64];
+
+static void clear_bus_master(void)
+{
+	struct pci_dev *dev;
+	u16 pci_cmd;
+	int i;
 
 	/*
 	 * get current pci bus master state for all devices
@@ -161,18 +170,14 @@ static void do_powersaver(union msr_long
 			pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
 		}
 	} while (dev != NULL);
+}
 
-	tmp_mask=inb(0x21);	/* works on C3. save mask. */
-	outb(0xFE,0x21);	/* TMR0 only */
-	outb(0xFF,0x80);	/* delay */
-
-	safe_halt();
-	wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
-	halt();
-
-	local_irq_disable();
 
-	outb(tmp_mask,0x21);	/* restore mask */
+static void restore_bus_master(void)
+{
+	struct pci_dev *dev;
+	u16 pci_cmd;
+	int i;
 
 	/* restore pci bus master state for all devices */
 	dev = NULL;
@@ -184,14 +189,106 @@ static void do_powersaver(union msr_long
 			pci_write_config_byte(dev, PCI_COMMAND, pci_cmd);
 		}
 	} while (dev != NULL);
+}
+
+
+static void do_longhaul1(int reserved, unsigned int clock_ratio_index)
+{
+	unsigned long flags;
+	unsigned int tmp_mask;
+	union msr_bcr2 bcr2;
+
+        down(&block_subsys_sem);
+	block_freeze_resume(1);
+	preempt_disable();
+	clear_bus_master();
+	local_irq_save(flags);
+
+	local_irq_disable();
+
+	tmp_mask=inb(0x21);	/* works on C3. save mask. */
+	outb(0xFE, 0x21);	/* TMR0 only */
+
+	rdmsrl(MSR_VIA_BCR2, bcr2.val);
+	/* Enable software clock multiplier */
+	bcr2.bits.ESOFTBF = 1;
+	bcr2.bits.CLOCKMUL = clock_ratio_index;
+	safe_halt();		/* Sync */
+	wrmsrl(MSR_VIA_BCR2, bcr2.val);	/* Change */
+	halt();
+
+	/* Disable software clock multiplier */
+	local_irq_disable();
+	rdmsrl(MSR_VIA_BCR2, bcr2.val);
+	bcr2.bits.ESOFTBF = 0;
+	wrmsrl(MSR_VIA_BCR2, bcr2.val);
+	local_irq_enable();
+
+	outb(tmp_mask, 0x21);	/* restore mask */
 	local_irq_restore(flags);
+	restore_bus_master();
 	preempt_enable();
+	block_freeze_resume(0);
+        up(&block_subsys_sem);
+}
+
+
+static void do_powersaver(union msr_longhaul *longhaul,
+			unsigned int clock_ratio_index)
+{
+	unsigned long flags;
+	unsigned int pic1_mask, pic2_mask;
+	int version;
+
+	switch (cpu_model) {
+	case CPU_EZRA_T:
+		version = 3;
+		break;
+	case CPU_NEHEMIAH:
+		version = 0xf;
+		break;
+	default:
+		return;
+	}
+
+        down(&block_subsys_sem);
+	block_freeze_resume(1);
+	preempt_disable();
+	clear_bus_master();
+	local_irq_save(flags);
+
+	local_irq_disable();
+	pic2_mask=inb(0xA1);
+	pic1_mask=inb(0x21);	/* works on C3. save mask. */
+	outb(0xFF,0xA1);	/* Overkill */
+	outb(0xFE,0x21);	/* TMR0 only */
+
+	rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
+	longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf;
+	longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
+	longhaul->bits.EnableSoftBusRatio = 1;
+	longhaul->bits.RevisionKey = 0;
+
+	safe_halt();		/* Sync */
+	wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
+	halt();
+
+	local_irq_disable();
 
 	/* disable bus ratio bit */
 	rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
 	longhaul->bits.EnableSoftBusRatio = 0;
 	longhaul->bits.RevisionKey = version;
 	wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
+
+	outb(pic2_mask,0xA1);
+	outb(pic1_mask,0x21);	/* restore mask */
+
+	local_irq_restore(flags);
+	restore_bus_master();
+	preempt_enable();
+	block_freeze_resume(0);
+        up(&block_subsys_sem);
 }
 
 /**
@@ -206,7 +303,6 @@ static void longhaul_setstate(unsigned i
 	int speed, mult;
 	struct cpufreq_freqs freqs;
 	union msr_longhaul longhaul;
-	union msr_bcr2 bcr2;
 	static unsigned int old_ratio=-1;
 
 	if (old_ratio == clock_ratio_index)
@@ -241,20 +337,7 @@ static void longhaul_setstate(unsigned i
 	 */
 	case TYPE_LONGHAUL_V1:
 	case TYPE_LONGHAUL_V2:
-		rdmsrl (MSR_VIA_BCR2, bcr2.val);
-		/* Enable software clock multiplier */
-		bcr2.bits.ESOFTBF = 1;
-		bcr2.bits.CLOCKMUL = clock_ratio_index;
-		local_irq_disable();
-		wrmsrl (MSR_VIA_BCR2, bcr2.val);
-		safe_halt();
-
-		/* Disable software clock multiplier */
-		rdmsrl (MSR_VIA_BCR2, bcr2.val);
-		bcr2.bits.ESOFTBF = 0;
-		local_irq_disable();
-		wrmsrl (MSR_VIA_BCR2, bcr2.val);
-		local_irq_enable();
+		do_longhaul1(0, clock_ratio_index);
 		break;
 
 	/*
diff -uprN -X linux-2.6.16.18-vanilla/Documentation/dontdiff linux-2.6.16.18-vanilla/block/genhd.c linux-2.6.16.18/block/genhd.c
--- linux-2.6.16.18-vanilla/block/genhd.c	2006-05-28 15:04:34.000000000 +0200
+++ linux-2.6.16.18/block/genhd.c	2006-05-28 15:34:31.000000000 +0200
@@ -16,9 +16,9 @@
 #include <linux/kobj_map.h>
 #include <linux/buffer_head.h>
 
-static struct subsystem block_subsys;
+struct subsystem block_subsys;
 
-static DECLARE_MUTEX(block_subsys_sem);
+DECLARE_MUTEX(block_subsys_sem);
 
 /*
  * Can be deleted altogether. Later.
@@ -511,8 +511,7 @@ static struct kset_uevent_ops block_ueve
 };
 
 /* declare block_subsys. */
-static decl_subsys(block, &ktype_block, &block_uevent_ops);
-
+decl_subsys(block, &ktype_block, &block_uevent_ops);
 
 /*
  * aggregate disk stat collector.  Uses the same stats that the sysfs


----------------------------------------------------------------------
Poznaj Stefana! Zmien komunikator! >>> http://link.interia.pl/f1924

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux