Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dadb49d8 authored by Rafa Bilski's avatar Rafa Bilski Committed by Dave Jones
Browse files

[CPUFREQ] Longhaul - Hook into ACPI C states.



Minimal change necessary for hardware support.

Changes in longhaul.c:
- most important - now C3 state is causing transition,
- code responsible for clearing "bus master" bit removed,
- protect bcr2 transition in the same way as longhaul.

Signed-off-by: default avatarRafa³ Bilski <rafalbilski@interia.pl>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 9c9a43ed
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -202,7 +202,7 @@ config X86_LONGRUN
config X86_LONGHAUL
config X86_LONGHAUL
	tristate "VIA Cyrix III Longhaul"
	tristate "VIA Cyrix III Longhaul"
	select CPU_FREQ_TABLE
	select CPU_FREQ_TABLE
	depends on BROKEN
	depends on ACPI_PROCESSOR
	help
	help
	  This adds the CPUFreq driver for VIA Samuel/CyrixIII,
	  This adds the CPUFreq driver for VIA Samuel/CyrixIII,
	  VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T
	  VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T
+108 −86
Original line number Original line Diff line number Diff line
@@ -29,11 +29,13 @@
#include <linux/cpufreq.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/pci.h>


#include <asm/msr.h>
#include <asm/msr.h>
#include <asm/timex.h>
#include <asm/timex.h>
#include <asm/io.h>
#include <asm/io.h>
#include <asm/acpi.h>
#include <linux/acpi.h>
#include <acpi/processor.h>


#include "longhaul.h"
#include "longhaul.h"


@@ -56,6 +58,8 @@ static int minvid, maxvid;
static unsigned int minmult, maxmult;
static unsigned int minmult, maxmult;
static int can_scale_voltage;
static int can_scale_voltage;
static int vrmrev;
static int vrmrev;
static struct acpi_processor *pr = NULL;
static struct acpi_processor_cx *cx = NULL;


/* Module parameters */
/* Module parameters */
static int dont_scale_voltage;
static int dont_scale_voltage;
@@ -118,84 +122,64 @@ static int longhaul_get_cpu_mult(void)
	return eblcr_table[invalue];
	return eblcr_table[invalue];
}
}


/* For processor with BCR2 MSR */


static void do_powersaver(union msr_longhaul *longhaul,
static void do_longhaul1(int cx_address, unsigned int clock_ratio_index)
			unsigned int clock_ratio_index)
{
{
	struct pci_dev *dev;
	union msr_bcr2 bcr2;
	unsigned long flags;
	u32 t;
	unsigned int tmp_mask;
	int version;
	int i;
	u16 pci_cmd;
	u16 cmd_state[64];


	switch (cpu_model) {
	rdmsrl(MSR_VIA_BCR2, bcr2.val);
	case CPU_EZRA_T:
	/* Enable software clock multiplier */
		version = 3;
	bcr2.bits.ESOFTBF = 1;
		break;
	bcr2.bits.CLOCKMUL = clock_ratio_index;
	case CPU_NEHEMIAH:

		version = 0xf;
	/* Sync to timer tick */
		break;
	safe_halt();
	default:
	ACPI_FLUSH_CPU_CACHE();
		return;
	/* Change frequency on next halt or sleep */
	wrmsrl(MSR_VIA_BCR2, bcr2.val);
	/* Invoke C3 */
	inb(cx_address);
	/* Dummy op - must do something useless after P_LVL3 read */
	t = inl(acpi_fadt.xpm_tmr_blk.address);

	/* Disable software clock multiplier */
	local_irq_disable();
	rdmsrl(MSR_VIA_BCR2, bcr2.val);
	bcr2.bits.ESOFTBF = 0;
	wrmsrl(MSR_VIA_BCR2, bcr2.val);
}
}


	rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
/* For processor with Longhaul MSR */
	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();
static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
	local_irq_save(flags);
{
	union msr_longhaul longhaul;
	u32 t;


	/*
	rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
	 * get current pci bus master state for all devices
	longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
	 * and clear bus master bit
	longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
	 */
	longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
	dev = NULL;
	i = 0;
	do {
		dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
		if (dev != NULL) {
			pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
			cmd_state[i++] = pci_cmd;
			pci_cmd &= ~PCI_COMMAND_MASTER;
			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 */


	/* Sync to timer tick */
	safe_halt();
	safe_halt();
	wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
	ACPI_FLUSH_CPU_CACHE();
	halt();
	/* Change frequency on next halt or sleep */
	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
	/* Invoke C3 */
	inb(cx_address);
	/* Dummy op - must do something useless after P_LVL3 read */
	t = inl(acpi_fadt.xpm_tmr_blk.address);


	/* Disable bus ratio bit */
	local_irq_disable();
	local_irq_disable();

	longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
	outb(tmp_mask,0x21);	/* restore mask */
	longhaul.bits.EnableSoftBusRatio = 0;

	longhaul.bits.EnableSoftBSEL = 0;
	/* restore pci bus master state for all devices */
	longhaul.bits.EnableSoftVID = 0;
	dev = NULL;
	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
	i = 0;
	do {
		dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
		if (dev != NULL) {
			pci_cmd = cmd_state[i++];
			pci_write_config_byte(dev, PCI_COMMAND, pci_cmd);
		}
	} while (dev != NULL);
	local_irq_restore(flags);
	preempt_enable();

	/* disable bus ratio bit */
	rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
	longhaul->bits.EnableSoftBusRatio = 0;
	longhaul->bits.RevisionKey = version;
	wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
}
}


/**
/**
@@ -209,9 +193,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
{
{
	int speed, mult;
	int speed, mult;
	struct cpufreq_freqs freqs;
	struct cpufreq_freqs freqs;
	union msr_longhaul longhaul;
	union msr_bcr2 bcr2;
	static unsigned int old_ratio=-1;
	static unsigned int old_ratio=-1;
	unsigned long flags;
	unsigned int pic1_mask, pic2_mask;


	if (old_ratio == clock_ratio_index)
	if (old_ratio == clock_ratio_index)
		return;
		return;
@@ -234,6 +218,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
	dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
	dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
			fsb, mult/10, mult%10, print_speed(speed/1000));
			fsb, mult/10, mult%10, print_speed(speed/1000));


	preempt_disable();
	local_irq_save(flags);

	pic2_mask = inb(0xA1);
	pic1_mask = inb(0x21);	/* works on C3. save mask. */
	outb(0xFF,0xA1);	/* Overkill */
	outb(0xFE,0x21);	/* TMR0 only */

	/* Disable bus master arbitration */
	if (pr->flags.bm_check) {
		acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1,
				  ACPI_MTX_DO_NOT_LOCK);
	}

	switch (longhaul_version) {
	switch (longhaul_version) {


	/*
	/*
@@ -245,20 +243,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
	 */
	 */
	case TYPE_LONGHAUL_V1:
	case TYPE_LONGHAUL_V1:
	case TYPE_LONGHAUL_V2:
	case TYPE_LONGHAUL_V2:
		rdmsrl (MSR_VIA_BCR2, bcr2.val);
		do_longhaul1(cx->address, clock_ratio_index);
		/* 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();
		break;
		break;


	/*
	/*
@@ -273,10 +258,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
	 * to work in practice.
	 * to work in practice.
	 */
	 */
	case TYPE_POWERSAVER:
	case TYPE_POWERSAVER:
		do_powersaver(&longhaul, clock_ratio_index);
		do_powersaver(cx->address, clock_ratio_index);
		break;
		break;
	}
	}


	/* Enable bus master arbitration */
	if (pr->flags.bm_check) {
		acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0,
				  ACPI_MTX_DO_NOT_LOCK);
	}

	outb(pic2_mask,0xA1);	/* restore mask */
	outb(pic1_mask,0x21);

	local_irq_restore(flags);
	preempt_enable();

	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
}


@@ -527,6 +524,18 @@ static unsigned int longhaul_get(unsigned int cpu)
	return calc_speed(longhaul_get_cpu_mult());
	return calc_speed(longhaul_get_cpu_mult());
}
}


acpi_status longhaul_walk_callback(acpi_handle obj_handle,
				  u32 nesting_level,
				  void *context, void **return_value)
{
	struct acpi_device *d;

	if ( acpi_bus_get_device(obj_handle, &d) ) {
		return 0;
	}
	*return_value = (void *)acpi_driver_data(d);
	return 1;
}


static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
{
{
@@ -534,6 +543,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
	char *cpuname=NULL;
	char *cpuname=NULL;
	int ret;
	int ret;


	/* Check ACPI support for C3 state */
	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
			 &longhaul_walk_callback, NULL, (void *)&pr);
	if (pr == NULL) goto err_acpi;

	cx = &pr->power.states[ACPI_STATE_C3];
	if (cx == NULL || cx->latency > 1000) goto err_acpi;

	/* Now check what we have on this motherboard */
	switch (c->x86_model) {
	switch (c->x86_model) {
	case 6:
	case 6:
		cpu_model = CPU_SAMUEL;
		cpu_model = CPU_SAMUEL;
@@ -634,6 +652,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
	cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
	cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);


	return 0;
	return 0;

err_acpi:
	printk(KERN_ERR PFX "No ACPI support for CPU frequency changes.\n");
	return -ENODEV;
}
}


static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)