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/include/asm/mach/pci.h | 3 +++ arch/arm/kernel/bios32.c | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h index df818876fa31..db9fedb57f2c 100644 --- a/arch/arm/include/asm/mach/pci.h +++ b/arch/arm/include/asm/mach/pci.h @@ -11,6 +11,7 @@ #ifndef __ASM_MACH_PCI_H #define __ASM_MACH_PCI_H +#include struct pci_sys_data; struct pci_ops; @@ -43,6 +44,8 @@ struct pci_sys_data { unsigned long io_offset; /* bus->cpu IO mapping offset */ struct pci_bus *bus; /* PCI bus */ struct list_head resources; /* root bus resources (apertures) */ + struct resource io_res; + char io_res_name[12]; /* Bridge swizzling */ u8 (*swizzle)(struct pci_dev *, u8 *); /* IRQ mapping */ 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 fa8bbb13ab49e77c00fa09e5504ec25b7176fb4b Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 14 Mar 2012 02:26:56 +0800 Subject: ARM: use new LEDS CPU trigger stub to replace old one Cc: Linus Walleij Signed-off-by: Bryan Wu --- arch/arm/Kconfig | 53 ------------------- arch/arm/include/asm/leds.h | 50 ------------------ arch/arm/kernel/Makefile | 1 - arch/arm/kernel/leds.c | 121 -------------------------------------------- arch/arm/kernel/process.c | 6 +-- arch/arm/kernel/time.c | 17 ------- 6 files changed, 3 insertions(+), 245 deletions(-) delete mode 100644 arch/arm/include/asm/leds.h delete mode 100644 arch/arm/kernel/leds.c (limited to 'arch/arm/kernel') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a91009c61870..dc57d3792359 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1739,59 +1739,6 @@ config FORCE_MAX_ZONEORDER This config option is actually maximum order plus one. For example, a value of 11 means that the largest free memory block is 2^10 pages. -config LEDS - bool "Timer and CPU usage LEDs" - depends on ARCH_CDB89712 || ARCH_EBSA110 || \ - ARCH_EBSA285 || ARCH_INTEGRATOR || \ - ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_NETWINDER || \ - ARCH_OMAP || ARCH_P720T || ARCH_PXA_IDP || \ - ARCH_SA1100 || ARCH_SHARK || ARCH_VERSATILE || \ - ARCH_AT91 || ARCH_DAVINCI || \ - ARCH_KS8695 || MACH_RD88F5182 || ARCH_REALVIEW - help - If you say Y here, the LEDs on your machine will be used - to provide useful information about your current system status. - - If you are compiling a kernel for a NetWinder or EBSA-285, you will - be able to select which LEDs are active using the options below. If - you are compiling a kernel for the EBSA-110 or the LART however, the - red LED will simply flash regularly to indicate that the system is - still functional. It is safe to say Y here if you have a CATS - system, but the driver will do nothing. - -config LEDS_TIMER - bool "Timer LED" if (!ARCH_CDB89712 && !ARCH_OMAP) || \ - OMAP_OSK_MISTRAL || MACH_OMAP_H2 \ - || MACH_OMAP_PERSEUS2 - depends on LEDS - depends on !GENERIC_CLOCKEVENTS - default y if ARCH_EBSA110 - help - If you say Y here, one of the system LEDs (the green one on the - NetWinder, the amber one on the EBSA285, or the red one on the LART) - will flash regularly to indicate that the system is still - operational. This is mainly useful to kernel hackers who are - debugging unstable kernels. - - The LART uses the same LED for both Timer LED and CPU usage LED - functions. You may choose to use both, but the Timer LED function - will overrule the CPU usage LED. - -config LEDS_CPU - bool "CPU usage LED" if (!ARCH_CDB89712 && !ARCH_EBSA110 && \ - !ARCH_OMAP) \ - || OMAP_OSK_MISTRAL || MACH_OMAP_H2 \ - || MACH_OMAP_PERSEUS2 - depends on LEDS - help - If you say Y here, the red LED will be used to give a good real - time indication of CPU usage, by lighting whenever the idle task - is not currently executing. - - The LART uses the same LED for both Timer LED and CPU usage LED - functions. You may choose to use both, but the Timer LED function - will overrule the CPU usage LED. - config ALIGNMENT_TRAP bool depends on CPU_CP15_MMU diff --git a/arch/arm/include/asm/leds.h b/arch/arm/include/asm/leds.h deleted file mode 100644 index c545739f39b7..000000000000 --- a/arch/arm/include/asm/leds.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * arch/arm/include/asm/leds.h - * - * Copyright (C) 1998 Russell King - * - * 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. - * - * Event-driven interface for LEDs on machines - * Added led_start and led_stop- Alex Holden, 28th Dec 1998. - */ -#ifndef ASM_ARM_LEDS_H -#define ASM_ARM_LEDS_H - - -typedef enum { - led_idle_start, - led_idle_end, - led_timer, - led_start, - led_stop, - led_claim, /* override idle & timer leds */ - led_release, /* restore idle & timer leds */ - led_start_timer_mode, - led_stop_timer_mode, - led_green_on, - led_green_off, - led_amber_on, - led_amber_off, - led_red_on, - led_red_off, - led_blue_on, - led_blue_off, - /* - * I want this between led_timer and led_start, but - * someone has decided to export this to user space - */ - led_halted -} led_event_t; - -/* Use this routine to handle LEDs */ - -#ifdef CONFIG_LEDS -extern void (*leds_event)(led_event_t); -#else -#define leds_event(e) -#endif - -#endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 7ad2d5cf7008..8951577c4ced 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -21,7 +21,6 @@ obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += compat.o -obj-$(CONFIG_LEDS) += leds.o obj-$(CONFIG_OC_ETM) += etm.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ISA_DMA_API) += dma.o diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c deleted file mode 100644 index 1911dae19e4f..000000000000 --- a/arch/arm/kernel/leds.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * LED support code, ripped out of arch/arm/kernel/time.c - * - * Copyright (C) 1994-2001 Russell King - * - * 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 -#include - -#include - -static void dummy_leds_event(led_event_t evt) -{ -} - -void (*leds_event)(led_event_t) = dummy_leds_event; - -struct leds_evt_name { - const char name[8]; - int on; - int off; -}; - -static const struct leds_evt_name evt_names[] = { - { "amber", led_amber_on, led_amber_off }, - { "blue", led_blue_on, led_blue_off }, - { "green", led_green_on, led_green_off }, - { "red", led_red_on, led_red_off }, -}; - -static ssize_t leds_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret = -EINVAL, len = strcspn(buf, " "); - - if (len > 0 && buf[len] == '\0') - len--; - - if (strncmp(buf, "claim", len) == 0) { - leds_event(led_claim); - ret = size; - } else if (strncmp(buf, "release", len) == 0) { - leds_event(led_release); - ret = size; - } else { - int i; - - for (i = 0; i < ARRAY_SIZE(evt_names); i++) { - if (strlen(evt_names[i].name) != len || - strncmp(buf, evt_names[i].name, len) != 0) - continue; - if (strncmp(buf+len, " on", 3) == 0) { - leds_event(evt_names[i].on); - ret = size; - } else if (strncmp(buf+len, " off", 4) == 0) { - leds_event(evt_names[i].off); - ret = size; - } - break; - } - } - return ret; -} - -static DEVICE_ATTR(event, 0200, NULL, leds_store); - -static struct bus_type leds_subsys = { - .name = "leds", - .dev_name = "leds", -}; - -static struct device leds_device = { - .id = 0, - .bus = &leds_subsys, -}; - -static int leds_suspend(void) -{ - leds_event(led_stop); - return 0; -} - -static void leds_resume(void) -{ - leds_event(led_start); -} - -static void leds_shutdown(void) -{ - leds_event(led_halted); -} - -static struct syscore_ops leds_syscore_ops = { - .shutdown = leds_shutdown, - .suspend = leds_suspend, - .resume = leds_resume, -}; - -static int __init leds_init(void) -{ - int ret; - ret = subsys_system_register(&leds_subsys, NULL); - if (ret == 0) - ret = device_register(&leds_device); - if (ret == 0) - ret = device_create_file(&leds_device, &dev_attr_event); - if (ret == 0) - register_syscore_ops(&leds_syscore_ops); - return ret; -} - -device_initcall(leds_init); - -EXPORT_SYMBOL(leds_event); diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 19c95ea65b2f..864580af1678 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -31,9 +31,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -189,7 +189,7 @@ void cpu_idle(void) while (1) { tick_nohz_idle_enter(); rcu_idle_enter(); - leds_event(led_idle_start); + ledtrig_cpu(CPU_LED_IDLE_START); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU if (cpu_is_offline(smp_processor_id())) @@ -220,7 +220,7 @@ void cpu_idle(void) } else local_irq_enable(); } - leds_event(led_idle_end); + ledtrig_cpu(CPU_LED_IDLE_END); rcu_idle_exit(); tick_nohz_idle_exit(); schedule_preempt_disabled(); diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index af2afb019672..09be0c3c9069 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -80,21 +79,6 @@ u32 arch_gettimeoffset(void) } #endif /* CONFIG_ARCH_USES_GETTIMEOFFSET */ -#ifdef CONFIG_LEDS_TIMER -static inline void do_leds(void) -{ - static unsigned int count = HZ/2; - - if (--count == 0) { - count = HZ/2; - leds_event(led_timer); - } -} -#else -#define do_leds() -#endif - - #ifndef CONFIG_GENERIC_CLOCKEVENTS /* * Kernel system timer support. @@ -102,7 +86,6 @@ static inline void do_leds(void) void timer_tick(void) { profile_tick(CPU_PROFILING); - do_leds(); xtime_update(1); #ifndef CONFIG_SMP update_process_times(user_mode(get_irq_regs())); -- 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/include/asm/pmu.h | 20 ++++++++++++-------- arch/arm/kernel/perf_event.c | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 16 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 4432305f4a2a..40d7dff8bc30 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -31,18 +31,22 @@ enum arm_pmu_type { * interrupt and passed the address of the low level handler, * and can be used to implement any platform specific handling * before or after calling it. - * @enable_irq: an optional handler which will be called after - * request_irq and be used to handle some platform specific - * irq enablement - * @disable_irq: an optional handler which will be called before - * free_irq and be used to handle some platform specific - * irq disablement + * @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 { irqreturn_t (*handle_irq)(int irq, void *dev, irq_handler_t pmu_handler); - void (*enable_irq)(int irq); - void (*disable_irq)(int irq); + int (*runtime_resume)(struct device *dev); + int (*runtime_suspend)(struct device *dev); }; #ifdef CONFIG_CPU_HAS_PMU 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 --- Documentation/devicetree/bindings/arm/pmu.txt | 4 ++++ arch/arm/kernel/perf_event.c | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'arch/arm/kernel') diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt index 1c044eb320cc..343781b9f246 100644 --- a/Documentation/devicetree/bindings/arm/pmu.txt +++ b/Documentation/devicetree/bindings/arm/pmu.txt @@ -7,8 +7,12 @@ representation in the device tree should be done as under:- Required properties: - compatible : should be one of + "arm,cortex-a15-pmu" "arm,cortex-a9-pmu" "arm,cortex-a8-pmu" + "arm,cortex-a7-pmu" + "arm,cortex-a5-pmu" + "arm,arm11mpcore-pmu" "arm,arm1176-pmu" "arm,arm1136-pmu" - interrupts : 1 combined interrupt or 1 per core. 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 --- MAINTAINERS | 1 - arch/arm/Kconfig | 8 +------- arch/arm/include/asm/pmu.h | 34 ---------------------------------- arch/arm/kernel/Makefile | 1 - arch/arm/kernel/perf_event.c | 8 -------- arch/arm/kernel/pmu.c | 36 ------------------------------------ 6 files changed, 1 insertion(+), 87 deletions(-) delete mode 100644 arch/arm/kernel/pmu.c (limited to 'arch/arm/kernel') diff --git a/MAINTAINERS b/MAINTAINERS index fdc0119963e7..437a7dd36843 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -595,7 +595,6 @@ M: Will Deacon S: Maintained F: arch/arm/kernel/perf_event* F: arch/arm/oprofile/common.c -F: arch/arm/kernel/pmu.c F: arch/arm/include/asm/pmu.h F: arch/arm/kernel/hw_breakpoint.c F: arch/arm/include/asm/hw_breakpoint.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6d6e18fee9fe..1d5ce138b877 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1179,12 +1179,6 @@ config XSCALE_PMU depends on CPU_XSCALE default y -config CPU_HAS_PMU - depends on (CPU_V6 || CPU_V6K || CPU_V7 || XSCALE_PMU) && \ - (!ARCH_OMAP3 || OMAP3_EMU) - default y - bool - config MULTI_IRQ_HANDLER bool help @@ -1757,7 +1751,7 @@ config HIGHPTE config HW_PERF_EVENTS bool "Enable hardware performance counter support for perf events" - depends on PERF_EVENTS && CPU_HAS_PMU + depends on PERF_EVENTS default y help Enable hardware performance counter support for perf events. If diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 40d7dff8bc30..05e0401d7697 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -49,40 +49,6 @@ struct arm_pmu_platdata { int (*runtime_suspend)(struct device *dev); }; -#ifdef CONFIG_CPU_HAS_PMU - -/** - * reserve_pmu() - reserve the hardware performance counters - * - * Reserve the hardware performance counters in the system for exclusive use. - * Returns 0 on success or -EBUSY if the lock is already held. - */ -extern int -reserve_pmu(enum arm_pmu_type type); - -/** - * release_pmu() - Relinquish control of the performance counters - * - * Release the performance counters and allow someone else to use them. - */ -extern void -release_pmu(enum arm_pmu_type type); - -#else /* CONFIG_CPU_HAS_PMU */ - -#include - -static inline int -reserve_pmu(enum arm_pmu_type type) -{ - return -ENODEV; -} - -static inline void -release_pmu(enum arm_pmu_type type) { } - -#endif /* CONFIG_CPU_HAS_PMU */ - #ifdef CONFIG_HW_PERF_EVENTS /* The events for a given PMU register set. */ 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/include/asm/perf_event.h | 9 ++++- arch/arm/include/asm/pmu.h | 11 +++++- 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 +- 6 files changed, 53 insertions(+), 52 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h index e074948d8143..625cd621a436 100644 --- a/arch/arm/include/asm/perf_event.h +++ b/arch/arm/include/asm/perf_event.h @@ -12,6 +12,13 @@ #ifndef __ARM_PERF_EVENT_H__ #define __ARM_PERF_EVENT_H__ -/* Nothing to see here... */ +/* + * The ARMv7 CPU PMU supports up to 32 event counters. + */ +#define ARMPMU_MAX_HWEVENTS 32 + +#define HW_OP_UNSUPPORTED 0xFFFF +#define C(_x) PERF_COUNT_HW_CACHE_##_x +#define CACHE_OP_UNSUPPORTED 0xFFFF #endif /* __ARM_PERF_EVENT_H__ */ diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index fbec73a0ee76..a993ad676047 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -89,7 +89,9 @@ struct arm_pmu { #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) -int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type); +extern const struct dev_pm_ops armpmu_dev_pm_ops; + +int armpmu_register(struct arm_pmu *armpmu, char *name, int type); u64 armpmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, @@ -99,6 +101,13 @@ int armpmu_event_set_period(struct perf_event *event, struct hw_perf_event *hwc, int idx); +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); + #endif /* CONFIG_HW_PERF_EVENTS */ #endif /* __ARM_PMU_H__ */ 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/include/asm/pmu.h | 2 ++ arch/arm/kernel/perf_event.c | 72 +++++++--------------------------------- arch/arm/kernel/perf_event_cpu.c | 66 +++++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 61 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index a993ad676047..a26170dce02e 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -78,6 +78,8 @@ struct arm_pmu { void (*start)(void); void (*stop)(void); void (*reset)(void *); + int (*request_irq)(irq_handler_t handler); + void (*free_irq)(void); int (*map_event)(struct perf_event *event); int num_events; atomic_t active_events; 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 From 9fc31ddc70ac594b5a1d8a83303b65008221088c Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 29 Aug 2012 11:16:59 +0100 Subject: ARM: Don't unconditionally bloat thread_info There is no point reserving space at the bottom of the kernel stack for per-thread crunch state, and per-thread VFP state if these are not being supported by the kernel being built. Remove these members from the thread union when these features are disabled. Reported-by: Tim Bird Signed-off-by: Russell King --- arch/arm/include/asm/thread_info.h | 2 ++ arch/arm/kernel/asm-offsets.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index b79f8e97f775..7b0a8f5f66fa 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -59,7 +59,9 @@ struct thread_info { __u32 syscall; /* syscall number */ __u8 used_cp[16]; /* thread used copro */ unsigned long tp_value; +#ifdef CONFIG_CRUNCH struct crunch_state crunchstate; +#endif union fp_state fpstate __attribute__((aligned(8))); union vfp_state vfpstate; #ifdef CONFIG_ARM_THUMBEE diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 1429d8989fb9..c985b481192c 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -59,10 +59,12 @@ int main(void) DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp)); DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); +#ifdef CONFIG_VFP DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); #ifdef CONFIG_SMP DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu)); #endif +#endif #ifdef CONFIG_ARM_THUMBEE DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); #endif -- cgit v1.2.3 From aa783b6fd60b3844e199b1c2d2f4068f3caa1358 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 1 Sep 2012 03:00:15 +0100 Subject: ARM: 7505/1: split out ATAGS parsing Make ATAGS parsing into a source file of its own, namely atags_parse.c. Also rename compat.c to atags_compat.c to make it clearer what it is about. Same for atags.c which is now atags_proc.c. Gather all the atags function declarations into a common atags.h. Signed-off-by: Nicolas Pitre Tested-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/kernel/Makefile | 6 +- arch/arm/kernel/atags.c | 83 -------------- arch/arm/kernel/atags.h | 3 + arch/arm/kernel/atags_compat.c | 219 +++++++++++++++++++++++++++++++++++++ arch/arm/kernel/atags_parse.c | 238 +++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/atags_proc.c | 83 ++++++++++++++ arch/arm/kernel/compat.c | 219 ------------------------------------- arch/arm/kernel/compat.h | 11 -- arch/arm/kernel/setup.c | 236 +--------------------------------------- 9 files changed, 547 insertions(+), 551 deletions(-) delete mode 100644 arch/arm/kernel/atags.c create mode 100644 arch/arm/kernel/atags_compat.c create mode 100644 arch/arm/kernel/atags_parse.c create mode 100644 arch/arm/kernel/atags_proc.c delete mode 100644 arch/arm/kernel/compat.c delete mode 100644 arch/arm/kernel/compat.h (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 7ad2d5cf7008..086c9d3bc360 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -15,11 +15,11 @@ CFLAGS_REMOVE_return_address.o = -pg # Object file lists. -obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ +obj-y := atags_parse.o elf.o entry-armv.o entry-common.o irq.o opcodes.o \ process.o ptrace.o return_address.o sched_clock.o \ setup.o signal.o stacktrace.o sys_arm.o time.o traps.o -obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += compat.o +obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += atags_compat.o obj-$(CONFIG_LEDS) += leds.o obj-$(CONFIG_OC_ETM) += etm.o @@ -52,7 +52,7 @@ test-kprobes-objs += kprobes-test-thumb.o else test-kprobes-objs += kprobes-test-arm.o endif -obj-$(CONFIG_ATAGS_PROC) += atags.o +obj-$(CONFIG_ATAGS_PROC) += atags_proc.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/arm/kernel/atags.c b/arch/arm/kernel/atags.c deleted file mode 100644 index 42a1a1415fa6..000000000000 --- a/arch/arm/kernel/atags.c +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include -#include - -struct buffer { - size_t size; - char data[]; -}; - -static int -read_buffer(char* page, char** start, off_t off, int count, - int* eof, void* data) -{ - struct buffer *buffer = (struct buffer *)data; - - if (off >= buffer->size) { - *eof = 1; - return 0; - } - - count = min((int) (buffer->size - off), count); - - memcpy(page, &buffer->data[off], count); - - return count; -} - -#define BOOT_PARAMS_SIZE 1536 -static char __initdata atags_copy[BOOT_PARAMS_SIZE]; - -void __init save_atags(const struct tag *tags) -{ - memcpy(atags_copy, tags, sizeof(atags_copy)); -} - -static int __init init_atags_procfs(void) -{ - /* - * This cannot go into save_atags() because kmalloc and proc don't work - * yet when it is called. - */ - struct proc_dir_entry *tags_entry; - struct tag *tag = (struct tag *)atags_copy; - struct buffer *b; - size_t size; - - if (tag->hdr.tag != ATAG_CORE) { - printk(KERN_INFO "No ATAGs?"); - return -EINVAL; - } - - for (; tag->hdr.size; tag = tag_next(tag)) - ; - - /* include the terminating ATAG_NONE */ - size = (char *)tag - atags_copy + sizeof(struct tag_header); - - WARN_ON(tag->hdr.tag != ATAG_NONE); - - b = kmalloc(sizeof(*b) + size, GFP_KERNEL); - if (!b) - goto nomem; - - b->size = size; - memcpy(b->data, atags_copy, size); - - tags_entry = create_proc_read_entry("atags", 0400, - NULL, read_buffer, b); - - if (!tags_entry) - goto nomem; - - return 0; - -nomem: - kfree(b); - printk(KERN_ERR "Exporting ATAGs: not enough memory\n"); - - return -ENOMEM; -} -arch_initcall(init_atags_procfs); diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h index e5f028d214a1..a888fdd381b5 100644 --- a/arch/arm/kernel/atags.h +++ b/arch/arm/kernel/atags.h @@ -3,3 +3,6 @@ extern void save_atags(struct tag *tags); #else static inline void save_atags(struct tag *tags) { } #endif + +void convert_to_tag_list(struct tag *tags); +struct machine_desc *setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr); diff --git a/arch/arm/kernel/atags_compat.c b/arch/arm/kernel/atags_compat.c new file mode 100644 index 000000000000..5236ad38f417 --- /dev/null +++ b/arch/arm/kernel/atags_compat.c @@ -0,0 +1,219 @@ +/* + * linux/arch/arm/kernel/atags_compat.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * We keep the old params compatibility cruft in one place (here) + * so we don't end up with lots of mess around other places. + * + * NOTE: + * The old struct param_struct is deprecated, but it will be kept in + * the kernel for 5 years from now (2001). This will allow boot loaders + * to convert to the new struct tag way. + */ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "atags.h" + +/* + * Usage: + * - do not go blindly adding fields, add them at the end + * - when adding fields, don't rely on the address until + * a patch from me has been released + * - unused fields should be zero (for future expansion) + * - this structure is relatively short-lived - only + * guaranteed to contain useful data in setup_arch() + * + * This is the old deprecated way to pass parameters to the kernel + */ +struct param_struct { + union { + struct { + unsigned long page_size; /* 0 */ + unsigned long nr_pages; /* 4 */ + unsigned long ramdisk_size; /* 8 */ + unsigned long flags; /* 12 */ +#define FLAG_READONLY 1 +#define FLAG_RDLOAD 4 +#define FLAG_RDPROMPT 8 + unsigned long rootdev; /* 16 */ + unsigned long video_num_cols; /* 20 */ + unsigned long video_num_rows; /* 24 */ + unsigned long video_x; /* 28 */ + unsigned long video_y; /* 32 */ + unsigned long memc_control_reg; /* 36 */ + unsigned char sounddefault; /* 40 */ + unsigned char adfsdrives; /* 41 */ + unsigned char bytes_per_char_h; /* 42 */ + unsigned char bytes_per_char_v; /* 43 */ + unsigned long pages_in_bank[4]; /* 44 */ + unsigned long pages_in_vram; /* 60 */ + unsigned long initrd_start; /* 64 */ + unsigned long initrd_size; /* 68 */ + unsigned long rd_start; /* 72 */ + unsigned long system_rev; /* 76 */ + unsigned long system_serial_low; /* 80 */ + unsigned long system_serial_high; /* 84 */ + unsigned long mem_fclk_21285; /* 88 */ + } s; + char unused[256]; + } u1; + union { + char paths[8][128]; + struct { + unsigned long magic; + char n[1024 - sizeof(unsigned long)]; + } s; + } u2; + char commandline[COMMAND_LINE_SIZE]; +}; + +static struct tag * __init memtag(struct tag *tag, unsigned long start, unsigned long size) +{ + tag = tag_next(tag); + tag->hdr.tag = ATAG_MEM; + tag->hdr.size = tag_size(tag_mem32); + tag->u.mem.size = size; + tag->u.mem.start = start; + + return tag; +} + +static void __init build_tag_list(struct param_struct *params, void *taglist) +{ + struct tag *tag = taglist; + + if (params->u1.s.page_size != PAGE_SIZE) { + printk(KERN_WARNING "Warning: bad configuration page, " + "trying to continue\n"); + return; + } + + printk(KERN_DEBUG "Converting old-style param struct to taglist\n"); + +#ifdef CONFIG_ARCH_NETWINDER + if (params->u1.s.nr_pages != 0x02000 && + params->u1.s.nr_pages != 0x04000 && + params->u1.s.nr_pages != 0x08000 && + params->u1.s.nr_pages != 0x10000) { + printk(KERN_WARNING "Warning: bad NeTTrom parameters " + "detected, using defaults\n"); + + params->u1.s.nr_pages = 0x1000; /* 16MB */ + params->u1.s.ramdisk_size = 0; + params->u1.s.flags = FLAG_READONLY; + params->u1.s.initrd_start = 0; + params->u1.s.initrd_size = 0; + params->u1.s.rd_start = 0; + } +#endif + + tag->hdr.tag = ATAG_CORE; + tag->hdr.size = tag_size(tag_core); + tag->u.core.flags = params->u1.s.flags & FLAG_READONLY; + tag->u.core.pagesize = params->u1.s.page_size; + tag->u.core.rootdev = params->u1.s.rootdev; + + tag = tag_next(tag); + tag->hdr.tag = ATAG_RAMDISK; + tag->hdr.size = tag_size(tag_ramdisk); + tag->u.ramdisk.flags = (params->u1.s.flags & FLAG_RDLOAD ? 1 : 0) | + (params->u1.s.flags & FLAG_RDPROMPT ? 2 : 0); + tag->u.ramdisk.size = params->u1.s.ramdisk_size; + tag->u.ramdisk.start = params->u1.s.rd_start; + + tag = tag_next(tag); + tag->hdr.tag = ATAG_INITRD; + tag->hdr.size = tag_size(tag_initrd); + tag->u.initrd.start = params->u1.s.initrd_start; + tag->u.initrd.size = params->u1.s.initrd_size; + + tag = tag_next(tag); + tag->hdr.tag = ATAG_SERIAL; + tag->hdr.size = tag_size(tag_serialnr); + tag->u.serialnr.low = params->u1.s.system_serial_low; + tag->u.serialnr.high = params->u1.s.system_serial_high; + + tag = tag_next(tag); + tag->hdr.tag = ATAG_REVISION; + tag->hdr.size = tag_size(tag_revision); + tag->u.revision.rev = params->u1.s.system_rev; + +#ifdef CONFIG_ARCH_ACORN + if (machine_is_riscpc()) { + int i; + for (i = 0; i < 4; i++) + tag = memtag(tag, PHYS_OFFSET + (i << 26), + params->u1.s.pages_in_bank[i] * PAGE_SIZE); + } else +#endif + tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE); + +#ifdef CONFIG_FOOTBRIDGE + if (params->u1.s.mem_fclk_21285) { + tag = tag_next(tag); + tag->hdr.tag = ATAG_MEMCLK; + tag->hdr.size = tag_size(tag_memclk); + tag->u.memclk.fmemclk = params->u1.s.mem_fclk_21285; + } +#endif + +#ifdef CONFIG_ARCH_EBSA285 + if (machine_is_ebsa285()) { + tag = tag_next(tag); + tag->hdr.tag = ATAG_VIDEOTEXT; + tag->hdr.size = tag_size(tag_videotext); + tag->u.videotext.x = params->u1.s.video_x; + tag->u.videotext.y = params->u1.s.video_y; + tag->u.videotext.video_page = 0; + tag->u.videotext.video_mode = 0; + tag->u.videotext.video_cols = params->u1.s.video_num_cols; + tag->u.videotext.video_ega_bx = 0; + tag->u.videotext.video_lines = params->u1.s.video_num_rows; + tag->u.videotext.video_isvga = 1; + tag->u.videotext.video_points = 8; + } +#endif + +#ifdef CONFIG_ARCH_ACORN + tag = tag_next(tag); + tag->hdr.tag = ATAG_ACORN; + tag->hdr.size = tag_size(tag_acorn); + tag->u.acorn.memc_control_reg = params->u1.s.memc_control_reg; + tag->u.acorn.vram_pages = params->u1.s.pages_in_vram; + tag->u.acorn.sounddefault = params->u1.s.sounddefault; + tag->u.acorn.adfsdrives = params->u1.s.adfsdrives; +#endif + + tag = tag_next(tag); + tag->hdr.tag = ATAG_CMDLINE; + tag->hdr.size = (strlen(params->commandline) + 3 + + sizeof(struct tag_header)) >> 2; + strcpy(tag->u.cmdline.cmdline, params->commandline); + + tag = tag_next(tag); + tag->hdr.tag = ATAG_NONE; + tag->hdr.size = 0; + + memmove(params, taglist, ((int)tag) - ((int)taglist) + + sizeof(struct tag_header)); +} + +void __init convert_to_tag_list(struct tag *tags) +{ + struct param_struct *params = (struct param_struct *)tags; + build_tag_list(params, ¶ms->u2); +} diff --git a/arch/arm/kernel/atags_parse.c b/arch/arm/kernel/atags_parse.c new file mode 100644 index 000000000000..14512e6931d8 --- /dev/null +++ b/arch/arm/kernel/atags_parse.c @@ -0,0 +1,238 @@ +/* + * Tag parsing. + * + * Copyright (C) 1995-2001 Russell King + * + * 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 is the traditional way of passing data to the kernel at boot time. Rather + * than passing a fixed inflexible structure to the kernel, we pass a list + * of variable-sized tags to the kernel. The first tag must be a ATAG_CORE + * tag for the list to be recognised (to distinguish the tagged list from + * a param_struct). The list is terminated with a zero-length tag (this tag + * is not parsed in any way). + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "atags.h" + +static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; + +#ifndef MEM_SIZE +#define MEM_SIZE (16*1024*1024) +#endif + +static struct { + struct tag_header hdr1; + struct tag_core core; + struct tag_header hdr2; + struct tag_mem32 mem; + struct tag_header hdr3; +} default_tags __initdata = { + { tag_size(tag_core), ATAG_CORE }, + { 1, PAGE_SIZE, 0xff }, + { tag_size(tag_mem32), ATAG_MEM }, + { MEM_SIZE }, + { 0, ATAG_NONE } +}; + +static int __init parse_tag_core(const struct tag *tag) +{ + if (tag->hdr.size > 2) { + if ((tag->u.core.flags & 1) == 0) + root_mountflags &= ~MS_RDONLY; + ROOT_DEV = old_decode_dev(tag->u.core.rootdev); + } + return 0; +} + +__tagtable(ATAG_CORE, parse_tag_core); + +static int __init parse_tag_mem32(const struct tag *tag) +{ + return arm_add_memory(tag->u.mem.start, tag->u.mem.size); +} + +__tagtable(ATAG_MEM, parse_tag_mem32); + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) +static int __init parse_tag_videotext(const struct tag *tag) +{ + screen_info.orig_x = tag->u.videotext.x; + screen_info.orig_y = tag->u.videotext.y; + screen_info.orig_video_page = tag->u.videotext.video_page; + screen_info.orig_video_mode = tag->u.videotext.video_mode; + screen_info.orig_video_cols = tag->u.videotext.video_cols; + screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx; + screen_info.orig_video_lines = tag->u.videotext.video_lines; + screen_info.orig_video_isVGA = tag->u.videotext.video_isvga; + screen_info.orig_video_points = tag->u.videotext.video_points; + return 0; +} + +__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); +#endif + +#ifdef CONFIG_BLK_DEV_RAM +static int __init parse_tag_ramdisk(const struct tag *tag) +{ + extern int rd_size, rd_image_start, rd_prompt, rd_doload; + + rd_image_start = tag->u.ramdisk.start; + rd_doload = (tag->u.ramdisk.flags & 1) == 0; + rd_prompt = (tag->u.ramdisk.flags & 2) == 0; + + if (tag->u.ramdisk.size) + rd_size = tag->u.ramdisk.size; + + return 0; +} + +__tagtable(ATAG_RAMDISK, parse_tag_ramdisk); +#endif + +static int __init parse_tag_serialnr(const struct tag *tag) +{ + system_serial_low = tag->u.serialnr.low; + system_serial_high = tag->u.serialnr.high; + return 0; +} + +__tagtable(ATAG_SERIAL, parse_tag_serialnr); + +static int __init parse_tag_revision(const struct tag *tag) +{ + system_rev = tag->u.revision.rev; + return 0; +} + +__tagtable(ATAG_REVISION, parse_tag_revision); + +static int __init parse_tag_cmdline(const struct tag *tag) +{ +#if defined(CONFIG_CMDLINE_EXTEND) + strlcat(default_command_line, " ", COMMAND_LINE_SIZE); + strlcat(default_command_line, tag->u.cmdline.cmdline, + COMMAND_LINE_SIZE); +#elif defined(CONFIG_CMDLINE_FORCE) + pr_warning("Ignoring tag cmdline (using the default kernel command line)\n"); +#else + strlcpy(default_command_line, tag->u.cmdline.cmdline, + COMMAND_LINE_SIZE); +#endif + return 0; +} + +__tagtable(ATAG_CMDLINE, parse_tag_cmdline); + +/* + * Scan the tag table for this tag, and call its parse function. + * The tag table is built by the linker from all the __tagtable + * declarations. + */ +static int __init parse_tag(const struct tag *tag) +{ + extern struct tagtable __tagtable_begin, __tagtable_end; + struct tagtable *t; + + for (t = &__tagtable_begin; t < &__tagtable_end; t++) + if (tag->hdr.tag == t->tag) { + t->parse(tag); + break; + } + + return t < &__tagtable_end; +} + +/* + * Parse all tags in the list, checking both the global and architecture + * specific tag tables. + */ +static void __init parse_tags(const struct tag *t) +{ + for (; t->hdr.size; t = tag_next(t)) + if (!parse_tag(t)) + printk(KERN_WARNING + "Ignoring unrecognised tag 0x%08x\n", + t->hdr.tag); +} + +static void __init squash_mem_tags(struct tag *tag) +{ + for (; tag->hdr.size; tag = tag_next(tag)) + if (tag->hdr.tag == ATAG_MEM) + tag->hdr.tag = ATAG_NONE; +} + +struct machine_desc * __init setup_machine_tags(phys_addr_t __atags_pointer, + unsigned int machine_nr) +{ + struct tag *tags = (struct tag *)&default_tags; + struct machine_desc *mdesc = NULL, *p; + char *from = default_command_line; + + default_tags.mem.start = PHYS_OFFSET; + + /* + * locate machine in the list of supported machines. + */ + for_each_machine_desc(p) + if (machine_nr == p->nr) { + printk("Machine: %s\n", p->name); + mdesc = p; + break; + } + + if (!mdesc) { + early_print("\nError: unrecognized/unsupported machine ID" + " (r1 = 0x%08x).\n\n", machine_nr); + dump_machine_table(); /* does not return */ + } + + if (__atags_pointer) + tags = phys_to_virt(__atags_pointer); + else if (mdesc->atag_offset) + tags = (void *)(PAGE_OFFSET + mdesc->atag_offset); + +#if defined(CONFIG_DEPRECATED_PARAM_STRUCT) + /* + * If we have the old style parameters, convert them to + * a tag list. + */ + if (tags->hdr.tag != ATAG_CORE) + convert_to_tag_list(tags); +#endif + if (tags->hdr.tag != ATAG_CORE) { + early_print("Warning: Neither atags nor dtb found\n"); + tags = (struct tag *)&default_tags; + } + + if (mdesc->fixup) + mdesc->fixup(tags, &from, &meminfo); + + if (tags->hdr.tag == ATAG_CORE) { + if (meminfo.nr_banks != 0) + squash_mem_tags(tags); + save_atags(tags); + parse_tags(tags); + } + + /* parse_early_param needs a boot_command_line */ + strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); + + return mdesc; +} diff --git a/arch/arm/kernel/atags_proc.c b/arch/arm/kernel/atags_proc.c new file mode 100644 index 000000000000..42a1a1415fa6 --- /dev/null +++ b/arch/arm/kernel/atags_proc.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +struct buffer { + size_t size; + char data[]; +}; + +static int +read_buffer(char* page, char** start, off_t off, int count, + int* eof, void* data) +{ + struct buffer *buffer = (struct buffer *)data; + + if (off >= buffer->size) { + *eof = 1; + return 0; + } + + count = min((int) (buffer->size - off), count); + + memcpy(page, &buffer->data[off], count); + + return count; +} + +#define BOOT_PARAMS_SIZE 1536 +static char __initdata atags_copy[BOOT_PARAMS_SIZE]; + +void __init save_atags(const struct tag *tags) +{ + memcpy(atags_copy, tags, sizeof(atags_copy)); +} + +static int __init init_atags_procfs(void) +{ + /* + * This cannot go into save_atags() because kmalloc and proc don't work + * yet when it is called. + */ + struct proc_dir_entry *tags_entry; + struct tag *tag = (struct tag *)atags_copy; + struct buffer *b; + size_t size; + + if (tag->hdr.tag != ATAG_CORE) { + printk(KERN_INFO "No ATAGs?"); + return -EINVAL; + } + + for (; tag->hdr.size; tag = tag_next(tag)) + ; + + /* include the terminating ATAG_NONE */ + size = (char *)tag - atags_copy + sizeof(struct tag_header); + + WARN_ON(tag->hdr.tag != ATAG_NONE); + + b = kmalloc(sizeof(*b) + size, GFP_KERNEL); + if (!b) + goto nomem; + + b->size = size; + memcpy(b->data, atags_copy, size); + + tags_entry = create_proc_read_entry("atags", 0400, + NULL, read_buffer, b); + + if (!tags_entry) + goto nomem; + + return 0; + +nomem: + kfree(b); + printk(KERN_ERR "Exporting ATAGs: not enough memory\n"); + + return -ENOMEM; +} +arch_initcall(init_atags_procfs); diff --git a/arch/arm/kernel/compat.c b/arch/arm/kernel/compat.c deleted file mode 100644 index 925652318b8b..000000000000 --- a/arch/arm/kernel/compat.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * linux/arch/arm/kernel/compat.c - * - * Copyright (C) 2001 Russell King - * - * 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. - * - * We keep the old params compatibility cruft in one place (here) - * so we don't end up with lots of mess around other places. - * - * NOTE: - * The old struct param_struct is deprecated, but it will be kept in - * the kernel for 5 years from now (2001). This will allow boot loaders - * to convert to the new struct tag way. - */ -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "compat.h" - -/* - * Usage: - * - do not go blindly adding fields, add them at the end - * - when adding fields, don't rely on the address until - * a patch from me has been released - * - unused fields should be zero (for future expansion) - * - this structure is relatively short-lived - only - * guaranteed to contain useful data in setup_arch() - * - * This is the old deprecated way to pass parameters to the kernel - */ -struct param_struct { - union { - struct { - unsigned long page_size; /* 0 */ - unsigned long nr_pages; /* 4 */ - unsigned long ramdisk_size; /* 8 */ - unsigned long flags; /* 12 */ -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 - unsigned long rootdev; /* 16 */ - unsigned long video_num_cols; /* 20 */ - unsigned long video_num_rows; /* 24 */ - unsigned long video_x; /* 28 */ - unsigned long video_y; /* 32 */ - unsigned long memc_control_reg; /* 36 */ - unsigned char sounddefault; /* 40 */ - unsigned char adfsdrives; /* 41 */ - unsigned char bytes_per_char_h; /* 42 */ - unsigned char bytes_per_char_v; /* 43 */ - unsigned long pages_in_bank[4]; /* 44 */ - unsigned long pages_in_vram; /* 60 */ - unsigned long initrd_start; /* 64 */ - unsigned long initrd_size; /* 68 */ - unsigned long rd_start; /* 72 */ - unsigned long system_rev; /* 76 */ - unsigned long system_serial_low; /* 80 */ - unsigned long system_serial_high; /* 84 */ - unsigned long mem_fclk_21285; /* 88 */ - } s; - char unused[256]; - } u1; - union { - char paths[8][128]; - struct { - unsigned long magic; - char n[1024 - sizeof(unsigned long)]; - } s; - } u2; - char commandline[COMMAND_LINE_SIZE]; -}; - -static struct tag * __init memtag(struct tag *tag, unsigned long start, unsigned long size) -{ - tag = tag_next(tag); - tag->hdr.tag = ATAG_MEM; - tag->hdr.size = tag_size(tag_mem32); - tag->u.mem.size = size; - tag->u.mem.start = start; - - return tag; -} - -static void __init build_tag_list(struct param_struct *params, void *taglist) -{ - struct tag *tag = taglist; - - if (params->u1.s.page_size != PAGE_SIZE) { - printk(KERN_WARNING "Warning: bad configuration page, " - "trying to continue\n"); - return; - } - - printk(KERN_DEBUG "Converting old-style param struct to taglist\n"); - -#ifdef CONFIG_ARCH_NETWINDER - if (params->u1.s.nr_pages != 0x02000 && - params->u1.s.nr_pages != 0x04000 && - params->u1.s.nr_pages != 0x08000 && - params->u1.s.nr_pages != 0x10000) { - printk(KERN_WARNING "Warning: bad NeTTrom parameters " - "detected, using defaults\n"); - - params->u1.s.nr_pages = 0x1000; /* 16MB */ - params->u1.s.ramdisk_size = 0; - params->u1.s.flags = FLAG_READONLY; - params->u1.s.initrd_start = 0; - params->u1.s.initrd_size = 0; - params->u1.s.rd_start = 0; - } -#endif - - tag->hdr.tag = ATAG_CORE; - tag->hdr.size = tag_size(tag_core); - tag->u.core.flags = params->u1.s.flags & FLAG_READONLY; - tag->u.core.pagesize = params->u1.s.page_size; - tag->u.core.rootdev = params->u1.s.rootdev; - - tag = tag_next(tag); - tag->hdr.tag = ATAG_RAMDISK; - tag->hdr.size = tag_size(tag_ramdisk); - tag->u.ramdisk.flags = (params->u1.s.flags & FLAG_RDLOAD ? 1 : 0) | - (params->u1.s.flags & FLAG_RDPROMPT ? 2 : 0); - tag->u.ramdisk.size = params->u1.s.ramdisk_size; - tag->u.ramdisk.start = params->u1.s.rd_start; - - tag = tag_next(tag); - tag->hdr.tag = ATAG_INITRD; - tag->hdr.size = tag_size(tag_initrd); - tag->u.initrd.start = params->u1.s.initrd_start; - tag->u.initrd.size = params->u1.s.initrd_size; - - tag = tag_next(tag); - tag->hdr.tag = ATAG_SERIAL; - tag->hdr.size = tag_size(tag_serialnr); - tag->u.serialnr.low = params->u1.s.system_serial_low; - tag->u.serialnr.high = params->u1.s.system_serial_high; - - tag = tag_next(tag); - tag->hdr.tag = ATAG_REVISION; - tag->hdr.size = tag_size(tag_revision); - tag->u.revision.rev = params->u1.s.system_rev; - -#ifdef CONFIG_ARCH_ACORN - if (machine_is_riscpc()) { - int i; - for (i = 0; i < 4; i++) - tag = memtag(tag, PHYS_OFFSET + (i << 26), - params->u1.s.pages_in_bank[i] * PAGE_SIZE); - } else -#endif - tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE); - -#ifdef CONFIG_FOOTBRIDGE - if (params->u1.s.mem_fclk_21285) { - tag = tag_next(tag); - tag->hdr.tag = ATAG_MEMCLK; - tag->hdr.size = tag_size(tag_memclk); - tag->u.memclk.fmemclk = params->u1.s.mem_fclk_21285; - } -#endif - -#ifdef CONFIG_ARCH_EBSA285 - if (machine_is_ebsa285()) { - tag = tag_next(tag); - tag->hdr.tag = ATAG_VIDEOTEXT; - tag->hdr.size = tag_size(tag_videotext); - tag->u.videotext.x = params->u1.s.video_x; - tag->u.videotext.y = params->u1.s.video_y; - tag->u.videotext.video_page = 0; - tag->u.videotext.video_mode = 0; - tag->u.videotext.video_cols = params->u1.s.video_num_cols; - tag->u.videotext.video_ega_bx = 0; - tag->u.videotext.video_lines = params->u1.s.video_num_rows; - tag->u.videotext.video_isvga = 1; - tag->u.videotext.video_points = 8; - } -#endif - -#ifdef CONFIG_ARCH_ACORN - tag = tag_next(tag); - tag->hdr.tag = ATAG_ACORN; - tag->hdr.size = tag_size(tag_acorn); - tag->u.acorn.memc_control_reg = params->u1.s.memc_control_reg; - tag->u.acorn.vram_pages = params->u1.s.pages_in_vram; - tag->u.acorn.sounddefault = params->u1.s.sounddefault; - tag->u.acorn.adfsdrives = params->u1.s.adfsdrives; -#endif - - tag = tag_next(tag); - tag->hdr.tag = ATAG_CMDLINE; - tag->hdr.size = (strlen(params->commandline) + 3 + - sizeof(struct tag_header)) >> 2; - strcpy(tag->u.cmdline.cmdline, params->commandline); - - tag = tag_next(tag); - tag->hdr.tag = ATAG_NONE; - tag->hdr.size = 0; - - memmove(params, taglist, ((int)tag) - ((int)taglist) + - sizeof(struct tag_header)); -} - -void __init convert_to_tag_list(struct tag *tags) -{ - struct param_struct *params = (struct param_struct *)tags; - build_tag_list(params, ¶ms->u2); -} diff --git a/arch/arm/kernel/compat.h b/arch/arm/kernel/compat.h deleted file mode 100644 index 39264ab1b9c6..000000000000 --- a/arch/arm/kernel/compat.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * linux/arch/arm/kernel/compat.h - * - * Copyright (C) 2001 Russell King - * - * 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. -*/ - -extern void convert_to_tag_list(struct tag *tags); diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index a81dcecc7343..0785472460a8 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -21,11 +21,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -56,15 +54,9 @@ #include #include -#if defined(CONFIG_DEPRECATED_PARAM_STRUCT) -#include "compat.h" -#endif #include "atags.h" #include "tcm.h" -#ifndef MEM_SIZE -#define MEM_SIZE (16*1024*1024) -#endif #if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE) char fpe_type[8]; @@ -145,7 +137,6 @@ static const char *machine_name; static char __initdata cmd_line[COMMAND_LINE_SIZE]; struct machine_desc *machine_desc __initdata; -static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } }; #define ENDIANNESS ((char)endian_test.l) @@ -583,21 +574,6 @@ static int __init early_mem(char *p) } early_param("mem", early_mem); -static void __init -setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz) -{ -#ifdef CONFIG_BLK_DEV_RAM - extern int rd_size, rd_image_start, rd_prompt, rd_doload; - - rd_image_start = image_start; - rd_prompt = prompt; - rd_doload = doload; - - if (rd_sz) - rd_size = rd_sz; -#endif -} - static void __init request_standard_resources(struct machine_desc *mdesc) { struct memblock_region *region; @@ -643,35 +619,6 @@ static void __init request_standard_resources(struct machine_desc *mdesc) request_resource(&ioport_resource, &lp2); } -/* - * Tag parsing. - * - * This is the new way of passing data to the kernel at boot time. Rather - * than passing a fixed inflexible structure to the kernel, we pass a list - * of variable-sized tags to the kernel. The first tag must be a ATAG_CORE - * tag for the list to be recognised (to distinguish the tagged list from - * a param_struct). The list is terminated with a zero-length tag (this tag - * is not parsed in any way). - */ -static int __init parse_tag_core(const struct tag *tag) -{ - if (tag->hdr.size > 2) { - if ((tag->u.core.flags & 1) == 0) - root_mountflags &= ~MS_RDONLY; - ROOT_DEV = old_decode_dev(tag->u.core.rootdev); - } - return 0; -} - -__tagtable(ATAG_CORE, parse_tag_core); - -static int __init parse_tag_mem32(const struct tag *tag) -{ - return arm_add_memory(tag->u.mem.start, tag->u.mem.size); -} - -__tagtable(ATAG_MEM, parse_tag_mem32); - #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) struct screen_info screen_info = { .orig_video_lines = 30, @@ -681,117 +628,8 @@ struct screen_info screen_info = { .orig_video_isVGA = 1, .orig_video_points = 8 }; - -static int __init parse_tag_videotext(const struct tag *tag) -{ - screen_info.orig_x = tag->u.videotext.x; - screen_info.orig_y = tag->u.videotext.y; - screen_info.orig_video_page = tag->u.videotext.video_page; - screen_info.orig_video_mode = tag->u.videotext.video_mode; - screen_info.orig_video_cols = tag->u.videotext.video_cols; - screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx; - screen_info.orig_video_lines = tag->u.videotext.video_lines; - screen_info.orig_video_isVGA = tag->u.videotext.video_isvga; - screen_info.orig_video_points = tag->u.videotext.video_points; - return 0; -} - -__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); #endif -static int __init parse_tag_ramdisk(const struct tag *tag) -{ - setup_ramdisk((tag->u.ramdisk.flags & 1) == 0, - (tag->u.ramdisk.flags & 2) == 0, - tag->u.ramdisk.start, tag->u.ramdisk.size); - return 0; -} - -__tagtable(ATAG_RAMDISK, parse_tag_ramdisk); - -static int __init parse_tag_serialnr(const struct tag *tag) -{ - system_serial_low = tag->u.serialnr.low; - system_serial_high = tag->u.serialnr.high; - return 0; -} - -__tagtable(ATAG_SERIAL, parse_tag_serialnr); - -static int __init parse_tag_revision(const struct tag *tag) -{ - system_rev = tag->u.revision.rev; - return 0; -} - -__tagtable(ATAG_REVISION, parse_tag_revision); - -static int __init parse_tag_cmdline(const struct tag *tag) -{ -#if defined(CONFIG_CMDLINE_EXTEND) - strlcat(default_command_line, " ", COMMAND_LINE_SIZE); - strlcat(default_command_line, tag->u.cmdline.cmdline, - COMMAND_LINE_SIZE); -#elif defined(CONFIG_CMDLINE_FORCE) - pr_warning("Ignoring tag cmdline (using the default kernel command line)\n"); -#else - strlcpy(default_command_line, tag->u.cmdline.cmdline, - COMMAND_LINE_SIZE); -#endif - return 0; -} - -__tagtable(ATAG_CMDLINE, parse_tag_cmdline); - -/* - * Scan the tag table for this tag, and call its parse function. - * The tag table is built by the linker from all the __tagtable - * declarations. - */ -static int __init parse_tag(const struct tag *tag) -{ - extern struct tagtable __tagtable_begin, __tagtable_end; - struct tagtable *t; - - for (t = &__tagtable_begin; t < &__tagtable_end; t++) - if (tag->hdr.tag == t->tag) { - t->parse(tag); - break; - } - - return t < &__tagtable_end; -} - -/* - * Parse all tags in the list, checking both the global and architecture - * specific tag tables. - */ -static void __init parse_tags(const struct tag *t) -{ - for (; t->hdr.size; t = tag_next(t)) - if (!parse_tag(t)) - printk(KERN_WARNING - "Ignoring unrecognised tag 0x%08x\n", - t->hdr.tag); -} - -/* - * This holds our defaults. - */ -static struct init_tags { - struct tag_header hdr1; - struct tag_core core; - struct tag_header hdr2; - struct tag_mem32 mem; - struct tag_header hdr3; -} init_tags __initdata = { - { tag_size(tag_core), ATAG_CORE }, - { 1, PAGE_SIZE, 0xff }, - { tag_size(tag_mem32), ATAG_MEM }, - { MEM_SIZE }, - { 0, ATAG_NONE } -}; - static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ @@ -858,78 +696,6 @@ static void __init reserve_crashkernel(void) static inline void reserve_crashkernel(void) {} #endif /* CONFIG_KEXEC */ -static void __init squash_mem_tags(struct tag *tag) -{ - for (; tag->hdr.size; tag = tag_next(tag)) - if (tag->hdr.tag == ATAG_MEM) - tag->hdr.tag = ATAG_NONE; -} - -static struct machine_desc * __init setup_machine_tags(unsigned int nr) -{ - struct tag *tags = (struct tag *)&init_tags; - struct machine_desc *mdesc = NULL, *p; - char *from = default_command_line; - - init_tags.mem.start = PHYS_OFFSET; - - /* - * locate machine in the list of supported machines. - */ - for_each_machine_desc(p) - if (nr == p->nr) { - printk("Machine: %s\n", p->name); - mdesc = p; - break; - } - - if (!mdesc) { - early_print("\nError: unrecognized/unsupported machine ID" - " (r1 = 0x%08x).\n\n", nr); - dump_machine_table(); /* does not return */ - } - - if (__atags_pointer) - tags = phys_to_virt(__atags_pointer); - else if (mdesc->atag_offset) - tags = (void *)(PAGE_OFFSET + mdesc->atag_offset); - -#if defined(CONFIG_DEPRECATED_PARAM_STRUCT) - /* - * If we have the old style parameters, convert them to - * a tag list. - */ - if (tags->hdr.tag != ATAG_CORE) - convert_to_tag_list(tags); -#endif - - if (tags->hdr.tag != ATAG_CORE) { -#if defined(CONFIG_OF) - /* - * If CONFIG_OF is set, then assume this is a reasonably - * modern system that should pass boot parameters - */ - early_print("Warning: Neither atags nor dtb found\n"); -#endif - tags = (struct tag *)&init_tags; - } - - if (mdesc->fixup) - mdesc->fixup(tags, &from, &meminfo); - - if (tags->hdr.tag == ATAG_CORE) { - if (meminfo.nr_banks != 0) - squash_mem_tags(tags); - save_atags(tags); - parse_tags(tags); - } - - /* parse_early_param needs a boot_command_line */ - strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); - - return mdesc; -} - static int __init meminfo_cmp(const void *_a, const void *_b) { const struct membank *a = _a, *b = _b; @@ -944,7 +710,7 @@ void __init setup_arch(char **cmdline_p) setup_processor(); mdesc = setup_machine_fdt(__atags_pointer); if (!mdesc) - mdesc = setup_machine_tags(machine_arch_type); + mdesc = setup_machine_tags(__atags_pointer, machine_arch_type); machine_desc = mdesc; machine_name = mdesc->name; -- cgit v1.2.3 From bd51e2f595580fb64bd8494badef746c4d626b25 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 1 Sep 2012 03:03:25 +0100 Subject: ARM: 7506/1: allow for ATAGS to be configured out when DT support is selected Now that ATAGS support is well contained, we can easily remove it from the kernel build if so desired. It has to explicitly be disabled, and only when DT support is selected. Note: disabling kernel ATAGS support does not prevent the usage of CONFIG_ARM_ATAG_DTB_COMPAT. Signed-off-by: Nicolas Pitre Tested-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/Kconfig | 26 +++++++++++++++++++------- arch/arm/kernel/Makefile | 5 +++-- arch/arm/kernel/atags.h | 11 +++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c5f9ae5dbd1a..5c7302e208ce 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1891,12 +1891,6 @@ config CC_STACKPROTECTOR neutralized via a kernel panic. This feature requires gcc version 4.2 or above. -config DEPRECATED_PARAM_STRUCT - bool "Provide old way to pass kernel parameters" - help - This was deprecated in 2001 and announced to live on for 5 years. - Some old boot loaders still use this way. - endmenu menu "Boot options" @@ -1909,6 +1903,23 @@ config USE_OF help Include support for flattened device tree machine descriptions. +config ATAGS + bool "Support for the traditional ATAGS boot data passing" if USE_OF + default y + help + This is the traditional way of passing data to the kernel at boot + time. If you are solely relying on the flattened device tree (or + the ARM_ATAG_DTB_COMPAT option) then you may unselect this option + to remove ATAGS support from your kernel binary. If unsure, + leave this to y. + +config DEPRECATED_PARAM_STRUCT + bool "Provide old way to pass kernel parameters" + depends on ATAGS + help + This was deprecated in 2001 and announced to live on for 5 years. + Some old boot loaders still use this way. + # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. config ZBOOT_ROM_TEXT @@ -2035,6 +2046,7 @@ config CMDLINE choice prompt "Kernel command line type" if CMDLINE != "" default CMDLINE_FROM_BOOTLOADER + depends on ATAGS config CMDLINE_FROM_BOOTLOADER bool "Use bootloader kernel arguments if available" @@ -2104,7 +2116,7 @@ config KEXEC config ATAGS_PROC bool "Export atags in procfs" - depends on KEXEC + depends on ATAGS && KEXEC default y help Should the atags used to boot the kernel be exported in an "atags" diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 086c9d3bc360..79e346a5d78b 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -15,10 +15,12 @@ CFLAGS_REMOVE_return_address.o = -pg # Object file lists. -obj-y := atags_parse.o elf.o entry-armv.o entry-common.o irq.o opcodes.o \ +obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ process.o ptrace.o return_address.o sched_clock.o \ setup.o signal.o stacktrace.o sys_arm.o time.o traps.o +obj-$(CONFIG_ATAGS) += atags_parse.o +obj-$(CONFIG_ATAGS_PROC) += atags_proc.o obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += atags_compat.o obj-$(CONFIG_LEDS) += leds.o @@ -52,7 +54,6 @@ test-kprobes-objs += kprobes-test-thumb.o else test-kprobes-objs += kprobes-test-arm.o endif -obj-$(CONFIG_ATAGS_PROC) += atags_proc.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h index a888fdd381b5..9edc9692332d 100644 --- a/arch/arm/kernel/atags.h +++ b/arch/arm/kernel/atags.h @@ -5,4 +5,15 @@ static inline void save_atags(struct tag *tags) { } #endif void convert_to_tag_list(struct tag *tags); + +#ifdef CONFIG_ATAGS struct machine_desc *setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr); +#else +static inline struct machine_desc * +setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr) +{ + early_print("no ATAGS support: can't continue\n"); + while (true); + unreachable(); +} +#endif -- cgit v1.2.3 From ec971ea5f2426a0bf9d5cca9a103743918c12978 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 5 Sep 2012 01:08:59 +0200 Subject: ARM: add cpufreq transiton notifier to adjust loops_per_jiffy for smp If CONFIG_SMP, cpufreq skips loops_per_jiffy update, because different arch has different per-cpu loops_per_jiffy definition. Signed-off-by: Richard Zhao Acked-by: Russell King Acked-by: Santosh Shilimkar Signed-off-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- arch/arm/kernel/smp.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index ebd8ad274d76..8e03567c9583 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -584,3 +585,56 @@ int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; } + +#ifdef CONFIG_CPU_FREQ + +static DEFINE_PER_CPU(unsigned long, l_p_j_ref); +static DEFINE_PER_CPU(unsigned long, l_p_j_ref_freq); +static unsigned long global_l_p_j_ref; +static unsigned long global_l_p_j_ref_freq; + +static int cpufreq_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + int cpu = freq->cpu; + + if (freq->flags & CPUFREQ_CONST_LOOPS) + return NOTIFY_OK; + + if (!per_cpu(l_p_j_ref, cpu)) { + per_cpu(l_p_j_ref, cpu) = + per_cpu(cpu_data, cpu).loops_per_jiffy; + per_cpu(l_p_j_ref_freq, cpu) = freq->old; + if (!global_l_p_j_ref) { + global_l_p_j_ref = loops_per_jiffy; + global_l_p_j_ref_freq = freq->old; + } + } + + if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || + (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || + (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { + loops_per_jiffy = cpufreq_scale(global_l_p_j_ref, + global_l_p_j_ref_freq, + freq->new); + per_cpu(cpu_data, cpu).loops_per_jiffy = + cpufreq_scale(per_cpu(l_p_j_ref, cpu), + per_cpu(l_p_j_ref_freq, cpu), + freq->new); + } + return NOTIFY_OK; +} + +static struct notifier_block cpufreq_notifier = { + .notifier_call = cpufreq_callback, +}; + +static int __init register_cpufreq_notifier(void) +{ + return cpufreq_register_notifier(&cpufreq_notifier, + CPUFREQ_TRANSITION_NOTIFIER); +} +core_initcall(register_cpufreq_notifier); + +#endif -- cgit v1.2.3 From abcee5fb0dfbb248d883a2f6bdb4820abe3ac524 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 8 Sep 2011 09:06:10 +0100 Subject: ARM: SoC: add per-platform SMP operations This adds a 'struct smp_operations' to abstract the CPU initialization and hot plugging functions on SMP systems, which otherwise conflict in a multiplatform kernel. This also helps shmobile and potentially others that have more than one method to do these. To allow the kernel to continue building, the platform hooks are defined as weak symbols which are overrided by the platform code. Once all platforms are converted, the "weak" attribute will be removed and the function made static. Unlike the original version from Marc, this new version from Arnd does not use a generalized abstraction for per-soc data structures but only tries to solve the problem for the SMP operations. This way, we can collapse the previous four data structures into a single struct, which is less systematic but also easier to follow as a causal reader. Signed-off-by: Marc Zyngier Acked-by: Nicolas Pitre Signed-off-by: Arnd Bergmann --- arch/arm/include/asm/mach/arch.h | 7 +++++ arch/arm/include/asm/smp.h | 33 +++++++++++++++++++++ arch/arm/kernel/setup.c | 4 ++- arch/arm/kernel/smp.c | 62 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 104 insertions(+), 2 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h index 0b1c94b8c652..917d4fcfd9b4 100644 --- a/arch/arm/include/asm/mach/arch.h +++ b/arch/arm/include/asm/mach/arch.h @@ -14,6 +14,12 @@ struct tag; struct meminfo; struct sys_timer; struct pt_regs; +struct smp_operations; +#ifdef CONFIG_SMP +#define smp_ops(ops) (&(ops)) +#else +#define smp_ops(ops) (struct smp_operations *)NULL +#endif struct machine_desc { unsigned int nr; /* architecture number */ @@ -35,6 +41,7 @@ struct machine_desc { unsigned char reserve_lp1 :1; /* never has lp1 */ unsigned char reserve_lp2 :1; /* never has lp2 */ char restart_mode; /* default restart mode */ + struct smp_operations *smp; /* SMP operations */ void (*fixup)(struct tag *, char **, struct meminfo *); void (*reserve)(void);/* reserve mem blocks */ diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index ae29293270a3..f79a9f51e322 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -93,4 +93,37 @@ extern void platform_cpu_enable(unsigned int cpu); extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); +struct smp_operations { +#ifdef CONFIG_SMP + /* + * Setup the set of possible CPUs (via set_cpu_possible) + */ + void (*smp_init_cpus)(void); + /* + * Initialize cpu_possible map, and enable coherency + */ + void (*smp_prepare_cpus)(unsigned int max_cpus); + + /* + * Perform platform specific initialisation of the specified CPU. + */ + void (*smp_secondary_init)(unsigned int cpu); + /* + * Boot a secondary CPU, and assign it the specified idle task. + * This also gives us the initial stack to use for this CPU. + */ + int (*smp_boot_secondary)(unsigned int cpu, struct task_struct *idle); +#ifdef CONFIG_HOTPLUG_CPU + int (*cpu_kill)(unsigned int cpu); + void (*cpu_die)(unsigned int cpu); + int (*cpu_disable)(unsigned int cpu); +#endif +#endif +}; + +/* + * set platform specific SMP operations + */ +extern void smp_set_ops(struct smp_operations *); + #endif /* ifndef __ASM_ARM_SMP_H */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index a81dcecc7343..725f9f2a9541 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -977,8 +977,10 @@ void __init setup_arch(char **cmdline_p) unflatten_device_tree(); #ifdef CONFIG_SMP - if (is_smp()) + if (is_smp()) { + smp_set_ops(mdesc->smp); smp_init_cpus(); + } #endif reserve_crashkernel(); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index ebd8ad274d76..d9241885521b 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include #include #include #include @@ -42,6 +42,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -60,6 +61,14 @@ enum ipi_msg_type { static DECLARE_COMPLETION(cpu_running); +static struct smp_operations smp_ops; + +void __init smp_set_ops(struct smp_operations *ops) +{ + if (ops) + smp_ops = *ops; +}; + int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) { int ret; @@ -100,9 +109,60 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) return ret; } +/* platform specific SMP operations */ +void __attribute__((weak)) __init smp_init_cpus(void) +{ + if (smp_ops.smp_init_cpus) + smp_ops.smp_init_cpus(); +} + +void __attribute__((weak)) __init platform_smp_prepare_cpus(unsigned int max_cpus) +{ + if (smp_ops.smp_prepare_cpus) + smp_ops.smp_prepare_cpus(max_cpus); +} + +void __attribute__((weak)) __cpuinit platform_secondary_init(unsigned int cpu) +{ + if (smp_ops.smp_secondary_init) + smp_ops.smp_secondary_init(cpu); +} + +int __attribute__((weak)) __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + if (smp_ops.smp_boot_secondary) + return smp_ops.smp_boot_secondary(cpu, idle); + return -ENOSYS; +} + #ifdef CONFIG_HOTPLUG_CPU static void percpu_timer_stop(void); +int __attribute__((weak)) platform_cpu_kill(unsigned int cpu) +{ + if (smp_ops.cpu_kill) + return smp_ops.cpu_kill(cpu); + return 1; +} + +void __attribute__((weak)) platform_cpu_die(unsigned int cpu) +{ + if (smp_ops.cpu_die) + smp_ops.cpu_die(cpu); +} + +int __attribute__((weak)) platform_cpu_disable(unsigned int cpu) +{ + if (smp_ops.cpu_disable) + return smp_ops.cpu_disable(cpu); + + /* + * By default, allow disabling all CPUs except the first one, + * since this is special on a lot of platforms, e.g. because + * of clock tick interrupts. + */ + return cpu == 0 ? -EPERM : 0; +} /* * __cpu_disable runs on the processor to be shutdown. */ -- cgit v1.2.3 From ac6c7998712d55bd15aa2dd5ae85f5988c0cb526 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 27 Sep 2011 14:48:23 +0100 Subject: ARM: smp: Make SMP operations mandatory Now that all SMP platforms have been converted to use struct smp_operations, remove the "weak" attribute from the hooks in smp.c, and make the functions static wherever possible. Signed-off-by: Marc Zyngier Acked-by: Nicolas Pitre Signed-off-by: Arnd Bergmann --- arch/arm/include/asm/smp.h | 14 -------------- arch/arm/kernel/smp.c | 18 +++++++++--------- 2 files changed, 9 insertions(+), 23 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index f79a9f51e322..3a8cfee26c91 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -60,15 +60,6 @@ extern int boot_secondary(unsigned int cpu, struct task_struct *); */ asmlinkage void secondary_start_kernel(void); -/* - * Perform platform specific initialisation of the specified CPU. - */ -extern void platform_secondary_init(unsigned int cpu); - -/* - * Initialize cpu_possible map, and enable coherency - */ -extern void platform_smp_prepare_cpus(unsigned int); /* * Initial data for bringing up a secondary CPU. @@ -81,15 +72,10 @@ struct secondary_data { extern struct secondary_data secondary_data; extern int __cpu_disable(void); -extern int platform_cpu_disable(unsigned int cpu); extern void __cpu_die(unsigned int cpu); extern void cpu_die(void); -extern void platform_cpu_die(unsigned int cpu); -extern int platform_cpu_kill(unsigned int cpu); -extern void platform_cpu_enable(unsigned int cpu); - extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index d9241885521b..ac3ce029afb8 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -110,25 +110,25 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) } /* platform specific SMP operations */ -void __attribute__((weak)) __init smp_init_cpus(void) +void __init smp_init_cpus(void) { if (smp_ops.smp_init_cpus) smp_ops.smp_init_cpus(); } -void __attribute__((weak)) __init platform_smp_prepare_cpus(unsigned int max_cpus) +static void __init platform_smp_prepare_cpus(unsigned int max_cpus) { if (smp_ops.smp_prepare_cpus) smp_ops.smp_prepare_cpus(max_cpus); } -void __attribute__((weak)) __cpuinit platform_secondary_init(unsigned int cpu) +static void __cpuinit platform_secondary_init(unsigned int cpu) { if (smp_ops.smp_secondary_init) smp_ops.smp_secondary_init(cpu); } -int __attribute__((weak)) __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { if (smp_ops.smp_boot_secondary) return smp_ops.smp_boot_secondary(cpu, idle); @@ -138,20 +138,20 @@ int __attribute__((weak)) __cpuinit boot_secondary(unsigned int cpu, struct task #ifdef CONFIG_HOTPLUG_CPU static void percpu_timer_stop(void); -int __attribute__((weak)) platform_cpu_kill(unsigned int cpu) +static int platform_cpu_kill(unsigned int cpu) { if (smp_ops.cpu_kill) return smp_ops.cpu_kill(cpu); return 1; } -void __attribute__((weak)) platform_cpu_die(unsigned int cpu) +static void platform_cpu_die(unsigned int cpu) { if (smp_ops.cpu_die) smp_ops.cpu_die(cpu); } -int __attribute__((weak)) platform_cpu_disable(unsigned int cpu) +static int platform_cpu_disable(unsigned int cpu) { if (smp_ops.cpu_disable) return smp_ops.cpu_disable(cpu); @@ -166,7 +166,7 @@ int __attribute__((weak)) platform_cpu_disable(unsigned int cpu) /* * __cpu_disable runs on the processor to be shutdown. */ -int __cpu_disable(void) +int __cpuinit __cpu_disable(void) { unsigned int cpu = smp_processor_id(); int ret; @@ -209,7 +209,7 @@ static DECLARE_COMPLETION(cpu_died); * called on the thread which is asking for a CPU to be shutdown - * waits until shutdown has completed, or it is timed out. */ -void __cpu_die(unsigned int cpu) +void __cpuinit __cpu_die(unsigned int cpu) { if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) { pr_err("CPU%u: cpu didn't die\n", cpu); -- cgit v1.2.3 From 28e8e29c616f947348cc66bea684d0035c76021a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 12 Jun 2012 11:16:27 +0100 Subject: ARM: consolidate pen_release instead of having per platform definitions Almost each SMP platform defines pen_release to manage booting secondary CPUs. This of course clashes with the single zImage effort. Add the pen_release definition to the ARM SMP code, and remove all others. This should only be used by platforms which lack any kind of CPU power management... Reported-by: Arnd Bergmann Signed-off-by: Marc Zyngier Acked-by: Nicolas Pitre Signed-off-by: Arnd Bergmann --- arch/arm/include/asm/smp.h | 1 + arch/arm/kernel/smp.c | 6 ++++++ arch/arm/mach-exynos/hotplug.c | 2 -- arch/arm/mach-exynos/platsmp.c | 7 ------- arch/arm/mach-msm/hotplug.c | 2 -- arch/arm/mach-msm/platsmp.c | 5 ----- arch/arm/mach-realview/hotplug.c | 2 -- arch/arm/mach-spear13xx/hotplug.c | 2 -- arch/arm/mach-spear13xx/platsmp.c | 5 ----- arch/arm/mach-ux500/hotplug.c | 2 -- arch/arm/mach-ux500/platsmp.c | 6 ------ arch/arm/mach-vexpress/hotplug.c | 2 -- arch/arm/plat-versatile/platsmp.c | 6 ------ 13 files changed, 7 insertions(+), 41 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 3a8cfee26c91..2e3be16c6766 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -70,6 +70,7 @@ struct secondary_data { void *stack; }; extern struct secondary_data secondary_data; +extern volatile int pen_release; extern int __cpu_disable(void); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index ac3ce029afb8..aa4ffe6e5ecf 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -51,6 +51,12 @@ */ struct secondary_data secondary_data; +/* + * control for which core is the next to come out of the secondary + * boot "holding pen" + */ +volatile int __cpuinitdata pen_release = -1; + enum ipi_msg_type { IPI_TIMER = 2, IPI_RESCHEDULE, diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c index edccc36dd692..f4d7dd20cdac 100644 --- a/arch/arm/mach-exynos/hotplug.c +++ b/arch/arm/mach-exynos/hotplug.c @@ -23,8 +23,6 @@ #include "common.h" -extern volatile int pen_release; - static inline void cpu_enter_lowpower(void) { unsigned int v; diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 3fad8ad3f92c..8d57e4223bdb 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -39,13 +39,6 @@ extern void exynos4_secondary_startup(void); #define CPU1_BOOT_REG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ S5P_INFORM5 : S5P_VA_SYSRAM) -/* - * control for which core is the next to come out of the secondary - * boot "holding pen" - */ - -volatile int __cpuinitdata pen_release = -1; - /* * Write pen_release in a way that is guaranteed to be visible to all * observers, irrespective of whether they're taking part in coherency diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c index fedaa25b2935..002ac1e47235 100644 --- a/arch/arm/mach-msm/hotplug.c +++ b/arch/arm/mach-msm/hotplug.c @@ -15,8 +15,6 @@ #include "core.h" -extern volatile int pen_release; - static inline void cpu_enter_lowpower(void) { /* Just flush the cache. Changing the coherency is not yet diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c index ba3c4b0d523d..57af32ef75ed 100644 --- a/arch/arm/mach-msm/platsmp.c +++ b/arch/arm/mach-msm/platsmp.c @@ -35,11 +35,6 @@ #define GIC_PPI_EDGE_MASK 0xFFFFD7FF extern void msm_secondary_startup(void); -/* - * control for which core is the next to come out of the secondary - * boot "holding pen". - */ -volatile int pen_release = -1; static DEFINE_SPINLOCK(boot_lock); diff --git a/arch/arm/mach-realview/hotplug.c b/arch/arm/mach-realview/hotplug.c index fef4f4495225..53818e5cd3ad 100644 --- a/arch/arm/mach-realview/hotplug.c +++ b/arch/arm/mach-realview/hotplug.c @@ -16,8 +16,6 @@ #include #include -extern volatile int pen_release; - static inline void cpu_enter_lowpower(void) { unsigned int v; diff --git a/arch/arm/mach-spear13xx/hotplug.c b/arch/arm/mach-spear13xx/hotplug.c index 485fd4367bb1..a7d2dd11a4f2 100644 --- a/arch/arm/mach-spear13xx/hotplug.c +++ b/arch/arm/mach-spear13xx/hotplug.c @@ -17,8 +17,6 @@ #include #include -extern volatile int pen_release; - static inline void cpu_enter_lowpower(void) { unsigned int v; diff --git a/arch/arm/mach-spear13xx/platsmp.c b/arch/arm/mach-spear13xx/platsmp.c index 806343c7b5d8..2eaa3fa7b432 100644 --- a/arch/arm/mach-spear13xx/platsmp.c +++ b/arch/arm/mach-spear13xx/platsmp.c @@ -21,11 +21,6 @@ #include #include -/* - * control for which core is the next to come out of the secondary - * boot "holding pen" - */ -volatile int __cpuinitdata pen_release = -1; static DEFINE_SPINLOCK(boot_lock); static void __iomem *scu_base = IOMEM(VA_SCU_BASE); diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c index b8e4d9ed62d8..2f6af259015d 100644 --- a/arch/arm/mach-ux500/hotplug.c +++ b/arch/arm/mach-ux500/hotplug.c @@ -17,8 +17,6 @@ #include -extern volatile int pen_release; - /* * platform-specific code to shutdown a CPU * diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index b6f4e0e787ec..d60873ee38bf 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -27,12 +27,6 @@ /* This is called from headsmp.S to wakeup the secondary core */ extern void u8500_secondary_startup(void); -/* - * control for which core is the next to come out of the secondary - * boot "holding pen" - */ -volatile int pen_release = -1; - /* * Write pen_release in a way that is guaranteed to be visible to all * observers, irrespective of whether they're taking part in coherency diff --git a/arch/arm/mach-vexpress/hotplug.c b/arch/arm/mach-vexpress/hotplug.c index 734423a39e7c..a141b98d84fe 100644 --- a/arch/arm/mach-vexpress/hotplug.c +++ b/arch/arm/mach-vexpress/hotplug.c @@ -16,8 +16,6 @@ #include #include -extern volatile int pen_release; - static inline void cpu_enter_lowpower(void) { unsigned int v; diff --git a/arch/arm/plat-versatile/platsmp.c b/arch/arm/plat-versatile/platsmp.c index 39e60ac4fe65..04ca4937d8ca 100644 --- a/arch/arm/plat-versatile/platsmp.c +++ b/arch/arm/plat-versatile/platsmp.c @@ -19,12 +19,6 @@ #include #include -/* - * control for which core is the next to come out of the secondary - * boot "holding pen" - */ -volatile int __cpuinitdata pen_release = -1; - /* * Write pen_release in a way that is guaranteed to be visible to all * observers, irrespective of whether they're taking part in coherency -- cgit v1.2.3 From 91a9fec022f5d98835e27d5307d487da3798040c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 31 Aug 2012 00:03:46 -0500 Subject: ARM: move debug macros to common location Based on suggestion by Russell King, create a common location for debug macros and select the included debug macro file using config option. Signed-off-by: Rob Herring Cc: Russell King --- arch/arm/Kconfig.debug | 5 +++ arch/arm/include/debug/icedcc.S | 90 +++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/debug.S | 87 ++------------------------------------- arch/arm/kernel/head.S | 4 +- 4 files changed, 100 insertions(+), 86 deletions(-) create mode 100644 arch/arm/include/debug/icedcc.S (limited to 'arch/arm/kernel') diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index f15f82bf3a50..4f9760bd234e 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -373,6 +373,11 @@ choice endchoice +config DEBUG_LL_INCLUDE + string + default "debug/icedcc.S" if DEBUG_ICEDCC + default "mach/debug-macro.S" + config EARLY_PRINTK bool "Early printk" depends on DEBUG_LL diff --git a/arch/arm/include/debug/icedcc.S b/arch/arm/include/debug/icedcc.S new file mode 100644 index 000000000000..43afcb021fa3 --- /dev/null +++ b/arch/arm/include/debug/icedcc.S @@ -0,0 +1,90 @@ +/* + * arch/arm/include/debug/icedcc.S + * + * Copyright (C) 1994-1999 Russell King + * + * 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. + * + */ + + @@ debug using ARM EmbeddedICE DCC channel + + .macro addruart, rp, rv, tmp + .endm + +#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7) + + .macro senduart, rd, rx + mcr p14, 0, \rd, c0, c5, 0 + .endm + + .macro busyuart, rd, rx +1001: + mrc p14, 0, \rx, c0, c1, 0 + tst \rx, #0x20000000 + beq 1001b + .endm + + .macro waituart, rd, rx + mov \rd, #0x2000000 +1001: + subs \rd, \rd, #1 + bmi 1002f + mrc p14, 0, \rx, c0, c1, 0 + tst \rx, #0x20000000 + bne 1001b +1002: + .endm + +#elif defined(CONFIG_CPU_XSCALE) + + .macro senduart, rd, rx + mcr p14, 0, \rd, c8, c0, 0 + .endm + + .macro busyuart, rd, rx +1001: + mrc p14, 0, \rx, c14, c0, 0 + tst \rx, #0x10000000 + beq 1001b + .endm + + .macro waituart, rd, rx + mov \rd, #0x10000000 +1001: + subs \rd, \rd, #1 + bmi 1002f + mrc p14, 0, \rx, c14, c0, 0 + tst \rx, #0x10000000 + bne 1001b +1002: + .endm + +#else + + .macro senduart, rd, rx + mcr p14, 0, \rd, c1, c0, 0 + .endm + + .macro busyuart, rd, rx +1001: + mrc p14, 0, \rx, c0, c0, 0 + tst \rx, #2 + beq 1001b + + .endm + + .macro waituart, rd, rx + mov \rd, #0x2000000 +1001: + subs \rd, \rd, #1 + bmi 1002f + mrc p14, 0, \rx, c0, c0, 0 + tst \rx, #2 + bne 1001b +1002: + .endm + +#endif /* CONFIG_CPU_V6 */ diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S index c45522c36787..66f711b2e0e8 100644 --- a/arch/arm/kernel/debug.S +++ b/arch/arm/kernel/debug.S @@ -20,90 +20,9 @@ * references to these in a production kernel! */ -#if defined(CONFIG_DEBUG_ICEDCC) - @@ debug using ARM EmbeddedICE DCC channel - - .macro addruart, rp, rv, tmp - .endm - -#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7) - - .macro senduart, rd, rx - mcr p14, 0, \rd, c0, c5, 0 - .endm - - .macro busyuart, rd, rx -1001: - mrc p14, 0, \rx, c0, c1, 0 - tst \rx, #0x20000000 - beq 1001b - .endm - - .macro waituart, rd, rx - mov \rd, #0x2000000 -1001: - subs \rd, \rd, #1 - bmi 1002f - mrc p14, 0, \rx, c0, c1, 0 - tst \rx, #0x20000000 - bne 1001b -1002: - .endm - -#elif defined(CONFIG_CPU_XSCALE) - - .macro senduart, rd, rx - mcr p14, 0, \rd, c8, c0, 0 - .endm - - .macro busyuart, rd, rx -1001: - mrc p14, 0, \rx, c14, c0, 0 - tst \rx, #0x10000000 - beq 1001b - .endm - - .macro waituart, rd, rx - mov \rd, #0x10000000 -1001: - subs \rd, \rd, #1 - bmi 1002f - mrc p14, 0, \rx, c14, c0, 0 - tst \rx, #0x10000000 - bne 1001b -1002: - .endm - -#else - - .macro senduart, rd, rx - mcr p14, 0, \rd, c1, c0, 0 - .endm - - .macro busyuart, rd, rx -1001: - mrc p14, 0, \rx, c0, c0, 0 - tst \rx, #2 - beq 1001b - - .endm - - .macro waituart, rd, rx - mov \rd, #0x2000000 -1001: - subs \rd, \rd, #1 - bmi 1002f - mrc p14, 0, \rx, c0, c0, 0 - tst \rx, #2 - bne 1001b -1002: - .endm - -#endif /* CONFIG_CPU_V6 */ - -#elif !defined(CONFIG_DEBUG_SEMIHOSTING) -#include -#endif /* CONFIG_DEBUG_ICEDCC */ +#if !defined(CONFIG_DEBUG_SEMIHOSTING) +#include CONFIG_DEBUG_LL_INCLUDE +#endif #ifdef CONFIG_MMU .macro addruart_current, rx, tmp1, tmp2 diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 3db960e20cb8..9874d0741191 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -23,8 +23,8 @@ #include #include -#ifdef CONFIG_DEBUG_LL -#include +#if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING) +#include CONFIG_DEBUG_LL_INCLUDE #endif /* -- cgit v1.2.3 From a42c362980430be74b77ec8f374964f61062379f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Sep 2012 18:39:28 +0100 Subject: ARM: Add irqtime accounting support Add support for irq time accounting. This commit prepares ARM by adding the call to enable_sched_clock_irqtime() in sched_clock(). We introduce a new kernel parameter - irqtime - which takes an integer. -1 for auto, 0 for disabled, and 1 for enabled. Auto mode selects IRQ accounting if we have a sched_clock() tick rate greater than 1MHz. Frederic Weisbecker is working on a patch set which moves the IRQ_TIME_ACCOUNTING into arch/, so that part is not incorporated into this patch; this facility becomes available on ARM only when both this patch and Frederic's patches are merged. Signed-off-by: Russell King --- arch/arm/kernel/sched_clock.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c index 27d186abbc06..9b8451a4330f 100644 --- a/arch/arm/kernel/sched_clock.c +++ b/arch/arm/kernel/sched_clock.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ struct clock_data { static void sched_clock_poll(unsigned long wrap_ticks); static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0); +static int irqtime = -1; + +core_param(irqtime, irqtime, int, 0400); static struct clock_data cd = { .mult = NSEC_PER_SEC / HZ, @@ -145,6 +149,10 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) */ cd.epoch_ns = 0; + /* Enable IRQ time accounting if we have a fast enough sched_clock */ + if (irqtime > 0 || (irqtime == -1 && rate >= 1000000)) + enable_sched_clock_irqtime(); + pr_debug("Registered %pF as sched_clock source\n", read); } -- cgit v1.2.3 From f48b5f12799dbabba4a9c799a9bef0775b2f977d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Sep 2012 18:09:57 +0100 Subject: ARM: 7523/1: arch_timers: enable the use of the virtual timer At the moment, the arch_timer driver only uses the physical timer, which can cause problem if PL2 hasn't enabled PL1 access in CNTHCTL, which is likely in a virtualized environment. Instead, the virtual timer is always available. This patch enables the use of the virtual timer, unless no interrupt is provided in the DT for it, in which case it falls back to the physical timer. Signed-off-by: Marc Zyngier Signed-off-by: Russell King --- arch/arm/kernel/arch_timer.c | 343 ++++++++++++++++++++++++++++++------------- 1 file changed, 241 insertions(+), 102 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index cf258807160d..c4e20b69197a 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c @@ -27,13 +27,23 @@ #include static unsigned long arch_timer_rate; -static int arch_timer_ppi; -static int arch_timer_ppi2; + +enum ppi_nr { + PHYS_SECURE_PPI, + PHYS_NONSECURE_PPI, + VIRT_PPI, + HYP_PPI, + MAX_TIMER_PPI +}; + +static int arch_timer_ppi[MAX_TIMER_PPI]; static struct clock_event_device __percpu **arch_timer_evt; extern void init_current_timer_delay(unsigned long freq); +static bool arch_timer_use_virtual = true; + /* * Architected system timer support. */ @@ -46,50 +56,104 @@ extern void init_current_timer_delay(unsigned long freq); #define ARCH_TIMER_REG_FREQ 1 #define ARCH_TIMER_REG_TVAL 2 -static void arch_timer_reg_write(int reg, u32 val) +#define ARCH_TIMER_PHYS_ACCESS 0 +#define ARCH_TIMER_VIRT_ACCESS 1 + +/* + * These register accessors are marked inline so the compiler can + * nicely work out which register we want, and chuck away the rest of + * the code. At least it does so with a recent GCC (4.6.3). + */ +static inline void arch_timer_reg_write(const int access, const int reg, u32 val) { - switch (reg) { - case ARCH_TIMER_REG_CTRL: - asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); - break; - case ARCH_TIMER_REG_TVAL: - asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val)); - break; + if (access == ARCH_TIMER_PHYS_ACCESS) { + switch (reg) { + case ARCH_TIMER_REG_CTRL: + asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); + break; + case ARCH_TIMER_REG_TVAL: + asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val)); + break; + } + } + + if (access == ARCH_TIMER_VIRT_ACCESS) { + switch (reg) { + case ARCH_TIMER_REG_CTRL: + asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val)); + break; + case ARCH_TIMER_REG_TVAL: + asm volatile("mcr p15, 0, %0, c14, c3, 0" : : "r" (val)); + break; + } } isb(); } -static u32 arch_timer_reg_read(int reg) +static inline u32 arch_timer_reg_read(const int access, const int reg) { - u32 val; + u32 val = 0; + + if (access == ARCH_TIMER_PHYS_ACCESS) { + switch (reg) { + case ARCH_TIMER_REG_CTRL: + asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); + break; + case ARCH_TIMER_REG_TVAL: + asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); + break; + case ARCH_TIMER_REG_FREQ: + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); + break; + } + } - switch (reg) { - case ARCH_TIMER_REG_CTRL: - asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); - break; - case ARCH_TIMER_REG_FREQ: - asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); - break; - case ARCH_TIMER_REG_TVAL: - asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); - break; - default: - BUG(); + if (access == ARCH_TIMER_VIRT_ACCESS) { + switch (reg) { + case ARCH_TIMER_REG_CTRL: + asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val)); + break; + case ARCH_TIMER_REG_TVAL: + asm volatile("mrc p15, 0, %0, c14, c3, 0" : "=r" (val)); + break; + } } return val; } -static irqreturn_t arch_timer_handler(int irq, void *dev_id) +static inline cycle_t arch_timer_counter_read(const int access) { - struct clock_event_device *evt = *(struct clock_event_device **)dev_id; - unsigned long ctrl; + cycle_t cval = 0; + + if (access == ARCH_TIMER_PHYS_ACCESS) + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); + + if (access == ARCH_TIMER_VIRT_ACCESS) + asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval)); + + return cval; +} - ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL); +static inline cycle_t arch_counter_get_cntpct(void) +{ + return arch_timer_counter_read(ARCH_TIMER_PHYS_ACCESS); +} + +static inline cycle_t arch_counter_get_cntvct(void) +{ + return arch_timer_counter_read(ARCH_TIMER_VIRT_ACCESS); +} + +static irqreturn_t inline timer_handler(const int access, + struct clock_event_device *evt) +{ + unsigned long ctrl; + ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL); if (ctrl & ARCH_TIMER_CTRL_IT_STAT) { ctrl |= ARCH_TIMER_CTRL_IT_MASK; - arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl); + arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl); evt->event_handler(evt); return IRQ_HANDLED; } @@ -97,63 +161,100 @@ static irqreturn_t arch_timer_handler(int irq, void *dev_id) return IRQ_NONE; } -static void arch_timer_disable(void) +static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id) { - unsigned long ctrl; + struct clock_event_device *evt = *(struct clock_event_device **)dev_id; - ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL); - ctrl &= ~ARCH_TIMER_CTRL_ENABLE; - arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl); + return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt); } -static void arch_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *clk) +static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id) { + struct clock_event_device *evt = *(struct clock_event_device **)dev_id; + + return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt); +} + +static inline void timer_set_mode(const int access, int mode) +{ + unsigned long ctrl; switch (mode) { case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - arch_timer_disable(); + ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL); + ctrl &= ~ARCH_TIMER_CTRL_ENABLE; + arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl); break; default: break; } } -static int arch_timer_set_next_event(unsigned long evt, - struct clock_event_device *unused) +static void arch_timer_set_mode_virt(enum clock_event_mode mode, + struct clock_event_device *clk) { - unsigned long ctrl; + timer_set_mode(ARCH_TIMER_VIRT_ACCESS, mode); +} + +static void arch_timer_set_mode_phys(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + timer_set_mode(ARCH_TIMER_PHYS_ACCESS, mode); +} - ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL); +static inline void set_next_event(const int access, unsigned long evt) +{ + unsigned long ctrl; + ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL); ctrl |= ARCH_TIMER_CTRL_ENABLE; ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; + arch_timer_reg_write(access, ARCH_TIMER_REG_TVAL, evt); + arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl); +} - arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt); - arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl); +static int arch_timer_set_next_event_virt(unsigned long evt, + struct clock_event_device *unused) +{ + set_next_event(ARCH_TIMER_VIRT_ACCESS, evt); + return 0; +} +static int arch_timer_set_next_event_phys(unsigned long evt, + struct clock_event_device *unused) +{ + set_next_event(ARCH_TIMER_PHYS_ACCESS, evt); return 0; } static int __cpuinit arch_timer_setup(struct clock_event_device *clk) { - /* Be safe... */ - arch_timer_disable(); - clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; clk->name = "arch_sys_timer"; clk->rating = 450; - clk->set_mode = arch_timer_set_mode; - clk->set_next_event = arch_timer_set_next_event; - clk->irq = arch_timer_ppi; + if (arch_timer_use_virtual) { + clk->irq = arch_timer_ppi[VIRT_PPI]; + clk->set_mode = arch_timer_set_mode_virt; + clk->set_next_event = arch_timer_set_next_event_virt; + } else { + clk->irq = arch_timer_ppi[PHYS_SECURE_PPI]; + clk->set_mode = arch_timer_set_mode_phys; + clk->set_next_event = arch_timer_set_next_event_phys; + } + + clk->set_mode(CLOCK_EVT_MODE_SHUTDOWN, NULL); clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); *__this_cpu_ptr(arch_timer_evt) = clk; - enable_percpu_irq(clk->irq, 0); - if (arch_timer_ppi2) - enable_percpu_irq(arch_timer_ppi2, 0); + if (arch_timer_use_virtual) + enable_percpu_irq(arch_timer_ppi[VIRT_PPI], 0); + else { + enable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], 0); + if (arch_timer_ppi[PHYS_NONSECURE_PPI]) + enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], 0); + } return 0; } @@ -173,8 +274,8 @@ static int arch_timer_available(void) return -ENXIO; if (arch_timer_rate == 0) { - arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0); - freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ); + freq = arch_timer_reg_read(ARCH_TIMER_PHYS_ACCESS, + ARCH_TIMER_REG_FREQ); /* Check the timer frequency. */ if (freq == 0) { @@ -185,43 +286,42 @@ static int arch_timer_available(void) arch_timer_rate = freq; } - pr_info_once("Architected local timer running at %lu.%02luMHz.\n", - arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100); + pr_info_once("Architected local timer running at %lu.%02luMHz (%s).\n", + arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100, + arch_timer_use_virtual ? "virt" : "phys"); return 0; } -static inline cycle_t arch_counter_get_cntpct(void) +static u32 notrace arch_counter_get_cntpct32(void) { - u32 cvall, cvalh; - - asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh)); + cycle_t cnt = arch_counter_get_cntpct(); - return ((cycle_t) cvalh << 32) | cvall; -} - -static inline cycle_t arch_counter_get_cntvct(void) -{ - u32 cvall, cvalh; - - asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh)); - - return ((cycle_t) cvalh << 32) | cvall; + /* + * The sched_clock infrastructure only knows about counters + * with at most 32bits. Forget about the upper 24 bits for the + * time being... + */ + return (u32)cnt; } static u32 notrace arch_counter_get_cntvct32(void) { - cycle_t cntvct = arch_counter_get_cntvct(); + cycle_t cnt = arch_counter_get_cntvct(); /* * The sched_clock infrastructure only knows about counters * with at most 32bits. Forget about the upper 24 bits for the * time being... */ - return (u32)(cntvct & (u32)~0); + return (u32)cnt; } static cycle_t arch_counter_read(struct clocksource *cs) { + /* + * Always use the physical counter for the clocksource. + * CNTHCTL.PL1PCTEN must be set to 1. + */ return arch_counter_get_cntpct(); } @@ -245,10 +345,16 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk) { pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n", clk->irq, smp_processor_id()); - disable_percpu_irq(clk->irq); - if (arch_timer_ppi2) - disable_percpu_irq(arch_timer_ppi2); - arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk); + + if (arch_timer_use_virtual) + disable_percpu_irq(arch_timer_ppi[VIRT_PPI]); + else { + disable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI]); + if (arch_timer_ppi[PHYS_NONSECURE_PPI]) + disable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI]); + } + + clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk); } static struct local_timer_ops arch_timer_ops __cpuinitdata = { @@ -261,36 +367,44 @@ static struct clock_event_device arch_timer_global_evt; static int __init arch_timer_register(void) { int err; + int ppi; err = arch_timer_available(); if (err) - return err; + goto out; arch_timer_evt = alloc_percpu(struct clock_event_device *); - if (!arch_timer_evt) - return -ENOMEM; + if (!arch_timer_evt) { + err = -ENOMEM; + goto out; + } clocksource_register_hz(&clocksource_counter, arch_timer_rate); - err = request_percpu_irq(arch_timer_ppi, arch_timer_handler, - "arch_timer", arch_timer_evt); + if (arch_timer_use_virtual) { + ppi = arch_timer_ppi[VIRT_PPI]; + err = request_percpu_irq(ppi, arch_timer_handler_virt, + "arch_timer", arch_timer_evt); + } else { + ppi = arch_timer_ppi[PHYS_SECURE_PPI]; + err = request_percpu_irq(ppi, arch_timer_handler_phys, + "arch_timer", arch_timer_evt); + if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) { + ppi = arch_timer_ppi[PHYS_NONSECURE_PPI]; + err = request_percpu_irq(ppi, arch_timer_handler_phys, + "arch_timer", arch_timer_evt); + if (err) + free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], + arch_timer_evt); + } + } + if (err) { pr_err("arch_timer: can't register interrupt %d (%d)\n", - arch_timer_ppi, err); + ppi, err); goto out_free; } - if (arch_timer_ppi2) { - err = request_percpu_irq(arch_timer_ppi2, arch_timer_handler, - "arch_timer", arch_timer_evt); - if (err) { - pr_err("arch_timer: can't register interrupt %d (%d)\n", - arch_timer_ppi2, err); - arch_timer_ppi2 = 0; - goto out_free_irq; - } - } - err = local_timer_register(&arch_timer_ops); if (err) { /* @@ -310,13 +424,19 @@ static int __init arch_timer_register(void) return 0; out_free_irq: - free_percpu_irq(arch_timer_ppi, arch_timer_evt); - if (arch_timer_ppi2) - free_percpu_irq(arch_timer_ppi2, arch_timer_evt); + if (arch_timer_use_virtual) + free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt); + else { + free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], + arch_timer_evt); + if (arch_timer_ppi[PHYS_NONSECURE_PPI]) + free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], + arch_timer_evt); + } out_free: free_percpu(arch_timer_evt); - +out: return err; } @@ -329,6 +449,7 @@ int __init arch_timer_of_register(void) { struct device_node *np; u32 freq; + int i; np = of_find_matching_node(NULL, arch_timer_of_match); if (!np) { @@ -340,22 +461,40 @@ int __init arch_timer_of_register(void) if (!of_property_read_u32(np, "clock-frequency", &freq)) arch_timer_rate = freq; - arch_timer_ppi = irq_of_parse_and_map(np, 0); - arch_timer_ppi2 = irq_of_parse_and_map(np, 1); - pr_info("arch_timer: found %s irqs %d %d\n", - np->name, arch_timer_ppi, arch_timer_ppi2); + for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) + arch_timer_ppi[i] = irq_of_parse_and_map(np, i); + + /* + * If no interrupt provided for virtual timer, we'll have to + * stick to the physical timer. It'd better be accessible... + */ + if (!arch_timer_ppi[VIRT_PPI]) { + arch_timer_use_virtual = false; + + if (!arch_timer_ppi[PHYS_SECURE_PPI] || + !arch_timer_ppi[PHYS_NONSECURE_PPI]) { + pr_warn("arch_timer: No interrupt available, giving up\n"); + return -EINVAL; + } + } return arch_timer_register(); } int __init arch_timer_sched_clock_init(void) { + u32 (*cnt32)(void); int err; err = arch_timer_available(); if (err) return err; - setup_sched_clock(arch_counter_get_cntvct32, 32, arch_timer_rate); + if (arch_timer_use_virtual) + cnt32 = arch_counter_get_cntvct32; + else + cnt32 = arch_counter_get_cntpct32; + + setup_sched_clock(cnt32, 32, arch_timer_rate); return 0; } -- cgit v1.2.3 From a1b2dde70419ae947fd7c9c8fcad7da005dc600e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Sep 2012 18:09:58 +0100 Subject: ARM: 7522/1: arch_timers: register a time/cycle counter Some subsystems (KVM for example) need access to a cycle counter. In the KVM case, this is used to measure the time delta between host and guest in order to accurately generate timer events for the guest. Signed-off-by: Marc Zyngier Signed-off-by: Russell King --- arch/arm/include/asm/arch_timer.h | 7 +++++++ arch/arm/kernel/arch_timer.c | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 62e75475e57e..ad9b155c8262 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -2,11 +2,13 @@ #define __ASMARM_ARCH_TIMER_H #include +#include #ifdef CONFIG_ARM_ARCH_TIMER #define ARCH_HAS_READ_CURRENT_TIMER int arch_timer_of_register(void); int arch_timer_sched_clock_init(void); +struct timecounter *arch_timer_get_timecounter(void); #else static inline int arch_timer_of_register(void) { @@ -17,6 +19,11 @@ static inline int arch_timer_sched_clock_init(void) { return -ENXIO; } + +static inline struct timecounter *arch_timer_get_timecounter(void) +{ + return NULL; +} #endif #endif diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index c4e20b69197a..c04c2a6da470 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c @@ -333,6 +333,15 @@ int read_current_timer(unsigned long *timer_val) return 0; } +static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) +{ + /* + * Always use the physical counter for the clocksource. + * CNTHCTL.PL1PCTEN must be set to 1. + */ + return arch_counter_get_cntpct(); +} + static struct clocksource clocksource_counter = { .name = "arch_sys_counter", .rating = 400, @@ -341,6 +350,18 @@ static struct clocksource clocksource_counter = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +static struct cyclecounter cyclecounter = { + .read = arch_counter_read_cc, + .mask = CLOCKSOURCE_MASK(56), +}; + +static struct timecounter timecounter; + +struct timecounter *arch_timer_get_timecounter(void) +{ + return &timecounter; +} + static void __cpuinit arch_timer_stop(struct clock_event_device *clk) { pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n", @@ -380,6 +401,10 @@ static int __init arch_timer_register(void) } clocksource_register_hz(&clocksource_counter, arch_timer_rate); + cyclecounter.mult = clocksource_counter.mult; + cyclecounter.shift = clocksource_counter.shift; + timecounter_init(&timecounter, &cyclecounter, + arch_counter_get_cntpct()); if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; -- cgit v1.2.3 From 3ddbebf878ac8d958bb34e87a742a6b3adc283a3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 17 Sep 2012 13:22:53 +0200 Subject: PCI: Discard __init annotations for pci_fixup_irqs() and related functions Remove the __init annotations in order to keep pci_fixup_irqs() around after init (e.g. for hotplug). This requires the same change for the implementation of pcibios_update_irq() on all architectures. While at it, all __devinit annotations are removed as well, since they will be useless now that HOTPLUG is always on. Signed-off-by: Thierry Reding Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman --- arch/alpha/kernel/pci.c | 2 +- arch/arm/kernel/bios32.c | 2 +- arch/ia64/pci/pci.c | 2 +- arch/mips/pci/pci.c | 2 +- arch/sh/drivers/pci/pci.c | 2 +- arch/sparc/kernel/leon_pci.c | 2 +- arch/tile/kernel/pci.c | 2 +- arch/tile/kernel/pci_gx.c | 2 +- arch/unicore32/kernel/pci.c | 2 +- arch/x86/pci/visws.c | 2 +- arch/xtensa/kernel/pci.c | 2 +- drivers/pci/setup-irq.c | 4 ++-- 12 files changed, 13 insertions(+), 13 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 9816d5a4d176..920392f61ef2 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -256,7 +256,7 @@ pcibios_fixup_bus(struct pci_bus *bus) } } -void __init +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 2b2f25e7fef5..0174fe6effef 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -272,7 +272,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8152, pci_fixup_it -void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { if (debug_pci) printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev)); diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 81acc7a57f3e..27db6a8afc44 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -461,7 +461,7 @@ void pcibios_set_master (struct pci_dev *dev) /* No special bus mastering setup handling */ } -void __devinit +void pcibios_update_irq (struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 690356808f8a..64f0419e5856 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -313,7 +313,7 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } } -void __init +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 40db2d0aef3f..1bd3e089b74f 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -192,7 +192,7 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } -void __init pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c index 21dcda75a520..404621b775fc 100644 --- a/arch/sparc/kernel/leon_pci.c +++ b/arch/sparc/kernel/leon_pci.c @@ -102,7 +102,7 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } -void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { #ifdef CONFIG_PCI_DEBUG printk(KERN_DEBUG "LEONPCI: Assigning IRQ %02d to %s\n", irq, diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index 33c10864d2f7..6245bba8b1d6 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c @@ -406,7 +406,7 @@ void pcibios_set_master(struct pci_dev *dev) /* * This is called from the generic Linux layer. */ -void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c index 0e213e35ffc3..5faad0b1bd21 100644 --- a/arch/tile/kernel/pci_gx.c +++ b/arch/tile/kernel/pci_gx.c @@ -1036,7 +1036,7 @@ char __devinit *pcibios_setup(char *str) /* * This is called from the generic Linux layer. */ -void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c index 46cb6c9de6c9..c07ecc5baff6 100644 --- a/arch/unicore32/kernel/pci.c +++ b/arch/unicore32/kernel/pci.c @@ -154,7 +154,7 @@ void __init puv3_pci_adjust_zones(unsigned long *zone_size, zhole_size[0] = 0; } -void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { if (debug_pci) printk(KERN_DEBUG "PCI: Assigning IRQ %02d to %s\n", diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c index 6f2f8eeed171..9d736e7ff642 100644 --- a/arch/x86/pci/visws.c +++ b/arch/x86/pci/visws.c @@ -62,7 +62,7 @@ out: return irq; } -void __init pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 69759e9cb3ea..6f9b40c47e99 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -212,7 +212,7 @@ void pcibios_set_master(struct pci_dev *dev) /* the next one is stolen from the alpha port... */ -void __init +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index eb219a1d16f7..270ae7b97120 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -18,7 +18,7 @@ #include -static void __init +static void pdev_fixup_irq(struct pci_dev *dev, u8 (*swizzle)(struct pci_dev *, u8 *), int (*map_irq)(const struct pci_dev *, u8, u8)) @@ -54,7 +54,7 @@ pdev_fixup_irq(struct pci_dev *dev, pcibios_update_irq(dev, irq); } -void __init +void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), int (*map_irq)(const struct pci_dev *, u8, u8)) { -- cgit v1.2.3 From 8885b7b637fa9aca7e1b00581a0173c6956966d3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 17 Sep 2012 13:22:54 +0200 Subject: PCI: Provide a default pcibios_update_irq() Most architectures implement this in exactly the same way. Instead of having each architecture duplicate this function, provide a single implementation in the core and make it a weak symbol so that it can be overridden on architectures where it is required. Signed-off-by: Thierry Reding Signed-off-by: Bjorn Helgaas --- arch/alpha/kernel/pci.c | 6 ------ arch/arm/kernel/bios32.c | 9 --------- arch/ia64/pci/pci.c | 8 -------- arch/m68k/kernel/pcibios.c | 5 ----- arch/mips/pci/pci.c | 6 ------ arch/sh/drivers/pci/pci.c | 5 ----- arch/sparc/kernel/leon_pci.c | 9 --------- arch/sparc/kernel/pci.c | 4 ---- arch/tile/kernel/pci.c | 8 -------- arch/tile/kernel/pci_gx.c | 8 -------- arch/unicore32/kernel/pci.c | 8 -------- arch/x86/pci/visws.c | 5 ----- arch/xtensa/kernel/pci.c | 8 -------- drivers/pci/setup-irq.c | 5 +++++ 14 files changed, 5 insertions(+), 89 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 920392f61ef2..ef757147cbf9 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -256,12 +256,6 @@ pcibios_fixup_bus(struct pci_bus *bus) } } -void -pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - int pcibios_enable_device(struct pci_dev *dev, int mask) { diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 0174fe6effef..9cf16b83bbb5 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -270,15 +270,6 @@ static void __devinit pci_fixup_it8152(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8152, pci_fixup_it8152); - - -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - if (debug_pci) - printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev)); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - /* * If the bus contains any of these devices, then we must not turn on * parity checking of any kind. Currently this is CyberPro 20x0 only. diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 27db6a8afc44..a7ebe9440271 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -461,14 +461,6 @@ void pcibios_set_master (struct pci_dev *dev) /* No special bus mastering setup handling */ } -void -pcibios_update_irq (struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); - - /* ??? FIXME -- record old value for shutdown. */ -} - int pcibios_enable_device (struct pci_dev *dev, int mask) { diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c index b2988aa1840b..73fa0b56a06c 100644 --- a/arch/m68k/kernel/pcibios.c +++ b/arch/m68k/kernel/pcibios.c @@ -87,11 +87,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - void __devinit pcibios_fixup_bus(struct pci_bus *bus) { struct pci_dev *dev; diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 64f0419e5856..04e35bcde07c 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -313,12 +313,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } } -void -pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - #ifdef CONFIG_HOTPLUG EXPORT_SYMBOL(PCIBIOS_MIN_IO); EXPORT_SYMBOL(PCIBIOS_MIN_MEM); diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 1bd3e089b74f..a7e078f2e2e4 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -192,11 +192,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - static void __init pcibios_bus_report_status_early(struct pci_channel *hose, int top_bus, int current_bus, diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c index 404621b775fc..fc0521161568 100644 --- a/arch/sparc/kernel/leon_pci.c +++ b/arch/sparc/kernel/leon_pci.c @@ -102,15 +102,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ -#ifdef CONFIG_PCI_DEBUG - printk(KERN_DEBUG "LEONPCI: Assigning IRQ %02d to %s\n", irq, - pci_name(dev)); -#endif - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - /* in/out routines taken from pcic.c * * This probably belongs here rather than ioport.c because diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 065b88c4f868..acc8c838ff72 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -622,10 +622,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *pbus) { } -void pcibios_update_irq(struct pci_dev *pdev, int irq) -{ -} - resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index 6245bba8b1d6..dbdab34f27cb 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c @@ -403,14 +403,6 @@ void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling. */ } -/* - * This is called from the generic Linux layer. - */ -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - /* * Enable memory and/or address decoding, as appropriate, for the * device described by the 'dev' struct. diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c index 5faad0b1bd21..2ba6d052f85d 100644 --- a/arch/tile/kernel/pci_gx.c +++ b/arch/tile/kernel/pci_gx.c @@ -1033,14 +1033,6 @@ char __devinit *pcibios_setup(char *str) return str; } -/* - * This is called from the generic Linux layer. - */ -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - /* * Enable memory address decoding, as appropriate, for the * device described by the 'dev' struct. The I/O decoding diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c index c07ecc5baff6..b0056f68d321 100644 --- a/arch/unicore32/kernel/pci.c +++ b/arch/unicore32/kernel/pci.c @@ -154,14 +154,6 @@ void __init puv3_pci_adjust_zones(unsigned long *zone_size, zhole_size[0] = 0; } -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - if (debug_pci) - printk(KERN_DEBUG "PCI: Assigning IRQ %02d to %s\n", - irq, pci_name(dev)); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - /* * If the bus contains any of these devices, then we must not turn on * parity checking of any kind. diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c index 9d736e7ff642..3e6d2a6db866 100644 --- a/arch/x86/pci/visws.c +++ b/arch/x86/pci/visws.c @@ -62,11 +62,6 @@ out: return irq; } -void pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - int __init pci_visws_init(void) { pcibios_enable_irq = &pci_visws_enable_irq; diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 6f9b40c47e99..54354de38a70 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -210,14 +210,6 @@ void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } -/* the next one is stolen from the alpha port... */ - -void -pcibios_update_irq(struct pci_dev *dev, int irq) -{ - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); -} - int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 270ae7b97120..9bd6864ec5d3 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -17,6 +17,11 @@ #include #include +void __weak pcibios_update_irq(struct pci_dev *dev, int irq) +{ + dev_dbg(&dev->dev, "assigning IRQ %02d\n", irq); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} static void pdev_fixup_irq(struct pci_dev *dev, -- cgit v1.2.3 From 1f66e06fb6414732bef7bf4a071ef76a837badec Mon Sep 17 00:00:00 2001 From: Wade Farnsworth Date: Fri, 7 Sep 2012 18:18:25 +0100 Subject: ARM: 7524/1: support syscall tracing As specified by ftrace-design.txt, TIF_SYSCALL_TRACEPOINT was added, as well as NR_syscalls in asm/unistd.h. Additionally, __sys_trace was modified to call trace_sys_enter and trace_sys_exit when appropriate. Tests #2 - #4 of "perf test" now complete successfully. Signed-off-by: Steven Walter Signed-off-by: Wade Farnsworth Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/Kconfig | 1 + arch/arm/include/asm/syscall.h | 4 ++++ arch/arm/include/asm/thread_info.h | 4 +++- arch/arm/include/asm/unistd.h | 8 ++++++++ arch/arm/kernel/entry-common.S | 9 +++++++++ arch/arm/kernel/ptrace.c | 11 +++++++++-- 6 files changed, 34 insertions(+), 3 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 2f88d8d97701..1382ef1142a3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -16,6 +16,7 @@ config ARM select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK + select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES if !XIP_KERNEL select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h index c334a23ddf75..47486a41c56e 100644 --- a/arch/arm/include/asm/syscall.h +++ b/arch/arm/include/asm/syscall.h @@ -9,6 +9,10 @@ #include +#include + +#define NR_syscalls (__NR_syscalls) + extern const unsigned long sys_call_table[]; static inline int syscall_get_nr(struct task_struct *task, diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index af7b0bda3355..5ded667e330f 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_AUDIT 9 +#define TIF_SYSCALL_TRACEPOINT 10 #define TIF_POLLING_NRFLAG 16 #define TIF_USING_IWMMXT 17 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ @@ -160,12 +161,13 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) /* Checks for any syscall work in entry-common.S */ -#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT) +#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SYSCALL_TRACEPOINT) /* * Change these and you break ASM code in entry-common.S diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 0cab47d4a83f..a566ec20d486 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -405,6 +405,14 @@ #define __NR_process_vm_readv (__NR_SYSCALL_BASE+376) #define __NR_process_vm_writev (__NR_SYSCALL_BASE+377) +/* + * This may need to be greater than __NR_last_syscall+1 in order to + * account for the padding in the syscall table + */ +#ifdef __KERNEL__ +#define __NR_syscalls (380) +#endif /* __KERNEL__ */ + /* * The following SWIs are ARM private. */ diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 978eac57e04a..f45987037bf1 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -94,6 +94,15 @@ ENDPROC(ret_from_fork) .equ NR_syscalls,0 #define CALL(x) .equ NR_syscalls,NR_syscalls+1 #include "calls.S" + +/* + * Ensure that the system call table is equal to __NR_syscalls, + * which is the value the rest of the system sees + */ +.ifne NR_syscalls - __NR_syscalls +.error "__NR_syscalls is not equal to the size of the syscall table" +.endif + #undef CALL #define CALL(x) .long x diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 3e0fc5f7ed4b..c382d3c76ac6 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -30,6 +30,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #define REG_PC 15 #define REG_PSR 16 /* @@ -918,11 +921,11 @@ static int ptrace_syscall_trace(struct pt_regs *regs, int scno, { unsigned long ip; + current_thread_info()->syscall = scno; + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return scno; - current_thread_info()->syscall = scno; - /* * IP is used to denote syscall entry/exit: * IP = 0 -> entry, =1 -> exit @@ -942,6 +945,8 @@ static int ptrace_syscall_trace(struct pt_regs *regs, int scno, asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) { int ret = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_ENTER); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, ret); audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); return ret; @@ -950,6 +955,8 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) asmlinkage int syscall_trace_exit(struct pt_regs *regs, int scno) { int ret = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_EXIT); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, ret); audit_syscall_exit(regs); return ret; } -- cgit v1.2.3 From ebb5e15c3eb942c047108063423d5d6a04b9f167 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 7 Sep 2012 18:20:30 +0100 Subject: ARM: 7525/1: ptrace: use updated syscall number for syscall auditing When tracing system calls, a debugger may change the syscall number in response to a SIGTRAP on syscall entry. This patch ensures that the new syscall number is passed to the audit code. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/ptrace.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index c382d3c76ac6..739db3a1b2d2 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -944,19 +944,19 @@ static int ptrace_syscall_trace(struct pt_regs *regs, int scno, asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) { - int ret = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_ENTER); + scno = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_ENTER); if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) - trace_sys_enter(regs, ret); + trace_sys_enter(regs, scno); audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); - return ret; + return scno; } asmlinkage int syscall_trace_exit(struct pt_regs *regs, int scno) { - int ret = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_EXIT); + scno = ptrace_syscall_trace(regs, scno, PTRACE_SYSCALL_EXIT); if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) - trace_sys_exit(regs, ret); + trace_sys_exit(regs, scno); audit_syscall_exit(regs); - return ret; + return scno; } -- cgit v1.2.3 From 559a593905e583fca23229b916c016e5211c6766 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 19 Sep 2012 08:16:07 +0100 Subject: ARM: 7536/1: smp: Formalize an IPI for wakeup Remove the offset from ipi_msg_type and assume that SGI0 is the wakeup interrupt now that all WFI hotplug users call gic_raise_softirq() with 0 instead of 1. This allows us to track how many wakeup interrupts are sent and also removes the unknown IPI printk message for WFI hotplug based systems. Reviewed-by: Nicolas Pitre Signed-off-by: Stephen Boyd Signed-off-by: Russell King --- arch/arm/include/asm/hardirq.h | 2 +- arch/arm/kernel/smp.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index 436e60b2cf7a..2740c2a2df63 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -5,7 +5,7 @@ #include #include -#define NR_IPI 5 +#define NR_IPI 6 typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index ebd8ad274d76..d98c37e97d9d 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -51,7 +51,8 @@ struct secondary_data secondary_data; enum ipi_msg_type { - IPI_TIMER = 2, + IPI_WAKEUP, + IPI_TIMER, IPI_RESCHEDULE, IPI_CALL_FUNC, IPI_CALL_FUNC_SINGLE, @@ -347,7 +348,8 @@ void arch_send_call_function_single_ipi(int cpu) } static const char *ipi_types[NR_IPI] = { -#define S(x,s) [x - IPI_TIMER] = s +#define S(x,s) [x] = s + S(IPI_WAKEUP, "CPU wakeup interrupts"), S(IPI_TIMER, "Timer broadcast interrupts"), S(IPI_RESCHEDULE, "Rescheduling interrupts"), S(IPI_CALL_FUNC, "Function call interrupts"), @@ -500,10 +502,13 @@ void handle_IPI(int ipinr, struct pt_regs *regs) unsigned int cpu = smp_processor_id(); struct pt_regs *old_regs = set_irq_regs(regs); - if (ipinr >= IPI_TIMER && ipinr < IPI_TIMER + NR_IPI) - __inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_TIMER]); + if (ipinr < NR_IPI) + __inc_irq_stat(cpu, ipi_irqs[ipinr]); switch (ipinr) { + case IPI_WAKEUP: + break; + case IPI_TIMER: irq_enter(); ipi_timer(); -- cgit v1.2.3 From 56942fec06efa0e17df0f4c3b438332c923b9014 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 21 Sep 2012 18:51:44 +0100 Subject: ARM: 7538/1: delay: add registration mechanism for delay timer sources The current timer-based delay loop relies on the architected timer to initiate the switch away from the polling-based implementation. This is unfortunate for platforms without the architected timers but with a suitable delay source (that is, constant frequency, always powered-up and ticking as long as the CPUs are online). This patch introduces a registration mechanism for the delay timer (which provides an unconditional read_current_timer implementation) and updates the architected timer code to use the new interface. Reviewed-by: Stephen Boyd Signed-off-by: Jonathan Austin Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/arch_timer.h | 1 - arch/arm/include/asm/delay.h | 9 +++++++++ arch/arm/include/asm/timex.h | 6 ------ arch/arm/kernel/arch_timer.c | 17 ++++++++--------- arch/arm/lib/delay.c | 34 ++++++++++++++++++++++++++-------- 5 files changed, 43 insertions(+), 24 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index ad9b155c8262..d40229d9a1c9 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -5,7 +5,6 @@ #include #ifdef CONFIG_ARM_ARCH_TIMER -#define ARCH_HAS_READ_CURRENT_TIMER int arch_timer_of_register(void); int arch_timer_sched_clock_init(void); struct timecounter *arch_timer_get_timecounter(void); diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h index dc6145120de3..ab98fdd083bd 100644 --- a/arch/arm/include/asm/delay.h +++ b/arch/arm/include/asm/delay.h @@ -15,6 +15,11 @@ #ifndef __ASSEMBLY__ +struct delay_timer { + unsigned long (*read_current_timer)(void); + unsigned long freq; +}; + extern struct arm_delay_ops { void (*delay)(unsigned long); void (*const_udelay)(unsigned long); @@ -56,6 +61,10 @@ extern void __loop_delay(unsigned long loops); extern void __loop_udelay(unsigned long usecs); extern void __loop_const_udelay(unsigned long); +/* Delay-loop timer registration. */ +#define ARCH_HAS_READ_CURRENT_TIMER +extern void register_current_timer_delay(const struct delay_timer *timer); + #endif /* __ASSEMBLY__ */ #endif /* defined(_ARM_DELAY_H) */ diff --git a/arch/arm/include/asm/timex.h b/arch/arm/include/asm/timex.h index ce119442277c..9acc135dad94 100644 --- a/arch/arm/include/asm/timex.h +++ b/arch/arm/include/asm/timex.h @@ -12,15 +12,9 @@ #ifndef _ASMARM_TIMEX_H #define _ASMARM_TIMEX_H -#include #include typedef unsigned long cycles_t; - -#ifdef ARCH_HAS_READ_CURRENT_TIMER #define get_cycles() ({ cycles_t c; read_current_timer(&c) ? 0 : c; }) -#else -#define get_cycles() (0) -#endif #endif diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index c04c2a6da470..c8ef20747ee7 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -39,8 +40,7 @@ enum ppi_nr { static int arch_timer_ppi[MAX_TIMER_PPI]; static struct clock_event_device __percpu **arch_timer_evt; - -extern void init_current_timer_delay(unsigned long freq); +static struct delay_timer arch_delay_timer; static bool arch_timer_use_virtual = true; @@ -325,12 +325,9 @@ static cycle_t arch_counter_read(struct clocksource *cs) return arch_counter_get_cntpct(); } -int read_current_timer(unsigned long *timer_val) +static unsigned long arch_timer_read_current_timer(void) { - if (!arch_timer_rate) - return -ENXIO; - *timer_val = arch_counter_get_cntpct(); - return 0; + return arch_counter_get_cntpct(); } static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) @@ -441,11 +438,13 @@ static int __init arch_timer_register(void) arch_timer_global_evt.cpumask = cpumask_of(0); err = arch_timer_setup(&arch_timer_global_evt); } - if (err) goto out_free_irq; - init_current_timer_delay(arch_timer_rate); + /* Use the architected timer for the delay loop. */ + arch_delay_timer.read_current_timer = &arch_timer_read_current_timer; + arch_delay_timer.freq = arch_timer_rate; + register_current_timer_delay(&arch_delay_timer); return 0; out_free_irq: diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c index d6dacc69254e..9d0a30032d7f 100644 --- a/arch/arm/lib/delay.c +++ b/arch/arm/lib/delay.c @@ -34,7 +34,18 @@ struct arm_delay_ops arm_delay_ops = { .udelay = __loop_udelay, }; -#ifdef ARCH_HAS_READ_CURRENT_TIMER +static const struct delay_timer *delay_timer; +static bool delay_calibrated; + +int read_current_timer(unsigned long *timer_val) +{ + if (!delay_timer) + return -ENXIO; + + *timer_val = delay_timer->read_current_timer(); + return 0; +} + static void __timer_delay(unsigned long cycles) { cycles_t start = get_cycles(); @@ -55,17 +66,24 @@ static void __timer_udelay(unsigned long usecs) __timer_const_udelay(usecs * UDELAY_MULT); } -void __init init_current_timer_delay(unsigned long freq) +void __init register_current_timer_delay(const struct delay_timer *timer) { - pr_info("Switching to timer-based delay loop\n"); - lpj_fine = freq / HZ; - arm_delay_ops.delay = __timer_delay; - arm_delay_ops.const_udelay = __timer_const_udelay; - arm_delay_ops.udelay = __timer_udelay; + if (!delay_calibrated) { + pr_info("Switching to timer-based delay loop\n"); + delay_timer = timer; + lpj_fine = timer->freq / HZ; + loops_per_jiffy = lpj_fine; + arm_delay_ops.delay = __timer_delay; + arm_delay_ops.const_udelay = __timer_const_udelay; + arm_delay_ops.udelay = __timer_udelay; + delay_calibrated = true; + } else { + pr_info("Ignoring duplicate/late registration of read_current_timer delay\n"); + } } unsigned long __cpuinit calibrate_delay_is_known(void) { + delay_calibrated = true; return lpj_fine; } -#endif -- cgit v1.2.3 From 4cabd1d9625c7d88acd143f4021fbef75394f154 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 21 Sep 2012 18:56:02 +0100 Subject: ARM: 7539/1: kexec: scan for dtb magic in segments This patch allows a dtb to be passed to a new kernel using the kexec mechinism. When loading segments from userspace, scan each segment's first four bytes for the dtb magic. If this is found set the kexec_boot_atags parameter to the relocate_kernel code to the phyical address of this segment. Reviewed-by: Simon Horman Reviewed-by: Will Deacon Signed-off-by: Matthew Leach Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/machine_kexec.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index dfcdb9f7c126..dee34efca748 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,24 @@ static atomic_t waiting_for_crash_ipi; int machine_kexec_prepare(struct kimage *image) { + struct kexec_segment *current_segment; + __be32 header; + int i, err; + + /* + * No segment at default ATAGs address. try to locate + * a dtb using magic. + */ + for (i = 0; i < image->nr_segments; i++) { + current_segment = &image->segment[i]; + + err = get_user(header, (__be32*)current_segment->buf); + if (err) + return err; + + if (be32_to_cpu(header) == OF_DT_HEADER) + kexec_boot_atags = current_segment->mem; + } return 0; } @@ -122,7 +141,9 @@ void machine_kexec(struct kimage *image) kexec_start_address = image->start; kexec_indirection_page = page_list; kexec_mach_type = machine_arch_type; - kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET; + if (!kexec_boot_atags) + kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET; + /* copy our kernel relocation code to the control code page */ memcpy(reboot_code_buffer, -- cgit v1.2.3 From c564df4db85aac8d1d65a56176a0a25f46138064 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 21 Sep 2012 18:56:11 +0100 Subject: ARM: 7540/1: kexec: Check segment memory addresses Ensure that the memory regions that are set within the segments correspond to physical contiguous memory regions. Reviewed-by: Simon Horman Reviewed-by: Will Deacon Signed-off-by: Matthew Leach Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/machine_kexec.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index dee34efca748..e29c3337ca81 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,11 @@ int machine_kexec_prepare(struct kimage *image) for (i = 0; i < image->nr_segments; i++) { current_segment = &image->segment[i]; + err = memblock_is_region_memory(current_segment->mem, + current_segment->memsz); + if (err) + return - EINVAL; + err = get_user(header, (__be32*)current_segment->buf); if (err) return err; -- cgit v1.2.3