1c902be71SArnd Bergmann /*
2c902be71SArnd Bergmann  * CBE Pervasive Monitor and Debug
3c902be71SArnd Bergmann  *
4c902be71SArnd Bergmann  * (C) Copyright IBM Corporation 2005
5c902be71SArnd Bergmann  *
6c902be71SArnd Bergmann  * Authors: Maximino Aguilar (maguilar@us.ibm.com)
7c902be71SArnd Bergmann  *          Michael N. Day (mnday@us.ibm.com)
8c902be71SArnd Bergmann  *
9c902be71SArnd Bergmann  * This program is free software; you can redistribute it and/or modify
10c902be71SArnd Bergmann  * it under the terms of the GNU General Public License as published by
11c902be71SArnd Bergmann  * the Free Software Foundation; either version 2, or (at your option)
12c902be71SArnd Bergmann  * any later version.
13c902be71SArnd Bergmann  *
14c902be71SArnd Bergmann  * This program is distributed in the hope that it will be useful,
15c902be71SArnd Bergmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16c902be71SArnd Bergmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c902be71SArnd Bergmann  * GNU General Public License for more details.
18c902be71SArnd Bergmann  *
19c902be71SArnd Bergmann  * You should have received a copy of the GNU General Public License
20c902be71SArnd Bergmann  * along with this program; if not, write to the Free Software
21c902be71SArnd Bergmann  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22c902be71SArnd Bergmann  */
23c902be71SArnd Bergmann 
24c902be71SArnd Bergmann #undef DEBUG
25c902be71SArnd Bergmann 
26c902be71SArnd Bergmann #include <linux/config.h>
27c902be71SArnd Bergmann #include <linux/interrupt.h>
28c902be71SArnd Bergmann #include <linux/irq.h>
29c902be71SArnd Bergmann #include <linux/percpu.h>
30c902be71SArnd Bergmann #include <linux/types.h>
31c902be71SArnd Bergmann #include <linux/kallsyms.h>
32c902be71SArnd Bergmann 
33c902be71SArnd Bergmann #include <asm/io.h>
34c902be71SArnd Bergmann #include <asm/machdep.h>
35c902be71SArnd Bergmann #include <asm/prom.h>
36c902be71SArnd Bergmann #include <asm/pgtable.h>
37c902be71SArnd Bergmann #include <asm/reg.h>
38c902be71SArnd Bergmann 
39c902be71SArnd Bergmann #include "pervasive.h"
40c902be71SArnd Bergmann 
41c902be71SArnd Bergmann static DEFINE_SPINLOCK(cbe_pervasive_lock);
42c902be71SArnd Bergmann struct cbe_pervasive {
43c902be71SArnd Bergmann 	struct pmd_regs __iomem *regs;
44c902be71SArnd Bergmann 	unsigned int thread;
45c902be71SArnd Bergmann };
46c902be71SArnd Bergmann 
47c902be71SArnd Bergmann /* can't use per_cpu from setup_arch */
48c902be71SArnd Bergmann static struct cbe_pervasive cbe_pervasive[NR_CPUS];
49c902be71SArnd Bergmann 
50c902be71SArnd Bergmann static void __init cbe_enable_pause_zero(void)
51c902be71SArnd Bergmann {
52c902be71SArnd Bergmann 	unsigned long thread_switch_control;
53c902be71SArnd Bergmann 	unsigned long temp_register;
54c902be71SArnd Bergmann 	struct cbe_pervasive *p;
55c902be71SArnd Bergmann 	int thread;
56c902be71SArnd Bergmann 
57c902be71SArnd Bergmann 	spin_lock_irq(&cbe_pervasive_lock);
58c902be71SArnd Bergmann 	p = &cbe_pervasive[smp_processor_id()];
59c902be71SArnd Bergmann 
60c902be71SArnd Bergmann 	if (!cbe_pervasive->regs)
61c902be71SArnd Bergmann 		goto out;
62c902be71SArnd Bergmann 
63c902be71SArnd Bergmann 	pr_debug("Power Management: CPU %d\n", smp_processor_id());
64c902be71SArnd Bergmann 
65c902be71SArnd Bergmann 	 /* Enable Pause(0) control bit */
66c902be71SArnd Bergmann 	temp_register = in_be64(&p->regs->pm_control);
67c902be71SArnd Bergmann 
68c902be71SArnd Bergmann 	out_be64(&p->regs->pm_control,
69c902be71SArnd Bergmann 		 temp_register|PMD_PAUSE_ZERO_CONTROL);
70c902be71SArnd Bergmann 
71c902be71SArnd Bergmann 	/* Enable DEC and EE interrupt request */
72c902be71SArnd Bergmann 	thread_switch_control  = mfspr(SPRN_TSC_CELL);
73c902be71SArnd Bergmann 	thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST;
74c902be71SArnd Bergmann 
75c902be71SArnd Bergmann 	switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) {
76c902be71SArnd Bergmann 	case CTRL_CT0:
77c902be71SArnd Bergmann 		thread_switch_control |= TSC_CELL_DEC_ENABLE_0;
78c902be71SArnd Bergmann 		thread = 0;
79c902be71SArnd Bergmann 		break;
80c902be71SArnd Bergmann 	case CTRL_CT1:
81c902be71SArnd Bergmann 		thread_switch_control |= TSC_CELL_DEC_ENABLE_1;
82c902be71SArnd Bergmann 		thread = 1;
83c902be71SArnd Bergmann 		break;
84c902be71SArnd Bergmann 	default:
85c902be71SArnd Bergmann 		printk(KERN_WARNING "%s: unknown configuration\n",
86c902be71SArnd Bergmann 			__FUNCTION__);
87c902be71SArnd Bergmann 		thread = -1;
88c902be71SArnd Bergmann 		break;
89c902be71SArnd Bergmann 	}
90c902be71SArnd Bergmann 
91c902be71SArnd Bergmann 	if (p->thread != thread)
92c902be71SArnd Bergmann 		printk(KERN_WARNING "%s: device tree inconsistant, "
93c902be71SArnd Bergmann 				     "cpu %i: %d/%d\n", __FUNCTION__,
94c902be71SArnd Bergmann 				     smp_processor_id(),
95c902be71SArnd Bergmann 				     p->thread, thread);
96c902be71SArnd Bergmann 
97c902be71SArnd Bergmann 	mtspr(SPRN_TSC_CELL, thread_switch_control);
98c902be71SArnd Bergmann 
99c902be71SArnd Bergmann out:
100c902be71SArnd Bergmann 	spin_unlock_irq(&cbe_pervasive_lock);
101c902be71SArnd Bergmann }
102c902be71SArnd Bergmann 
103c902be71SArnd Bergmann static void cbe_idle(void)
104c902be71SArnd Bergmann {
105c902be71SArnd Bergmann 	unsigned long ctrl;
106c902be71SArnd Bergmann 
107c902be71SArnd Bergmann 	cbe_enable_pause_zero();
108c902be71SArnd Bergmann 
109c902be71SArnd Bergmann 	while (1) {
110c902be71SArnd Bergmann 		if (!need_resched()) {
111c902be71SArnd Bergmann 			local_irq_disable();
112c902be71SArnd Bergmann 			while (!need_resched()) {
113c902be71SArnd Bergmann 				/* go into low thread priority */
114c902be71SArnd Bergmann 				HMT_low();
115c902be71SArnd Bergmann 
116c902be71SArnd Bergmann 				/*
117c902be71SArnd Bergmann 				 * atomically disable thread execution
118c902be71SArnd Bergmann 				 * and runlatch.
119c902be71SArnd Bergmann 				 * External and Decrementer exceptions
120c902be71SArnd Bergmann 				 * are still handled when the thread
121c902be71SArnd Bergmann 				 * is disabled but now enter in
122c902be71SArnd Bergmann 				 * cbe_system_reset_exception()
123c902be71SArnd Bergmann 				 */
124c902be71SArnd Bergmann 				ctrl = mfspr(SPRN_CTRLF);
125c902be71SArnd Bergmann 				ctrl &= ~(CTRL_RUNLATCH | CTRL_TE);
126c902be71SArnd Bergmann 				mtspr(SPRN_CTRLT, ctrl);
127c902be71SArnd Bergmann 			}
128c902be71SArnd Bergmann 			/* restore thread prio */
129c902be71SArnd Bergmann 			HMT_medium();
130c902be71SArnd Bergmann 			local_irq_enable();
131c902be71SArnd Bergmann 		}
132c902be71SArnd Bergmann 
133c902be71SArnd Bergmann 		/*
134c902be71SArnd Bergmann 		 * turn runlatch on again before scheduling the
135c902be71SArnd Bergmann 		 * process we just woke up
136c902be71SArnd Bergmann 		 */
137c902be71SArnd Bergmann 		ppc64_runlatch_on();
138c902be71SArnd Bergmann 
139c902be71SArnd Bergmann 		preempt_enable_no_resched();
140c902be71SArnd Bergmann 		schedule();
141c902be71SArnd Bergmann 		preempt_disable();
142c902be71SArnd Bergmann 	}
143c902be71SArnd Bergmann }
144c902be71SArnd Bergmann 
1458fce10a3SArnd Bergmann static int cbe_system_reset_exception(struct pt_regs *regs)
146c902be71SArnd Bergmann {
147c902be71SArnd Bergmann 	switch (regs->msr & SRR1_WAKEMASK) {
148c902be71SArnd Bergmann 	case SRR1_WAKEEE:
149c902be71SArnd Bergmann 		do_IRQ(regs);
150c902be71SArnd Bergmann 		break;
151c902be71SArnd Bergmann 	case SRR1_WAKEDEC:
152c902be71SArnd Bergmann 		timer_interrupt(regs);
153c902be71SArnd Bergmann 		break;
154c902be71SArnd Bergmann 	case SRR1_WAKEMT:
155c902be71SArnd Bergmann 		/* no action required */
156c902be71SArnd Bergmann 		break;
157c902be71SArnd Bergmann 	default:
158c902be71SArnd Bergmann 		/* do system reset */
159c902be71SArnd Bergmann 		return 0;
160c902be71SArnd Bergmann 	}
161c902be71SArnd Bergmann 	/* everything handled */
162c902be71SArnd Bergmann 	return 1;
163c902be71SArnd Bergmann }
164c902be71SArnd Bergmann 
165c902be71SArnd Bergmann static int __init cbe_find_pmd_mmio(int cpu, struct cbe_pervasive *p)
166c902be71SArnd Bergmann {
167c902be71SArnd Bergmann 	struct device_node *node;
168c902be71SArnd Bergmann 	unsigned int *int_servers;
169c902be71SArnd Bergmann 	char *addr;
170c902be71SArnd Bergmann 	unsigned long real_address;
171c902be71SArnd Bergmann 	unsigned int size;
172c902be71SArnd Bergmann 
173c902be71SArnd Bergmann 	struct pmd_regs __iomem *pmd_mmio_area;
174c902be71SArnd Bergmann 	int hardid, thread;
175c902be71SArnd Bergmann 	int proplen;
176c902be71SArnd Bergmann 
177c902be71SArnd Bergmann 	pmd_mmio_area = NULL;
178c902be71SArnd Bergmann 	hardid = get_hard_smp_processor_id(cpu);
179c902be71SArnd Bergmann 	for (node = NULL; (node = of_find_node_by_type(node, "cpu"));) {
180c902be71SArnd Bergmann 		int_servers = (void *) get_property(node,
181c902be71SArnd Bergmann 				"ibm,ppc-interrupt-server#s", &proplen);
182c902be71SArnd Bergmann 		if (!int_servers) {
183c902be71SArnd Bergmann 			printk(KERN_WARNING "%s misses "
184c902be71SArnd Bergmann 				"ibm,ppc-interrupt-server#s property",
185c902be71SArnd Bergmann 				node->full_name);
186c902be71SArnd Bergmann 			continue;
187c902be71SArnd Bergmann 		}
188c902be71SArnd Bergmann 		for (thread = 0; thread < proplen / sizeof (int); thread++) {
189c902be71SArnd Bergmann 			if (hardid == int_servers[thread]) {
190c902be71SArnd Bergmann 				addr = get_property(node, "pervasive", NULL);
191c902be71SArnd Bergmann 				goto found;
192c902be71SArnd Bergmann 			}
193c902be71SArnd Bergmann 		}
194c902be71SArnd Bergmann 	}
195c902be71SArnd Bergmann 
196c902be71SArnd Bergmann 	printk(KERN_WARNING "%s: CPU %d not found\n", __FUNCTION__, cpu);
197c902be71SArnd Bergmann 	return -EINVAL;
198c902be71SArnd Bergmann 
199c902be71SArnd Bergmann found:
200c902be71SArnd Bergmann 	real_address = *(unsigned long*) addr;
201c902be71SArnd Bergmann 	addr += sizeof (unsigned long);
202c902be71SArnd Bergmann 	size = *(unsigned int*) addr;
203c902be71SArnd Bergmann 
204c902be71SArnd Bergmann 	pr_debug("pervasive area for CPU %d at %lx, size %x\n",
205c902be71SArnd Bergmann 			cpu, real_address, size);
206c902be71SArnd Bergmann 	p->regs = __ioremap(real_address, size, _PAGE_NO_CACHE);
207c902be71SArnd Bergmann 	p->thread = thread;
208c902be71SArnd Bergmann 	return 0;
209c902be71SArnd Bergmann }
210c902be71SArnd Bergmann 
211c902be71SArnd Bergmann void __init cell_pervasive_init(void)
212c902be71SArnd Bergmann {
213c902be71SArnd Bergmann 	struct cbe_pervasive *p;
214c902be71SArnd Bergmann 	int cpu;
215c902be71SArnd Bergmann 	int ret;
216c902be71SArnd Bergmann 
217c902be71SArnd Bergmann 	if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
218c902be71SArnd Bergmann 		return;
219c902be71SArnd Bergmann 
220c902be71SArnd Bergmann 	for_each_cpu(cpu) {
221c902be71SArnd Bergmann 		p = &cbe_pervasive[cpu];
222c902be71SArnd Bergmann 		ret = cbe_find_pmd_mmio(cpu, p);
223c902be71SArnd Bergmann 		if (ret)
224c902be71SArnd Bergmann 			return;
225c902be71SArnd Bergmann 	}
226c902be71SArnd Bergmann 
227c902be71SArnd Bergmann 	ppc_md.idle_loop = cbe_idle;
228c902be71SArnd Bergmann 	ppc_md.system_reset_exception = cbe_system_reset_exception;
229c902be71SArnd Bergmann }
230