|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Cell Broadband Engine Performance Monitor | 
|  | * | 
|  | * (C) Copyright IBM Corporation 2001,2006 | 
|  | * | 
|  | * Author: | 
|  | *    David Erb (djerb@us.ibm.com) | 
|  | *    Kevin Corry (kevcorry@us.ibm.com) | 
|  | */ | 
|  |  | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/irqdomain.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/export.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 <asm/cell-regs.h> | 
|  |  | 
|  | #include "interrupt.h" | 
|  |  | 
|  | /* | 
|  | * When writing to write-only mmio addresses, save a shadow copy. All of the | 
|  | * registers are 32-bit, but stored in the upper-half of a 64-bit field in | 
|  | * pmd_regs. | 
|  | */ | 
|  |  | 
|  | #define WRITE_WO_MMIO(reg, x)					\ | 
|  | do {							\ | 
|  | u32 _x = (x);					\ | 
|  | struct cbe_pmd_regs __iomem *pmd_regs;		\ | 
|  | struct cbe_pmd_shadow_regs *shadow_regs;	\ | 
|  | pmd_regs = cbe_get_cpu_pmd_regs(cpu);		\ | 
|  | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);	\ | 
|  | out_be64(&(pmd_regs->reg), (((u64)_x) << 32));	\ | 
|  | shadow_regs->reg = _x;				\ | 
|  | } while (0) | 
|  |  | 
|  | #define READ_SHADOW_REG(val, reg)				\ | 
|  | do {							\ | 
|  | struct cbe_pmd_shadow_regs *shadow_regs;	\ | 
|  | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu);	\ | 
|  | (val) = shadow_regs->reg;			\ | 
|  | } while (0) | 
|  |  | 
|  | #define READ_MMIO_UPPER32(val, reg)				\ | 
|  | do {							\ | 
|  | struct cbe_pmd_regs __iomem *pmd_regs;		\ | 
|  | pmd_regs = cbe_get_cpu_pmd_regs(cpu);		\ | 
|  | (val) = (u32)(in_be64(&pmd_regs->reg) >> 32);	\ | 
|  | } while (0) | 
|  |  | 
|  | /* | 
|  | * Physical counter registers. | 
|  | * Each physical counter can act as one 32-bit counter or two 16-bit counters. | 
|  | */ | 
|  |  | 
|  | u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr) | 
|  | { | 
|  | u32 val_in_latch, val = 0; | 
|  |  | 
|  | if (phys_ctr < NR_PHYS_CTRS) { | 
|  | READ_SHADOW_REG(val_in_latch, counter_value_in_latch); | 
|  |  | 
|  | /* Read the latch or the actual counter, whichever is newer. */ | 
|  | if (val_in_latch & (1 << phys_ctr)) { | 
|  | READ_SHADOW_REG(val, pm_ctr[phys_ctr]); | 
|  | } else { | 
|  | READ_MMIO_UPPER32(val, pm_ctr[phys_ctr]); | 
|  | } | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_read_phys_ctr); | 
|  |  | 
|  | void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) | 
|  | { | 
|  | struct cbe_pmd_shadow_regs *shadow_regs; | 
|  | u32 pm_ctrl; | 
|  |  | 
|  | if (phys_ctr < NR_PHYS_CTRS) { | 
|  | /* Writing to a counter only writes to a hardware latch. | 
|  | * The new value is not propagated to the actual counter | 
|  | * until the performance monitor is enabled. | 
|  | */ | 
|  | WRITE_WO_MMIO(pm_ctr[phys_ctr], val); | 
|  |  | 
|  | pm_ctrl = cbe_read_pm(cpu, pm_control); | 
|  | if (pm_ctrl & CBE_PM_ENABLE_PERF_MON) { | 
|  | /* The counters are already active, so we need to | 
|  | * rewrite the pm_control register to "re-enable" | 
|  | * the PMU. | 
|  | */ | 
|  | cbe_write_pm(cpu, pm_control, pm_ctrl); | 
|  | } else { | 
|  | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); | 
|  | shadow_regs->counter_value_in_latch |= (1 << phys_ctr); | 
|  | } | 
|  | } | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_write_phys_ctr); | 
|  |  | 
|  | /* | 
|  | * "Logical" counter registers. | 
|  | * These will read/write 16-bits or 32-bits depending on the | 
|  | * current size of the counter. Counters 4 - 7 are always 16-bit. | 
|  | */ | 
|  |  | 
|  | u32 cbe_read_ctr(u32 cpu, u32 ctr) | 
|  | { | 
|  | u32 val; | 
|  | u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); | 
|  |  | 
|  | val = cbe_read_phys_ctr(cpu, phys_ctr); | 
|  |  | 
|  | if (cbe_get_ctr_size(cpu, phys_ctr) == 16) | 
|  | val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); | 
|  |  | 
|  | return val; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_read_ctr); | 
|  |  | 
|  | void cbe_write_ctr(u32 cpu, u32 ctr, u32 val) | 
|  | { | 
|  | u32 phys_ctr; | 
|  | u32 phys_val; | 
|  |  | 
|  | phys_ctr = ctr & (NR_PHYS_CTRS - 1); | 
|  |  | 
|  | if (cbe_get_ctr_size(cpu, phys_ctr) == 16) { | 
|  | phys_val = cbe_read_phys_ctr(cpu, phys_ctr); | 
|  |  | 
|  | if (ctr < NR_PHYS_CTRS) | 
|  | val = (val << 16) | (phys_val & 0xffff); | 
|  | else | 
|  | val = (val & 0xffff) | (phys_val & 0xffff0000); | 
|  | } | 
|  |  | 
|  | cbe_write_phys_ctr(cpu, phys_ctr, val); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_write_ctr); | 
|  |  | 
|  | /* | 
|  | * Counter-control registers. | 
|  | * Each "logical" counter has a corresponding control register. | 
|  | */ | 
|  |  | 
|  | u32 cbe_read_pm07_control(u32 cpu, u32 ctr) | 
|  | { | 
|  | u32 pm07_control = 0; | 
|  |  | 
|  | if (ctr < NR_CTRS) | 
|  | READ_SHADOW_REG(pm07_control, pm07_control[ctr]); | 
|  |  | 
|  | 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. | 
|  | */ | 
|  |  | 
|  | u32 cbe_read_pm(u32 cpu, enum pm_reg_name reg) | 
|  | { | 
|  | u32 val = 0; | 
|  |  | 
|  | switch (reg) { | 
|  | case group_control: | 
|  | READ_SHADOW_REG(val, group_control); | 
|  | break; | 
|  |  | 
|  | case debug_bus_control: | 
|  | READ_SHADOW_REG(val, debug_bus_control); | 
|  | break; | 
|  |  | 
|  | case trace_address: | 
|  | READ_MMIO_UPPER32(val, trace_address); | 
|  | break; | 
|  |  | 
|  | case ext_tr_timer: | 
|  | READ_SHADOW_REG(val, ext_tr_timer); | 
|  | break; | 
|  |  | 
|  | case pm_status: | 
|  | READ_MMIO_UPPER32(val, pm_status); | 
|  | break; | 
|  |  | 
|  | case pm_control: | 
|  | READ_SHADOW_REG(val, pm_control); | 
|  | break; | 
|  |  | 
|  | case pm_interval: | 
|  | READ_MMIO_UPPER32(val, pm_interval); | 
|  | break; | 
|  |  | 
|  | case pm_start_stop: | 
|  | READ_SHADOW_REG(val, pm_start_stop); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_read_pm); | 
|  |  | 
|  | void cbe_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) | 
|  | { | 
|  | switch (reg) { | 
|  | case group_control: | 
|  | WRITE_WO_MMIO(group_control, val); | 
|  | break; | 
|  |  | 
|  | case debug_bus_control: | 
|  | WRITE_WO_MMIO(debug_bus_control, val); | 
|  | break; | 
|  |  | 
|  | case trace_address: | 
|  | WRITE_WO_MMIO(trace_address, val); | 
|  | break; | 
|  |  | 
|  | case ext_tr_timer: | 
|  | WRITE_WO_MMIO(ext_tr_timer, val); | 
|  | break; | 
|  |  | 
|  | case pm_status: | 
|  | WRITE_WO_MMIO(pm_status, val); | 
|  | break; | 
|  |  | 
|  | case pm_control: | 
|  | WRITE_WO_MMIO(pm_control, val); | 
|  | break; | 
|  |  | 
|  | case pm_interval: | 
|  | WRITE_WO_MMIO(pm_interval, val); | 
|  | break; | 
|  |  | 
|  | case pm_start_stop: | 
|  | WRITE_WO_MMIO(pm_start_stop, val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_write_pm); | 
|  |  | 
|  | /* | 
|  | * Get/set the size of a physical counter to either 16 or 32 bits. | 
|  | */ | 
|  |  | 
|  | u32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr) | 
|  | { | 
|  | u32 pm_ctrl, size = 0; | 
|  |  | 
|  | if (phys_ctr < NR_PHYS_CTRS) { | 
|  | pm_ctrl = cbe_read_pm(cpu, pm_control); | 
|  | size = (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_get_ctr_size); | 
|  |  | 
|  | void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) | 
|  | { | 
|  | u32 pm_ctrl; | 
|  |  | 
|  | if (phys_ctr < NR_PHYS_CTRS) { | 
|  | pm_ctrl = cbe_read_pm(cpu, pm_control); | 
|  | switch (ctr_size) { | 
|  | case 16: | 
|  | pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); | 
|  | break; | 
|  |  | 
|  | case 32: | 
|  | pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); | 
|  | break; | 
|  | } | 
|  | cbe_write_pm(cpu, pm_control, pm_ctrl); | 
|  | } | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(cbe_set_ctr_size); | 
|  |  | 
|  | /* | 
|  | * Enable/disable the entire performance monitoring unit. | 
|  | * When we enable the PMU, all pending writes to counters get committed. | 
|  | */ | 
|  |  | 
|  | void cbe_enable_pm(u32 cpu) | 
|  | { | 
|  | struct cbe_pmd_shadow_regs *shadow_regs; | 
|  | u32 pm_ctrl; | 
|  |  | 
|  | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); | 
|  | shadow_regs->counter_value_in_latch = 0; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | u32 pm_ctrl; | 
|  | 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. | 
|  | * The trace buffer is two 64-bit registers. Reading from | 
|  | * the second half automatically increments the trace_address. | 
|  | */ | 
|  |  | 
|  | void cbe_read_trace_buffer(u32 cpu, u64 *buf) | 
|  | { | 
|  | struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu); | 
|  |  | 
|  | *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; | 
|  |  | 
|  | for_each_online_node(node) { | 
|  | irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI | | 
|  | (node << IIC_IRQ_NODE_SHIFT)); | 
|  | if (!irq) { | 
|  | printk("ERROR: Unable to allocate irq for node %d\n", | 
|  | node); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | rc = request_irq(irq, cbe_pm_irq, | 
|  | 0, "cbe-pmu-0", NULL); | 
|  | if (rc) { | 
|  | printk("ERROR: Request for irq on node %d failed\n", | 
|  | node); | 
|  | return rc; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | machine_arch_initcall(cell, 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) { | 
|  | 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); | 
|  |  |