* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/interrupt.h>
#include <linux/types.h>
#include <asm/io.h>
+#include <asm/irq_regs.h>
#include <asm/machdep.h>
+#include <asm/pmc.h>
#include <asm/reg.h>
#include <asm/spu.h>
#include "cbe_regs.h"
#include "interrupt.h"
-#include "pmu.h"
/*
* When writing to write-only mmio addresses, save a shadow copy. All of the
return val;
}
+EXPORT_SYMBOL_GPL(cbe_read_phys_ctr);
void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val)
{
}
}
}
+EXPORT_SYMBOL_GPL(cbe_write_phys_ctr);
/*
* "Logical" counter registers.
return val;
}
+EXPORT_SYMBOL_GPL(cbe_read_ctr);
void cbe_write_ctr(u32 cpu, u32 ctr, u32 val)
{
cbe_write_phys_ctr(cpu, phys_ctr, val);
}
+EXPORT_SYMBOL_GPL(cbe_write_ctr);
/*
* Counter-control registers.
return pm07_control;
}
+EXPORT_SYMBOL_GPL(cbe_read_pm07_control);
void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val)
{
if (ctr < NR_CTRS)
WRITE_WO_MMIO(pm07_control[ctr], val);
}
+EXPORT_SYMBOL_GPL(cbe_write_pm07_control);
/*
* Other PMU control registers. Most of these are write-only.
return val;
}
+EXPORT_SYMBOL_GPL(cbe_read_pm);
void cbe_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)
{
break;
}
}
+EXPORT_SYMBOL_GPL(cbe_write_pm);
/*
* Get/set the size of a physical counter to either 16 or 32 bits.
return size;
}
+EXPORT_SYMBOL_GPL(cbe_get_ctr_size);
void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)
{
cbe_write_pm(cpu, pm_control, pm_ctrl);
}
}
+EXPORT_SYMBOL_GPL(cbe_set_ctr_size);
/*
* Enable/disable the entire performance monitoring unit.
pm_ctrl = cbe_read_pm(cpu, pm_control) | CBE_PM_ENABLE_PERF_MON;
cbe_write_pm(cpu, pm_control, pm_ctrl);
}
+EXPORT_SYMBOL_GPL(cbe_enable_pm);
void cbe_disable_pm(u32 cpu)
{
pm_ctrl = cbe_read_pm(cpu, pm_control) & ~CBE_PM_ENABLE_PERF_MON;
cbe_write_pm(cpu, pm_control, pm_ctrl);
}
+EXPORT_SYMBOL_GPL(cbe_disable_pm);
/*
* Reading from the trace_buffer.
*buf++ = in_be64(&pmd_regs->trace_buffer_0_63);
*buf++ = in_be64(&pmd_regs->trace_buffer_64_127);
}
+EXPORT_SYMBOL_GPL(cbe_read_trace_buffer);
+
+/*
+ * Enabling/disabling interrupts for the entire performance monitoring unit.
+ */
+
+u32 cbe_get_and_clear_pm_interrupts(u32 cpu)
+{
+ /* Reading pm_status clears the interrupt bits. */
+ return cbe_read_pm(cpu, pm_status);
+}
+EXPORT_SYMBOL_GPL(cbe_get_and_clear_pm_interrupts);
+
+void cbe_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)
+{
+ /* Set which node and thread will handle the next interrupt. */
+ iic_set_interrupt_routing(cpu, thread, 0);
+
+ /* Enable the interrupt bits in the pm_status register. */
+ if (mask)
+ cbe_write_pm(cpu, pm_status, mask);
+}
+EXPORT_SYMBOL_GPL(cbe_enable_pm_interrupts);
+
+void cbe_disable_pm_interrupts(u32 cpu)
+{
+ cbe_get_and_clear_pm_interrupts(cpu);
+ cbe_write_pm(cpu, pm_status, 0);
+}
+EXPORT_SYMBOL_GPL(cbe_disable_pm_interrupts);
+
+static irqreturn_t cbe_pm_irq(int irq, void *dev_id)
+{
+ perf_irq(get_irq_regs());
+ return IRQ_HANDLED;
+}
+
+static int __init cbe_init_pm_irq(void)
+{
+ unsigned int irq;
+ int rc, node;
+
+ if (!machine_is(cell))
+ return 0;
+
+ for_each_node(node) {
+ irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI |
+ (node << IIC_IRQ_NODE_SHIFT));
+ if (irq == NO_IRQ) {
+ printk("ERROR: Unable to allocate irq for node %d\n",
+ node);
+ return -EINVAL;
+ }
+
+ rc = request_irq(irq, cbe_pm_irq,
+ IRQF_DISABLED, "cbe-pmu-0", NULL);
+ if (rc) {
+ printk("ERROR: Request for irq on node %d failed\n",
+ node);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+arch_initcall(cbe_init_pm_irq);
+
+void cbe_sync_irq(int node)
+{
+ unsigned int irq;
+
+ irq = irq_find_mapping(NULL,
+ IIC_IRQ_IOEX_PMI
+ | (node << IIC_IRQ_NODE_SHIFT));
+
+ if (irq == NO_IRQ) {
+ printk(KERN_WARNING "ERROR, unable to get existing irq %d " \
+ "for node %d\n", irq, node);
+ return;
+ }
+
+ synchronize_irq(irq);
+}
+EXPORT_SYMBOL_GPL(cbe_sync_irq);