1 /*
2  * pervasive backend for the cbe_cpufreq driver
3  *
4  * This driver makes use of the pervasive unit to
5  * engage the desired frequency.
6  *
7  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
8  *
9  * Author: Christian Krafft <krafft@de.ibm.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25 
26 #include <linux/io.h>
27 #include <linux/kernel.h>
28 #include <linux/time.h>
29 #include <asm/machdep.h>
30 #include <asm/hw_irq.h>
31 #include <asm/cell-regs.h>
32 
33 #include "ppc_cbe_cpufreq.h"
34 
35 /* to write to MIC register */
36 static u64 MIC_Slow_Fast_Timer_table[] = {
37 	[0 ... 7] = 0x007fc00000000000ull,
38 };
39 
40 /* more values for the MIC */
41 static u64 MIC_Slow_Next_Timer_table[] = {
42 	0x0000240000000000ull,
43 	0x0000268000000000ull,
44 	0x000029C000000000ull,
45 	0x00002D0000000000ull,
46 	0x0000300000000000ull,
47 	0x0000334000000000ull,
48 	0x000039C000000000ull,
49 	0x00003FC000000000ull,
50 };
51 
52 
53 int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode)
54 {
55 	struct cbe_pmd_regs __iomem *pmd_regs;
56 	struct cbe_mic_tm_regs __iomem *mic_tm_regs;
57 	unsigned long flags;
58 	u64 value;
59 #ifdef DEBUG
60 	long time;
61 #endif
62 
63 	local_irq_save(flags);
64 
65 	mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu);
66 	pmd_regs = cbe_get_cpu_pmd_regs(cpu);
67 
68 #ifdef DEBUG
69 	time = jiffies;
70 #endif
71 
72 	out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]);
73 	out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]);
74 
75 	out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]);
76 	out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]);
77 
78 	value = in_be64(&pmd_regs->pmcr);
79 	/* set bits to zero */
80 	value &= 0xFFFFFFFFFFFFFFF8ull;
81 	/* set bits to next pmode */
82 	value |= pmode;
83 
84 	out_be64(&pmd_regs->pmcr, value);
85 
86 #ifdef DEBUG
87 	/* wait until new pmode appears in status register */
88 	value = in_be64(&pmd_regs->pmsr) & 0x07;
89 	while (value != pmode) {
90 		cpu_relax();
91 		value = in_be64(&pmd_regs->pmsr) & 0x07;
92 	}
93 
94 	time = jiffies  - time;
95 	time = jiffies_to_msecs(time);
96 	pr_debug("had to wait %lu ms for a transition using " \
97 		 "pervasive unit\n", time);
98 #endif
99 	local_irq_restore(flags);
100 
101 	return 0;
102 }
103 
104 
105 int cbe_cpufreq_get_pmode(int cpu)
106 {
107 	int ret;
108 	struct cbe_pmd_regs __iomem *pmd_regs;
109 
110 	pmd_regs = cbe_get_cpu_pmd_regs(cpu);
111 	ret = in_be64(&pmd_regs->pmsr) & 0x07;
112 
113 	return ret;
114 }
115 
116