Commit 3a647c1d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "These are changes for drivers that are intimately tied to some SoC and
  for some reason could not get merged through the respective subsystem
  maintainer tree.

  The largest single change here this time around is the Tegra
  iommu/memory controller driver, which gets updated to the new iommu DT
  binding.  More drivers like this are likely to follow for the
  following merge window, but we should be able to do those through the
  iommu maintainer.

  Other notable changes are:
   - reset controller drivers from the reset maintainer (socfpga, sti,
     berlin)
   - fixes for the keystone navigator driver merged last time
   - at91 rtc driver changes related to the at91 cleanups
   - ARM perf driver changes from Will Deacon
   - updates for the brcmstb_gisb driver"

* tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (53 commits)
  clocksource: arch_timer: Allow the device tree to specify uninitialized timer registers
  clocksource: arch_timer: Fix code to use physical timers when requested
  memory: Add NVIDIA Tegra memory controller support
  bus: brcmstb_gisb: Add register offset tables for older chips
  bus: brcmstb_gisb: Look up register offsets in a table
  bus: brcmstb_gisb: Introduce wrapper functions for MMIO accesses
  bus: brcmstb_gisb: Make the driver buildable on MIPS
  of: Add NVIDIA Tegra memory controller binding
  ARM: tegra: Move AHB Kconfig to drivers/amba
  amba: Add Kconfig file
  clk: tegra: Implement memory-controller clock
  serial: samsung: Fix serial config dependencies for exynos7
  bus: brcmstb_gisb: resolve section mismatch
  ARM: common: edma: edma_pm_resume may be unused
  ARM: common: edma: add suspend resume hook
  powerpc/iommu: Rename iommu_[un]map_sg functions
  rtc: at91sam9: add DT bindings documentation
  rtc: at91sam9: use clk API instead of relying on AT91_SLOW_CLOCK
  ARM: at91: add clk_lookup entry for RTT devices
  rtc: at91sam9: rework the Kconfig description
  ...
parents 6cd94d5e 5db45002
...@@ -22,6 +22,14 @@ to deliver its interrupts via SPIs. ...@@ -22,6 +22,14 @@ to deliver its interrupts via SPIs.
- always-on : a boolean property. If present, the timer is powered through an - always-on : a boolean property. If present, the timer is powered through an
always-on power domain, therefore it never loses context. always-on power domain, therefore it never loses context.
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
any of the generic timer CPU registers, which contain their
architecturally-defined reset values. Only supported for 32-bit
systems which follow the ARMv7 architected reset values.
Example: Example:
timer { timer {
......
...@@ -2,7 +2,11 @@ Broadcom GISB bus Arbiter controller ...@@ -2,7 +2,11 @@ Broadcom GISB bus Arbiter controller
Required properties: Required properties:
- compatible: should be "brcm,gisb-arb" - compatible:
"brcm,gisb-arb" or "brcm,bcm7445-gisb-arb" for 28nm chips
"brcm,bcm7435-gisb-arb" for newer 40nm chips
"brcm,bcm7400-gisb-arb" for older 40nm chips and all 65nm chips
"brcm,bcm7038-gisb-arb" for 130nm chips
- reg: specifies the base physical address and size of the registers - reg: specifies the base physical address and size of the registers
- interrupt-parent: specifies the phandle to the parent interrupt controller - interrupt-parent: specifies the phandle to the parent interrupt controller
this arbiter gets interrupt line from this arbiter gets interrupt line from
......
NVIDIA Tegra Memory Controller device tree bindings
===================================================
Required properties:
- compatible: Should be "nvidia,tegra<chip>-mc"
- reg: Physical base address and length of the controller's registers.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- mc: the module's clock input
- interrupts: The interrupt outputs from the controller.
- #iommu-cells: Should be 1. The single cell of the IOMMU specifier defines
the SWGROUP of the master.
This device implements an IOMMU that complies with the generic IOMMU binding.
See ../iommu/iommu.txt for details.
Example:
--------
mc: memory-controller@0,70019000 {
compatible = "nvidia,tegra124-mc";
reg = <0x0 0x70019000 0x0 0x1000>;
clocks = <&tegra_car TEGRA124_CLK_MC>;
clock-names = "mc";
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
#iommu-cells = <1>;
};
sdhci@0,700b0000 {
compatible = "nvidia,tegra124-sdhci";
...
iommus = <&mc TEGRA_SWGROUP_SDMMC1A>;
};
STMicroelectronics STi family Sysconfig Picophy SoftReset Controller
=============================================================================
This binding describes a reset controller device that is used to enable and
disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in
the STi family SoC system configuration registers.
The actual action taken when softreset is asserted is hardware dependent.
However, when asserted it may not be possible to access the hardware's
registers and after an assert/deassert sequence the hardware's previous state
may no longer be valid.
Please refer to Documentation/devicetree/bindings/reset/reset.txt
for common reset controller binding usage.
Required properties:
- compatible: Should be "st,stih407-picophyreset"
- #reset-cells: 1, see below
Example:
picophyreset: picophyreset-controller {
compatible = "st,stih407-picophyreset";
#reset-cells = <1>;
};
Specifying picophyreset control of devices
=======================================
Device nodes should specify the reset channel required in their "resets"
property, containing a phandle to the picophyreset device node and an
index specifying which channel to use, as described in
Documentation/devicetree/bindings/reset/reset.txt.
Example:
usb2_picophy0: usbpicophy@0 {
resets = <&picophyreset STIH407_PICOPHY0_RESET>;
};
Macro definitions for the supported reset channels can be found in:
include/dt-bindings/reset-controller/stih407-resets.h
Atmel AT91SAM9260 Real Time Timer
Required properties:
- compatible: should be: "atmel,at91sam9260-rtt"
- reg: should encode the memory region of the RTT controller
- interrupts: rtt alarm/event interrupt
- clocks: should contain the 32 KHz slow clk that will drive the RTT block.
- atmel,rtt-rtc-time-reg: should encode the GPBR register used to store
the time base when the RTT is used as an RTC.
The first cell should point to the GPBR node and the second one
encode the offset within the GPBR block (or in other words, the
GPBR register used to store the time base).
Example:
rtt@fffffd20 {
compatible = "atmel,at91sam9260-rtt";
reg = <0xfffffd20 0x10>;
interrupts = <1 4 7>;
clocks = <&clk32k>;
atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
};
* OMAP HDQ One wire bus master controller
Required properties:
- compatible : should be "ti,omap3-1w"
- reg : Address and length of the register set for the device
- interrupts : interrupt line.
- ti,hwmods : "hdq1w"
Example:
- From omap3.dtsi
hdqw1w: 1w@480b2000 {
compatible = "ti,omap3-1w";
reg = <0x480b2000 0x1000>;
interrupts = <58>;
ti,hwmods = "hdq1w";
};
...@@ -1246,9 +1246,6 @@ source "arch/arm/common/Kconfig" ...@@ -1246,9 +1246,6 @@ source "arch/arm/common/Kconfig"
menu "Bus support" menu "Bus support"
config ARM_AMBA
bool
config ISA config ISA
bool bool
help help
......
...@@ -245,6 +245,8 @@ struct edma { ...@@ -245,6 +245,8 @@ struct edma {
/* list of channels with no even trigger; terminated by "-1" */ /* list of channels with no even trigger; terminated by "-1" */
const s8 *noevent; const s8 *noevent;
struct edma_soc_info *info;
/* The edma_inuse bit for each PaRAM slot is clear unless the /* The edma_inuse bit for each PaRAM slot is clear unless the
* channel is in use ... by ARM or DSP, for QDMA, or whatever. * channel is in use ... by ARM or DSP, for QDMA, or whatever.
*/ */
...@@ -296,7 +298,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no, ...@@ -296,7 +298,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
~(0x7 << bit), queue_no << bit); ~(0x7 << bit), queue_no << bit);
} }
static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, static void assign_priority_to_queue(unsigned ctlr, int queue_no,
int priority) int priority)
{ {
int bit = queue_no * 4; int bit = queue_no * 4;
...@@ -315,7 +317,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, ...@@ -315,7 +317,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
* included in that particular EDMA variant (Eg : dm646x) * included in that particular EDMA variant (Eg : dm646x)
* *
*/ */
static void __init map_dmach_param(unsigned ctlr) static void map_dmach_param(unsigned ctlr)
{ {
int i; int i;
for (i = 0; i < EDMA_MAX_DMACH; i++) for (i = 0; i < EDMA_MAX_DMACH; i++)
...@@ -1798,6 +1800,7 @@ static int edma_probe(struct platform_device *pdev) ...@@ -1798,6 +1800,7 @@ static int edma_probe(struct platform_device *pdev)
edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
edma_write_array(j, EDMA_QRAE, i, 0x0); edma_write_array(j, EDMA_QRAE, i, 0x0);
} }
edma_cc[j]->info = info[j];
arch_num_cc++; arch_num_cc++;
edma_dev_info.id = j; edma_dev_info.id = j;
...@@ -1807,9 +1810,56 @@ static int edma_probe(struct platform_device *pdev) ...@@ -1807,9 +1810,56 @@ static int edma_probe(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int edma_pm_resume(struct device *dev)
{
int i, j;
for (j = 0; j < arch_num_cc; j++) {
struct edma *cc = edma_cc[j];
s8 (*queue_priority_mapping)[2];
queue_priority_mapping = cc->info->queue_priority_mapping;
/* Event queue priority mapping */
for (i = 0; queue_priority_mapping[i][0] != -1; i++)
assign_priority_to_queue(j,
queue_priority_mapping[i][0],
queue_priority_mapping[i][1]);
/*
* Map the channel to param entry if channel mapping logic
* exist
*/
if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
map_dmach_param(j);
for (i = 0; i < cc->num_channels; i++) {
if (test_bit(i, cc->edma_inuse)) {
/* ensure access through shadow region 0 */
edma_or_array2(j, EDMA_DRAE, 0, i >> 5,
BIT(i & 0x1f));
setup_dma_interrupt(i,
cc->intr_data[i].callback,
cc->intr_data[i].data);
}
}
}
return 0;
}
#endif
static const struct dev_pm_ops edma_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, edma_pm_resume)
};
static struct platform_driver edma_driver = { static struct platform_driver edma_driver = {
.driver = { .driver = {
.name = "edma", .name = "edma",
.pm = &edma_pm_ops,
.of_match_table = edma_of_ids, .of_match_table = edma_of_ids,
}, },
.probe = edma_probe, .probe = edma_probe,
......
...@@ -78,6 +78,15 @@ static inline u32 arch_timer_get_cntfrq(void) ...@@ -78,6 +78,15 @@ static inline u32 arch_timer_get_cntfrq(void)
return val; return val;
} }
static inline u64 arch_counter_get_cntpct(void)
{
u64 cval;
isb();
asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
return cval;
}
static inline u64 arch_counter_get_cntvct(void) static inline u64 arch_counter_get_cntvct(void)
{ {
u64 cval; u64 cval;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#ifndef __ARM_PERF_EVENT_H__ #ifndef __ARM_PERF_EVENT_H__
#define __ARM_PERF_EVENT_H__ #define __ARM_PERF_EVENT_H__
#ifdef CONFIG_HW_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
struct pt_regs; struct pt_regs;
extern unsigned long perf_instruction_pointer(struct pt_regs *regs); extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
extern unsigned long perf_misc_flags(struct pt_regs *regs); extern unsigned long perf_misc_flags(struct pt_regs *regs);
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <asm/cputype.h>
/* /*
* struct arm_pmu_platdata - ARM PMU platform data * struct arm_pmu_platdata - ARM PMU platform data
* *
...@@ -66,19 +68,25 @@ struct pmu_hw_events { ...@@ -66,19 +68,25 @@ struct pmu_hw_events {
/* /*
* The events that are active on the PMU for the given index. * The events that are active on the PMU for the given index.
*/ */
struct perf_event **events; struct perf_event *events[ARMPMU_MAX_HWEVENTS];
/* /*
* A 1 bit for an index indicates that the counter is being used for * A 1 bit for an index indicates that the counter is being used for
* an event. A 0 means that the counter can be used. * an event. A 0 means that the counter can be used.
*/ */
unsigned long *used_mask; DECLARE_BITMAP(used_mask, ARMPMU_MAX_HWEVENTS);
/* /*
* Hardware lock to serialize accesses to PMU registers. Needed for the * Hardware lock to serialize accesses to PMU registers. Needed for the
* read/modify/write sequences. * read/modify/write sequences.
*/ */
raw_spinlock_t pmu_lock; raw_spinlock_t pmu_lock;
/*
* When using percpu IRQs, we need a percpu dev_id. Place it here as we
* already have to allocate this struct per cpu.
*/
struct arm_pmu *percpu_pmu;
}; };
struct arm_pmu { struct arm_pmu {
...@@ -107,7 +115,8 @@ struct arm_pmu { ...@@ -107,7 +115,8 @@ struct arm_pmu {
struct mutex reserve_mutex; struct mutex reserve_mutex;
u64 max_period; u64 max_period;
struct platform_device *plat_device; struct platform_device *plat_device;
struct pmu_hw_events *(*get_hw_events)(void); struct pmu_hw_events __percpu *hw_events;
struct notifier_block hotplug_nb;
}; };
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
...@@ -127,6 +136,27 @@ int armpmu_map_event(struct perf_event *event, ...@@ -127,6 +136,27 @@ int armpmu_map_event(struct perf_event *event,
[PERF_COUNT_HW_CACHE_RESULT_MAX], [PERF_COUNT_HW_CACHE_RESULT_MAX],
u32 raw_event_mask); u32 raw_event_mask);
struct pmu_probe_info {
unsigned int cpuid;
unsigned int mask;
int (*init)(struct arm_pmu *);
};
#define PMU_PROBE(_cpuid, _mask, _fn) \
{ \
.cpuid = (_cpuid), \
.mask = (_mask), \
.init = (_fn), \
}
#define ARM_PMU_PROBE(_cpuid, _fn) \
PMU_PROBE(_cpuid, ARM_CPU_PART_MASK, _fn)
#define ARM_PMU_XSCALE_MASK ((0xff << 24) | ARM_CPU_XSCALE_ARCH_MASK)
#define XSCALE_PMU_PROBE(_version, _fn) \
PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn)
#endif /* CONFIG_HW_PERF_EVENTS */ #endif /* CONFIG_HW_PERF_EVENTS */
#endif /* __ARM_PMU_H__ */ #endif /* __ARM_PMU_H__ */
...@@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o ...@@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
......
/*
* ARM callchain support
*
* Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
* Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
*
* This code is based on the ARM OProfile backtrace code.
*/
#include <linux/perf_event.h>
#include <linux/uaccess.h>
#include <asm/stacktrace.h>
/*
* The registers we're interested in are at the end of the variable
* length saved register structure. The fp points at the end of this
* structure so the address of this struct is:
* (struct frame_tail *)(xxx->fp)-1
*
* This code has been adapted from the ARM OProfile support.
*/
struct frame_tail {
struct frame_tail __user *fp;
unsigned long sp;
unsigned long lr;
} __attribute__((packed));
/*
* Get the return address for a single stackframe and return a pointer to the
* next frame tail.
*/
static struct frame_tail __user *
user_backtrace(struct frame_tail __user *tail,
struct perf_callchain_entry *entry)
{
struct frame_tail buftail;
unsigned long err;
if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
return NULL;
pagefault_disable();
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
pagefault_enable();
if (err)
return NULL;
perf_callchain_store(entry, buftail.lr);
/*
* Frame pointers should strictly progress back up the stack
* (towards higher addresses).
*/
if (tail + 1 >= buftail.fp)
return NULL;
return buftail.fp - 1;
}
void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
struct frame_tail __user *tail;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}
perf_callchain_store(entry, regs->ARM_pc);
if (!current->mm)
return;
tail = (struct frame_tail __user *)regs->ARM_fp - 1;
while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, entry);
}
/*
* Gets called by walk_stackframe() for every stackframe. This will be called
* whist unwinding the stackframe and is like a subroutine return so we use
* the PC.
*/
static int
callchain_trace(struct stackframe *fr,
void *data)
{
struct perf_callchain_entry *entry = data;
perf_callchain_store(entry, fr->pc);
return 0;
}
void
perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
struct stackframe fr;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}
arm_get_current_stackframe(regs, &fr);
walk_stackframe(&fr, callchain_trace, entry);
}
unsigned long perf_instruction_pointer(struct pt_regs *regs)
{
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
return perf_guest_cbs->get_guest_ip();
return instruction_pointer(regs);
}
unsigned long perf_misc_flags(struct pt_regs *regs)
{
int misc = 0;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
if (perf_guest_cbs->is_user_mode())
misc |= PERF_RECORD_MISC_GUEST_USER;
else
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else {
if (user_mode(regs))
misc |= PERF_RECORD_MISC_USER;
else
misc |= PERF_RECORD_MISC_KERNEL;
}
return misc;
}
...@@ -7,21 +7,18 @@ ...@@ -7,21 +7,18 @@
* Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
* *
* This code is based on the sparc64 perf event code, which is in turn based * This code is based on the sparc64 perf event code, which is in turn based
* on the x86 code. Callchain code is based on the ARM OProfile backtrace * on the x86 code.
* code.
*/ */
#define pr_fmt(fmt) "hw perfevents: " fmt #define pr_fmt(fmt) "hw perfevents: " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/uaccess.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
#include <asm/pmu.h> #include <asm/pmu.h>
#include <asm/stacktrace.h>
static int static int
armpmu_map_cache_event(const unsigned (*cache_map) armpmu_map_cache_event(const unsigned (*cache_map)
...@@ -80,8 +77,12 @@ armpmu_map_event(struct perf_event *event, ...@@ -80,8 +77,12 @@ armpmu_map_event(struct perf_event *event,
u32 raw_event_mask) u32 raw_event_mask)
{ {
u64 config = event->attr.config; u64 config = event->attr.config;
int type = event->attr.type;
switch (event->attr.type) { if (type == event->pmu->type)
return armpmu_map_raw_event(raw_event_mask, config);
switch (type) {
case PERF_TYPE_HARDWARE: case PERF_TYPE_HARDWARE:
return armpmu_map_hw_event(event_map, config); return armpmu_map_hw_event(event_map, config);
case PERF_TYPE_HW_CACHE: case PERF_TYPE_HW_CACHE:
...@@ -200,7 +201,7 @@ static void ...@@ -200,7 +201,7 @@ static void
armpmu_del(struct perf_event *event, int flags) armpmu_del(struct perf_event *event, int flags)
{ {
struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *hw_events = armpmu->get_hw_events(); struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx; int idx = hwc->idx;
...@@ -217,7 +218,7 @@ static int ...@@ -217,7 +218,7 @@ static int
armpmu_add(struct perf_event *event, int flags) armpmu_add(struct perf_event *event, int flags)
{ {
struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *hw_events = armpmu->get_hw_events(); struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx; int idx;
int err = 0; int err = 0;
...@@ -274,14 +275,12 @@ validate_group(struct perf_event *event) ...@@ -274,14 +275,12 @@ validate_group(struct perf_event *event)
{ {
struct perf_event *sibling, *leader = event->group_leader; struct perf_event *sibling, *leader = event->group_leader;
struct pmu_hw_events fake_pmu; struct pmu_hw_events fake_pmu;
DECLARE_BITMAP(fake_used_mask, ARMPMU_MAX_HWEVENTS);
/* /*
* Initialise the fake PMU. We only need to populate the * Initialise the fake PMU. We only need to populate the
* used_mask for the purposes of validation. * used_mask for the purposes of validation.
*/ */
memset(fake_used_mask, 0, sizeof(fake_used_mask)); memset(&fake_pmu.used_mask, 0, sizeof(fake_pmu.used_mask));
fake_pmu.used_mask = fake_used_mask;
if (!validate_event(&fake_pmu, leader)) if (!validate_event(&fake_pmu, leader))
return -EINVAL; return -EINVAL;
...@@ -305,17 +304,21 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) ...@@ -305,17 +304,21 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
int ret; int ret;
u64 start_clock, finish_clock; u64 start_clock, finish_clock;
if (irq_is_percpu(irq)) /*
dev = *(void **)dev; * we request the IRQ with a (possibly percpu) struct arm_pmu**, but
armpmu = dev; * the handlers expect a struct arm_pmu*. The percpu_irq framework will
* do any necessary shifting, we just need to perform the first
* dereference.
*/
armpmu = *(void **)dev;
plat_device = armpmu->plat_device; plat_device = armpmu->plat_device;
plat = dev_get_platdata(&plat_device->dev); plat = dev_get_platdata(&plat_device->dev);
start_clock = sched_clock(); start_clock = sched_clock();
if (plat && plat->handle_irq) if (plat && plat->handle_irq)
ret = plat->handle_irq(irq, dev, armpmu->handle_irq); ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq);
else else
ret = armpmu->handle_irq(irq, dev); ret = armpmu->handle_irq(irq, armpmu);
finish_clock = sched_clock(); finish_clock = sched_clock();
perf_sample_event_took(finish_clock - start_clock); perf_sample_event_took(finish_clock - start_clock);
...@@ -468,7 +471,7 @@ static int armpmu_event_init(struct perf_event *event) ...@@ -468,7 +471,7 @@ static int armpmu_event_init(struct perf_event *event)
static void armpmu_enable(struct pmu *pmu) static void armpmu_enable(struct pmu *pmu)
{ {
struct arm_pmu *armpmu = to_arm_pmu(pmu); struct arm_pmu *armpmu = to_arm_pmu(pmu);
struct pmu_hw_events *hw_events = armpmu->get_hw_events(); struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
if (enabled) if (enabled)
...@@ -533,130 +536,3 @@ int armpmu_register(struct arm_pmu *armpmu, int type) ...@@ -533,130 +536,3 @@ int armpmu_register(struct arm_pmu *armpmu, int type)
return perf_pmu_register(&armpmu->pmu, armpmu->name, type); return perf_pmu_register(&armpmu->pmu, armpmu->name, type);
} }
/*
* Callchain handling code.
*/
/*
* The registers we're interested in are at the end of the variable
* length saved register structure. The fp points at the end of this
* structure so the address of this struct is:
* (struct frame_tail *)(xxx->fp)-1
*
* This code has been adapted from the ARM OProfile support.
*/
struct frame_tail {
struct frame_tail __user *fp;
unsigned long sp;
unsigned long lr;
} __attribute__((packed));
/*
* Get the return address for a single stackframe and return a pointer to the
* next frame tail.
*/
static struct frame_tail __user *
user_backtrace(struct frame_tail __user *tail,
struct perf_callchain_entry *entry)
{
struct frame_tail buftail;
unsigned long err;
if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
return NULL;
pagefault_disable();
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
pagefault_enable();
if (err)
return NULL;
perf_callchain_store(entry, buftail.lr);
/*
* Frame pointers should strictly progress back up the stack
* (towards higher addresses).
*/
if (tail + 1 >= buftail.fp)
return NULL;
return buftail.fp - 1;
}
void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
struct frame_tail __user *tail;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}
perf_callchain_store(entry, regs->ARM_pc);
if (!current->mm)
return;
tail = (struct frame_tail __user *)regs->ARM_fp - 1;
while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, entry);
}
/*
* Gets called by walk_stackframe() for every stackframe. This will be called
* whist unwinding the stackframe and is like a subroutine return so we use
* the PC.
*/
static int
callchain_trace(struct stackframe *fr,
void *data)
{
struct perf_callchain_entry *entry = data;
perf_callchain_store(entry, fr->pc);
return 0;
}
void
perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
struct stackframe fr;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}
arm_get_current_stackframe(regs, &fr);
walk_stackframe(&fr, callchain_trace, entry);
}
unsigned long perf_instruction_pointer(struct pt_regs *regs)
{
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
return perf_guest_cbs->get_guest_ip();
return instruction_pointer(regs);
}
unsigned long perf_misc_flags(struct pt_regs *regs)
{
int misc = 0;
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
if (perf_guest_cbs->is_user_mode())
misc |= PERF_RECORD_MISC_GUEST_USER;
else
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else {
if (user_mode(regs))
misc |= PERF_RECORD_MISC_USER;
else
misc |= PERF_RECORD_MISC_KERNEL;
}
return misc;
}
...@@ -35,11 +35,6 @@ ...@@ -35,11 +35,6 @@
/* Set at runtime when we know what CPU type we are. */ /* Set at runtime when we know what CPU type we are. */
static struct arm_pmu *cpu_pmu; static struct arm_pmu *cpu_pmu;
static DEFINE_PER_CPU(struct arm_pmu *, percpu_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 * Despite the names, these two functions are CPU-specific and are used
* by the OProfile/perf code. * by the OProfile/perf code.
...@@ -69,11 +64,6 @@ EXPORT_SYMBOL_GPL(perf_num_counters); ...@@ -69,11 +64,6 @@ EXPORT_SYMBOL_GPL(perf_num_counters);
#include "perf_event_v6.c" #include "perf_event_v6.c"
#include "perf_event_v7.c" #include "perf_event_v7.c"
static struct pmu_hw_events *cpu_pmu_get_cpu_events(void)
{
return this_cpu_ptr(&cpu_hw_events);
}
static void cpu_pmu_enable_percpu_irq(void *data) static void cpu_pmu_enable_percpu_irq(void *data)
{ {
int irq = *(int *)data; int irq = *(int *)data;
...@@ -92,20 +82,21 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) ...@@ -92,20 +82,21 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
{ {
int i, irq, irqs; int i, irq, irqs;
struct platform_device *pmu_device = cpu_pmu->plat_device; struct platform_device *pmu_device = cpu_pmu->plat_device;
struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events;
irqs = min(pmu_device->num_resources, num_possible_cpus()); irqs = min(pmu_device->num_resources, num_possible_cpus());
irq = platform_get_irq(pmu_device, 0); irq = platform_get_irq(pmu_device, 0);
if (irq >= 0 && irq_is_percpu(irq)) { if (irq >= 0 && irq_is_percpu(irq)) {
on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
free_percpu_irq(irq, &percpu_pmu); free_percpu_irq(irq, &hw_events->percpu_pmu);
} else { } else {
for (i = 0; i < irqs; ++i) { for (i = 0; i < irqs; ++i) {
if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs))
continue; continue;
irq = platform_get_irq(pmu_device, i); irq = platform_get_irq(pmu_device, i);
if (irq >= 0) if (irq >= 0)
free_irq(irq, cpu_pmu); free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, i));
} }
} }
} }
...@@ -114,19 +105,21 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) ...@@ -114,19 +105,21 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
{ {
int i, err, irq, irqs; int i, err, irq, irqs;
struct platform_device *pmu_device = cpu_pmu->plat_device; struct platform_device *pmu_device = cpu_pmu->plat_device;
struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events;
if (!pmu_device) if (!pmu_device)
return -ENODEV; return -ENODEV;
irqs = min(pmu_device->num_resources, num_possible_cpus()); irqs = min(pmu_device->num_resources, num_possible_cpus());
if (irqs < 1) { if (irqs < 1) {
printk_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n"); pr_warn_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n");
return 0; return 0;
} }
irq = platform_get_irq(pmu_device, 0); irq = platform_get_irq(pmu_device, 0);
if (irq >= 0 && irq_is_percpu(irq)) { if (irq >= 0 && irq_is_percpu(irq)) {
err = request_percpu_irq(irq, handler, "arm-pmu", &percpu_pmu); err = request_percpu_irq(irq, handler, "arm-pmu",
&hw_events->percpu_pmu);
if (err) { if (err) {
pr_err("unable to request IRQ%d for ARM PMU counters\n", pr_err("unable to request IRQ%d for ARM PMU counters\n",
irq); irq);
...@@ -153,7 +146,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) ...@@ -153,7 +146,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
err = request_irq(irq, handler, err = request_irq(irq, handler,
IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu",
cpu_pmu); per_cpu_ptr(&hw_events->percpu_pmu, i));
if (err) { if (err) {
pr_err("unable to request IRQ%d for ARM PMU counters\n", pr_err("unable to request IRQ%d for ARM PMU counters\n",
irq); irq);
...@@ -167,18 +160,50 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) ...@@ -167,18 +160,50 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return 0; return 0;
} }
static void cpu_pmu_init(struct arm_pmu *cpu_pmu) /*
* 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 cpu_pmu_notify(struct notifier_block *b, unsigned long action,
void *hcpu)
{
struct arm_pmu *pmu = container_of(b, struct arm_pmu, hotplug_nb);
if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
return NOTIFY_DONE;
if (pmu->reset)
pmu->reset(pmu);
else
return NOTIFY_DONE;
return NOTIFY_OK;
}
static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
{ {
int err;
int cpu; int cpu;
struct pmu_hw_events __percpu *cpu_hw_events;
cpu_hw_events = alloc_percpu(struct pmu_hw_events);
if (!cpu_hw_events)
return -ENOMEM;
cpu_pmu->hotplug_nb.notifier_call = cpu_pmu_notify;
err = register_cpu_notifier(&cpu_pmu->hotplug_nb);
if (err)
goto out_hw_events;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); struct pmu_hw_events *events = per_cpu_ptr(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); raw_spin_lock_init(&events->pmu_lock);
per_cpu(percpu_pmu, cpu) = cpu_pmu; events->percpu_pmu = cpu_pmu;
} }
cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; cpu_pmu->hw_events = cpu_hw_events;
cpu_pmu->request_irq = cpu_pmu_request_irq; cpu_pmu->request_irq = cpu_pmu_request_irq;
cpu_pmu->free_irq = cpu_pmu_free_irq; cpu_pmu->free_irq = cpu_pmu_free_irq;
...@@ -189,31 +214,19 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu) ...@@ -189,31 +214,19 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
/* If no interrupts available, set the corresponding capability flag */ /* If no interrupts available, set the corresponding capability flag */
if (!platform_get_irq(cpu_pmu->plat_device, 0)) if (!platform_get_irq(cpu_pmu->plat_device, 0))
cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
}
/*
* 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 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) return 0;
cpu_pmu->reset(cpu_pmu);
else
return NOTIFY_DONE;
return NOTIFY_OK; out_hw_events:
free_percpu(cpu_hw_events);
return err;
} }
static struct notifier_block cpu_pmu_hotplug_notifier = { static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
.notifier_call = cpu_pmu_notify, {
}; unregister_cpu_notifier(&cpu_pmu->hotplug_nb);
free_percpu(cpu_pmu->hw_events);
}
/* /*
* PMU platform driver and devicetree bindings. * PMU platform driver and devicetree bindings.
...@@ -241,48 +254,34 @@ static struct platform_device_id cpu_pmu_plat_device_ids[] = { ...@@ -241,48 +254,34 @@ static struct platform_device_id cpu_pmu_plat_device_ids[] = {
{}, {},
}; };
static const struct pmu_probe_info pmu_probe_table[] = {
ARM_PMU_PROBE(ARM_CPU_PART_ARM1136, armv6_1136_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM1156, armv6_1156_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM1176, armv6_1176_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_ARM11MPCORE, armv6mpcore_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A8, armv7_a8_pmu_init),
ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A9, armv7_a9_pmu_init),
XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V1, xscale1pmu_init),
XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V2, xscale2pmu_init),
{ /* sentinel value */ }
};
/* /*
* CPU PMU identification and probing. * CPU PMU identification and probing.
*/ */
static int probe_current_pmu(struct arm_pmu *pmu) static int probe_current_pmu(struct arm_pmu *pmu)
{ {
int cpu = get_cpu(); int cpu = get_cpu();
unsigned int cpuid = read_cpuid_id();
int ret = -ENODEV; int ret = -ENODEV;
const struct pmu_probe_info *info;
pr_info("probing PMU on CPU %d\n", cpu); pr_info("probing PMU on CPU %d\n", cpu);
switch (read_cpuid_part()) { for (info = pmu_probe_table; info->init != NULL; info++) {
/* ARM Ltd CPUs. */ if ((cpuid & info->mask) != info->cpuid)
case ARM_CPU_PART_ARM1136: continue;
ret = armv6_1136_pmu_init(pmu); ret = info->init(pmu);
break;
case ARM_CPU_PART_ARM1156:
ret = armv6_1156_pmu_init(pmu);
break;
case ARM_CPU_PART_ARM1176:
ret = armv6_1176_pmu_init(pmu);
break;
case ARM_CPU_PART_ARM11MPCORE:
ret = armv6mpcore_pmu_init(pmu);
break;
case ARM_CPU_PART_CORTEX_A8:
ret = armv7_a8_pmu_init(pmu);
break;
case ARM_CPU_PART_CORTEX_A9:
ret = armv7_a9_pmu_init(pmu);
break;
default:
if (read_cpuid_implementor() == ARM_CPU_IMP_INTEL) {
switch (xscale_cpu_arch_version()) {
case ARM_CPU_XSCALE_ARCH_V1:
ret = xscale1pmu_init(pmu);
break;
case ARM_CPU_XSCALE_ARCH_V2:
ret = xscale2pmu_init(pmu);
break;
}
}
break; break;
} }
...@@ -299,13 +298,13 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) ...@@ -299,13 +298,13 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
int ret = -ENODEV; int ret = -ENODEV;
if (cpu_pmu) { if (cpu_pmu) {
pr_info("attempt to register multiple PMU devices!"); pr_info("attempt to register multiple PMU devices!\n");
return -ENOSPC; return -ENOSPC;
} }
pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL); pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL);
if (!pmu) { if (!pmu) {
pr_info("failed to allocate PMU device!"); pr_info("failed to allocate PMU device!\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -320,18 +319,24 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) ...@@ -320,18 +319,24 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
} }
if (ret) { if (ret) {
pr_info("failed to probe PMU!"); pr_info("failed to probe PMU!\n");
goto out_free; goto out_free;
} }
cpu_pmu_init(cpu_pmu); ret = cpu_pmu_init(cpu_pmu);
ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW); if (ret)
goto out_free;
if (!ret) ret = armpmu_register(cpu_pmu, -1);
return 0; if (ret)
goto out_destroy;
return 0;
out_destroy:
cpu_pmu_destroy(cpu_pmu);
out_free: out_free:
pr_info("failed to register PMU devices!"); pr_info("failed to register PMU devices!\n");
kfree(pmu); kfree(pmu);
return ret; return ret;
} }
...@@ -348,16 +353,6 @@ static struct platform_driver cpu_pmu_driver = { ...@@ -348,16 +353,6 @@ static struct platform_driver cpu_pmu_driver = {
static int __init register_pmu_driver(void) static int __init register_pmu_driver(void)
{ {
int err; return platform_driver_register(&cpu_pmu_driver);
err = register_cpu_notifier(&cpu_pmu_hotplug_notifier);
if (err)
return err;
err = platform_driver_register(&cpu_pmu_driver);
if (err)
unregister_cpu_notifier(&cpu_pmu_hotplug_notifier);
return err;
} }
device_initcall(register_pmu_driver); device_initcall(register_pmu_driver);
...@@ -262,7 +262,7 @@ static void armv6pmu_enable_event(struct perf_event *event) ...@@ -262,7 +262,7 @@ static void armv6pmu_enable_event(struct perf_event *event)
unsigned long val, mask, evt, flags; unsigned long val, mask, evt, flags;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
if (ARMV6_CYCLE_COUNTER == idx) { if (ARMV6_CYCLE_COUNTER == idx) {
...@@ -300,7 +300,7 @@ armv6pmu_handle_irq(int irq_num, ...@@ -300,7 +300,7 @@ armv6pmu_handle_irq(int irq_num,
unsigned long pmcr = armv6_pmcr_read(); unsigned long pmcr = armv6_pmcr_read();
struct perf_sample_data data; struct perf_sample_data data;
struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev;
struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs; struct pt_regs *regs;
int idx; int idx;
...@@ -356,7 +356,7 @@ armv6pmu_handle_irq(int irq_num, ...@@ -356,7 +356,7 @@ armv6pmu_handle_irq(int irq_num,
static void armv6pmu_start(struct arm_pmu *cpu_pmu) static void armv6pmu_start(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = armv6_pmcr_read(); val = armv6_pmcr_read();
...@@ -368,7 +368,7 @@ static void armv6pmu_start(struct arm_pmu *cpu_pmu) ...@@ -368,7 +368,7 @@ static void armv6pmu_start(struct arm_pmu *cpu_pmu)
static void armv6pmu_stop(struct arm_pmu *cpu_pmu) static void armv6pmu_stop(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = armv6_pmcr_read(); val = armv6_pmcr_read();
...@@ -409,7 +409,7 @@ static void armv6pmu_disable_event(struct perf_event *event) ...@@ -409,7 +409,7 @@ static void armv6pmu_disable_event(struct perf_event *event)
unsigned long val, mask, evt, flags; unsigned long val, mask, evt, flags;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
if (ARMV6_CYCLE_COUNTER == idx) { if (ARMV6_CYCLE_COUNTER == idx) {
...@@ -444,7 +444,7 @@ static void armv6mpcore_pmu_disable_event(struct perf_event *event) ...@@ -444,7 +444,7 @@ static void armv6mpcore_pmu_disable_event(struct perf_event *event)
unsigned long val, mask, flags, evt = 0; unsigned long val, mask, flags, evt = 0;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
if (ARMV6_CYCLE_COUNTER == idx) { if (ARMV6_CYCLE_COUNTER == idx) {
......
...@@ -564,13 +564,11 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx) ...@@ -564,13 +564,11 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx)
return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx)); return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx));
} }
static inline int armv7_pmnc_select_counter(int idx) static inline void armv7_pmnc_select_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); u32 counter = ARMV7_IDX_TO_COUNTER(idx);
asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
isb(); isb();
return idx;
} }
static inline u32 armv7pmu_read_counter(struct perf_event *event) static inline u32 armv7pmu_read_counter(struct perf_event *event)
...@@ -580,13 +578,15 @@ static inline u32 armv7pmu_read_counter(struct perf_event *event) ...@@ -580,13 +578,15 @@ static inline u32 armv7pmu_read_counter(struct perf_event *event)
int idx = hwc->idx; int idx = hwc->idx;
u32 value = 0; u32 value = 0;
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
pr_err("CPU%u reading wrong counter %d\n", pr_err("CPU%u reading wrong counter %d\n",
smp_processor_id(), idx); smp_processor_id(), idx);
else if (idx == ARMV7_IDX_CYCLE_COUNTER) } else if (idx == ARMV7_IDX_CYCLE_COUNTER) {
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
else if (armv7_pmnc_select_counter(idx) == idx) } else {
armv7_pmnc_select_counter(idx);
asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value));
}
return value; return value;
} }
...@@ -597,45 +597,43 @@ static inline void armv7pmu_write_counter(struct perf_event *event, u32 value) ...@@ -597,45 +597,43 @@ static inline void armv7pmu_write_counter(struct perf_event *event, u32 value)
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx; int idx = hwc->idx;
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
pr_err("CPU%u writing wrong counter %d\n", pr_err("CPU%u writing wrong counter %d\n",
smp_processor_id(), idx); smp_processor_id(), idx);
else if (idx == ARMV7_IDX_CYCLE_COUNTER) } else if (idx == ARMV7_IDX_CYCLE_COUNTER) {
asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value));
else if (armv7_pmnc_select_counter(idx) == idx) } else {
armv7_pmnc_select_counter(idx);
asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value)); asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value));
}
} }
static inline void armv7_pmnc_write_evtsel(int idx, u32 val) static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
{ {
if (armv7_pmnc_select_counter(idx) == idx) { armv7_pmnc_select_counter(idx);
val &= ARMV7_EVTYPE_MASK; val &= ARMV7_EVTYPE_MASK;
asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
}
} }
static inline int armv7_pmnc_enable_counter(int idx) static inline void armv7_pmnc_enable_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); u32 counter = ARMV7_IDX_TO_COUNTER(idx);
asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter)));
return idx;
} }
static inline int armv7_pmnc_disable_counter(int idx) static inline void armv7_pmnc_disable_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); u32 counter = ARMV7_IDX_TO_COUNTER(idx);
asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter)));
return idx;
} }
static inline int armv7_pmnc_enable_intens(int idx) static inline void armv7_pmnc_enable_intens(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); u32 counter = ARMV7_IDX_TO_COUNTER(idx);
asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter)));
return idx;
} }
static inline int armv7_pmnc_disable_intens(int idx) static inline void armv7_pmnc_disable_intens(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); u32 counter = ARMV7_IDX_TO_COUNTER(idx);
asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter)));
...@@ -643,8 +641,6 @@ static inline int armv7_pmnc_disable_intens(int idx) ...@@ -643,8 +641,6 @@ static inline int armv7_pmnc_disable_intens(int idx)
/* Clear the overflow flag in case an interrupt is pending. */ /* Clear the overflow flag in case an interrupt is pending. */
asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter)));
isb(); isb();
return idx;
} }
static inline u32 armv7_pmnc_getreset_flags(void) static inline u32 armv7_pmnc_getreset_flags(void)
...@@ -667,34 +663,34 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu) ...@@ -667,34 +663,34 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)
u32 val; u32 val;
unsigned int cnt; unsigned int cnt;
printk(KERN_INFO "PMNC registers dump:\n"); pr_info("PMNC registers dump:\n");
asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
printk(KERN_INFO "PMNC =0x%08x\n", val); pr_info("PMNC =0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
printk(KERN_INFO "CNTENS=0x%08x\n", val); pr_info("CNTENS=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
printk(KERN_INFO "INTENS=0x%08x\n", val); pr_info("INTENS=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
printk(KERN_INFO "FLAGS =0x%08x\n", val); pr_info("FLAGS =0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
printk(KERN_INFO "SELECT=0x%08x\n", val); pr_info("SELECT=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
printk(KERN_INFO "CCNT =0x%08x\n", val); pr_info("CCNT =0x%08x\n", val);
for (cnt = ARMV7_IDX_COUNTER0; for (cnt = ARMV7_IDX_COUNTER0;
cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
armv7_pmnc_select_counter(cnt); armv7_pmnc_select_counter(cnt);
asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
printk(KERN_INFO "CNT[%d] count =0x%08x\n", pr_info("CNT[%d] count =0x%08x\n",
ARMV7_IDX_TO_COUNTER(cnt), val); ARMV7_IDX_TO_COUNTER(cnt), val);
asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", pr_info("CNT[%d] evtsel=0x%08x\n",
ARMV7_IDX_TO_COUNTER(cnt), val); ARMV7_IDX_TO_COUNTER(cnt), val);
} }
} }
...@@ -705,7 +701,7 @@ static void armv7pmu_enable_event(struct perf_event *event) ...@@ -705,7 +701,7 @@ static void armv7pmu_enable_event(struct perf_event *event)
unsigned long flags; unsigned long flags;
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
...@@ -751,7 +747,7 @@ static void armv7pmu_disable_event(struct perf_event *event) ...@@ -751,7 +747,7 @@ static void armv7pmu_disable_event(struct perf_event *event)
unsigned long flags; unsigned long flags;
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
...@@ -783,7 +779,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) ...@@ -783,7 +779,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
u32 pmnc; u32 pmnc;
struct perf_sample_data data; struct perf_sample_data data;
struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev;
struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs; struct pt_regs *regs;
int idx; int idx;
...@@ -843,7 +839,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) ...@@ -843,7 +839,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
static void armv7pmu_start(struct arm_pmu *cpu_pmu) static void armv7pmu_start(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags; unsigned long flags;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Enable all counters */ /* Enable all counters */
...@@ -854,7 +850,7 @@ static void armv7pmu_start(struct arm_pmu *cpu_pmu) ...@@ -854,7 +850,7 @@ static void armv7pmu_start(struct arm_pmu *cpu_pmu)
static void armv7pmu_stop(struct arm_pmu *cpu_pmu) static void armv7pmu_stop(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags; unsigned long flags;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Disable all counters */ /* Disable all counters */
...@@ -1287,7 +1283,7 @@ static void krait_pmu_disable_event(struct perf_event *event) ...@@ -1287,7 +1283,7 @@ static void krait_pmu_disable_event(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx; int idx = hwc->idx;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
/* Disable counter and interrupt */ /* Disable counter and interrupt */
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
...@@ -1313,7 +1309,7 @@ static void krait_pmu_enable_event(struct perf_event *event) ...@@ -1313,7 +1309,7 @@ static void krait_pmu_enable_event(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx; int idx = hwc->idx;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
/* /*
* Enable counter and interrupt, and set the counter to count * Enable counter and interrupt, and set the counter to count
......
...@@ -138,7 +138,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) ...@@ -138,7 +138,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev)
unsigned long pmnc; unsigned long pmnc;
struct perf_sample_data data; struct perf_sample_data data;
struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev;
struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs; struct pt_regs *regs;
int idx; int idx;
...@@ -198,7 +198,7 @@ static void xscale1pmu_enable_event(struct perf_event *event) ...@@ -198,7 +198,7 @@ static void xscale1pmu_enable_event(struct perf_event *event)
unsigned long val, mask, evt, flags; unsigned long val, mask, evt, flags;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
switch (idx) { switch (idx) {
...@@ -234,7 +234,7 @@ static void xscale1pmu_disable_event(struct perf_event *event) ...@@ -234,7 +234,7 @@ static void xscale1pmu_disable_event(struct perf_event *event)
unsigned long val, mask, evt, flags; unsigned long val, mask, evt, flags;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
switch (idx) { switch (idx) {
...@@ -287,7 +287,7 @@ xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc, ...@@ -287,7 +287,7 @@ xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc,
static void xscale1pmu_start(struct arm_pmu *cpu_pmu) static void xscale1pmu_start(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = xscale1pmu_read_pmnc(); val = xscale1pmu_read_pmnc();
...@@ -299,7 +299,7 @@ static void xscale1pmu_start(struct arm_pmu *cpu_pmu) ...@@ -299,7 +299,7 @@ static void xscale1pmu_start(struct arm_pmu *cpu_pmu)
static void xscale1pmu_stop(struct arm_pmu *cpu_pmu) static void xscale1pmu_stop(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = xscale1pmu_read_pmnc(); val = xscale1pmu_read_pmnc();
...@@ -485,7 +485,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) ...@@ -485,7 +485,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev)
unsigned long pmnc, of_flags; unsigned long pmnc, of_flags;
struct perf_sample_data data; struct perf_sample_data data;
struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev;
struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs; struct pt_regs *regs;
int idx; int idx;
...@@ -539,7 +539,7 @@ static void xscale2pmu_enable_event(struct perf_event *event) ...@@ -539,7 +539,7 @@ static void xscale2pmu_enable_event(struct perf_event *event)
unsigned long flags, ien, evtsel; unsigned long flags, ien, evtsel;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
ien = xscale2pmu_read_int_enable(); ien = xscale2pmu_read_int_enable();
...@@ -585,7 +585,7 @@ static void xscale2pmu_disable_event(struct perf_event *event) ...@@ -585,7 +585,7 @@ static void xscale2pmu_disable_event(struct perf_event *event)
unsigned long flags, ien, evtsel, of_flags; unsigned long flags, ien, evtsel, of_flags;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
int idx = hwc->idx; int idx = hwc->idx;
ien = xscale2pmu_read_int_enable(); ien = xscale2pmu_read_int_enable();
...@@ -651,7 +651,7 @@ xscale2pmu_get_event_idx(struct pmu_hw_events *cpuc, ...@@ -651,7 +651,7 @@ xscale2pmu_get_event_idx(struct pmu_hw_events *cpuc,
static void xscale2pmu_start(struct arm_pmu *cpu_pmu) static void xscale2pmu_start(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64;
...@@ -663,7 +663,7 @@ static void xscale2pmu_start(struct arm_pmu *cpu_pmu) ...@@ -663,7 +663,7 @@ static void xscale2pmu_start(struct arm_pmu *cpu_pmu)
static void xscale2pmu_stop(struct arm_pmu *cpu_pmu) static void xscale2pmu_stop(struct arm_pmu *cpu_pmu)
{ {
unsigned long flags, val; unsigned long flags, val;
struct pmu_hw_events *events = cpu_pmu->get_hw_events(); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
raw_spin_lock_irqsave(&events->pmu_lock, flags); raw_spin_lock_irqsave(&events->pmu_lock, flags);
val = xscale2pmu_read_pmnc(); val = xscale2pmu_read_pmnc();
......
...@@ -2,6 +2,7 @@ menuconfig ARCH_TEGRA ...@@ -2,6 +2,7 @@ menuconfig ARCH_TEGRA
bool "NVIDIA Tegra" if ARCH_MULTI_V7 bool "NVIDIA Tegra" if ARCH_MULTI_V7
select ARCH_REQUIRE_GPIOLIB select ARCH_REQUIRE_GPIOLIB
select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
select ARM_AMBA
select ARM_GIC select ARM_GIC
select CLKSRC_MMIO select CLKSRC_MMIO
select HAVE_ARM_SCU if SMP select HAVE_ARM_SCU if SMP
...@@ -59,12 +60,4 @@ config ARCH_TEGRA_124_SOC ...@@ -59,12 +60,4 @@ config ARCH_TEGRA_124_SOC
Support for NVIDIA Tegra T124 processor family, based on the Support for NVIDIA Tegra T124 processor family, based on the
ARM CortexA15MP CPU ARM CortexA15MP CPU
config TEGRA_AHB
bool "Enable AHB driver for NVIDIA Tegra SoCs"
default y
help
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
which controls AHB bus master arbitration and some
performance parameters(priority, prefech size).
endif endif
...@@ -169,9 +169,6 @@ endmenu ...@@ -169,9 +169,6 @@ endmenu
menu "Bus support" menu "Bus support"
config ARM_AMBA
bool
config PCI config PCI
bool "PCI support" bool "PCI support"
help help
......
...@@ -104,6 +104,15 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl) ...@@ -104,6 +104,15 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl)
asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl));
} }
static inline u64 arch_counter_get_cntpct(void)
{
/*
* AArch64 kernel and user space mandate the use of CNTVCT.
*/
BUG();
return 0;
}
static inline u64 arch_counter_get_cntvct(void) static inline u64 arch_counter_get_cntvct(void)
{ {
u64 cval; u64 cval;
......
...@@ -137,13 +137,16 @@ static inline void set_iommu_table_base_and_group(struct device *dev, ...@@ -137,13 +137,16 @@ static inline void set_iommu_table_base_and_group(struct device *dev,
iommu_add_device(dev); iommu_add_device(dev);
} }
extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl, extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
struct scatterlist *sglist, int nelems, struct scatterlist *sglist, int nelems,
unsigned long mask, enum dma_data_direction direction, unsigned long mask,
struct dma_attrs *attrs); enum dma_data_direction direction,
extern void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, struct dma_attrs *attrs);
int nelems, enum dma_data_direction direction, extern void ppc_iommu_unmap_sg(struct iommu_table *tbl,
struct dma_attrs *attrs); struct scatterlist *sglist,
int nelems,
enum dma_data_direction direction,
struct dma_attrs *attrs);
extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
size_t size, dma_addr_t *dma_handle, size_t size, dma_addr_t *dma_handle,
......
...@@ -60,16 +60,16 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist, ...@@ -60,16 +60,16 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction direction, int nelems, enum dma_data_direction direction,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
return iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems, return ppc_iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
device_to_mask(dev), direction, attrs); device_to_mask(dev), direction, attrs);
} }
static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist, static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction direction, int nelems, enum dma_data_direction direction,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems, direction, ppc_iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems,
attrs); direction, attrs);
} }
/* We support DMA to/from any memory page via the iommu */ /* We support DMA to/from any memory page via the iommu */
......
...@@ -428,10 +428,10 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, ...@@ -428,10 +428,10 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
ppc_md.tce_flush(tbl); ppc_md.tce_flush(tbl);
} }
int iommu_map_sg(struct device *dev, struct iommu_table *tbl, int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
struct scatterlist *sglist, int nelems, struct scatterlist *sglist, int nelems,
unsigned long mask, enum dma_data_direction direction, unsigned long mask, enum dma_data_direction direction,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
dma_addr_t dma_next = 0, dma_addr; dma_addr_t dma_next = 0, dma_addr;
struct scatterlist *s, *outs, *segstart; struct scatterlist *s, *outs, *segstart;
...@@ -539,7 +539,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl, ...@@ -539,7 +539,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
DBG("mapped %d elements:\n", outcount); DBG("mapped %d elements:\n", outcount);
/* For the sake of iommu_unmap_sg, we clear out the length in the /* For the sake of ppc_iommu_unmap_sg, we clear out the length in the
* next entry of the sglist if we didn't fill the list completely * next entry of the sglist if we didn't fill the list completely
*/ */
if (outcount < incount) { if (outcount < incount) {
...@@ -572,9 +572,9 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl, ...@@ -572,9 +572,9 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
} }
void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
int nelems, enum dma_data_direction direction, int nelems, enum dma_data_direction direction,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
struct scatterlist *sg; struct scatterlist *sg;
......
...@@ -621,8 +621,9 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, ...@@ -621,8 +621,9 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
else else
return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg,
device_to_mask(dev), direction, attrs); nents, device_to_mask(dev),
direction, attrs);
} }
static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
...@@ -632,8 +633,8 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, ...@@ -632,8 +633,8 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
else else
iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents,
attrs); direction, attrs);
} }
static int dma_fixed_dma_supported(struct device *dev, u64 mask) static int dma_fixed_dma_supported(struct device *dev, u64 mask)
......
menu "Device Drivers" menu "Device Drivers"
source "drivers/amba/Kconfig"
source "drivers/base/Kconfig" source "drivers/base/Kconfig"
source "drivers/bus/Kconfig" source "drivers/bus/Kconfig"
......
config ARM_AMBA
bool
if ARM_AMBA
config TEGRA_AHB
bool "Enable AHB driver for NVIDIA Tegra SoCs"
default y if ARCH_TEGRA
help
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
which controls AHB bus master arbitration and some performance
parameters (priority, prefetch size).
endif
...@@ -6,7 +6,7 @@ menu "Bus devices" ...@@ -6,7 +6,7 @@ menu "Bus devices"
config BRCMSTB_GISB_ARB config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter" bool "Broadcom STB GISB bus arbiter"
depends on ARM depends on ARM || MIPS
help help
Driver for the Broadcom Set Top Box System-on-a-chip internal bus Driver for the Broadcom Set Top Box System-on-a-chip internal bus
arbiter. This driver provides timeout and target abort error handling arbiter. This driver provides timeout and target abort error handling
......
This diff is collapsed.
...@@ -25,26 +25,72 @@ ...@@ -25,26 +25,72 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/pm.h> #include <linux/pm.h>
#ifdef CONFIG_ARM
#include <asm/bug.h> #include <asm/bug.h>
#include <asm/signal.h> #include <asm/signal.h>
#endif
#define ARB_TIMER 0x008
#define ARB_ERR_CAP_CLR 0x7e4
#define ARB_ERR_CAP_CLEAR (1 << 0) #define ARB_ERR_CAP_CLEAR (1 << 0)
#define ARB_ERR_CAP_HI_ADDR 0x7e8
#define ARB_ERR_CAP_ADDR 0x7ec
#define ARB_ERR_CAP_DATA 0x7f0
#define ARB_ERR_CAP_STATUS 0x7f4
#define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) #define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12)
#define ARB_ERR_CAP_STATUS_TEA (1 << 11) #define ARB_ERR_CAP_STATUS_TEA (1 << 11)
#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) #define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2)
#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c #define ARB_ERR_CAP_STATUS_BS_MASK 0x3c
#define ARB_ERR_CAP_STATUS_WRITE (1 << 1) #define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
#define ARB_ERR_CAP_STATUS_VALID (1 << 0) #define ARB_ERR_CAP_STATUS_VALID (1 << 0)
#define ARB_ERR_CAP_MASTER 0x7f8
enum {
ARB_TIMER,
ARB_ERR_CAP_CLR,
ARB_ERR_CAP_HI_ADDR,
ARB_ERR_CAP_ADDR,
ARB_ERR_CAP_DATA,
ARB_ERR_CAP_STATUS,
ARB_ERR_CAP_MASTER,
};
static const int gisb_offsets_bcm7038[] = {
[ARB_TIMER] = 0x00c,
[ARB_ERR_CAP_CLR] = 0x0c4,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0c8,
[ARB_ERR_CAP_DATA] = 0x0cc,
[ARB_ERR_CAP_STATUS] = 0x0d0,
[ARB_ERR_CAP_MASTER] = -1,
};
static const int gisb_offsets_bcm7400[] = {
[ARB_TIMER] = 0x00c,
[ARB_ERR_CAP_CLR] = 0x0c8,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0cc,
[ARB_ERR_CAP_DATA] = 0x0d0,
[ARB_ERR_CAP_STATUS] = 0x0d4,
[ARB_ERR_CAP_MASTER] = 0x0d8,
};
static const int gisb_offsets_bcm7435[] = {
[ARB_TIMER] = 0x00c,
[ARB_ERR_CAP_CLR] = 0x168,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x16c,
[ARB_ERR_CAP_DATA] = 0x170,
[ARB_ERR_CAP_STATUS] = 0x174,
[ARB_ERR_CAP_MASTER] = 0x178,
};
static const int gisb_offsets_bcm7445[] = {
[ARB_TIMER] = 0x008,
[ARB_ERR_CAP_CLR] = 0x7e4,
[ARB_ERR_CAP_HI_ADDR] = 0x7e8,
[ARB_ERR_CAP_ADDR] = 0x7ec,
[ARB_ERR_CAP_DATA] = 0x7f0,
[ARB_ERR_CAP_STATUS] = 0x7f4,
[ARB_ERR_CAP_MASTER] = 0x7f8,
};
struct brcmstb_gisb_arb_device { struct brcmstb_gisb_arb_device {
void __iomem *base; void __iomem *base;
const int *gisb_offsets;
struct mutex lock; struct mutex lock;
struct list_head next; struct list_head next;
u32 valid_mask; u32 valid_mask;
...@@ -54,6 +100,26 @@ struct brcmstb_gisb_arb_device { ...@@ -54,6 +100,26 @@ struct brcmstb_gisb_arb_device {
static LIST_HEAD(brcmstb_gisb_arb_device_list); static LIST_HEAD(brcmstb_gisb_arb_device_list);
static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg)
{
int offset = gdev->gisb_offsets[reg];
/* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
if (offset == -1)
return 1;
return ioread32(gdev->base + offset);
}
static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
{
int offset = gdev->gisb_offsets[reg];
if (offset == -1)
return;
iowrite32(val, gdev->base + reg);
}
static ssize_t gisb_arb_get_timeout(struct device *dev, static ssize_t gisb_arb_get_timeout(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -63,7 +129,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev, ...@@ -63,7 +129,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev,
u32 timeout; u32 timeout;
mutex_lock(&gdev->lock); mutex_lock(&gdev->lock);
timeout = ioread32(gdev->base + ARB_TIMER); timeout = gisb_read(gdev, ARB_TIMER);
mutex_unlock(&gdev->lock); mutex_unlock(&gdev->lock);
return sprintf(buf, "%d", timeout); return sprintf(buf, "%d", timeout);
...@@ -85,7 +151,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev, ...@@ -85,7 +151,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev,
return -EINVAL; return -EINVAL;
mutex_lock(&gdev->lock); mutex_lock(&gdev->lock);
iowrite32(val, gdev->base + ARB_TIMER); gisb_write(gdev, val, ARB_TIMER);
mutex_unlock(&gdev->lock); mutex_unlock(&gdev->lock);
return count; return count;
...@@ -112,18 +178,18 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, ...@@ -112,18 +178,18 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
const char *m_name; const char *m_name;
char m_fmt[11]; char m_fmt[11];
cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS); cap_status = gisb_read(gdev, ARB_ERR_CAP_STATUS);
/* Invalid captured address, bail out */ /* Invalid captured address, bail out */
if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) if (!(cap_status & ARB_ERR_CAP_STATUS_VALID))
return 1; return 1;
/* Read the address and master */ /* Read the address and master */
arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff; arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff;
#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) #if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32; arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
#endif #endif
master = ioread32(gdev->base + ARB_ERR_CAP_MASTER); master = gisb_read(gdev, ARB_ERR_CAP_MASTER);
m_name = brcmstb_gisb_master_to_str(gdev, master); m_name = brcmstb_gisb_master_to_str(gdev, master);
if (!m_name) { if (!m_name) {
...@@ -138,11 +204,12 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, ...@@ -138,11 +204,12 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
m_name); m_name);
/* clear the GISB error */ /* clear the GISB error */
iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR); gisb_write(gdev, ARB_ERR_CAP_CLEAR, ARB_ERR_CAP_CLR);
return 0; return 0;
} }
#ifdef CONFIG_ARM
static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
...@@ -161,6 +228,7 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, ...@@ -161,6 +228,7 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
return ret; return ret;
} }
#endif
static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id)
{ {
...@@ -188,10 +256,20 @@ static struct attribute_group gisb_arb_sysfs_attr_group = { ...@@ -188,10 +256,20 @@ static struct attribute_group gisb_arb_sysfs_attr_group = {
.attrs = gisb_arb_sysfs_attrs, .attrs = gisb_arb_sysfs_attrs,
}; };
static int brcmstb_gisb_arb_probe(struct platform_device *pdev) static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,gisb-arb", .data = gisb_offsets_bcm7445 },
{ .compatible = "brcm,bcm7445-gisb-arb", .data = gisb_offsets_bcm7445 },
{ .compatible = "brcm,bcm7435-gisb-arb", .data = gisb_offsets_bcm7435 },
{ .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 },
{ .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 },
{ },
};
static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
{ {
struct device_node *dn = pdev->dev.of_node; struct device_node *dn = pdev->dev.of_node;
struct brcmstb_gisb_arb_device *gdev; struct brcmstb_gisb_arb_device *gdev;
const struct of_device_id *of_id;
struct resource *r; struct resource *r;
int err, timeout_irq, tea_irq; int err, timeout_irq, tea_irq;
unsigned int num_masters, j = 0; unsigned int num_masters, j = 0;
...@@ -212,6 +290,13 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) ...@@ -212,6 +290,13 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
if (IS_ERR(gdev->base)) if (IS_ERR(gdev->base))
return PTR_ERR(gdev->base); return PTR_ERR(gdev->base);
of_id = of_match_node(brcmstb_gisb_arb_of_match, dn);
if (!of_id) {
pr_err("failed to look up compatible string\n");
return -EINVAL;
}
gdev->gisb_offsets = of_id->data;
err = devm_request_irq(&pdev->dev, timeout_irq, err = devm_request_irq(&pdev->dev, timeout_irq,
brcmstb_gisb_timeout_handler, 0, pdev->name, brcmstb_gisb_timeout_handler, 0, pdev->name,
gdev); gdev);
...@@ -257,8 +342,10 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) ...@@ -257,8 +342,10 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
#ifdef CONFIG_ARM
hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0,
"imprecise external abort"); "imprecise external abort");
#endif
dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n",
gdev->base, timeout_irq, tea_irq); gdev->base, timeout_irq, tea_irq);
...@@ -272,7 +359,7 @@ static int brcmstb_gisb_arb_suspend(struct device *dev) ...@@ -272,7 +359,7 @@ static int brcmstb_gisb_arb_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
gdev->saved_timeout = ioread32(gdev->base + ARB_TIMER); gdev->saved_timeout = gisb_read(gdev, ARB_TIMER);
return 0; return 0;
} }
...@@ -285,7 +372,7 @@ static int brcmstb_gisb_arb_resume_noirq(struct device *dev) ...@@ -285,7 +372,7 @@ static int brcmstb_gisb_arb_resume_noirq(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
iowrite32(gdev->saved_timeout, gdev->base + ARB_TIMER); gisb_write(gdev, gdev->saved_timeout, ARB_TIMER);
return 0; return 0;
} }
...@@ -299,13 +386,7 @@ static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = { ...@@ -299,13 +386,7 @@ static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = {
.resume_noirq = brcmstb_gisb_arb_resume_noirq, .resume_noirq = brcmstb_gisb_arb_resume_noirq,
}; };
static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,gisb-arb" },
{ },
};
static struct platform_driver brcmstb_gisb_arb_driver = { static struct platform_driver brcmstb_gisb_arb_driver = {
.probe = brcmstb_gisb_arb_probe,
.driver = { .driver = {
.name = "brcm-gisb-arb", .name = "brcm-gisb-arb",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -316,7 +397,8 @@ static struct platform_driver brcmstb_gisb_arb_driver = { ...@@ -316,7 +397,8 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
static int __init brcm_gisb_driver_init(void) static int __init brcm_gisb_driver_init(void)
{ {
return platform_driver_register(&brcmstb_gisb_arb_driver); return platform_driver_probe(&brcmstb_gisb_arb_driver,
brcmstb_gisb_arb_probe);
} }
module_init(brcm_gisb_driver_init); module_init(brcm_gisb_driver_init);
...@@ -222,10 +222,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) ...@@ -222,10 +222,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
} }
/* Error found so break the for loop */ /* Error found so break the for loop */
break; return IRQ_HANDLED;
} }
} }
return IRQ_HANDLED;
dev_err(l3->dev, "L3 %s IRQ not handled!!\n",
inttype ? "debug" : "application");
return IRQ_NONE;
} }
static const struct of_device_id l3_noc_match[] = { static const struct of_device_id l3_noc_match[] = {
...@@ -296,11 +300,66 @@ static int omap_l3_probe(struct platform_device *pdev) ...@@ -296,11 +300,66 @@ static int omap_l3_probe(struct platform_device *pdev)
return ret; return ret;
} }
#ifdef CONFIG_PM
/**
* l3_resume_noirq() - resume function for l3_noc
* @dev: pointer to l3_noc device structure
*
* We only have the resume handler only since we
* have already maintained the delta register
* configuration as part of configuring the system
*/
static int l3_resume_noirq(struct device *dev)
{
struct omap_l3 *l3 = dev_get_drvdata(dev);
int i;
struct l3_flagmux_data *flag_mux;
void __iomem *base, *mask_regx = NULL;
u32 mask_val;
for (i = 0; i < l3->num_modules; i++) {
base = l3->l3_base[i];
flag_mux = l3->l3_flagmux[i];
if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits)
continue;
mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 +
(L3_APPLICATION_ERROR << 3);
mask_val = readl_relaxed(mask_regx);
mask_val &= ~(flag_mux->mask_app_bits);
writel_relaxed(mask_val, mask_regx);
mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 +
(L3_DEBUG_ERROR << 3);
mask_val = readl_relaxed(mask_regx);
mask_val &= ~(flag_mux->mask_dbg_bits);
writel_relaxed(mask_val, mask_regx);
}
/* Dummy read to force OCP barrier */
if (mask_regx)
(void)readl(mask_regx);
return 0;
}
static const struct dev_pm_ops l3_dev_pm_ops = {
.resume_noirq = l3_resume_noirq,
};
#define L3_DEV_PM_OPS (&l3_dev_pm_ops)
#else
#define L3_DEV_PM_OPS NULL
#endif
static struct platform_driver omap_l3_driver = { static struct platform_driver omap_l3_driver = {
.probe = omap_l3_probe, .probe = omap_l3_probe,
.driver = { .driver = {
.name = "omap_l3_noc", .name = "omap_l3_noc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = L3_DEV_PM_OPS,
.of_match_table = of_match_ptr(l3_noc_match), .of_match_table = of_match_ptr(l3_noc_match),
}, },
}; };
......
...@@ -185,3 +185,16 @@ struct clk *tegra_clk_register_divider(const char *name, ...@@ -185,3 +185,16 @@ struct clk *tegra_clk_register_divider(const char *name,
return clk; return clk;
} }
static const struct clk_div_table mc_div_table[] = {
{ .val = 0, .div = 2 },
{ .val = 1, .div = 1 },
{ .val = 0, .div = 0 },
};
struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
void __iomem *reg, spinlock_t *lock)
{
return clk_register_divider_table(NULL, name, parent_name, 0, reg,
16, 1, 0, mc_div_table, lock);
}
...@@ -173,6 +173,7 @@ static DEFINE_SPINLOCK(pll_d_lock); ...@@ -173,6 +173,7 @@ static DEFINE_SPINLOCK(pll_d_lock);
static DEFINE_SPINLOCK(pll_d2_lock); static DEFINE_SPINLOCK(pll_d2_lock);
static DEFINE_SPINLOCK(pll_u_lock); static DEFINE_SPINLOCK(pll_u_lock);
static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_re_lock);
static DEFINE_SPINLOCK(emc_lock);
static struct div_nmp pllxc_nmp = { static struct div_nmp pllxc_nmp = {
.divm_shift = 0, .divm_shift = 0,
...@@ -1228,7 +1229,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, ...@@ -1228,7 +1229,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base,
ARRAY_SIZE(mux_pllmcp_clkm), ARRAY_SIZE(mux_pllmcp_clkm),
CLK_SET_RATE_NO_REPARENT, CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC, clk_base + CLK_SOURCE_EMC,
29, 3, 0, NULL); 29, 3, 0, &emc_lock);
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
clks[TEGRA114_CLK_MC] = clk;
for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
data = &tegra_periph_clk_list[i]; data = &tegra_periph_clk_list[i];
......
...@@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(pll_d2_lock); ...@@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(pll_d2_lock);
static DEFINE_SPINLOCK(pll_e_lock); static DEFINE_SPINLOCK(pll_e_lock);
static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_re_lock);
static DEFINE_SPINLOCK(pll_u_lock); static DEFINE_SPINLOCK(pll_u_lock);
static DEFINE_SPINLOCK(emc_lock);
/* possible OSC frequencies in Hz */ /* possible OSC frequencies in Hz */
static unsigned long tegra124_input_freq[] = { static unsigned long tegra124_input_freq[] = {
...@@ -1127,7 +1128,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, ...@@ -1127,7 +1128,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base,
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
ARRAY_SIZE(mux_pllmcp_clkm), 0, ARRAY_SIZE(mux_pllmcp_clkm), 0,
clk_base + CLK_SOURCE_EMC, clk_base + CLK_SOURCE_EMC,
29, 3, 0, NULL); 29, 3, 0, &emc_lock);
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
clks[TEGRA124_CLK_MC] = clk;
/* cml0 */ /* cml0 */
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
......
...@@ -140,6 +140,8 @@ static struct cpu_clk_suspend_context { ...@@ -140,6 +140,8 @@ static struct cpu_clk_suspend_context {
static void __iomem *clk_base; static void __iomem *clk_base;
static void __iomem *pmc_base; static void __iomem *pmc_base;
static DEFINE_SPINLOCK(emc_lock);
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
_clk_num, _gate_flags, _clk_id) \ _clk_num, _gate_flags, _clk_id) \
TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
...@@ -819,11 +821,15 @@ static void __init tegra20_periph_clk_init(void) ...@@ -819,11 +821,15 @@ static void __init tegra20_periph_clk_init(void)
ARRAY_SIZE(mux_pllmcp_clkm), ARRAY_SIZE(mux_pllmcp_clkm),
CLK_SET_RATE_NO_REPARENT, CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC, clk_base + CLK_SOURCE_EMC,
30, 2, 0, NULL); 30, 2, 0, &emc_lock);
clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
57, periph_clk_enb_refcnt); 57, periph_clk_enb_refcnt);
clks[TEGRA20_CLK_EMC] = clk; clks[TEGRA20_CLK_EMC] = clk;
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
clks[TEGRA20_CLK_MC] = clk;
/* dsi */ /* dsi */
clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
48, periph_clk_enb_refcnt); 48, periph_clk_enb_refcnt);
......
...@@ -177,6 +177,7 @@ static unsigned long input_freq; ...@@ -177,6 +177,7 @@ static unsigned long input_freq;
static DEFINE_SPINLOCK(cml_lock); static DEFINE_SPINLOCK(cml_lock);
static DEFINE_SPINLOCK(pll_d_lock); static DEFINE_SPINLOCK(pll_d_lock);
static DEFINE_SPINLOCK(emc_lock);
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
_clk_num, _gate_flags, _clk_id) \ _clk_num, _gate_flags, _clk_id) \
...@@ -1157,11 +1158,15 @@ static void __init tegra30_periph_clk_init(void) ...@@ -1157,11 +1158,15 @@ static void __init tegra30_periph_clk_init(void)
ARRAY_SIZE(mux_pllmcp_clkm), ARRAY_SIZE(mux_pllmcp_clkm),
CLK_SET_RATE_NO_REPARENT, CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC, clk_base + CLK_SOURCE_EMC,
30, 2, 0, NULL); 30, 2, 0, &emc_lock);
clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
57, periph_clk_enb_refcnt); 57, periph_clk_enb_refcnt);
clks[TEGRA30_CLK_EMC] = clk; clks[TEGRA30_CLK_EMC] = clk;
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
clks[TEGRA30_CLK_MC] = clk;
/* cml0 */ /* cml0 */
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
0, 0, &cml_lock); 0, 0, &cml_lock);
......
...@@ -86,6 +86,8 @@ struct clk *tegra_clk_register_divider(const char *name, ...@@ -86,6 +86,8 @@ struct clk *tegra_clk_register_divider(const char *name,
const char *parent_name, void __iomem *reg, const char *parent_name, void __iomem *reg,
unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
u8 frac_width, spinlock_t *lock); u8 frac_width, spinlock_t *lock);
struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
void __iomem *reg, spinlock_t *lock);
/* /*
* Tegra PLL: * Tegra PLL:
......
...@@ -462,7 +462,10 @@ static void __init arch_counter_register(unsigned type) ...@@ -462,7 +462,10 @@ static void __init arch_counter_register(unsigned type)
/* Register the CP15 based counter if we have one */ /* Register the CP15 based counter if we have one */
if (type & ARCH_CP15_TIMER) { if (type & ARCH_CP15_TIMER) {
arch_timer_read_counter = arch_counter_get_cntvct; if (arch_timer_use_virtual)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
} else { } else {
arch_timer_read_counter = arch_counter_get_cntvct_mem; arch_timer_read_counter = arch_counter_get_cntvct_mem;
...@@ -701,6 +704,14 @@ static void __init arch_timer_init(struct device_node *np) ...@@ -701,6 +704,14 @@ static void __init arch_timer_init(struct device_node *np)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i); arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np); arch_timer_detect_rate(NULL, np);
/*
* If we cannot rely on firmware initializing the timer registers then
* we should use the physical timers instead.
*/
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_use_virtual = false;
/* /*
* If HYP mode is available, we know that the physical timer * If HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so * has been configured to be accessible from PL1. Use it, so
......
...@@ -163,14 +163,14 @@ config TEGRA_IOMMU_GART ...@@ -163,14 +163,14 @@ config TEGRA_IOMMU_GART
hardware included on Tegra SoCs. hardware included on Tegra SoCs.
config TEGRA_IOMMU_SMMU config TEGRA_IOMMU_SMMU
bool "Tegra SMMU IOMMU Support" bool "NVIDIA Tegra SMMU Support"
depends on ARCH_TEGRA && TEGRA_AHB depends on ARCH_TEGRA
depends on TEGRA_AHB
depends on TEGRA_MC
select IOMMU_API select IOMMU_API
help help
Enables support for remapping discontiguous physical memory This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra
shared with the operating system into contiguous I/O virtual SoCs (Tegra30 up to Tegra124).
space through the SMMU (System Memory Management Unit)
hardware included on Tegra SoCs.
config EXYNOS_IOMMU config EXYNOS_IOMMU
bool "Exynos IOMMU Support" bool "Exynos IOMMU Support"
......
...@@ -3424,6 +3424,7 @@ static const struct iommu_ops amd_iommu_ops = { ...@@ -3424,6 +3424,7 @@ static const struct iommu_ops amd_iommu_ops = {
.detach_dev = amd_iommu_detach_device, .detach_dev = amd_iommu_detach_device,
.map = amd_iommu_map, .map = amd_iommu_map,
.unmap = amd_iommu_unmap, .unmap = amd_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = amd_iommu_iova_to_phys, .iova_to_phys = amd_iommu_iova_to_phys,
.pgsize_bitmap = AMD_IOMMU_PGSIZES, .pgsize_bitmap = AMD_IOMMU_PGSIZES,
}; };
......
...@@ -1652,6 +1652,7 @@ static const struct iommu_ops arm_smmu_ops = { ...@@ -1652,6 +1652,7 @@ static const struct iommu_ops arm_smmu_ops = {
.detach_dev = arm_smmu_detach_dev, .detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map, .map = arm_smmu_map,
.unmap = arm_smmu_unmap, .unmap = arm_smmu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = arm_smmu_iova_to_phys, .iova_to_phys = arm_smmu_iova_to_phys,
.add_device = arm_smmu_add_device, .add_device = arm_smmu_add_device,
.remove_device = arm_smmu_remove_device, .remove_device = arm_smmu_remove_device,
......
...@@ -1178,6 +1178,7 @@ static const struct iommu_ops exynos_iommu_ops = { ...@@ -1178,6 +1178,7 @@ static const struct iommu_ops exynos_iommu_ops = {
.detach_dev = exynos_iommu_detach_device, .detach_dev = exynos_iommu_detach_device,
.map = exynos_iommu_map, .map = exynos_iommu_map,
.unmap = exynos_iommu_unmap, .unmap = exynos_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = exynos_iommu_iova_to_phys, .iova_to_phys = exynos_iommu_iova_to_phys,
.add_device = exynos_iommu_add_device, .add_device = exynos_iommu_add_device,
.remove_device = exynos_iommu_remove_device, .remove_device = exynos_iommu_remove_device,
......
...@@ -4467,6 +4467,7 @@ static const struct iommu_ops intel_iommu_ops = { ...@@ -4467,6 +4467,7 @@ static const struct iommu_ops intel_iommu_ops = {
.detach_dev = intel_iommu_detach_device, .detach_dev = intel_iommu_detach_device,
.map = intel_iommu_map, .map = intel_iommu_map,
.unmap = intel_iommu_unmap, .unmap = intel_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = intel_iommu_iova_to_phys, .iova_to_phys = intel_iommu_iova_to_phys,
.add_device = intel_iommu_add_device, .add_device = intel_iommu_add_device,
.remove_device = intel_iommu_remove_device, .remove_device = intel_iommu_remove_device,
......
...@@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) ...@@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
kfree(nb); kfree(nb);
return err; return err;
} }
return bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
if (err) {
bus_unregister_notifier(bus, nb);
kfree(nb);
return err;
}
return 0;
} }
/** /**
...@@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) ...@@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
*/ */
int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
{ {
int err;
if (bus->iommu_ops != NULL) if (bus->iommu_ops != NULL)
return -EBUSY; return -EBUSY;
bus->iommu_ops = ops; bus->iommu_ops = ops;
/* Do IOMMU specific setup for this bus-type */ /* Do IOMMU specific setup for this bus-type */
return iommu_bus_init(bus, ops); err = iommu_bus_init(bus, ops);
if (err)
bus->iommu_ops = NULL;
return err;
} }
EXPORT_SYMBOL_GPL(bus_set_iommu); EXPORT_SYMBOL_GPL(bus_set_iommu);
...@@ -1124,6 +1138,38 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) ...@@ -1124,6 +1138,38 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
} }
EXPORT_SYMBOL_GPL(iommu_unmap); EXPORT_SYMBOL_GPL(iommu_unmap);
size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents, int prot)
{
struct scatterlist *s;
size_t mapped = 0;
unsigned int i;
int ret;
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s));
/* We are mapping on page boundarys, so offset must be 0 */
if (s->offset)
goto out_err;
ret = iommu_map(domain, iova + mapped, phys, s->length, prot);
if (ret)
goto out_err;
mapped += s->length;
}
return mapped;
out_err:
/* undo mappings already done */
iommu_unmap(domain, iova, mapped);
return 0;
}
EXPORT_SYMBOL_GPL(default_iommu_map_sg);
int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
phys_addr_t paddr, u64 size, int prot) phys_addr_t paddr, u64 size, int prot)
......
...@@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = { ...@@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = {
.detach_dev = ipmmu_detach_device, .detach_dev = ipmmu_detach_device,
.map = ipmmu_map, .map = ipmmu_map,
.unmap = ipmmu_unmap, .unmap = ipmmu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = ipmmu_iova_to_phys, .iova_to_phys = ipmmu_iova_to_phys,
.add_device = ipmmu_add_device, .add_device = ipmmu_add_device,
.remove_device = ipmmu_remove_device, .remove_device = ipmmu_remove_device,
......
...@@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = { ...@@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = {
.detach_dev = msm_iommu_detach_dev, .detach_dev = msm_iommu_detach_dev,
.map = msm_iommu_map, .map = msm_iommu_map,
.unmap = msm_iommu_unmap, .unmap = msm_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = msm_iommu_iova_to_phys, .iova_to_phys = msm_iommu_iova_to_phys,
.pgsize_bitmap = MSM_IOMMU_PGSIZES, .pgsize_bitmap = MSM_IOMMU_PGSIZES,
}; };
......
...@@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = { ...@@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = {
.detach_dev = omap_iommu_detach_dev, .detach_dev = omap_iommu_detach_dev,
.map = omap_iommu_map, .map = omap_iommu_map,
.unmap = omap_iommu_unmap, .unmap = omap_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = omap_iommu_iova_to_phys, .iova_to_phys = omap_iommu_iova_to_phys,
.add_device = omap_iommu_add_device, .add_device = omap_iommu_add_device,
.remove_device = omap_iommu_remove_device, .remove_device = omap_iommu_remove_device,
......
...@@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = { ...@@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = {
.detach_dev = shmobile_iommu_detach_device, .detach_dev = shmobile_iommu_detach_device,
.map = shmobile_iommu_map, .map = shmobile_iommu_map,
.unmap = shmobile_iommu_unmap, .unmap = shmobile_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = shmobile_iommu_iova_to_phys, .iova_to_phys = shmobile_iommu_iova_to_phys,
.add_device = shmobile_iommu_add_device, .add_device = shmobile_iommu_add_device,
.pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K,
......
This diff is collapsed.
...@@ -61,16 +61,6 @@ config TEGRA20_MC ...@@ -61,16 +61,6 @@ config TEGRA20_MC
analysis, especially for IOMMU/GART(Graphics Address analysis, especially for IOMMU/GART(Graphics Address
Relocation Table) module. Relocation Table) module.
config TEGRA30_MC
bool "Tegra30 Memory Controller(MC) driver"
default y
depends on ARCH_TEGRA_3x_SOC
help
This driver is for the Memory Controller(MC) module available
in Tegra30 SoCs, mainly for a address translation fault
analysis, especially for IOMMU/SMMU(System Memory Management
Unit) module.
config FSL_CORENET_CF config FSL_CORENET_CF
tristate "Freescale CoreNet Error Reporting" tristate "Freescale CoreNet Error Reporting"
depends on FSL_SOC_BOOKE depends on FSL_SOC_BOOKE
...@@ -85,4 +75,6 @@ config FSL_IFC ...@@ -85,4 +75,6 @@ config FSL_IFC
bool bool
depends on FSL_SOC depends on FSL_SOC
source "drivers/memory/tegra/Kconfig"
endif endif
...@@ -12,4 +12,5 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o ...@@ -12,4 +12,5 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
obj-$(CONFIG_TEGRA_MC) += tegra/
config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support"
default y
depends on ARCH_TEGRA
help
This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs.
tegra-mc-y := mc.o
tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o
tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
/*
* Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
*
* 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 <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "mc.h"
#define MC_INTSTATUS 0x000
#define MC_INT_DECERR_MTS (1 << 16)
#define MC_INT_SECERR_SEC (1 << 13)
#define MC_INT_DECERR_VPR (1 << 12)
#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
#define MC_INT_ARBITRATION_EMEM (1 << 9)
#define MC_INT_SECURITY_VIOLATION (1 << 8)
#define MC_INT_DECERR_EMEM (1 << 6)
#define MC_INTMASK 0x004
#define MC_ERR_STATUS 0x08
#define MC_ERR_STATUS_TYPE_SHIFT 28
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_READABLE (1 << 27)
#define MC_ERR_STATUS_WRITABLE (1 << 26)
#define MC_ERR_STATUS_NONSECURE (1 << 25)
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
#define MC_ERR_STATUS_SECURITY (1 << 17)
#define MC_ERR_STATUS_RW (1 << 16)
#define MC_ERR_STATUS_CLIENT_MASK 0x7f
#define MC_ERR_ADR 0x0c
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
#define MC_EMEM_ARB_MISC0 0xd8
static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
{ .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
{ .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc },
#endif
{ }
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
{
unsigned long long tick;
unsigned int i;
u32 value;
/* compute the number of MC clock cycles per tick */
tick = mc->tick * clk_get_rate(mc->clk);
do_div(tick, NSEC_PER_SEC);
value = readl(mc->regs + MC_EMEM_ARB_CFG);
value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK;
value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick);
writel(value, mc->regs + MC_EMEM_ARB_CFG);
/* write latency allowance defaults */
for (i = 0; i < mc->soc->num_clients; i++) {
const struct tegra_mc_la *la = &mc->soc->clients[i].la;
u32 value;
value = readl(mc->regs + la->reg);
value &= ~(la->mask << la->shift);
value |= (la->def & la->mask) << la->shift;
writel(value, mc->regs + la->reg);
}
return 0;
}
static const char *const status_names[32] = {
[ 1] = "External interrupt",
[ 6] = "EMEM address decode error",
[ 8] = "Security violation",
[ 9] = "EMEM arbitration error",
[10] = "Page fault",
[11] = "Invalid APB ASID update",
[12] = "VPR violation",
[13] = "Secure carveout violation",
[16] = "MTS carveout violation",
};
static const char *const error_names[8] = {
[2] = "EMEM decode error",
[3] = "TrustZone violation",
[4] = "Carveout violation",
[6] = "SMMU translation error",
};
static irqreturn_t tegra_mc_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status, mask;
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS);
mask = mc_readl(mc, MC_INTMASK);
for_each_set_bit(bit, &status, 32) {
const char *error = status_names[bit] ?: "unknown";
const char *client = "unknown", *desc;
const char *direction, *secure;
phys_addr_t addr = 0;
unsigned int i;
char perm[7];
u8 id, type;
u32 value;
value = mc_readl(mc, MC_ERR_STATUS);
#ifdef CONFIG_PHYS_ADDR_T_64BIT
if (mc->soc->num_address_bits > 32) {
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
MC_ERR_STATUS_ADR_HI_MASK);
addr <<= 32;
}
#endif
if (value & MC_ERR_STATUS_RW)
direction = "write";
else
direction = "read";
if (value & MC_ERR_STATUS_SECURITY)
secure = "secure ";
else
secure = "";
id = value & MC_ERR_STATUS_CLIENT_MASK;
for (i = 0; i < mc->soc->num_clients; i++) {
if (mc->soc->clients[i].id == id) {
client = mc->soc->clients[i].name;
break;
}
}
type = (value & MC_ERR_STATUS_TYPE_MASK) >>
MC_ERR_STATUS_TYPE_SHIFT;
desc = error_names[type];
switch (value & MC_ERR_STATUS_TYPE_MASK) {
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
perm[0] = ' ';
perm[1] = '[';
if (value & MC_ERR_STATUS_READABLE)
perm[2] = 'R';
else
perm[2] = '-';
if (value & MC_ERR_STATUS_WRITABLE)
perm[3] = 'W';
else
perm[3] = '-';
if (value & MC_ERR_STATUS_NONSECURE)
perm[4] = '-';
else
perm[4] = 'S';
perm[5] = ']';
perm[6] = '\0';
break;
default:
perm[0] = '\0';
break;
}
value = mc_readl(mc, MC_ERR_ADR);
addr |= value;
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
client, secure, direction, &addr, error,
desc, perm);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
return IRQ_HANDLED;
}
static int tegra_mc_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct resource *res;
struct tegra_mc *mc;
u32 value;
int err;
match = of_match_node(tegra_mc_of_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
platform_set_drvdata(pdev, mc);
mc->soc = match->data;
mc->dev = &pdev->dev;
/* length of MC tick in nanoseconds */
mc->tick = 30;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mc->regs))
return PTR_ERR(mc->regs);
mc->clk = devm_clk_get(&pdev->dev, "mc");
if (IS_ERR(mc->clk)) {
dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
PTR_ERR(mc->clk));
return PTR_ERR(mc->clk);
}
err = tegra_mc_setup_latency_allowance(mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup latency allowance: %d\n",
err);
return err;
}
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
if (IS_ERR(mc->smmu)) {
dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
PTR_ERR(mc->smmu));
return PTR_ERR(mc->smmu);
}
}
mc->irq = platform_get_irq(pdev, 0);
if (mc->irq < 0) {
dev_err(&pdev->dev, "interrupt not specified\n");
return mc->irq;
}
err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED,
dev_name(&pdev->dev), mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
err);
return err;
}
value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
MC_INT_ARBITRATION_EMEM | MC_INT_SECURITY_VIOLATION |
MC_INT_DECERR_EMEM;
mc_writel(mc, value, MC_INTMASK);
return 0;
}
static struct platform_driver tegra_mc_driver = {
.driver = {
.name = "tegra-mc",
.of_match_table = tegra_mc_of_match,
.suppress_bind_attrs = true,
},
.prevent_deferred_probe = true,
.probe = tegra_mc_probe,
};
static int tegra_mc_init(void)
{
return platform_driver_register(&tegra_mc_driver);
}
arch_initcall(tegra_mc_init);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-$(CONFIG_RESET_CONTROLLER) += core.o
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_STI) += sti/
...@@ -125,6 +125,21 @@ int reset_control_deassert(struct reset_control *rstc) ...@@ -125,6 +125,21 @@ int reset_control_deassert(struct reset_control *rstc)
} }
EXPORT_SYMBOL_GPL(reset_control_deassert); EXPORT_SYMBOL_GPL(reset_control_deassert);
/**
* reset_control_status - returns a negative errno if not supported, a
* positive value if the reset line is asserted, or zero if the reset
* line is not asserted.
* @rstc: reset controller
*/
int reset_control_status(struct reset_control *rstc)
{
if (rstc->rcdev->ops->status)
return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(reset_control_status);
/** /**
* of_reset_control_get - Lookup and obtain a reference to a reset controller. * of_reset_control_get - Lookup and obtain a reference to a reset controller.
* @node: device to be reset by the controller * @node: device to be reset by the controller
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment