Commit 27a513ca authored by Russell King's avatar Russell King

Merge branch 'devel-stable' into for-next

Conflicts:
	arch/arm/kernel/perf_event_cpu.c
parents 970d96f9 bcc8fa83
This diff is collapsed.
...@@ -137,17 +137,12 @@ int mcpm_wait_for_cpu_powerdown(unsigned int cpu, unsigned int cluster); ...@@ -137,17 +137,12 @@ int mcpm_wait_for_cpu_powerdown(unsigned int cpu, unsigned int cluster);
/** /**
* mcpm_cpu_suspend - bring the calling CPU in a suspended state * mcpm_cpu_suspend - bring the calling CPU in a suspended state
* *
* @expected_residency: duration in microseconds the CPU is expected * The calling CPU is suspended. This is similar to mcpm_cpu_power_down()
* to remain suspended, or 0 if unknown/infinity. * except for possible extra platform specific configuration steps to allow
* * an asynchronous wake-up e.g. with a pending interrupt.
* The calling CPU is suspended. The expected residency argument is used
* as a hint by the platform specific backend to implement the appropriate
* sleep state level according to the knowledge it has on wake-up latency
* for the given hardware.
* *
* If this CPU is found to be the "last man standing" in the cluster * If this CPU is found to be the "last man standing" in the cluster
* then the cluster may be prepared for power-down too, if the expected * then the cluster may be prepared for power-down too.
* residency makes it worthwhile.
* *
* This must be called with interrupts disabled. * This must be called with interrupts disabled.
* *
...@@ -157,7 +152,7 @@ int mcpm_wait_for_cpu_powerdown(unsigned int cpu, unsigned int cluster); ...@@ -157,7 +152,7 @@ int mcpm_wait_for_cpu_powerdown(unsigned int cpu, unsigned int cluster);
* This will return if mcpm_platform_register() has not been called * This will return if mcpm_platform_register() has not been called
* previously in which case the caller should take appropriate action. * previously in which case the caller should take appropriate action.
*/ */
void mcpm_cpu_suspend(u64 expected_residency); void mcpm_cpu_suspend(void);
/** /**
* mcpm_cpu_powered_up - housekeeping workafter a CPU has been powered up * mcpm_cpu_powered_up - housekeeping workafter a CPU has been powered up
...@@ -234,12 +229,6 @@ struct mcpm_platform_ops { ...@@ -234,12 +229,6 @@ struct mcpm_platform_ops {
void (*cpu_is_up)(unsigned int cpu, unsigned int cluster); void (*cpu_is_up)(unsigned int cpu, unsigned int cluster);
void (*cluster_is_up)(unsigned int cluster); void (*cluster_is_up)(unsigned int cluster);
int (*wait_for_powerdown)(unsigned int cpu, unsigned int cluster); int (*wait_for_powerdown)(unsigned int cpu, unsigned int cluster);
/* deprecated callbacks */
int (*power_up)(unsigned int cpu, unsigned int cluster);
void (*power_down)(void);
void (*suspend)(u64);
void (*powered_up)(void);
}; };
/** /**
...@@ -251,35 +240,6 @@ struct mcpm_platform_ops { ...@@ -251,35 +240,6 @@ struct mcpm_platform_ops {
*/ */
int __init mcpm_platform_register(const struct mcpm_platform_ops *ops); int __init mcpm_platform_register(const struct mcpm_platform_ops *ops);
/* Synchronisation structures for coordinating safe cluster setup/teardown: */
/*
* When modifying this structure, make sure you update the MCPM_SYNC_ defines
* to match.
*/
struct mcpm_sync_struct {
/* individual CPU states */
struct {
s8 cpu __aligned(__CACHE_WRITEBACK_GRANULE);
} cpus[MAX_CPUS_PER_CLUSTER];
/* cluster state */
s8 cluster __aligned(__CACHE_WRITEBACK_GRANULE);
/* inbound-side state */
s8 inbound __aligned(__CACHE_WRITEBACK_GRANULE);
};
struct sync_struct {
struct mcpm_sync_struct clusters[MAX_NR_CLUSTERS];
};
void __mcpm_cpu_going_down(unsigned int cpu, unsigned int cluster);
void __mcpm_cpu_down(unsigned int cpu, unsigned int cluster);
void __mcpm_outbound_leave_critical(unsigned int cluster, int state);
bool __mcpm_outbound_enter_critical(unsigned int this_cpu, unsigned int cluster);
int __mcpm_cluster_state(unsigned int cluster);
/** /**
* mcpm_sync_init - Initialize the cluster synchronization support * mcpm_sync_init - Initialize the cluster synchronization support
* *
...@@ -318,6 +278,29 @@ int __init mcpm_loopback(void (*cache_disable)(void)); ...@@ -318,6 +278,29 @@ int __init mcpm_loopback(void (*cache_disable)(void));
void __init mcpm_smp_set_ops(void); void __init mcpm_smp_set_ops(void);
/*
* Synchronisation structures for coordinating safe cluster setup/teardown.
* This is private to the MCPM core code and shared between C and assembly.
* When modifying this structure, make sure you update the MCPM_SYNC_ defines
* to match.
*/
struct mcpm_sync_struct {
/* individual CPU states */
struct {
s8 cpu __aligned(__CACHE_WRITEBACK_GRANULE);
} cpus[MAX_CPUS_PER_CLUSTER];
/* cluster state */
s8 cluster __aligned(__CACHE_WRITEBACK_GRANULE);
/* inbound-side state */
s8 inbound __aligned(__CACHE_WRITEBACK_GRANULE);
};
struct sync_struct {
struct mcpm_sync_struct clusters[MAX_NR_CLUSTERS];
};
#else #else
/* /*
......
...@@ -19,4 +19,11 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); ...@@ -19,4 +19,11 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs);
#define perf_misc_flags(regs) perf_misc_flags(regs) #define perf_misc_flags(regs) perf_misc_flags(regs)
#endif #endif
#define perf_arch_fetch_caller_regs(regs, __ip) { \
(regs)->ARM_pc = (__ip); \
(regs)->ARM_fp = (unsigned long) __builtin_frame_address(0); \
(regs)->ARM_sp = current_stack_pointer; \
(regs)->ARM_cpsr = SVC_MODE; \
}
#endif /* __ARM_PERF_EVENT_H__ */ #endif /* __ARM_PERF_EVENT_H__ */
...@@ -24,22 +24,10 @@ ...@@ -24,22 +24,10 @@
* interrupt and passed the address of the low level handler, * interrupt and passed the address of the low level handler,
* and can be used to implement any platform specific handling * and can be used to implement any platform specific handling
* before or after calling it. * before or after calling it.
* @runtime_resume: an optional handler which will be called by the
* runtime PM framework following a call to pm_runtime_get().
* Note that if pm_runtime_get() is called more than once in
* succession this handler will only be called once.
* @runtime_suspend: an optional handler which will be called by the
* runtime PM framework following a call to pm_runtime_put().
* Note that if pm_runtime_get() is called more than once in
* succession this handler will only be called following the
* final call to pm_runtime_put() that actually disables the
* hardware.
*/ */
struct arm_pmu_platdata { struct arm_pmu_platdata {
irqreturn_t (*handle_irq)(int irq, void *dev, irqreturn_t (*handle_irq)(int irq, void *dev,
irq_handler_t pmu_handler); irq_handler_t pmu_handler);
int (*runtime_resume)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
}; };
#ifdef CONFIG_HW_PERF_EVENTS #ifdef CONFIG_HW_PERF_EVENTS
...@@ -92,6 +80,7 @@ struct pmu_hw_events { ...@@ -92,6 +80,7 @@ struct pmu_hw_events {
struct arm_pmu { struct arm_pmu {
struct pmu pmu; struct pmu pmu;
cpumask_t active_irqs; cpumask_t active_irqs;
cpumask_t supported_cpus;
int *irq_affinity; int *irq_affinity;
char *name; char *name;
irqreturn_t (*handle_irq)(int irq_num, void *dev); irqreturn_t (*handle_irq)(int irq_num, void *dev);
...@@ -122,8 +111,6 @@ struct arm_pmu { ...@@ -122,8 +111,6 @@ struct arm_pmu {
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
extern const struct dev_pm_ops armpmu_dev_pm_ops;
int armpmu_register(struct arm_pmu *armpmu, int type); int armpmu_register(struct arm_pmu *armpmu, int type);
u64 armpmu_event_update(struct perf_event *event); u64 armpmu_event_update(struct perf_event *event);
...@@ -158,6 +145,10 @@ struct pmu_probe_info { ...@@ -158,6 +145,10 @@ struct pmu_probe_info {
#define XSCALE_PMU_PROBE(_version, _fn) \ #define XSCALE_PMU_PROBE(_version, _fn) \
PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn) PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn)
int arm_pmu_device_probe(struct platform_device *pdev,
const struct of_device_id *of_table,
const struct pmu_probe_info *probe_table);
#endif /* CONFIG_HW_PERF_EVENTS */ #endif /* CONFIG_HW_PERF_EVENTS */
#endif /* __ARM_PMU_H__ */ #endif /* __ARM_PMU_H__ */
...@@ -71,7 +71,9 @@ obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o ...@@ -71,7 +71,9 @@ obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o \
perf_event_xscale.o perf_event_v6.o \
perf_event_v7.o
CFLAGS_pj4-cp0.o := -marm CFLAGS_pj4-cp0.o := -marm
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
......
This diff is collapsed.
This diff is collapsed.
...@@ -31,6 +31,14 @@ ...@@ -31,6 +31,14 @@
*/ */
#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K)
#include <asm/cputype.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <linux/of.h>
#include <linux/platform_device.h>
enum armv6_perf_types { enum armv6_perf_types {
ARMV6_PERFCTR_ICACHE_MISS = 0x0, ARMV6_PERFCTR_ICACHE_MISS = 0x0,
ARMV6_PERFCTR_IBUF_STALL = 0x1, ARMV6_PERFCTR_IBUF_STALL = 0x1,
...@@ -543,24 +551,39 @@ static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -543,24 +551,39 @@ static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu)
return 0; return 0;
} }
#else
static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu)
{
return -ENODEV;
}
static int armv6_1156_pmu_init(struct arm_pmu *cpu_pmu) static struct of_device_id armv6_pmu_of_device_ids[] = {
{ {.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init},
return -ENODEV; {.compatible = "arm,arm1176-pmu", .data = armv6_1176_pmu_init},
} {.compatible = "arm,arm1136-pmu", .data = armv6_1136_pmu_init},
{ /* sentinel value */ }
};
static int armv6_1176_pmu_init(struct arm_pmu *cpu_pmu) static const struct pmu_probe_info armv6_pmu_probe_table[] = {
ARM_PMU_PROBE(ARM_CPU_PART_ARM1136, armv6_1136_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM1156, armv6_1156_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM1176, armv6_1176_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM11MPCORE, armv6mpcore_pmu_init),
{ /* sentinel value */ }
};
static int armv6_pmu_device_probe(struct platform_device *pdev)
{ {
return -ENODEV; return arm_pmu_device_probe(pdev, armv6_pmu_of_device_ids,
armv6_pmu_probe_table);
} }
static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu) static struct platform_driver armv6_pmu_driver = {
.driver = {
.name = "armv6-pmu",
.of_match_table = armv6_pmu_of_device_ids,
},
.probe = armv6_pmu_device_probe,
};
static int __init register_armv6_pmu_driver(void)
{ {
return -ENODEV; return platform_driver_register(&armv6_pmu_driver);
} }
device_initcall(register_armv6_pmu_driver);
#endif /* CONFIG_CPU_V6 || CONFIG_CPU_V6K */ #endif /* CONFIG_CPU_V6 || CONFIG_CPU_V6K */
...@@ -19,9 +19,15 @@ ...@@ -19,9 +19,15 @@
#ifdef CONFIG_CPU_V7 #ifdef CONFIG_CPU_V7
#include <asm/cp15.h> #include <asm/cp15.h>
#include <asm/cputype.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <asm/vfp.h> #include <asm/vfp.h>
#include "../vfp/vfpinstr.h" #include "../vfp/vfpinstr.h"
#include <linux/of.h>
#include <linux/platform_device.h>
/* /*
* Common ARMv7 event types * Common ARMv7 event types
* *
...@@ -1056,15 +1062,22 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1056,15 +1062,22 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->max_period = (1LLU << 32) - 1; cpu_pmu->max_period = (1LLU << 32) - 1;
}; };
static u32 armv7_read_num_pmnc_events(void) static void armv7_read_num_pmnc_events(void *info)
{ {
u32 nb_cnt; int *nb_cnt = info;
/* Read the nb of CNTx counters supported from PMNC */ /* Read the nb of CNTx counters supported from PMNC */
nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK; *nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK;
/* Add the CPU cycles counter and return */ /* Add the CPU cycles counter */
return nb_cnt + 1; *nb_cnt += 1;
}
static int armv7_probe_num_events(struct arm_pmu *arm_pmu)
{
return smp_call_function_any(&arm_pmu->supported_cpus,
armv7_read_num_pmnc_events,
&arm_pmu->num_events, 1);
} }
static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1072,8 +1085,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1072,8 +1085,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a8"; cpu_pmu->name = "armv7_cortex_a8";
cpu_pmu->map_event = armv7_a8_map_event; cpu_pmu->map_event = armv7_a8_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events(); return armv7_probe_num_events(cpu_pmu);
return 0;
} }
static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1081,8 +1093,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1081,8 +1093,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a9"; cpu_pmu->name = "armv7_cortex_a9";
cpu_pmu->map_event = armv7_a9_map_event; cpu_pmu->map_event = armv7_a9_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events(); return armv7_probe_num_events(cpu_pmu);
return 0;
} }
static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1090,8 +1101,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1090,8 +1101,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a5"; cpu_pmu->name = "armv7_cortex_a5";
cpu_pmu->map_event = armv7_a5_map_event; cpu_pmu->map_event = armv7_a5_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events(); return armv7_probe_num_events(cpu_pmu);
return 0;
} }
static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1099,9 +1109,8 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1099,9 +1109,8 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a15"; cpu_pmu->name = "armv7_cortex_a15";
cpu_pmu->map_event = armv7_a15_map_event; cpu_pmu->map_event = armv7_a15_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->set_event_filter = armv7pmu_set_event_filter; cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1109,9 +1118,8 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1109,9 +1118,8 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a7"; cpu_pmu->name = "armv7_cortex_a7";
cpu_pmu->map_event = armv7_a7_map_event; cpu_pmu->map_event = armv7_a7_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->set_event_filter = armv7pmu_set_event_filter; cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1119,16 +1127,15 @@ static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1119,16 +1127,15 @@ static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a12"; cpu_pmu->name = "armv7_cortex_a12";
cpu_pmu->map_event = armv7_a12_map_event; cpu_pmu->map_event = armv7_a12_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->set_event_filter = armv7pmu_set_event_filter; cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu)
{ {
armv7_a12_pmu_init(cpu_pmu); int ret = armv7_a12_pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a17"; cpu_pmu->name = "armv7_cortex_a17";
return 0; return ret;
} }
/* /*
...@@ -1508,14 +1515,13 @@ static int krait_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1508,14 +1515,13 @@ static int krait_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->map_event = krait_map_event_no_branch; cpu_pmu->map_event = krait_map_event_no_branch;
else else
cpu_pmu->map_event = krait_map_event; cpu_pmu->map_event = krait_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->set_event_filter = armv7pmu_set_event_filter; cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
cpu_pmu->reset = krait_pmu_reset; cpu_pmu->reset = krait_pmu_reset;
cpu_pmu->enable = krait_pmu_enable_event; cpu_pmu->enable = krait_pmu_enable_event;
cpu_pmu->disable = krait_pmu_disable_event; cpu_pmu->disable = krait_pmu_disable_event;
cpu_pmu->get_event_idx = krait_pmu_get_event_idx; cpu_pmu->get_event_idx = krait_pmu_get_event_idx;
cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx; cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
/* /*
...@@ -1833,13 +1839,12 @@ static int scorpion_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1833,13 +1839,12 @@ static int scorpion_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_scorpion"; cpu_pmu->name = "armv7_scorpion";
cpu_pmu->map_event = scorpion_map_event; cpu_pmu->map_event = scorpion_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->reset = scorpion_pmu_reset; cpu_pmu->reset = scorpion_pmu_reset;
cpu_pmu->enable = scorpion_pmu_enable_event; cpu_pmu->enable = scorpion_pmu_enable_event;
cpu_pmu->disable = scorpion_pmu_disable_event; cpu_pmu->disable = scorpion_pmu_disable_event;
cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx; cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx;
cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx; cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu)
...@@ -1847,62 +1852,52 @@ static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -1847,62 +1852,52 @@ static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu); armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_scorpion_mp"; cpu_pmu->name = "armv7_scorpion_mp";
cpu_pmu->map_event = scorpion_map_event; cpu_pmu->map_event = scorpion_map_event;
cpu_pmu->num_events = armv7_read_num_pmnc_events();
cpu_pmu->reset = scorpion_pmu_reset; cpu_pmu->reset = scorpion_pmu_reset;
cpu_pmu->enable = scorpion_pmu_enable_event; cpu_pmu->enable = scorpion_pmu_enable_event;
cpu_pmu->disable = scorpion_pmu_disable_event; cpu_pmu->disable = scorpion_pmu_disable_event;
cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx; cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx;
cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx; cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
return 0; return armv7_probe_num_events(cpu_pmu);
} }
#else
static inline int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) static const struct of_device_id armv7_pmu_of_device_ids[] = {
{ {.compatible = "arm,cortex-a17-pmu", .data = armv7_a17_pmu_init},
return -ENODEV; {.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init},
} {.compatible = "arm,cortex-a12-pmu", .data = armv7_a12_pmu_init},
{.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init},
static inline int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) {.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init},
{ {.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init},
return -ENODEV; {.compatible = "arm,cortex-a5-pmu", .data = armv7_a5_pmu_init},
} {.compatible = "qcom,krait-pmu", .data = krait_pmu_init},
{.compatible = "qcom,scorpion-pmu", .data = scorpion_pmu_init},
static inline int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) {.compatible = "qcom,scorpion-mp-pmu", .data = scorpion_mp_pmu_init},
{ {},
return -ENODEV; };
}
static inline int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
{
return -ENODEV;
}
static inline int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
{
return -ENODEV;
}
static inline int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) static const struct pmu_probe_info armv7_pmu_probe_table[] = {
{ ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A8, armv7_a8_pmu_init),
return -ENODEV; ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A9, armv7_a9_pmu_init),
} { /* sentinel value */ }
};
static inline int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu)
{
return -ENODEV;
}
static inline int krait_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_pmu_device_probe(struct platform_device *pdev)
{ {
return -ENODEV; return arm_pmu_device_probe(pdev, armv7_pmu_of_device_ids,
armv7_pmu_probe_table);
} }
static inline int scorpion_pmu_init(struct arm_pmu *cpu_pmu) static struct platform_driver armv7_pmu_driver = {
{ .driver = {
return -ENODEV; .name = "armv7-pmu",
} .of_match_table = armv7_pmu_of_device_ids,
},
.probe = armv7_pmu_device_probe,
};
static inline int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) static int __init register_armv7_pmu_driver(void)
{ {
return -ENODEV; return platform_driver_register(&armv7_pmu_driver);
} }
device_initcall(register_armv7_pmu_driver);
#endif /* CONFIG_CPU_V7 */ #endif /* CONFIG_CPU_V7 */
...@@ -13,6 +13,14 @@ ...@@ -13,6 +13,14 @@
*/ */
#ifdef CONFIG_CPU_XSCALE #ifdef CONFIG_CPU_XSCALE
#include <asm/cputype.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <linux/of.h>
#include <linux/platform_device.h>
enum xscale_perf_types { enum xscale_perf_types {
XSCALE_PERFCTR_ICACHE_MISS = 0x00, XSCALE_PERFCTR_ICACHE_MISS = 0x00,
XSCALE_PERFCTR_ICACHE_NO_DELIVER = 0x01, XSCALE_PERFCTR_ICACHE_NO_DELIVER = 0x01,
...@@ -740,14 +748,28 @@ static int xscale2pmu_init(struct arm_pmu *cpu_pmu) ...@@ -740,14 +748,28 @@ static int xscale2pmu_init(struct arm_pmu *cpu_pmu)
return 0; return 0;
} }
#else
static inline int xscale1pmu_init(struct arm_pmu *cpu_pmu) static const struct pmu_probe_info xscale_pmu_probe_table[] = {
XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V1, xscale1pmu_init),
XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V2, xscale2pmu_init),
{ /* sentinel value */ }
};
static int xscale_pmu_device_probe(struct platform_device *pdev)
{ {
return -ENODEV; return arm_pmu_device_probe(pdev, NULL, xscale_pmu_probe_table);
} }
static inline int xscale2pmu_init(struct arm_pmu *cpu_pmu) static struct platform_driver xscale_pmu_driver = {
.driver = {
.name = "xscale-pmu",
},
.probe = xscale_pmu_device_probe,
};
static int __init register_xscale_pmu_driver(void)
{ {
return -ENODEV; return platform_driver_register(&xscale_pmu_driver);
} }
device_initcall(register_xscale_pmu_driver);
#endif /* CONFIG_CPU_XSCALE */ #endif /* CONFIG_CPU_XSCALE */
...@@ -311,13 +311,7 @@ static int exynos5420_cpu_suspend(unsigned long arg) ...@@ -311,13 +311,7 @@ static int exynos5420_cpu_suspend(unsigned long arg)
if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) { if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) {
mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume); mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume);
mcpm_cpu_suspend();
/*
* Residency value passed to mcpm_cpu_suspend back-end
* has to be given clear semantics. Set to 0 as a
* temporary value.
*/
mcpm_cpu_suspend(0);
} }
pr_info("Failed to suspend the system\n"); pr_info("Failed to suspend the system\n");
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation. * version 2, as published by the Free Software Foundation.
*/ */
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/memblock.h> #include <linux/memblock.h>
...@@ -13,7 +15,9 @@ ...@@ -13,7 +15,9 @@
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/cp15.h> #include <asm/cp15.h>
#include <asm/mcpm.h> #include <asm/cacheflush.h>
#include <asm/smp.h>
#include <asm/smp_plat.h>
#include "core.h" #include "core.h"
...@@ -94,11 +98,16 @@ static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) ...@@ -94,11 +98,16 @@ static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
} while (data != readl_relaxed(fabric + FAB_SF_MODE)); } while (data != readl_relaxed(fabric + FAB_SF_MODE));
} }
static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster) static int hip04_boot_secondary(unsigned int l_cpu, struct task_struct *idle)
{ {
unsigned int mpidr, cpu, cluster;
unsigned long data; unsigned long data;
void __iomem *sys_dreq, *sys_status; void __iomem *sys_dreq, *sys_status;
mpidr = cpu_logical_map(l_cpu);
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
if (!sysctrl) if (!sysctrl)
return -ENODEV; return -ENODEV;
if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
...@@ -118,6 +127,7 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster) ...@@ -118,6 +127,7 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
cpu_relax(); cpu_relax();
data = readl_relaxed(sys_status); data = readl_relaxed(sys_status);
} while (data & CLUSTER_DEBUG_RESET_STATUS); } while (data & CLUSTER_DEBUG_RESET_STATUS);
hip04_set_snoop_filter(cluster, 1);
} }
data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
...@@ -126,11 +136,15 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster) ...@@ -126,11 +136,15 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
do { do {
cpu_relax(); cpu_relax();
} while (data == readl_relaxed(sys_status)); } while (data == readl_relaxed(sys_status));
/* /*
* We may fail to power up core again without this delay. * We may fail to power up core again without this delay.
* It's not mentioned in document. It's found by test. * It's not mentioned in document. It's found by test.
*/ */
udelay(20); udelay(20);
arch_send_wakeup_ipi_mask(cpumask_of(l_cpu));
out: out:
hip04_cpu_table[cluster][cpu]++; hip04_cpu_table[cluster][cpu]++;
spin_unlock_irq(&boot_lock); spin_unlock_irq(&boot_lock);
...@@ -138,31 +152,30 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster) ...@@ -138,31 +152,30 @@ static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
return 0; return 0;
} }
static void hip04_mcpm_power_down(void) #ifdef CONFIG_HOTPLUG_CPU
static void hip04_cpu_die(unsigned int l_cpu)
{ {
unsigned int mpidr, cpu, cluster; unsigned int mpidr, cpu, cluster;
bool skip_wfi = false, last_man = false; bool last_man;
mpidr = read_cpuid_mpidr(); mpidr = cpu_logical_map(l_cpu);
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
__mcpm_cpu_going_down(cpu, cluster);
spin_lock(&boot_lock); spin_lock(&boot_lock);
BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
hip04_cpu_table[cluster][cpu]--; hip04_cpu_table[cluster][cpu]--;
if (hip04_cpu_table[cluster][cpu] == 1) { if (hip04_cpu_table[cluster][cpu] == 1) {
/* A power_up request went ahead of us. */ /* A power_up request went ahead of us. */
skip_wfi = true; spin_unlock(&boot_lock);
return;
} else if (hip04_cpu_table[cluster][cpu] > 1) { } else if (hip04_cpu_table[cluster][cpu] > 1) {
pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu); pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu);
BUG(); BUG();
} }
last_man = hip04_cluster_is_down(cluster); last_man = hip04_cluster_is_down(cluster);
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
spin_unlock(&boot_lock); spin_unlock(&boot_lock);
if (last_man) {
/* Since it's Cortex A15, disable L2 prefetching. */ /* Since it's Cortex A15, disable L2 prefetching. */
asm volatile( asm volatile(
"mcr p15, 1, %0, c15, c0, 3 \n\t" "mcr p15, 1, %0, c15, c0, 3 \n\t"
...@@ -170,34 +183,30 @@ static void hip04_mcpm_power_down(void) ...@@ -170,34 +183,30 @@ static void hip04_mcpm_power_down(void)
"dsb " "dsb "
: : "r" (0x400) ); : : "r" (0x400) );
v7_exit_coherency_flush(all); v7_exit_coherency_flush(all);
hip04_set_snoop_filter(cluster, 0);
__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
} else { } else {
spin_unlock(&boot_lock);
v7_exit_coherency_flush(louis); v7_exit_coherency_flush(louis);
} }
__mcpm_cpu_down(cpu, cluster); for (;;)
if (!skip_wfi)
wfi(); wfi();
} }
static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster) static int hip04_cpu_kill(unsigned int l_cpu)
{ {
unsigned int mpidr, cpu, cluster;
unsigned int data, tries, count; unsigned int data, tries, count;
int ret = -ETIMEDOUT;
mpidr = cpu_logical_map(l_cpu);
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
BUG_ON(cluster >= HIP04_MAX_CLUSTERS || BUG_ON(cluster >= HIP04_MAX_CLUSTERS ||
cpu >= HIP04_MAX_CPUS_PER_CLUSTER); cpu >= HIP04_MAX_CPUS_PER_CLUSTER);
count = TIMEOUT_MSEC / POLL_MSEC; count = TIMEOUT_MSEC / POLL_MSEC;
spin_lock_irq(&boot_lock); spin_lock_irq(&boot_lock);
for (tries = 0; tries < count; tries++) { for (tries = 0; tries < count; tries++) {
if (hip04_cpu_table[cluster][cpu]) { if (hip04_cpu_table[cluster][cpu])
ret = -EBUSY;
goto err; goto err;
}
cpu_relax(); cpu_relax();
data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
if (data & CORE_WFI_STATUS(cpu)) if (data & CORE_WFI_STATUS(cpu))
...@@ -220,64 +229,22 @@ static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster) ...@@ -220,64 +229,22 @@ static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
} }
if (tries >= count) if (tries >= count)
goto err; goto err;
if (hip04_cluster_is_down(cluster))
hip04_set_snoop_filter(cluster, 0);
spin_unlock_irq(&boot_lock); spin_unlock_irq(&boot_lock);
return 0; return 1;
err: err:
spin_unlock_irq(&boot_lock); spin_unlock_irq(&boot_lock);
return ret; return 0;
}
static void hip04_mcpm_powered_up(void)
{
unsigned int mpidr, cpu, cluster;
mpidr = read_cpuid_mpidr();
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
spin_lock(&boot_lock);
if (!hip04_cpu_table[cluster][cpu])
hip04_cpu_table[cluster][cpu] = 1;
spin_unlock(&boot_lock);
}
static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level)
{
asm volatile (" \n"
" cmp r0, #0 \n"
" bxeq lr \n"
/* calculate fabric phys address */
" adr r2, 2f \n"
" ldmia r2, {r1, r3} \n"
" sub r0, r2, r1 \n"
" ldr r2, [r0, r3] \n"
/* get cluster id from MPIDR */
" mrc p15, 0, r0, c0, c0, 5 \n"
" ubfx r1, r0, #8, #8 \n"
/* 1 << cluster id */
" mov r0, #1 \n"
" mov r3, r0, lsl r1 \n"
" ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
" tst r0, r3 \n"
" bxne lr \n"
" orr r1, r0, r3 \n"
" str r1, [r2, #"__stringify(FAB_SF_MODE)"] \n"
"1: ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
" tst r0, r3 \n"
" beq 1b \n"
" bx lr \n"
" .align 2 \n"
"2: .word . \n"
" .word fabric_phys_addr \n"
);
} }
#endif
static const struct mcpm_platform_ops hip04_mcpm_ops = {
.power_up = hip04_mcpm_power_up, static struct smp_operations __initdata hip04_smp_ops = {
.power_down = hip04_mcpm_power_down, .smp_boot_secondary = hip04_boot_secondary,
.wait_for_powerdown = hip04_mcpm_wait_for_powerdown, #ifdef CONFIG_HOTPLUG_CPU
.powered_up = hip04_mcpm_powered_up, .cpu_die = hip04_cpu_die,
.cpu_kill = hip04_cpu_kill,
#endif
}; };
static bool __init hip04_cpu_table_init(void) static bool __init hip04_cpu_table_init(void)
...@@ -298,7 +265,7 @@ static bool __init hip04_cpu_table_init(void) ...@@ -298,7 +265,7 @@ static bool __init hip04_cpu_table_init(void)
return true; return true;
} }
static int __init hip04_mcpm_init(void) static int __init hip04_smp_init(void)
{ {
struct device_node *np, *np_sctl, *np_fab; struct device_node *np, *np_sctl, *np_fab;
struct resource fab_res; struct resource fab_res;
...@@ -353,10 +320,6 @@ static int __init hip04_mcpm_init(void) ...@@ -353,10 +320,6 @@ static int __init hip04_mcpm_init(void)
ret = -EINVAL; ret = -EINVAL;
goto err_table; goto err_table;
} }
ret = mcpm_platform_register(&hip04_mcpm_ops);
if (ret) {
goto err_table;
}
/* /*
* Fill the instruction address that is used after secondary core * Fill the instruction address that is used after secondary core
...@@ -364,13 +327,11 @@ static int __init hip04_mcpm_init(void) ...@@ -364,13 +327,11 @@ static int __init hip04_mcpm_init(void)
*/ */
writel_relaxed(hip04_boot_method[0], relocation); writel_relaxed(hip04_boot_method[0], relocation);
writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */ writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */
writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8); writel_relaxed(virt_to_phys(secondary_startup), relocation + 8);
writel_relaxed(0, relocation + 12); writel_relaxed(0, relocation + 12);
iounmap(relocation); iounmap(relocation);
mcpm_sync_init(hip04_mcpm_power_up_setup); smp_set_ops(&hip04_smp_ops);
mcpm_smp_set_ops();
pr_info("HiP04 MCPM initialized\n");
return ret; return ret;
err_table: err_table:
iounmap(fabric); iounmap(fabric);
...@@ -383,4 +344,4 @@ static int __init hip04_mcpm_init(void) ...@@ -383,4 +344,4 @@ static int __init hip04_mcpm_init(void)
err: err:
return ret; return ret;
} }
early_initcall(hip04_mcpm_init); early_initcall(hip04_smp_init);
...@@ -108,13 +108,7 @@ static int notrace bl_powerdown_finisher(unsigned long arg) ...@@ -108,13 +108,7 @@ static int notrace bl_powerdown_finisher(unsigned long arg)
unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
mcpm_set_entry_vector(cpu, cluster, cpu_resume); mcpm_set_entry_vector(cpu, cluster, cpu_resume);
mcpm_cpu_suspend();
/*
* Residency value passed to mcpm_cpu_suspend back-end
* has to be given clear semantics. Set to 0 as a
* temporary value.
*/
mcpm_cpu_suspend(0);
/* return value != 0 means failure */ /* return value != 0 means failure */
return 1; return 1;
......
...@@ -304,6 +304,11 @@ struct pmu { ...@@ -304,6 +304,11 @@ struct pmu {
* Free pmu-private AUX data structures * Free pmu-private AUX data structures
*/ */
void (*free_aux) (void *aux); /* optional */ void (*free_aux) (void *aux); /* optional */
/*
* Filter events for PMU-specific reasons.
*/
int (*filter_match) (struct perf_event *event); /* optional */
}; };
/** /**
......
...@@ -1506,11 +1506,17 @@ static int __init perf_workqueue_init(void) ...@@ -1506,11 +1506,17 @@ static int __init perf_workqueue_init(void)
core_initcall(perf_workqueue_init); core_initcall(perf_workqueue_init);
static inline int pmu_filter_match(struct perf_event *event)
{
struct pmu *pmu = event->pmu;
return pmu->filter_match ? pmu->filter_match(event) : 1;
}
static inline int static inline int
event_filter_match(struct perf_event *event) event_filter_match(struct perf_event *event)
{ {
return (event->cpu == -1 || event->cpu == smp_processor_id()) return (event->cpu == -1 || event->cpu == smp_processor_id())
&& perf_cgroup_match(event); && perf_cgroup_match(event) && pmu_filter_match(event);
} }
static void static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment