From c2794437091a4fda72c4a4f3567dd728dcc0c3c9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 29 Feb 2012 18:10:58 -0600 Subject: ARM: Add fixed PCI i/o mapping This adds a fixed virtual mapping for PCI i/o addresses. The mapping is located at the last 2MB of vmalloc region (0xfee00000-0xff000000). 2MB is used to align with PMD size, but IO_SPACE_LIMIT is 1MB. The space is reserved after .map_io and can be mapped at any time later with pci_ioremap_io. Platforms which need early i/o mapping (e.g. for vga console) can call pci_map_io_early in their .map_io function. This has changed completely from the 1st implementation which only supported creating the static mapping at .map_io. Signed-off-by: Rob Herring Cc: Russell King Acked-by: Nicolas Pitre --- arch/arm/kernel/bios32.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 25552508c3fd..c3165f0fef63 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -13,6 +13,7 @@ #include #include +#include #include static int debug_pci; @@ -627,3 +628,15 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, return 0; } + +void __init pci_map_io_early(unsigned long pfn) +{ + struct map_desc pci_io_desc = { + .virtual = PCI_IO_VIRT_BASE, + .type = MT_DEVICE, + .length = SZ_64K, + }; + + pci_io_desc.pfn = pfn; + iotable_init(&pci_io_desc, 1); +} -- cgit v1.2.3 From 3c5d1699887bfcd17e6d9842ba7e2b3234e665db Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Jul 2012 21:59:03 -0500 Subject: ARM: move PCI i/o resource setup into common code With consolidation of the PCI i/o mappings, the i/o space is being set to start at a PCI bus addr of 0x0 and fixed to 64K per bus. In this case the core ARM PCI setup code can setup the i/o resource. Currently, the resource is only setup if the platform did not setup an i/o resource. Signed-off-by: Rob Herring Reviewed-by: Arnd Bergmann --- arch/arm/kernel/bios32.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index c3165f0fef63..036f7ea5812a 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -424,6 +424,38 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return irq; } +static int __init pcibios_init_resources(int busnr, struct pci_sys_data *sys) +{ + int ret; + struct pci_host_bridge_window *window; + + if (list_empty(&sys->resources)) { + pci_add_resource_offset(&sys->resources, + &iomem_resource, sys->mem_offset); + } + + list_for_each_entry(window, &sys->resources, list) { + if (resource_type(window->res) == IORESOURCE_IO) + return 0; + } + + sys->io_res.start = (busnr * SZ_64K) ? : pcibios_min_io; + sys->io_res.end = (busnr + 1) * SZ_64K - 1; + sys->io_res.flags = IORESOURCE_IO; + sys->io_res.name = sys->io_res_name; + sprintf(sys->io_res_name, "PCI%d I/O", busnr); + + ret = request_resource(&ioport_resource, &sys->io_res); + if (ret) { + pr_err("PCI: unable to allocate I/O port region (%d)\n", ret); + return ret; + } + pci_add_resource_offset(&sys->resources, &sys->io_res, + sys->io_offset); + + return 0; +} + static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head) { struct pci_sys_data *sys = NULL; @@ -446,11 +478,10 @@ static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head) ret = hw->setup(nr, sys); if (ret > 0) { - if (list_empty(&sys->resources)) { - pci_add_resource_offset(&sys->resources, - &ioport_resource, sys->io_offset); - pci_add_resource_offset(&sys->resources, - &iomem_resource, sys->mem_offset); + ret = pcibios_init_resources(nr, sys); + if (ret) { + kfree(sys); + break; } if (hw->scan) -- cgit v1.2.3 From 7be2958e97b37256b8016db39ac6cf51f711e390 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 31 May 2012 13:05:20 -0500 Subject: ARM: PMU: Add runtime PM Support Add runtime PM support to the ARM PMU driver so that devices such as OMAP supporting dynamic PM can use the platform->runtime_* hooks to initialise hardware at runtime. Without having these runtime PM hooks in place any configuration of the PMU hardware would be lost when low power states are entered and hence would prevent PMU from working. This change also replaces the PMU platform functions enable_irq and disable_irq added by Ming Lei with runtime_resume and runtime_suspend funtions. Ming had added the enable_irq and disable_irq functions as a method to configure the cross trigger interface on OMAP4 for routing the PMU interrupts. By adding runtime PM support, we can move the code called by enable_irq and disable_irq into the runtime PM callbacks runtime_resume and runtime_suspend. Cc: Ming Lei Cc: Benoit Cousson Cc: Paul Walmsley Cc: Kevin Hilman Signed-off-by: Jon Hunter Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index ab243b87118d..c44647ef7357 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -364,8 +365,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu) { int i, irq, irqs; struct platform_device *pmu_device = armpmu->plat_device; - struct arm_pmu_platdata *plat = - dev_get_platdata(&pmu_device->dev); irqs = min(pmu_device->num_resources, num_possible_cpus()); @@ -373,13 +372,11 @@ armpmu_release_hardware(struct arm_pmu *armpmu) if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) continue; irq = platform_get_irq(pmu_device, i); - if (irq >= 0) { - if (plat && plat->disable_irq) - plat->disable_irq(irq); + if (irq >= 0) free_irq(irq, armpmu); - } } + pm_runtime_put_sync(&pmu_device->dev); release_pmu(armpmu->type); } @@ -412,6 +409,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) return -ENODEV; } + pm_runtime_get_sync(&pmu_device->dev); + for (i = 0; i < irqs; ++i) { err = 0; irq = platform_get_irq(pmu_device, i); @@ -437,8 +436,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) irq); armpmu_release_hardware(armpmu); return err; - } else if (plat && plat->enable_irq) - plat->enable_irq(irq); + } cpumask_set_cpu(i, &armpmu->active_irqs); } @@ -581,6 +579,28 @@ static void armpmu_disable(struct pmu *pmu) armpmu->stop(); } +#ifdef CONFIG_PM_RUNTIME +static int armpmu_runtime_resume(struct device *dev) +{ + struct arm_pmu_platdata *plat = dev_get_platdata(dev); + + if (plat && plat->runtime_resume) + return plat->runtime_resume(dev); + + return 0; +} + +static int armpmu_runtime_suspend(struct device *dev) +{ + struct arm_pmu_platdata *plat = dev_get_platdata(dev); + + if (plat && plat->runtime_suspend) + return plat->runtime_suspend(dev); + + return 0; +} +#endif + static void __init armpmu_init(struct arm_pmu *armpmu) { atomic_set(&armpmu->active_events, 0); @@ -647,9 +667,14 @@ static int __devinit armpmu_device_probe(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops armpmu_dev_pm_ops = { + SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) +}; + static struct platform_driver armpmu_driver = { .driver = { .name = "arm-pmu", + .pm = &armpmu_dev_pm_ops, .of_match_table = armpmu_of_device_ids, }, .probe = armpmu_device_probe, -- cgit v1.2.3 From 50243efde0993f6fe98f27a35692d0e8efdf7a0f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sat, 28 Jul 2012 16:05:55 +0100 Subject: ARM: perf: add devicetree bindings for 11MPcore, A5, A7 and A15 PMUs This patch adds separate devicetree bindings for 11MPcore and Cortex-{A5,A7,A15} PMUs in preparation for improved devicetree parsing in the ARM perf-event CPU PMU driver. Cc: Grant Likely Acked-by: Rob Herring Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index c44647ef7357..3aa338849133 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -646,10 +646,14 @@ arch_initcall(cpu_pmu_reset); * PMU platform driver and devicetree bindings. */ static struct of_device_id armpmu_of_device_ids[] = { + {.compatible = "arm,cortex-a15-pmu"}, {.compatible = "arm,cortex-a9-pmu"}, {.compatible = "arm,cortex-a8-pmu"}, - {.compatible = "arm,arm1136-pmu"}, + {.compatible = "arm,cortex-a7-pmu"}, + {.compatible = "arm,cortex-a5-pmu"}, + {.compatible = "arm,arm11mpcore-pmu"}, {.compatible = "arm,arm1176-pmu"}, + {.compatible = "arm,arm1136-pmu"}, {}, }; -- cgit v1.2.3 From f0d1bc47953743aef9d2ed5326bc5973a3db08ab Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sat, 28 Jul 2012 16:27:03 +0100 Subject: ARM: pmu: remove unused reservation mechanism The PMU reservation mechanism was originally intended to allow OProfile and perf-events to co-ordinate over access to the CPU PMU. Since then, OProfile for ARM has moved to using perf as its backend, so the reservation code is no longer used. This patch removes the reservation code for the CPU PMU on ARM. Signed-off-by: Will Deacon --- arch/arm/kernel/Makefile | 1 - arch/arm/kernel/perf_event.c | 8 -------- arch/arm/kernel/pmu.c | 36 ------------------------------------ 3 files changed, 45 deletions(-) delete mode 100644 arch/arm/kernel/pmu.c (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 7ad2d5cf7008..cfbe3b4d1d5e 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o obj-$(CONFIG_IWMMXT) += iwmmxt.o -obj-$(CONFIG_CPU_HAS_PMU) += pmu.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 3aa338849133..1ac1531dcfef 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -377,7 +377,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu) } pm_runtime_put_sync(&pmu_device->dev); - release_pmu(armpmu->type); } static int @@ -391,12 +390,6 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) if (!pmu_device) return -ENODEV; - err = reserve_pmu(armpmu->type); - if (err) { - pr_warning("unable to reserve pmu\n"); - return err; - } - plat = dev_get_platdata(&pmu_device->dev); if (plat && plat->handle_irq) handle_irq = armpmu_platform_irq; @@ -706,7 +699,6 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu) raw_spin_lock_init(&events->pmu_lock); } armpmu->get_hw_events = armpmu_get_cpu_events; - armpmu->type = ARM_PMU_DEVICE_CPU; } /* diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c deleted file mode 100644 index 2334bf8a650a..000000000000 --- a/arch/arm/kernel/pmu.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * linux/arch/arm/kernel/pmu.c - * - * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles - * Copyright (C) 2010 ARM Ltd, Will Deacon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include - -#include - -/* - * PMU locking to ensure mutual exclusion between different subsystems. - */ -static unsigned long pmu_lock[BITS_TO_LONGS(ARM_NUM_PMU_DEVICES)]; - -int -reserve_pmu(enum arm_pmu_type type) -{ - return test_and_set_bit_lock(type, pmu_lock) ? -EBUSY : 0; -} -EXPORT_SYMBOL_GPL(reserve_pmu); - -void -release_pmu(enum arm_pmu_type type) -{ - clear_bit_unlock(type, pmu_lock); -} -EXPORT_SYMBOL_GPL(release_pmu); -- cgit v1.2.3 From 9f44f9a234020947dd16500a203c9580a66ed67d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sat, 28 Jul 2012 16:44:38 +0100 Subject: ARM: perf: remove mysterious compiler barrier There's a rather strange compiler barrier in the PMU disabling code which was presumably placed there by aliens. There's no valid reason for the barrier and one can only suspect that it's up to no good. This patch removes it before it has a chance to spread. Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 1ac1531dcfef..22ed5120a6ed 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -223,7 +223,6 @@ armpmu_stop(struct perf_event *event, int flags) */ if (!(hwc->state & PERF_HES_STOPPED)) { armpmu->disable(hwc, hwc->idx); - barrier(); /* why? */ armpmu_event_update(event, hwc, hwc->idx); hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; } -- cgit v1.2.3 From 04236f9fe07462849215c67cae6147661368bfad Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sat, 28 Jul 2012 17:42:22 +0100 Subject: ARM: perf: probe devicetree in preference to current CPU The CPU PMU is probed using the current cpuid information as part of the early_initcall initialising the architecture perf backend. For architectures without NMI (such as ARM), this does not need to be performed early and can be deferred to the driver probe callback. This also allows us to probe the devicetree in preference to parsing the current cpuid, which may be invalid on a big.LITTLE multi-cluster system. This patch defers the PMU probing and uses the devicetree information when available. Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 175 +++++++++++++++++++----------------- arch/arm/kernel/perf_event_v6.c | 8 +- arch/arm/kernel/perf_event_v7.c | 22 ++--- arch/arm/kernel/perf_event_xscale.c | 8 +- 4 files changed, 110 insertions(+), 103 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 22ed5120a6ed..7c29914bdcc6 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -610,9 +611,11 @@ static void __init armpmu_init(struct arm_pmu *armpmu) }; } -int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) +int armpmu_register(struct arm_pmu *armpmu, char *name, int type) { armpmu_init(armpmu); + pr_info("enabled with %s PMU driver, %d counters available\n", + armpmu->name, armpmu->num_events); return perf_pmu_register(&armpmu->pmu, name, type); } @@ -621,74 +624,12 @@ int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) #include "perf_event_v6.c" #include "perf_event_v7.c" -/* - * Ensure the PMU has sane values out of reset. - * This requires SMP to be available, so exists as a separate initcall. - */ -static int __init -cpu_pmu_reset(void) -{ - if (cpu_pmu && cpu_pmu->reset) - return on_each_cpu(cpu_pmu->reset, NULL, 1); - return 0; -} -arch_initcall(cpu_pmu_reset); - -/* - * PMU platform driver and devicetree bindings. - */ -static struct of_device_id armpmu_of_device_ids[] = { - {.compatible = "arm,cortex-a15-pmu"}, - {.compatible = "arm,cortex-a9-pmu"}, - {.compatible = "arm,cortex-a8-pmu"}, - {.compatible = "arm,cortex-a7-pmu"}, - {.compatible = "arm,cortex-a5-pmu"}, - {.compatible = "arm,arm11mpcore-pmu"}, - {.compatible = "arm,arm1176-pmu"}, - {.compatible = "arm,arm1136-pmu"}, - {}, -}; - -static struct platform_device_id armpmu_plat_device_ids[] = { - {.name = "arm-pmu"}, - {}, -}; - -static int __devinit armpmu_device_probe(struct platform_device *pdev) -{ - if (!cpu_pmu) - return -ENODEV; - - cpu_pmu->plat_device = pdev; - return 0; -} - -static const struct dev_pm_ops armpmu_dev_pm_ops = { - SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) -}; - -static struct platform_driver armpmu_driver = { - .driver = { - .name = "arm-pmu", - .pm = &armpmu_dev_pm_ops, - .of_match_table = armpmu_of_device_ids, - }, - .probe = armpmu_device_probe, - .id_table = armpmu_plat_device_ids, -}; - -static int __init register_pmu_driver(void) -{ - return platform_driver_register(&armpmu_driver); -} -device_initcall(register_pmu_driver); - static struct pmu_hw_events *armpmu_get_cpu_events(void) { return &__get_cpu_var(cpu_hw_events); } -static void __init cpu_pmu_init(struct arm_pmu *armpmu) +static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) { int cpu; for_each_possible_cpu(cpu) { @@ -697,7 +638,11 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu) events->used_mask = per_cpu(used_mask, cpu); raw_spin_lock_init(&events->pmu_lock); } - armpmu->get_hw_events = armpmu_get_cpu_events; + cpu_pmu->get_hw_events = armpmu_get_cpu_events; + + /* Ensure the PMU has sane values out of reset. */ + if (cpu_pmu && cpu_pmu->reset) + on_each_cpu(cpu_pmu->reset, NULL, 1); } /* @@ -722,41 +667,68 @@ static struct notifier_block __cpuinitdata pmu_cpu_notifier = { .notifier_call = pmu_cpu_notify, }; +static const struct dev_pm_ops armpmu_dev_pm_ops = { + SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) +}; + +/* + * PMU platform driver and devicetree bindings. + */ +static struct of_device_id __devinitdata cpu_pmu_of_device_ids[] = { + {.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init}, + {.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init}, + {.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init}, + {.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init}, + {.compatible = "arm,cortex-a5-pmu", .data = armv7_a5_pmu_init}, + {.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init}, + {.compatible = "arm,arm1176-pmu", .data = armv6pmu_init}, + {.compatible = "arm,arm1136-pmu", .data = armv6pmu_init}, + {}, +}; + +static struct platform_device_id __devinitdata cpu_pmu_plat_device_ids[] = { + {.name = "arm-pmu"}, + {}, +}; + /* - * CPU PMU identification and registration. + * CPU PMU identification and probing. */ -static int __init -init_hw_perf_events(void) +static struct arm_pmu *__devinit probe_current_pmu(void) { + struct arm_pmu *pmu = NULL; + int cpu = get_cpu(); unsigned long cpuid = read_cpuid_id(); unsigned long implementor = (cpuid & 0xFF000000) >> 24; unsigned long part_number = (cpuid & 0xFFF0); + pr_info("probing PMU on CPU %d\n", cpu); + /* ARM Ltd CPUs. */ if (0x41 == implementor) { switch (part_number) { case 0xB360: /* ARM1136 */ case 0xB560: /* ARM1156 */ case 0xB760: /* ARM1176 */ - cpu_pmu = armv6pmu_init(); + pmu = armv6pmu_init(); break; case 0xB020: /* ARM11mpcore */ - cpu_pmu = armv6mpcore_pmu_init(); + pmu = armv6mpcore_pmu_init(); break; case 0xC080: /* Cortex-A8 */ - cpu_pmu = armv7_a8_pmu_init(); + pmu = armv7_a8_pmu_init(); break; case 0xC090: /* Cortex-A9 */ - cpu_pmu = armv7_a9_pmu_init(); + pmu = armv7_a9_pmu_init(); break; case 0xC050: /* Cortex-A5 */ - cpu_pmu = armv7_a5_pmu_init(); + pmu = armv7_a5_pmu_init(); break; case 0xC0F0: /* Cortex-A15 */ - cpu_pmu = armv7_a15_pmu_init(); + pmu = armv7_a15_pmu_init(); break; case 0xC070: /* Cortex-A7 */ - cpu_pmu = armv7_a7_pmu_init(); + pmu = armv7_a7_pmu_init(); break; } /* Intel CPUs [xscale]. */ @@ -764,27 +736,62 @@ init_hw_perf_events(void) part_number = (cpuid >> 13) & 0x7; switch (part_number) { case 1: - cpu_pmu = xscale1pmu_init(); + pmu = xscale1pmu_init(); break; case 2: - cpu_pmu = xscale2pmu_init(); + pmu = xscale2pmu_init(); break; } } + put_cpu(); + return pmu; +} + +static int __devinit cpu_pmu_device_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct arm_pmu *(*init_fn)(void); + struct device_node *node = pdev->dev.of_node; + if (cpu_pmu) { - pr_info("enabled with %s PMU driver, %d counters available\n", - cpu_pmu->name, cpu_pmu->num_events); - cpu_pmu_init(cpu_pmu); - register_cpu_notifier(&pmu_cpu_notifier); - armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); + pr_info("attempt to register multiple PMU devices!"); + return -ENOSPC; + } + + if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { + init_fn = of_id->data; + cpu_pmu = init_fn(); } else { - pr_info("no hardware support available\n"); + cpu_pmu = probe_current_pmu(); } + if (!cpu_pmu) + return -ENODEV; + + cpu_pmu->plat_device = pdev; + cpu_pmu_init(cpu_pmu); + register_cpu_notifier(&pmu_cpu_notifier); + armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); + return 0; } -early_initcall(init_hw_perf_events); + +static struct platform_driver cpu_pmu_driver = { + .driver = { + .name = "arm-pmu", + .pm = &armpmu_dev_pm_ops, + .of_match_table = cpu_pmu_of_device_ids, + }, + .probe = cpu_pmu_device_probe, + .id_table = cpu_pmu_plat_device_ids, +}; + +static int __init register_pmu_driver(void) +{ + return platform_driver_register(&cpu_pmu_driver); +} +device_initcall(register_pmu_driver); /* * Callchain handling code. diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index c90fcb2b6967..a4328b12eed5 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -664,7 +664,7 @@ static struct arm_pmu armv6pmu = { .max_period = (1LLU << 32) - 1, }; -static struct arm_pmu *__init armv6pmu_init(void) +static struct arm_pmu *__devinit armv6pmu_init(void) { return &armv6pmu; } @@ -698,17 +698,17 @@ static struct arm_pmu armv6mpcore_pmu = { .max_period = (1LLU << 32) - 1, }; -static struct arm_pmu *__init armv6mpcore_pmu_init(void) +static struct arm_pmu *__devinit armv6mpcore_pmu_init(void) { return &armv6mpcore_pmu; } #else -static struct arm_pmu *__init armv6pmu_init(void) +static struct arm_pmu *__devinit armv6pmu_init(void) { return NULL; } -static struct arm_pmu *__init armv6mpcore_pmu_init(void) +static struct arm_pmu *__devinit armv6mpcore_pmu_init(void) { return NULL; } diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index f04070bd2183..d65a1b82e13f 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1245,7 +1245,7 @@ static struct arm_pmu armv7pmu = { .max_period = (1LLU << 32) - 1, }; -static u32 __init armv7_read_num_pmnc_events(void) +static u32 __devinit armv7_read_num_pmnc_events(void) { u32 nb_cnt; @@ -1256,7 +1256,7 @@ static u32 __init armv7_read_num_pmnc_events(void) return nb_cnt + 1; } -static struct arm_pmu *__init armv7_a8_pmu_init(void) +static struct arm_pmu *__devinit armv7_a8_pmu_init(void) { armv7pmu.name = "ARMv7 Cortex-A8"; armv7pmu.map_event = armv7_a8_map_event; @@ -1264,7 +1264,7 @@ static struct arm_pmu *__init armv7_a8_pmu_init(void) return &armv7pmu; } -static struct arm_pmu *__init armv7_a9_pmu_init(void) +static struct arm_pmu *__devinit armv7_a9_pmu_init(void) { armv7pmu.name = "ARMv7 Cortex-A9"; armv7pmu.map_event = armv7_a9_map_event; @@ -1272,7 +1272,7 @@ static struct arm_pmu *__init armv7_a9_pmu_init(void) return &armv7pmu; } -static struct arm_pmu *__init armv7_a5_pmu_init(void) +static struct arm_pmu *__devinit armv7_a5_pmu_init(void) { armv7pmu.name = "ARMv7 Cortex-A5"; armv7pmu.map_event = armv7_a5_map_event; @@ -1280,7 +1280,7 @@ static struct arm_pmu *__init armv7_a5_pmu_init(void) return &armv7pmu; } -static struct arm_pmu *__init armv7_a15_pmu_init(void) +static struct arm_pmu *__devinit armv7_a15_pmu_init(void) { armv7pmu.name = "ARMv7 Cortex-A15"; armv7pmu.map_event = armv7_a15_map_event; @@ -1289,7 +1289,7 @@ static struct arm_pmu *__init armv7_a15_pmu_init(void) return &armv7pmu; } -static struct arm_pmu *__init armv7_a7_pmu_init(void) +static struct arm_pmu *__devinit armv7_a7_pmu_init(void) { armv7pmu.name = "ARMv7 Cortex-A7"; armv7pmu.map_event = armv7_a7_map_event; @@ -1298,27 +1298,27 @@ static struct arm_pmu *__init armv7_a7_pmu_init(void) return &armv7pmu; } #else -static struct arm_pmu *__init armv7_a8_pmu_init(void) +static struct arm_pmu *__devinit armv7_a8_pmu_init(void) { return NULL; } -static struct arm_pmu *__init armv7_a9_pmu_init(void) +static struct arm_pmu *__devinit armv7_a9_pmu_init(void) { return NULL; } -static struct arm_pmu *__init armv7_a5_pmu_init(void) +static struct arm_pmu *__devinit armv7_a5_pmu_init(void) { return NULL; } -static struct arm_pmu *__init armv7_a15_pmu_init(void) +static struct arm_pmu *__devinit armv7_a15_pmu_init(void) { return NULL; } -static struct arm_pmu *__init armv7_a7_pmu_init(void) +static struct arm_pmu *__devinit armv7_a7_pmu_init(void) { return NULL; } diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index f759fe0bab63..dcc478c07456 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -449,7 +449,7 @@ static struct arm_pmu xscale1pmu = { .max_period = (1LLU << 32) - 1, }; -static struct arm_pmu *__init xscale1pmu_init(void) +static struct arm_pmu *__devinit xscale1pmu_init(void) { return &xscale1pmu; } @@ -816,17 +816,17 @@ static struct arm_pmu xscale2pmu = { .max_period = (1LLU << 32) - 1, }; -static struct arm_pmu *__init xscale2pmu_init(void) +static struct arm_pmu *__devinit xscale2pmu_init(void) { return &xscale2pmu; } #else -static struct arm_pmu *__init xscale1pmu_init(void) +static struct arm_pmu *__devinit xscale1pmu_init(void) { return NULL; } -static struct arm_pmu *__init xscale2pmu_init(void) +static struct arm_pmu *__devinit xscale2pmu_init(void) { return NULL; } -- cgit v1.2.3 From 6dbc00297095122ea89e016ce6affad0b7c0ddac Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sun, 29 Jul 2012 12:36:28 +0100 Subject: ARM: perf: prepare for moving CPU PMU code into separate file The CPU PMU code is tightly coupled with generic ARM PMU handling code. This makes it cumbersome when trying to add support for other ARM PMUs (e.g. interconnect, L2 cache controller, bus) as the generic parts of the code are not readily reusable. This patch cleans up perf_event.c so that reusable code is exposed via header files to other potential PMU drivers. The CPU code is consistently named to identify it as such and also to prepare for moving it into a separate file. Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 69 +++++++++++++++---------------------- arch/arm/kernel/perf_event_v6.c | 4 +-- arch/arm/kernel/perf_event_v7.c | 10 +++--- arch/arm/kernel/perf_event_xscale.c | 2 +- 4 files changed, 35 insertions(+), 50 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 7c29914bdcc6..9e3afd1994d9 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -29,26 +29,17 @@ #include #include -/* - * ARMv6 supports a maximum of 3 events, starting from index 0. If we add - * another platform that supports more, we need to increase this to be the - * largest of all platforms. - * - * ARMv7 supports up to 32 events: - * cycle counter CCNT + 31 events counters CNT0..30. - * Cortex-A8 has 1+4 counters, Cortex-A9 has 1+6 counters. - */ -#define ARMPMU_MAX_HWEVENTS 32 +/* Set at runtime when we know what CPU type we are. */ +static struct arm_pmu *cpu_pmu; static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); -#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) - -/* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *cpu_pmu; - +/* + * Despite the names, these two functions are CPU-specific and are used + * by the OProfile/perf code. + */ const char *perf_pmu_name(void) { if (!cpu_pmu) @@ -69,13 +60,6 @@ int perf_num_counters(void) } EXPORT_SYMBOL_GPL(perf_num_counters); -#define HW_OP_UNSUPPORTED 0xFFFF - -#define C(_x) \ - PERF_COUNT_HW_CACHE_##_x - -#define CACHE_OP_UNSUPPORTED 0xFFFF - static int armpmu_map_cache_event(const unsigned (*cache_map) [PERF_COUNT_HW_CACHE_MAX] @@ -106,7 +90,7 @@ armpmu_map_cache_event(const unsigned (*cache_map) } static int -armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) +armpmu_map_hw_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) { int mapping = (*event_map)[config]; return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; @@ -118,19 +102,20 @@ armpmu_map_raw_event(u32 raw_event_mask, u64 config) return (int)(config & raw_event_mask); } -static int map_cpu_event(struct perf_event *event, - const unsigned (*event_map)[PERF_COUNT_HW_MAX], - const unsigned (*cache_map) - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX], - u32 raw_event_mask) +int +armpmu_map_event(struct perf_event *event, + const unsigned (*event_map)[PERF_COUNT_HW_MAX], + const unsigned (*cache_map) + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX], + u32 raw_event_mask) { u64 config = event->attr.config; switch (event->attr.type) { case PERF_TYPE_HARDWARE: - return armpmu_map_event(event_map, config); + return armpmu_map_hw_event(event_map, config); case PERF_TYPE_HW_CACHE: return armpmu_map_cache_event(cache_map, config); case PERF_TYPE_RAW: @@ -594,6 +579,10 @@ static int armpmu_runtime_suspend(struct device *dev) } #endif +const struct dev_pm_ops armpmu_dev_pm_ops = { + SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) +}; + static void __init armpmu_init(struct arm_pmu *armpmu) { atomic_set(&armpmu->active_events, 0); @@ -624,7 +613,7 @@ int armpmu_register(struct arm_pmu *armpmu, char *name, int type) #include "perf_event_v6.c" #include "perf_event_v7.c" -static struct pmu_hw_events *armpmu_get_cpu_events(void) +static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) { return &__get_cpu_var(cpu_hw_events); } @@ -638,7 +627,7 @@ static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) events->used_mask = per_cpu(used_mask, cpu); raw_spin_lock_init(&events->pmu_lock); } - cpu_pmu->get_hw_events = armpmu_get_cpu_events; + cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; /* Ensure the PMU has sane values out of reset. */ if (cpu_pmu && cpu_pmu->reset) @@ -651,8 +640,8 @@ static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading * junk values out of them. */ -static int __cpuinit pmu_cpu_notify(struct notifier_block *b, - unsigned long action, void *hcpu) +static int __cpuinit cpu_pmu_notify(struct notifier_block *b, + unsigned long action, void *hcpu) { if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) return NOTIFY_DONE; @@ -663,12 +652,8 @@ static int __cpuinit pmu_cpu_notify(struct notifier_block *b, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata pmu_cpu_notifier = { - .notifier_call = pmu_cpu_notify, -}; - -static const struct dev_pm_ops armpmu_dev_pm_ops = { - SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) +static struct notifier_block __cpuinitdata cpu_pmu_hotplug_notifier = { + .notifier_call = cpu_pmu_notify, }; /* @@ -771,7 +756,7 @@ static int __devinit cpu_pmu_device_probe(struct platform_device *pdev) cpu_pmu->plat_device = pdev; cpu_pmu_init(cpu_pmu); - register_cpu_notifier(&pmu_cpu_notifier); + register_cpu_notifier(&cpu_pmu_hotplug_notifier); armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); return 0; diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index a4328b12eed5..6ccc07971745 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -645,7 +645,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, static int armv6_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv6_perf_map, + return armpmu_map_event(event, &armv6_perf_map, &armv6_perf_cache_map, 0xFF); } @@ -679,7 +679,7 @@ static struct arm_pmu *__devinit armv6pmu_init(void) static int armv6mpcore_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv6mpcore_perf_map, + return armpmu_map_event(event, &armv6mpcore_perf_map, &armv6mpcore_perf_cache_map, 0xFF); } diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index d65a1b82e13f..bd4b090ebcfd 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1204,31 +1204,31 @@ static void armv7pmu_reset(void *info) static int armv7_a8_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv7_a8_perf_map, + return armpmu_map_event(event, &armv7_a8_perf_map, &armv7_a8_perf_cache_map, 0xFF); } static int armv7_a9_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv7_a9_perf_map, + return armpmu_map_event(event, &armv7_a9_perf_map, &armv7_a9_perf_cache_map, 0xFF); } static int armv7_a5_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv7_a5_perf_map, + return armpmu_map_event(event, &armv7_a5_perf_map, &armv7_a5_perf_cache_map, 0xFF); } static int armv7_a15_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv7_a15_perf_map, + return armpmu_map_event(event, &armv7_a15_perf_map, &armv7_a15_perf_cache_map, 0xFF); } static int armv7_a7_map_event(struct perf_event *event) { - return map_cpu_event(event, &armv7_a7_perf_map, + return armpmu_map_event(event, &armv7_a7_perf_map, &armv7_a7_perf_cache_map, 0xFF); } diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index dcc478c07456..426e19f380a2 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -430,7 +430,7 @@ xscale1pmu_write_counter(int counter, u32 val) static int xscale_map_event(struct perf_event *event) { - return map_cpu_event(event, &xscale_perf_map, + return armpmu_map_event(event, &xscale_perf_map, &xscale_perf_cache_map, 0xFF); } -- cgit v1.2.3 From 5505b206ca006d0506d1d3b3c494aa86234f66e2 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sun, 29 Jul 2012 13:09:14 +0100 Subject: ARM: perf: move CPU-specific PMU handling code into separate file This patch moves the CPU-specific PMU handling code out of perf_event.c and into perf_event_cpu.c. Signed-off-by: Will Deacon --- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/perf_event.c | 211 +---------------------------------- arch/arm/kernel/perf_event_cpu.c | 231 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 211 deletions(-) create mode 100644 arch/arm/kernel/perf_event_cpu.c (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index cfbe3b4d1d5e..1c4321430737 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -69,7 +69,7 @@ obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o obj-$(CONFIG_IWMMXT) += iwmmxt.o -obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 9e3afd1994d9..86fd39937171 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -12,54 +12,15 @@ */ #define pr_fmt(fmt) "hw perfevents: " fmt -#include -#include #include -#include -#include -#include #include -#include -#include #include +#include -#include -#include #include #include #include -/* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *cpu_pmu; - -static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); -static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); -static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); - -/* - * Despite the names, these two functions are CPU-specific and are used - * by the OProfile/perf code. - */ -const char *perf_pmu_name(void) -{ - if (!cpu_pmu) - return NULL; - - return cpu_pmu->pmu.name; -} -EXPORT_SYMBOL_GPL(perf_pmu_name); - -int perf_num_counters(void) -{ - int max_events = 0; - - if (cpu_pmu != NULL) - max_events = cpu_pmu->num_events; - - return max_events; -} -EXPORT_SYMBOL_GPL(perf_num_counters); - static int armpmu_map_cache_event(const unsigned (*cache_map) [PERF_COUNT_HW_CACHE_MAX] @@ -608,176 +569,6 @@ int armpmu_register(struct arm_pmu *armpmu, char *name, int type) return perf_pmu_register(&armpmu->pmu, name, type); } -/* Include the PMU-specific implementations. */ -#include "perf_event_xscale.c" -#include "perf_event_v6.c" -#include "perf_event_v7.c" - -static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) -{ - return &__get_cpu_var(cpu_hw_events); -} - -static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) -{ - int cpu; - for_each_possible_cpu(cpu) { - struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); - events->events = per_cpu(hw_events, cpu); - events->used_mask = per_cpu(used_mask, cpu); - raw_spin_lock_init(&events->pmu_lock); - } - cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; - - /* Ensure the PMU has sane values out of reset. */ - if (cpu_pmu && cpu_pmu->reset) - on_each_cpu(cpu_pmu->reset, NULL, 1); -} - -/* - * PMU hardware loses all context when a CPU goes offline. - * When a CPU is hotplugged back in, since some hardware registers are - * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading - * junk values out of them. - */ -static int __cpuinit cpu_pmu_notify(struct notifier_block *b, - unsigned long action, void *hcpu) -{ - if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) - return NOTIFY_DONE; - - if (cpu_pmu && cpu_pmu->reset) - cpu_pmu->reset(NULL); - - return NOTIFY_OK; -} - -static struct notifier_block __cpuinitdata cpu_pmu_hotplug_notifier = { - .notifier_call = cpu_pmu_notify, -}; - -/* - * PMU platform driver and devicetree bindings. - */ -static struct of_device_id __devinitdata cpu_pmu_of_device_ids[] = { - {.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init}, - {.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init}, - {.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init}, - {.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init}, - {.compatible = "arm,cortex-a5-pmu", .data = armv7_a5_pmu_init}, - {.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init}, - {.compatible = "arm,arm1176-pmu", .data = armv6pmu_init}, - {.compatible = "arm,arm1136-pmu", .data = armv6pmu_init}, - {}, -}; - -static struct platform_device_id __devinitdata cpu_pmu_plat_device_ids[] = { - {.name = "arm-pmu"}, - {}, -}; - -/* - * CPU PMU identification and probing. - */ -static struct arm_pmu *__devinit probe_current_pmu(void) -{ - struct arm_pmu *pmu = NULL; - int cpu = get_cpu(); - unsigned long cpuid = read_cpuid_id(); - unsigned long implementor = (cpuid & 0xFF000000) >> 24; - unsigned long part_number = (cpuid & 0xFFF0); - - pr_info("probing PMU on CPU %d\n", cpu); - - /* ARM Ltd CPUs. */ - if (0x41 == implementor) { - switch (part_number) { - case 0xB360: /* ARM1136 */ - case 0xB560: /* ARM1156 */ - case 0xB760: /* ARM1176 */ - pmu = armv6pmu_init(); - break; - case 0xB020: /* ARM11mpcore */ - pmu = armv6mpcore_pmu_init(); - break; - case 0xC080: /* Cortex-A8 */ - pmu = armv7_a8_pmu_init(); - break; - case 0xC090: /* Cortex-A9 */ - pmu = armv7_a9_pmu_init(); - break; - case 0xC050: /* Cortex-A5 */ - pmu = armv7_a5_pmu_init(); - break; - case 0xC0F0: /* Cortex-A15 */ - pmu = armv7_a15_pmu_init(); - break; - case 0xC070: /* Cortex-A7 */ - pmu = armv7_a7_pmu_init(); - break; - } - /* Intel CPUs [xscale]. */ - } else if (0x69 == implementor) { - part_number = (cpuid >> 13) & 0x7; - switch (part_number) { - case 1: - pmu = xscale1pmu_init(); - break; - case 2: - pmu = xscale2pmu_init(); - break; - } - } - - put_cpu(); - return pmu; -} - -static int __devinit cpu_pmu_device_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id; - struct arm_pmu *(*init_fn)(void); - struct device_node *node = pdev->dev.of_node; - - if (cpu_pmu) { - pr_info("attempt to register multiple PMU devices!"); - return -ENOSPC; - } - - if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { - init_fn = of_id->data; - cpu_pmu = init_fn(); - } else { - cpu_pmu = probe_current_pmu(); - } - - if (!cpu_pmu) - return -ENODEV; - - cpu_pmu->plat_device = pdev; - cpu_pmu_init(cpu_pmu); - register_cpu_notifier(&cpu_pmu_hotplug_notifier); - armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); - - return 0; -} - -static struct platform_driver cpu_pmu_driver = { - .driver = { - .name = "arm-pmu", - .pm = &armpmu_dev_pm_ops, - .of_match_table = cpu_pmu_of_device_ids, - }, - .probe = cpu_pmu_device_probe, - .id_table = cpu_pmu_plat_device_ids, -}; - -static int __init register_pmu_driver(void) -{ - return platform_driver_register(&cpu_pmu_driver); -} -device_initcall(register_pmu_driver); - /* * Callchain handling code. */ diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c new file mode 100644 index 000000000000..56ddc989c909 --- /dev/null +++ b/arch/arm/kernel/perf_event_cpu.c @@ -0,0 +1,231 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2012 ARM Limited + * + * Author: Will Deacon + */ +#define pr_fmt(fmt) "CPU PMU: " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Set at runtime when we know what CPU type we are. */ +static struct arm_pmu *cpu_pmu; + +static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); +static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); +static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); + +/* + * Despite the names, these two functions are CPU-specific and are used + * by the OProfile/perf code. + */ +const char *perf_pmu_name(void) +{ + if (!cpu_pmu) + return NULL; + + return cpu_pmu->pmu.name; +} +EXPORT_SYMBOL_GPL(perf_pmu_name); + +int perf_num_counters(void) +{ + int max_events = 0; + + if (cpu_pmu != NULL) + max_events = cpu_pmu->num_events; + + return max_events; +} +EXPORT_SYMBOL_GPL(perf_num_counters); + +/* Include the PMU-specific implementations. */ +#include "perf_event_xscale.c" +#include "perf_event_v6.c" +#include "perf_event_v7.c" + +static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) +{ + return &__get_cpu_var(cpu_hw_events); +} + +static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) +{ + int cpu; + for_each_possible_cpu(cpu) { + struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); + events->events = per_cpu(hw_events, cpu); + events->used_mask = per_cpu(used_mask, cpu); + raw_spin_lock_init(&events->pmu_lock); + } + cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; + + /* Ensure the PMU has sane values out of reset. */ + if (cpu_pmu && cpu_pmu->reset) + on_each_cpu(cpu_pmu->reset, NULL, 1); +} + +/* + * PMU hardware loses all context when a CPU goes offline. + * When a CPU is hotplugged back in, since some hardware registers are + * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading + * junk values out of them. + */ +static int __cpuinit cpu_pmu_notify(struct notifier_block *b, + unsigned long action, void *hcpu) +{ + if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) + return NOTIFY_DONE; + + if (cpu_pmu && cpu_pmu->reset) + cpu_pmu->reset(NULL); + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata cpu_pmu_hotplug_notifier = { + .notifier_call = cpu_pmu_notify, +}; + +/* + * PMU platform driver and devicetree bindings. + */ +static struct of_device_id __devinitdata cpu_pmu_of_device_ids[] = { + {.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init}, + {.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init}, + {.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init}, + {.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init}, + {.compatible = "arm,cortex-a5-pmu", .data = armv7_a5_pmu_init}, + {.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init}, + {.compatible = "arm,arm1176-pmu", .data = armv6pmu_init}, + {.compatible = "arm,arm1136-pmu", .data = armv6pmu_init}, + {}, +}; + +static struct platform_device_id __devinitdata cpu_pmu_plat_device_ids[] = { + {.name = "arm-pmu"}, + {}, +}; + +/* + * CPU PMU identification and probing. + */ +static struct arm_pmu *__devinit probe_current_pmu(void) +{ + struct arm_pmu *pmu = NULL; + int cpu = get_cpu(); + unsigned long cpuid = read_cpuid_id(); + unsigned long implementor = (cpuid & 0xFF000000) >> 24; + unsigned long part_number = (cpuid & 0xFFF0); + + pr_info("probing PMU on CPU %d\n", cpu); + + /* ARM Ltd CPUs. */ + if (0x41 == implementor) { + switch (part_number) { + case 0xB360: /* ARM1136 */ + case 0xB560: /* ARM1156 */ + case 0xB760: /* ARM1176 */ + pmu = armv6pmu_init(); + break; + case 0xB020: /* ARM11mpcore */ + pmu = armv6mpcore_pmu_init(); + break; + case 0xC080: /* Cortex-A8 */ + pmu = armv7_a8_pmu_init(); + break; + case 0xC090: /* Cortex-A9 */ + pmu = armv7_a9_pmu_init(); + break; + case 0xC050: /* Cortex-A5 */ + pmu = armv7_a5_pmu_init(); + break; + case 0xC0F0: /* Cortex-A15 */ + pmu = armv7_a15_pmu_init(); + break; + case 0xC070: /* Cortex-A7 */ + pmu = armv7_a7_pmu_init(); + break; + } + /* Intel CPUs [xscale]. */ + } else if (0x69 == implementor) { + part_number = (cpuid >> 13) & 0x7; + switch (part_number) { + case 1: + pmu = xscale1pmu_init(); + break; + case 2: + pmu = xscale2pmu_init(); + break; + } + } + + put_cpu(); + return pmu; +} + +static int __devinit cpu_pmu_device_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct arm_pmu *(*init_fn)(void); + struct device_node *node = pdev->dev.of_node; + + if (cpu_pmu) { + pr_info("attempt to register multiple PMU devices!"); + return -ENOSPC; + } + + if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { + init_fn = of_id->data; + cpu_pmu = init_fn(); + } else { + cpu_pmu = probe_current_pmu(); + } + + if (!cpu_pmu) + return -ENODEV; + + cpu_pmu->plat_device = pdev; + cpu_pmu_init(cpu_pmu); + register_cpu_notifier(&cpu_pmu_hotplug_notifier); + armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); + + return 0; +} + +static struct platform_driver cpu_pmu_driver = { + .driver = { + .name = "arm-pmu", + .pm = &armpmu_dev_pm_ops, + .of_match_table = cpu_pmu_of_device_ids, + }, + .probe = cpu_pmu_device_probe, + .id_table = cpu_pmu_plat_device_ids, +}; + +static int __init register_pmu_driver(void) +{ + return platform_driver_register(&cpu_pmu_driver); +} +device_initcall(register_pmu_driver); -- cgit v1.2.3 From 051f1b13144dd8553d5a5104dde94c7263ae3ba7 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 31 Jul 2012 10:34:25 +0100 Subject: ARM: perf: move irq registration into pmu implementation This patch moves the CPU-specific IRQ registration and parsing code into the CPU PMU backend. This is required because a PMU may have more than one interrupt, which in turn can be either PPI (per-cpu) or SPI (requiring strict affinity setting at the interrupt distributor). Signed-off-by: Sudeep KarkadaNagesha [will: cosmetic edits and reworked interrupt dispatching] Signed-off-by: Will Deacon --- arch/arm/kernel/perf_event.c | 72 +++++++--------------------------------- arch/arm/kernel/perf_event_cpu.c | 66 +++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 61 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 86fd39937171..93971b1a4f0b 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -297,87 +297,39 @@ validate_group(struct perf_event *event) return 0; } -static irqreturn_t armpmu_platform_irq(int irq, void *dev) +static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) { struct arm_pmu *armpmu = (struct arm_pmu *) dev; struct platform_device *plat_device = armpmu->plat_device; struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); - return plat->handle_irq(irq, dev, armpmu->handle_irq); + if (plat && plat->handle_irq) + return plat->handle_irq(irq, dev, armpmu->handle_irq); + else + return armpmu->handle_irq(irq, dev); } static void armpmu_release_hardware(struct arm_pmu *armpmu) { - int i, irq, irqs; - struct platform_device *pmu_device = armpmu->plat_device; - - irqs = min(pmu_device->num_resources, num_possible_cpus()); - - for (i = 0; i < irqs; ++i) { - if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) - continue; - irq = platform_get_irq(pmu_device, i); - if (irq >= 0) - free_irq(irq, armpmu); - } - - pm_runtime_put_sync(&pmu_device->dev); + armpmu->free_irq(); + pm_runtime_put_sync(&armpmu->plat_device->dev); } static int armpmu_reserve_hardware(struct arm_pmu *armpmu) { - struct arm_pmu_platdata *plat; - irq_handler_t handle_irq; - int i, err, irq, irqs; + int err; struct platform_device *pmu_device = armpmu->plat_device; if (!pmu_device) return -ENODEV; - plat = dev_get_platdata(&pmu_device->dev); - if (plat && plat->handle_irq) - handle_irq = armpmu_platform_irq; - else - handle_irq = armpmu->handle_irq; - - irqs = min(pmu_device->num_resources, num_possible_cpus()); - if (irqs < 1) { - pr_err("no irqs for PMUs defined\n"); - return -ENODEV; - } - pm_runtime_get_sync(&pmu_device->dev); - - for (i = 0; i < irqs; ++i) { - err = 0; - irq = platform_get_irq(pmu_device, i); - if (irq < 0) - continue; - - /* - * If we have a single PMU interrupt that we can't shift, - * assume that we're running on a uniprocessor machine and - * continue. Otherwise, continue without this interrupt. - */ - if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { - pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", - irq, i); - continue; - } - - err = request_irq(irq, handle_irq, - IRQF_DISABLED | IRQF_NOBALANCING, - "arm-pmu", armpmu); - if (err) { - pr_err("unable to request IRQ%d for ARM PMU counters\n", - irq); - armpmu_release_hardware(armpmu); - return err; - } - - cpumask_set_cpu(i, &armpmu->active_irqs); + err = armpmu->request_irq(armpmu_dispatch_irq); + if (err) { + armpmu_release_hardware(armpmu); + return err; } return 0; diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c index 56ddc989c909..8d7d8d4de9d6 100644 --- a/arch/arm/kernel/perf_event_cpu.c +++ b/arch/arm/kernel/perf_event_cpu.c @@ -70,6 +70,67 @@ static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) return &__get_cpu_var(cpu_hw_events); } +static void cpu_pmu_free_irq(void) +{ + int i, irq, irqs; + struct platform_device *pmu_device = cpu_pmu->plat_device; + + irqs = min(pmu_device->num_resources, num_possible_cpus()); + + for (i = 0; i < irqs; ++i) { + if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) + continue; + irq = platform_get_irq(pmu_device, i); + if (irq >= 0) + free_irq(irq, cpu_pmu); + } +} + +static int cpu_pmu_request_irq(irq_handler_t handler) +{ + int i, err, irq, irqs; + struct platform_device *pmu_device = cpu_pmu->plat_device; + + if (!pmu_device) + return -ENODEV; + + irqs = min(pmu_device->num_resources, num_possible_cpus()); + if (irqs < 1) { + pr_err("no irqs for PMUs defined\n"); + return -ENODEV; + } + + for (i = 0; i < irqs; ++i) { + err = 0; + irq = platform_get_irq(pmu_device, i); + if (irq < 0) + continue; + + /* + * If we have a single PMU interrupt that we can't shift, + * assume that we're running on a uniprocessor machine and + * continue. Otherwise, continue without this interrupt. + */ + if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { + pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, i); + continue; + } + + err = request_irq(irq, handler, IRQF_NOBALANCING, "arm-pmu", + cpu_pmu); + if (err) { + pr_err("unable to request IRQ%d for ARM PMU counters\n", + irq); + return err; + } + + cpumask_set_cpu(i, &cpu_pmu->active_irqs); + } + + return 0; +} + static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) { int cpu; @@ -79,7 +140,10 @@ static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu) events->used_mask = per_cpu(used_mask, cpu); raw_spin_lock_init(&events->pmu_lock); } - cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; + + cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; + cpu_pmu->request_irq = cpu_pmu_request_irq; + cpu_pmu->free_irq = cpu_pmu_free_irq; /* Ensure the PMU has sane values out of reset. */ if (cpu_pmu && cpu_pmu->reset) -- cgit v1.2.3