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

Commit 74889e41 authored by Christian Krafft's avatar Christian Krafft Committed by Arnd Bergmann
Browse files

[CELL] cbe_cpufreq: reorganize code



This patch reorganizes the code of the driver into three files.
Two cbe_cpufreq_pmi.c and cbe_cpufreq_pervasive.c care about hardware.
cbe_cpufreq.c contains the logic.
There is no changed behaviour, except that the PMI related function
is now located in a seperate module cbe_cpufreq_pmi. This module
will be required by cbe_cpufreq, if CONFIG_CBE_CPUFREQ_PMI has been set.

Signed-off-by: default avatarChristian Krafft <krafft@de.ibm.com>
Signed-off-by: default avatarArnd Bergmann <arnd.bergmann@de.ibm.com>
parent 1e21fd5a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -73,4 +73,14 @@ config CBE_CPUFREQ
	  For details, take a look at <file:Documentation/cpu-freq/>.
	  If you don't have such processor, say N

config CBE_CPUFREQ_PMI
	tristate "CBE frequency scaling using PMI interface"
	depends on CBE_CPUFREQ && PPC_PMI && EXPERIMENTAL
	default n
	help
	  Select this, if you want to use the PMI interface
	  to switch frequencies. Using PMI, the
	  processor will not only be able to run at lower speed,
	  but also at lower core voltage.

endmenu
+3 −1
Original line number Diff line number Diff line
@@ -4,7 +4,9 @@ obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \
obj-$(CONFIG_CBE_RAS)			+= ras.o

obj-$(CONFIG_CBE_THERM)			+= cbe_thermal.o
obj-$(CONFIG_CBE_CPUFREQ)		+= cbe_cpufreq.o
obj-$(CONFIG_CBE_CPUFREQ_PMI)		+= cbe_cpufreq_pmi.o
obj-$(CONFIG_CBE_CPUFREQ)		+= cbe-cpufreq.o
cbe-cpufreq-y				+= cbe_cpufreq_pervasive.o cbe_cpufreq.o

ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_PPC_CELL_NATIVE)		+= smp.o
+9 −187
Original line number Diff line number Diff line
/*
 * cpufreq driver for the cell processor
 *
 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
 *
 * Author: Christian Krafft <krafft@de.ibm.com>
 *
@@ -21,18 +21,10 @@
 */

#include <linux/cpufreq.h>
#include <linux/timer.h>

#include <asm/hw_irq.h>
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/processor.h>
#include <asm/prom.h>
#include <asm/time.h>
#include <asm/pmi.h>
#include <asm/of_platform.h>

#include "cbe_regs.h"
#include <asm/prom.h>
#include "cbe_cpufreq.h"

static DEFINE_MUTEX(cbe_switch_mutex);

@@ -50,183 +42,24 @@ static struct cpufreq_frequency_table cbe_freqs[] = {
	{0,	CPUFREQ_TABLE_END},
};

/* to write to MIC register */
static u64 MIC_Slow_Fast_Timer_table[] = {
	[0 ... 7] = 0x007fc00000000000ull,
};

/* more values for the MIC */
static u64 MIC_Slow_Next_Timer_table[] = {
	0x0000240000000000ull,
	0x0000268000000000ull,
	0x000029C000000000ull,
	0x00002D0000000000ull,
	0x0000300000000000ull,
	0x0000334000000000ull,
	0x000039C000000000ull,
	0x00003FC000000000ull,
};

static u8 pmi_slow_mode_limit[MAX_BE];

/*
 * hardware specific functions
 */

static bool cbe_cpufreq_has_pmi;

#ifdef CONFIG_PPC_PMI
static int set_pmode_pmi(int cpu, unsigned int pmode)
{
	int ret;
	pmi_message_t pmi_msg;
#ifdef DEBUG
	long time;
#endif

	pmi_msg.type = PMI_TYPE_FREQ_CHANGE;
	pmi_msg.data1 =	cbe_cpu_to_node(cpu);
	pmi_msg.data2 = pmode;

#ifdef DEBUG
	time = jiffies;
#endif

	pmi_send_message(pmi_msg);
	ret = pmi_msg.data2;

	pr_debug("PMI returned slow mode %d\n", ret);

#ifdef DEBUG
	time = jiffies - time; /* actual cycles (not cpu cycles!) */
	time = jiffies_to_msecs(time);
	pr_debug("had to wait %lu ms for a transition using PMI.\n", time);
#endif
	return ret;
}
#endif

static int get_pmode(int cpu)
{
	int ret;
	struct cbe_pmd_regs __iomem *pmd_regs;

	pmd_regs = cbe_get_cpu_pmd_regs(cpu);
	ret = in_be64(&pmd_regs->pmsr) & 0x07;

	return ret;
}

static int set_pmode_reg(int cpu, unsigned int pmode)
{
	struct cbe_pmd_regs __iomem *pmd_regs;
	struct cbe_mic_tm_regs __iomem *mic_tm_regs;
	u64 flags;
	u64 value;
#ifdef DEBUG
	long time;
#endif

	local_irq_save(flags);

	mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu);
	pmd_regs = cbe_get_cpu_pmd_regs(cpu);

#ifdef DEBUG
	time = jiffies;
#endif
	out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]);
	out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]);

	out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]);
	out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]);

	value = in_be64(&pmd_regs->pmcr);
	/* set bits to zero */
	value &= 0xFFFFFFFFFFFFFFF8ull;
	/* set bits to next pmode */
	value |= pmode;

	out_be64(&pmd_regs->pmcr, value);

#ifdef DEBUG
	/* wait until new pmode appears in status register */
	value = in_be64(&pmd_regs->pmsr) & 0x07;
	while(value != pmode) {
		cpu_relax();
		value = in_be64(&pmd_regs->pmsr) & 0x07;
	}

	time = jiffies - time;
	time = jiffies_to_msecs(time);
	pr_debug("had to wait %lu ms for a transition using " \
		 "the pervasive unit.\n", time);
#endif
	local_irq_restore(flags);

	return 0;
}

static int set_pmode(int cpu, unsigned int slow_mode)
static int set_pmode(unsigned int cpu, unsigned int slow_mode)
{
	int rc;
#ifdef CONFIG_PPC_PMI

	if (cbe_cpufreq_has_pmi)
		rc = set_pmode_pmi(cpu, slow_mode);
		rc = cbe_cpufreq_set_pmode_pmi(cpu, slow_mode);
	else
#endif
		rc = set_pmode_reg(cpu, slow_mode);
		rc = cbe_cpufreq_set_pmode(cpu, slow_mode);

	pr_debug("register contains slow mode %d\n", get_pmode(cpu));
	pr_debug("register contains slow mode %d\n", cbe_cpufreq_get_pmode(cpu));

	return rc;
}

static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg)
{
	u8 node; slow_mode;

	BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE);

	node = pmi_msg.data1;
	slow_mode = pmi_msg.data2;

	pmi_slow_mode_limit[node] = slow_mode;

	pr_debug("cbe_handle_pmi: node: %d, max slow_mode=%d\n", slow_mode);
}

static int pmi_notifier(struct notifier_block *nb,
				       unsigned long event, void *data)
{
	struct cpufreq_policy *policy = data;
	u8 node;

	node = cbe_cpu_to_node(policy->cpu);

	pr_debug("got notified, event=%lu, node=%u\n", event, node);

	if (pmi_slow_mode_limit[node] != 0) {
		pr_debug("limiting node %d to slow mode %d\n",
			 node, pmi_slow_mode_limit[node]);

		cpufreq_verify_within_limits(policy, 0,
			 cbe_freqs[pmi_slow_mode_limit[node]].frequency);
	}

	return 0;
}

static struct notifier_block pmi_notifier_block = {
	.notifier_call = pmi_notifier,
};

static struct pmi_handler cbe_pmi_handler = {
	.type			= PMI_TYPE_FREQ_CHANGE,
	.handle_pmi_message	= cbe_cpufreq_handle_pmi,
};


/*
 * cpufreq functions
 */
@@ -270,7 +103,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
	 * of a transition */
	policy->cpuinfo.transition_latency = 25000;

	cur_pmode = get_pmode(policy->cpu);
	cur_pmode = cbe_cpufreq_get_pmode(policy->cpu);
	pr_debug("current pmode is at %d\n",cur_pmode);

	policy->cur = cbe_freqs[cur_pmode].frequency;
@@ -297,7 +130,6 @@ static int cbe_cpufreq_verify(struct cpufreq_policy *policy)
	return cpufreq_frequency_table_verify(policy, cbe_freqs);
}


static int cbe_cpufreq_target(struct cpufreq_policy *policy,
			      unsigned int target_freq,
			      unsigned int relation)
@@ -352,22 +184,12 @@ static int __init cbe_cpufreq_init(void)
	if (!machine_is(cell))
		return -ENODEV;

	cbe_cpufreq_has_pmi = pmi_register_handler(&cbe_pmi_handler) == 0;

	if (cbe_cpufreq_has_pmi)
		cpufreq_register_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER);

	return cpufreq_register_driver(&cbe_cpufreq_driver);
}

static void __exit cbe_cpufreq_exit(void)
{
	cpufreq_unregister_driver(&cbe_cpufreq_driver);

	if (cbe_cpufreq_has_pmi) {
		cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER);
		pmi_unregister_handler(&cbe_pmi_handler);
	}
}

module_init(cbe_cpufreq_init);
+24 −0
Original line number Diff line number Diff line
/*
 * cbe_cpufreq.h
 *
 * This file contains the definitions used by the cbe_cpufreq driver.
 *
 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
 *
 * Author: Christian Krafft <krafft@de.ibm.com>
 *
 */

#include <linux/cpufreq.h>
#include <linux/types.h>

int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode);
int cbe_cpufreq_get_pmode(int cpu);

int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode);

#if defined(CONFIG_CBE_CPUFREQ_PMI) || defined(CONFIG_CBE_CPUFREQ_PMI_MODULE)
extern bool cbe_cpufreq_has_pmi;
#else
#define cbe_cpufreq_has_pmi (0)
#endif
+115 −0
Original line number Diff line number Diff line
/*
 * pervasive backend for the cbe_cpufreq driver
 *
 * This driver makes use of the pervasive unit to
 * engage the desired frequency.
 *
 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
 *
 * Author: Christian Krafft <krafft@de.ibm.com>
 *
 * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <asm/machdep.h>
#include <asm/hw_irq.h>

#include "cbe_regs.h"
#include "cbe_cpufreq.h"

/* to write to MIC register */
static u64 MIC_Slow_Fast_Timer_table[] = {
	[0 ... 7] = 0x007fc00000000000ull,
};

/* more values for the MIC */
static u64 MIC_Slow_Next_Timer_table[] = {
	0x0000240000000000ull,
	0x0000268000000000ull,
	0x000029C000000000ull,
	0x00002D0000000000ull,
	0x0000300000000000ull,
	0x0000334000000000ull,
	0x000039C000000000ull,
	0x00003FC000000000ull,
};


int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode)
{
	struct cbe_pmd_regs __iomem *pmd_regs;
	struct cbe_mic_tm_regs __iomem *mic_tm_regs;
	u64 flags;
	u64 value;
#ifdef DEBUG
	long time;
#endif

	local_irq_save(flags);

	mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu);
	pmd_regs = cbe_get_cpu_pmd_regs(cpu);

#ifdef DEBUG
	time = jiffies;
#endif

	out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]);
	out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]);

	out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]);
	out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]);

	value = in_be64(&pmd_regs->pmcr);
	/* set bits to zero */
	value &= 0xFFFFFFFFFFFFFFF8ull;
	/* set bits to next pmode */
	value |= pmode;

	out_be64(&pmd_regs->pmcr, value);

#ifdef DEBUG
	/* wait until new pmode appears in status register */
	value = in_be64(&pmd_regs->pmsr) & 0x07;
	while (value != pmode) {
		cpu_relax();
		value = in_be64(&pmd_regs->pmsr) & 0x07;
	}

	time = jiffies  - time;
	time = jiffies_to_msecs(time);
	pr_debug("had to wait %lu ms for a transition using " \
		 "pervasive unit\n", time);
#endif
	local_irq_restore(flags);

	return 0;
}


int cbe_cpufreq_get_pmode(int cpu)
{
	int ret;
	struct cbe_pmd_regs __iomem *pmd_regs;

	pmd_regs = cbe_get_cpu_pmd_regs(cpu);
	ret = in_be64(&pmd_regs->pmsr) & 0x07;

	return ret;
}
Loading