mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
ARM: SoC driver updates for 3.19
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIVAwUAVIcj4mCrR//JCVInAQIvWg//WD72+2q0RmEvu8r/YN4SDfg5iY7OMzgy Jyt6rN1IhXBY5GJL5Hil1q2JP/7o8vypekllohmBYWzXO3ZJ2VK6NPIXEMuzaiCz D9gmb+N6FdR2L2iYPv7B/3uOf55pHjBu525+vLspCTOgcWBrLgCnA9e9Yg462AEf VP3x+kV0AH25lovEi3mPrc2e46jnl0Mzp3f3PCkPqRSEMn7sxu9ipii+elxvArYp jYYCB03ZEBFa7T0e4HD38gnVLbC6dTj47AcSCWYP9WhxJ2RmCQKRBEnJre02hgar NPg8z+OrUACIAkvJHzg3WccmXdi0aqQ2JDsl46Tkl7pA6NdyMLfizT3OiZnMRmgc 34H0ZSxclW+j25aI8OmDpv2ypZev+UAzkbRobcvF+aV/zJeAX88tPgcshfCUVZll ZIqO7oJB73nCl1XBLv2ZrLV2tcOox6jL/5LQt0WYA5Szg5upo7D1fZl8v5jXX7eJ C62ychuABs6hsmH5jEy+73kdpHbYft7dZfGZxdgq1AIOkdWoynCze/R7Vj24xoXR 118cTNN9ZTPHmN5yxUvuGoqA3FWOqkJXaTS4W0hRD6OxOGTsTV4FIlRnD+K7feOW ng1yfIcvKR1Dx7tsySTHQK+bZGNnovA/ENPK6VDuhbwE62Lx7N5hcbsSIKKwRI9C D1m1fC+AIcQ= =MwMG -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
3a647c1d7a
@ -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 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:
|
||||
|
||||
timer {
|
||||
|
@ -2,7 +2,11 @@ Broadcom GISB bus Arbiter controller
|
||||
|
||||
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
|
||||
- interrupt-parent: specifies the phandle to the parent interrupt controller
|
||||
this arbiter gets interrupt line from
|
||||
|
@ -0,0 +1,36 @@
|
||||
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>;
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
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
|
23
Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt
Normal file
23
Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt
Normal file
@ -0,0 +1,23 @@
|
||||
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>;
|
||||
};
|
17
Documentation/devicetree/bindings/w1/omap-hdq.txt
Normal file
17
Documentation/devicetree/bindings/w1/omap-hdq.txt
Normal file
@ -0,0 +1,17 @@
|
||||
* 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"
|
||||
|
||||
menu "Bus support"
|
||||
|
||||
config ARM_AMBA
|
||||
bool
|
||||
|
||||
config ISA
|
||||
bool
|
||||
help
|
||||
|
@ -245,6 +245,8 @@ struct edma {
|
||||
/* list of channels with no even trigger; terminated by "-1" */
|
||||
const s8 *noevent;
|
||||
|
||||
struct edma_soc_info *info;
|
||||
|
||||
/* The edma_inuse bit for each PaRAM slot is clear unless the
|
||||
* 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,
|
||||
~(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 bit = queue_no * 4;
|
||||
@ -315,7 +317,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no,
|
||||
* 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;
|
||||
for (i = 0; i < EDMA_MAX_DMACH; i++)
|
||||
@ -1798,6 +1800,7 @@ static int edma_probe(struct platform_device *pdev)
|
||||
edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
|
||||
edma_write_array(j, EDMA_QRAE, i, 0x0);
|
||||
}
|
||||
edma_cc[j]->info = info[j];
|
||||
arch_num_cc++;
|
||||
|
||||
edma_dev_info.id = j;
|
||||
@ -1807,9 +1810,56 @@ static int edma_probe(struct platform_device *pdev)
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "edma",
|
||||
.pm = &edma_pm_ops,
|
||||
.of_match_table = edma_of_ids,
|
||||
},
|
||||
.probe = edma_probe,
|
||||
|
@ -78,6 +78,15 @@ static inline u32 arch_timer_get_cntfrq(void)
|
||||
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)
|
||||
{
|
||||
u64 cval;
|
||||
|
@ -12,7 +12,7 @@
|
||||
#ifndef __ARM_PERF_EVENT_H__
|
||||
#define __ARM_PERF_EVENT_H__
|
||||
|
||||
#ifdef CONFIG_HW_PERF_EVENTS
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
struct pt_regs;
|
||||
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
|
||||
extern unsigned long perf_misc_flags(struct pt_regs *regs);
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include <asm/cputype.h>
|
||||
|
||||
/*
|
||||
* struct arm_pmu_platdata - ARM PMU platform data
|
||||
*
|
||||
@ -66,19 +68,25 @@ struct pmu_hw_events {
|
||||
/*
|
||||
* 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
|
||||
* 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
|
||||
* read/modify/write sequences.
|
||||
*/
|
||||
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 {
|
||||
@ -107,7 +115,8 @@ struct arm_pmu {
|
||||
struct mutex reserve_mutex;
|
||||
u64 max_period;
|
||||
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))
|
||||
@ -127,6 +136,27 @@ int armpmu_map_event(struct perf_event *event,
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX],
|
||||
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 /* __ARM_PMU_H__ */
|
||||
|
@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
|
||||
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
|
||||
obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.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
|
||||
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
||||
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
||||
|
136
arch/arm/kernel/perf_callchain.c
Normal file
136
arch/arm/kernel/perf_callchain.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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 @@
|
||||
* 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
|
||||
* on the x86 code. Callchain code is based on the ARM OProfile backtrace
|
||||
* code.
|
||||
* on the x86 code.
|
||||
*/
|
||||
#define pr_fmt(fmt) "hw perfevents: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
#include <asm/pmu.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
static int
|
||||
armpmu_map_cache_event(const unsigned (*cache_map)
|
||||
@ -80,8 +77,12 @@ armpmu_map_event(struct perf_event *event,
|
||||
u32 raw_event_mask)
|
||||
{
|
||||
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:
|
||||
return armpmu_map_hw_event(event_map, config);
|
||||
case PERF_TYPE_HW_CACHE:
|
||||
@ -200,7 +201,7 @@ static void
|
||||
armpmu_del(struct perf_event *event, int flags)
|
||||
{
|
||||
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;
|
||||
int idx = hwc->idx;
|
||||
|
||||
@ -217,7 +218,7 @@ static int
|
||||
armpmu_add(struct perf_event *event, int flags)
|
||||
{
|
||||
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;
|
||||
int idx;
|
||||
int err = 0;
|
||||
@ -274,14 +275,12 @@ validate_group(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *sibling, *leader = event->group_leader;
|
||||
struct pmu_hw_events fake_pmu;
|
||||
DECLARE_BITMAP(fake_used_mask, ARMPMU_MAX_HWEVENTS);
|
||||
|
||||
/*
|
||||
* Initialise the fake PMU. We only need to populate the
|
||||
* used_mask for the purposes of validation.
|
||||
*/
|
||||
memset(fake_used_mask, 0, sizeof(fake_used_mask));
|
||||
fake_pmu.used_mask = fake_used_mask;
|
||||
memset(&fake_pmu.used_mask, 0, sizeof(fake_pmu.used_mask));
|
||||
|
||||
if (!validate_event(&fake_pmu, leader))
|
||||
return -EINVAL;
|
||||
@ -305,17 +304,21 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
|
||||
int ret;
|
||||
u64 start_clock, finish_clock;
|
||||
|
||||
if (irq_is_percpu(irq))
|
||||
dev = *(void **)dev;
|
||||
armpmu = dev;
|
||||
/*
|
||||
* we request the IRQ with a (possibly percpu) struct arm_pmu**, but
|
||||
* 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 = dev_get_platdata(&plat_device->dev);
|
||||
|
||||
start_clock = sched_clock();
|
||||
if (plat && plat->handle_irq)
|
||||
ret = plat->handle_irq(irq, dev, armpmu->handle_irq);
|
||||
ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq);
|
||||
else
|
||||
ret = armpmu->handle_irq(irq, dev);
|
||||
ret = armpmu->handle_irq(irq, armpmu);
|
||||
finish_clock = sched_clock();
|
||||
|
||||
perf_sample_event_took(finish_clock - start_clock);
|
||||
@ -468,7 +471,7 @@ static int armpmu_event_init(struct perf_event *event)
|
||||
static void armpmu_enable(struct 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);
|
||||
|
||||
if (enabled)
|
||||
@ -533,130 +536,3 @@ int armpmu_register(struct arm_pmu *armpmu, int 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 @@
|
||||
/* Set at runtime when we know what CPU type we are. */
|
||||
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
|
||||
* by the OProfile/perf code.
|
||||
@ -69,11 +64,6 @@ EXPORT_SYMBOL_GPL(perf_num_counters);
|
||||
#include "perf_event_v6.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)
|
||||
{
|
||||
int irq = *(int *)data;
|
||||
@ -92,20 +82,21 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
int i, irq, irqs;
|
||||
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());
|
||||
|
||||
irq = platform_get_irq(pmu_device, 0);
|
||||
if (irq >= 0 && irq_is_percpu(irq)) {
|
||||
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 {
|
||||
for (i = 0; i < irqs; ++i) {
|
||||
if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs))
|
||||
continue;
|
||||
irq = platform_get_irq(pmu_device, i);
|
||||
if (irq >= 0)
|
||||
free_irq(irq, cpu_pmu);
|
||||
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)
|
||||
{
|
||||
int i, err, irq, irqs;
|
||||
struct platform_device *pmu_device = cpu_pmu->plat_device;
|
||||
struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events;
|
||||
|
||||
if (!pmu_device)
|
||||
return -ENODEV;
|
||||
|
||||
irqs = min(pmu_device->num_resources, num_possible_cpus());
|
||||
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;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pmu_device, 0);
|
||||
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) {
|
||||
pr_err("unable to request IRQ%d for ARM PMU counters\n",
|
||||
irq);
|
||||
@ -153,7 +146,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
|
||||
|
||||
err = request_irq(irq, handler,
|
||||
IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu",
|
||||
cpu_pmu);
|
||||
per_cpu_ptr(&hw_events->percpu_pmu, i));
|
||||
if (err) {
|
||||
pr_err("unable to request IRQ%d for ARM PMU counters\n",
|
||||
irq);
|
||||
@ -167,18 +160,50 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
|
||||
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;
|
||||
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) {
|
||||
struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu);
|
||||
events->events = per_cpu(hw_events, cpu);
|
||||
events->used_mask = per_cpu(used_mask, cpu);
|
||||
struct pmu_hw_events *events = per_cpu_ptr(cpu_hw_events, cpu);
|
||||
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->free_irq = cpu_pmu_free_irq;
|
||||
|
||||
@ -189,32 +214,20 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
|
||||
/* If no interrupts available, set the corresponding capability flag */
|
||||
if (!platform_get_irq(cpu_pmu->plat_device, 0))
|
||||
cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
|
||||
|
||||
return 0;
|
||||
|
||||
out_hw_events:
|
||||
free_percpu(cpu_hw_events);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (cpu_pmu && cpu_pmu->reset)
|
||||
cpu_pmu->reset(cpu_pmu);
|
||||
else
|
||||
return NOTIFY_DONE;
|
||||
|
||||
return NOTIFY_OK;
|
||||
unregister_cpu_notifier(&cpu_pmu->hotplug_nb);
|
||||
free_percpu(cpu_pmu->hw_events);
|
||||
}
|
||||
|
||||
static struct notifier_block cpu_pmu_hotplug_notifier = {
|
||||
.notifier_call = cpu_pmu_notify,
|
||||
};
|
||||
|
||||
/*
|
||||
* PMU platform driver and devicetree bindings.
|
||||
*/
|
||||
@ -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.
|
||||
*/
|
||||
static int probe_current_pmu(struct arm_pmu *pmu)
|
||||
{
|
||||
int cpu = get_cpu();
|
||||
unsigned int cpuid = read_cpuid_id();
|
||||
int ret = -ENODEV;
|
||||
const struct pmu_probe_info *info;
|
||||
|
||||
pr_info("probing PMU on CPU %d\n", cpu);
|
||||
|
||||
switch (read_cpuid_part()) {
|
||||
/* ARM Ltd CPUs. */
|
||||
case ARM_CPU_PART_ARM1136:
|
||||
ret = armv6_1136_pmu_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;
|
||||
}
|
||||
}
|
||||
for (info = pmu_probe_table; info->init != NULL; info++) {
|
||||
if ((cpuid & info->mask) != info->cpuid)
|
||||
continue;
|
||||
ret = info->init(pmu);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -299,13 +298,13 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (cpu_pmu) {
|
||||
pr_info("attempt to register multiple PMU devices!");
|
||||
pr_info("attempt to register multiple PMU devices!\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL);
|
||||
if (!pmu) {
|
||||
pr_info("failed to allocate PMU device!");
|
||||
pr_info("failed to allocate PMU device!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -320,18 +319,24 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
pr_info("failed to probe PMU!");
|
||||
pr_info("failed to probe PMU!\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
cpu_pmu_init(cpu_pmu);
|
||||
ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW);
|
||||
ret = cpu_pmu_init(cpu_pmu);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
ret = armpmu_register(cpu_pmu, -1);
|
||||
if (ret)
|
||||
goto out_destroy;
|
||||
|
||||
return 0;
|
||||
|
||||
out_destroy:
|
||||
cpu_pmu_destroy(cpu_pmu);
|
||||
out_free:
|
||||
pr_info("failed to register PMU devices!");
|
||||
pr_info("failed to register PMU devices!\n");
|
||||
kfree(pmu);
|
||||
return ret;
|
||||
}
|
||||
@ -348,16 +353,6 @@ static struct platform_driver cpu_pmu_driver = {
|
||||
|
||||
static int __init register_pmu_driver(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
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;
|
||||
return platform_driver_register(&cpu_pmu_driver);
|
||||
}
|
||||
device_initcall(register_pmu_driver);
|
||||
|
@ -262,7 +262,7 @@ static void armv6pmu_enable_event(struct perf_event *event)
|
||||
unsigned long val, mask, evt, flags;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
if (ARMV6_CYCLE_COUNTER == idx) {
|
||||
@ -300,7 +300,7 @@ armv6pmu_handle_irq(int irq_num,
|
||||
unsigned long pmcr = armv6_pmcr_read();
|
||||
struct perf_sample_data data;
|
||||
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;
|
||||
int idx;
|
||||
|
||||
@ -356,7 +356,7 @@ armv6pmu_handle_irq(int irq_num,
|
||||
static void armv6pmu_start(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = armv6_pmcr_read();
|
||||
@ -368,7 +368,7 @@ static void armv6pmu_start(struct arm_pmu *cpu_pmu)
|
||||
static void armv6pmu_stop(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = armv6_pmcr_read();
|
||||
@ -409,7 +409,7 @@ static void armv6pmu_disable_event(struct perf_event *event)
|
||||
unsigned long val, mask, evt, flags;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
if (ARMV6_CYCLE_COUNTER == idx) {
|
||||
@ -444,7 +444,7 @@ static void armv6mpcore_pmu_disable_event(struct perf_event *event)
|
||||
unsigned long val, mask, flags, evt = 0;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
if (ARMV6_CYCLE_COUNTER == 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));
|
||||
}
|
||||
|
||||
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);
|
||||
asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
|
||||
isb();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
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;
|
||||
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",
|
||||
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));
|
||||
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));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@ -597,45 +597,43 @@ static inline void armv7pmu_write_counter(struct perf_event *event, u32 value)
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
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",
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
|
||||
{
|
||||
if (armv7_pmnc_select_counter(idx) == idx) {
|
||||
val &= ARMV7_EVTYPE_MASK;
|
||||
asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
|
||||
}
|
||||
armv7_pmnc_select_counter(idx);
|
||||
val &= ARMV7_EVTYPE_MASK;
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
/* Clear the overflow flag in case an interrupt is pending. */
|
||||
asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter)));
|
||||
isb();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline u32 armv7_pmnc_getreset_flags(void)
|
||||
@ -667,34 +663,34 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)
|
||||
u32 val;
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
printk(KERN_INFO "CCNT =0x%08x\n", val);
|
||||
pr_info("CCNT =0x%08x\n", val);
|
||||
|
||||
for (cnt = ARMV7_IDX_COUNTER0;
|
||||
cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
|
||||
armv7_pmnc_select_counter(cnt);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -705,7 +701,7 @@ static void armv7pmu_enable_event(struct perf_event *event)
|
||||
unsigned long flags;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
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;
|
||||
|
||||
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
|
||||
@ -751,7 +747,7 @@ static void armv7pmu_disable_event(struct perf_event *event)
|
||||
unsigned long flags;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
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;
|
||||
|
||||
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
|
||||
@ -783,7 +779,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
|
||||
u32 pmnc;
|
||||
struct perf_sample_data data;
|
||||
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;
|
||||
int idx;
|
||||
|
||||
@ -843,7 +839,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
|
||||
static void armv7pmu_start(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
/* Enable all counters */
|
||||
@ -854,7 +850,7 @@ static void armv7pmu_start(struct arm_pmu *cpu_pmu)
|
||||
static void armv7pmu_stop(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
/* Disable all counters */
|
||||
@ -1287,7 +1283,7 @@ static void krait_pmu_disable_event(struct perf_event *event)
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx = hwc->idx;
|
||||
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 */
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
@ -1313,7 +1309,7 @@ static void krait_pmu_enable_event(struct perf_event *event)
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx = hwc->idx;
|
||||
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
|
||||
|
@ -138,7 +138,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev)
|
||||
unsigned long pmnc;
|
||||
struct perf_sample_data data;
|
||||
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;
|
||||
int idx;
|
||||
|
||||
@ -198,7 +198,7 @@ static void xscale1pmu_enable_event(struct perf_event *event)
|
||||
unsigned long val, mask, evt, flags;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
switch (idx) {
|
||||
@ -234,7 +234,7 @@ static void xscale1pmu_disable_event(struct perf_event *event)
|
||||
unsigned long val, mask, evt, flags;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
switch (idx) {
|
||||
@ -287,7 +287,7 @@ xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc,
|
||||
static void xscale1pmu_start(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = xscale1pmu_read_pmnc();
|
||||
@ -299,7 +299,7 @@ static void xscale1pmu_start(struct arm_pmu *cpu_pmu)
|
||||
static void xscale1pmu_stop(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = xscale1pmu_read_pmnc();
|
||||
@ -485,7 +485,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev)
|
||||
unsigned long pmnc, of_flags;
|
||||
struct perf_sample_data data;
|
||||
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;
|
||||
int idx;
|
||||
|
||||
@ -539,7 +539,7 @@ static void xscale2pmu_enable_event(struct perf_event *event)
|
||||
unsigned long flags, ien, evtsel;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
ien = xscale2pmu_read_int_enable();
|
||||
@ -585,7 +585,7 @@ static void xscale2pmu_disable_event(struct perf_event *event)
|
||||
unsigned long flags, ien, evtsel, of_flags;
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
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;
|
||||
|
||||
ien = xscale2pmu_read_int_enable();
|
||||
@ -651,7 +651,7 @@ out:
|
||||
static void xscale2pmu_start(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64;
|
||||
@ -663,7 +663,7 @@ static void xscale2pmu_start(struct arm_pmu *cpu_pmu)
|
||||
static void xscale2pmu_stop(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
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);
|
||||
val = xscale2pmu_read_pmnc();
|
||||
|
@ -2,6 +2,7 @@ menuconfig ARCH_TEGRA
|
||||
bool "NVIDIA Tegra" if ARCH_MULTI_V7
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
|
||||
select ARM_AMBA
|
||||
select ARM_GIC
|
||||
select CLKSRC_MMIO
|
||||
select HAVE_ARM_SCU if SMP
|
||||
@ -59,12 +60,4 @@ config ARCH_TEGRA_124_SOC
|
||||
Support for NVIDIA Tegra T124 processor family, based on the
|
||||
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
|
||||
|
@ -169,9 +169,6 @@ endmenu
|
||||
|
||||
menu "Bus support"
|
||||
|
||||
config ARM_AMBA
|
||||
bool
|
||||
|
||||
config PCI
|
||||
bool "PCI support"
|
||||
help
|
||||
|
@ -104,6 +104,15 @@ static inline void arch_timer_set_cntkctl(u32 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)
|
||||
{
|
||||
u64 cval;
|
||||
|
@ -137,13 +137,16 @@ static inline void set_iommu_table_base_and_group(struct device *dev,
|
||||
iommu_add_device(dev);
|
||||
}
|
||||
|
||||
extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
|
||||
struct scatterlist *sglist, int nelems,
|
||||
unsigned long mask, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs);
|
||||
extern void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
|
||||
int nelems, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs);
|
||||
extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
|
||||
struct scatterlist *sglist, int nelems,
|
||||
unsigned long mask,
|
||||
enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs);
|
||||
extern void ppc_iommu_unmap_sg(struct iommu_table *tbl,
|
||||
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,
|
||||
size_t size, dma_addr_t *dma_handle,
|
||||
|
@ -60,16 +60,16 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
|
||||
int nelems, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
return iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
|
||||
device_to_mask(dev), direction, attrs);
|
||||
return ppc_iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
|
||||
device_to_mask(dev), direction, attrs);
|
||||
}
|
||||
|
||||
static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
||||
int nelems, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems, direction,
|
||||
attrs);
|
||||
ppc_iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems,
|
||||
direction, attrs);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
ppc_md.tce_flush(tbl);
|
||||
}
|
||||
|
||||
int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
|
||||
struct scatterlist *sglist, int nelems,
|
||||
unsigned long mask, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
|
||||
struct scatterlist *sglist, int nelems,
|
||||
unsigned long mask, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
dma_addr_t dma_next = 0, dma_addr;
|
||||
struct scatterlist *s, *outs, *segstart;
|
||||
@ -539,7 +539,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
|
||||
|
||||
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
|
||||
*/
|
||||
if (outcount < incount) {
|
||||
@ -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,
|
||||
int nelems, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
|
||||
int nelems, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
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))
|
||||
return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
|
||||
else
|
||||
return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents,
|
||||
device_to_mask(dev), direction, attrs);
|
||||
return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg,
|
||||
nents, device_to_mask(dev),
|
||||
direction, attrs);
|
||||
}
|
||||
|
||||
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))
|
||||
dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
|
||||
else
|
||||
iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction,
|
||||
attrs);
|
||||
ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents,
|
||||
direction, attrs);
|
||||
}
|
||||
|
||||
static int dma_fixed_dma_supported(struct device *dev, u64 mask)
|
||||
|
@ -1,5 +1,7 @@
|
||||
menu "Device Drivers"
|
||||
|
||||
source "drivers/amba/Kconfig"
|
||||
|
||||
source "drivers/base/Kconfig"
|
||||
|
||||
source "drivers/bus/Kconfig"
|
||||
|
14
drivers/amba/Kconfig
Normal file
14
drivers/amba/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
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"
|
||||
|
||||
config BRCMSTB_GISB_ARB
|
||||
bool "Broadcom STB GISB bus arbiter"
|
||||
depends on ARM
|
||||
depends on ARM || MIPS
|
||||
help
|
||||
Driver for the Broadcom Set Top Box System-on-a-chip internal bus
|
||||
arbiter. This driver provides timeout and target abort error handling
|
||||
|
@ -16,17 +16,17 @@
|
||||
|
||||
#include <linux/arm-cci.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/irq_regs.h>
|
||||
#include <asm/pmu.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
#define DRIVER_NAME "CCI-400"
|
||||
@ -98,6 +98,8 @@ static unsigned long cci_ctrl_phys;
|
||||
|
||||
#define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K)
|
||||
|
||||
#define CCI_PMU_CNTR_MASK ((1ULL << 32) -1)
|
||||
|
||||
/*
|
||||
* Instead of an event id to monitor CCI cycles, a dedicated counter is
|
||||
* provided. Use 0xff to represent CCI cycles and hope that no future revisions
|
||||
@ -170,18 +172,29 @@ static char *const pmu_names[] = {
|
||||
[CCI_REV_R1] = "CCI_400_r1",
|
||||
};
|
||||
|
||||
struct cci_pmu_drv_data {
|
||||
struct cci_pmu_hw_events {
|
||||
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
|
||||
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
|
||||
raw_spinlock_t pmu_lock;
|
||||
};
|
||||
|
||||
struct cci_pmu {
|
||||
void __iomem *base;
|
||||
struct arm_pmu *cci_pmu;
|
||||
struct pmu pmu;
|
||||
int nr_irqs;
|
||||
int irqs[CCI_PMU_MAX_HW_EVENTS];
|
||||
unsigned long active_irqs;
|
||||
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
|
||||
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
|
||||
struct pmu_port_event_ranges *port_ranges;
|
||||
struct pmu_hw_events hw_events;
|
||||
struct cci_pmu_hw_events hw_events;
|
||||
struct platform_device *plat_device;
|
||||
int num_events;
|
||||
atomic_t active_events;
|
||||
struct mutex reserve_mutex;
|
||||
cpumask_t cpus;
|
||||
};
|
||||
static struct cci_pmu_drv_data *pmu;
|
||||
static struct cci_pmu *pmu;
|
||||
|
||||
#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu))
|
||||
|
||||
static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
|
||||
{
|
||||
@ -252,7 +265,7 @@ static int pmu_validate_hw_event(u8 hw_event)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int pmu_is_valid_counter(struct arm_pmu *cci_pmu, int idx)
|
||||
static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx)
|
||||
{
|
||||
return CCI_PMU_CYCLE_CNTR_IDX <= idx &&
|
||||
idx <= CCI_PMU_CNTR_LAST(cci_pmu);
|
||||
@ -293,14 +306,9 @@ static u32 pmu_get_max_counters(void)
|
||||
return n_cnts + 1;
|
||||
}
|
||||
|
||||
static struct pmu_hw_events *pmu_get_hw_events(void)
|
||||
static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event)
|
||||
{
|
||||
return &pmu->hw_events;
|
||||
}
|
||||
|
||||
static int pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_event = &event->hw;
|
||||
unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK;
|
||||
int idx;
|
||||
@ -336,7 +344,7 @@ static int pmu_map_event(struct perf_event *event)
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler)
|
||||
static int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler)
|
||||
{
|
||||
int i;
|
||||
struct platform_device *pmu_device = cci_pmu->plat_device;
|
||||
@ -371,17 +379,91 @@ static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmu_free_irq(struct cci_pmu *cci_pmu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pmu->nr_irqs; i++) {
|
||||
if (!test_and_clear_bit(i, &pmu->active_irqs))
|
||||
continue;
|
||||
|
||||
free_irq(pmu->irqs[i], cci_pmu);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 pmu_read_counter(struct perf_event *event)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
u32 value;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return 0;
|
||||
}
|
||||
value = pmu_read_register(idx, CCI_PMU_CNTR);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void pmu_write_counter(struct perf_event *event, u32 value)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx)))
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
else
|
||||
pmu_write_register(value, idx, CCI_PMU_CNTR);
|
||||
}
|
||||
|
||||
static u64 pmu_event_update(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
u64 delta, prev_raw_count, new_raw_count;
|
||||
|
||||
do {
|
||||
prev_raw_count = local64_read(&hwc->prev_count);
|
||||
new_raw_count = pmu_read_counter(event);
|
||||
} while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
|
||||
new_raw_count) != prev_raw_count);
|
||||
|
||||
delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK;
|
||||
|
||||
local64_add(delta, &event->count);
|
||||
|
||||
return new_raw_count;
|
||||
}
|
||||
|
||||
static void pmu_read(struct perf_event *event)
|
||||
{
|
||||
pmu_event_update(event);
|
||||
}
|
||||
|
||||
void pmu_event_set_period(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
/*
|
||||
* The CCI PMU counters have a period of 2^32. To account for the
|
||||
* possiblity of extreme interrupt latency we program for a period of
|
||||
* half that. Hopefully we can handle the interrupt before another 2^31
|
||||
* events occur and the counter overtakes its previous value.
|
||||
*/
|
||||
u64 val = 1ULL << 31;
|
||||
local64_set(&hwc->prev_count, val);
|
||||
pmu_write_counter(event, val);
|
||||
}
|
||||
|
||||
static irqreturn_t pmu_handle_irq(int irq_num, void *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct arm_pmu *cci_pmu = (struct arm_pmu *)dev;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs *regs;
|
||||
struct cci_pmu *cci_pmu = dev;
|
||||
struct cci_pmu_hw_events *events = &pmu->hw_events;
|
||||
int idx, handled = IRQ_NONE;
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
regs = get_irq_regs();
|
||||
/*
|
||||
* Iterate over counters and update the corresponding perf events.
|
||||
* This should work regardless of whether we have per-counter overflow
|
||||
@ -403,154 +485,407 @@ static irqreturn_t pmu_handle_irq(int irq_num, void *dev)
|
||||
|
||||
pmu_write_register(CCI_PMU_OVRFLW_FLAG, idx, CCI_PMU_OVRFLW);
|
||||
|
||||
pmu_event_update(event);
|
||||
pmu_event_set_period(event);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
armpmu_event_update(event);
|
||||
perf_sample_data_init(&data, 0, hw_counter->last_period);
|
||||
if (!armpmu_event_set_period(event))
|
||||
continue;
|
||||
|
||||
if (perf_event_overflow(event, &data, regs))
|
||||
cci_pmu->disable(event);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static void pmu_free_irq(struct arm_pmu *cci_pmu)
|
||||
static int cci_pmu_get_hw(struct cci_pmu *cci_pmu)
|
||||
{
|
||||
int i;
|
||||
int ret = pmu_request_irq(cci_pmu, pmu_handle_irq);
|
||||
if (ret) {
|
||||
pmu_free_irq(cci_pmu);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < pmu->nr_irqs; i++) {
|
||||
if (!test_and_clear_bit(i, &pmu->active_irqs))
|
||||
continue;
|
||||
static void cci_pmu_put_hw(struct cci_pmu *cci_pmu)
|
||||
{
|
||||
pmu_free_irq(cci_pmu);
|
||||
}
|
||||
|
||||
free_irq(pmu->irqs[i], cci_pmu);
|
||||
static void hw_perf_event_destroy(struct perf_event *event)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
atomic_t *active_events = &cci_pmu->active_events;
|
||||
struct mutex *reserve_mutex = &cci_pmu->reserve_mutex;
|
||||
|
||||
if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) {
|
||||
cci_pmu_put_hw(cci_pmu);
|
||||
mutex_unlock(reserve_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void pmu_enable_event(struct perf_event *event)
|
||||
static void cci_pmu_enable(struct pmu *pmu)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
|
||||
struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
|
||||
int enabled = bitmap_weight(hw_events->used_mask, cci_pmu->num_events);
|
||||
unsigned long flags;
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
|
||||
/* Configure the event to count, unless you are counting cycles */
|
||||
if (idx != CCI_PMU_CYCLE_CNTR_IDX)
|
||||
pmu_set_event(idx, hw_counter->config_base);
|
||||
|
||||
pmu_enable_counter(idx);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void pmu_disable_event(struct perf_event *event)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return;
|
||||
}
|
||||
|
||||
pmu_disable_counter(idx);
|
||||
}
|
||||
|
||||
static void pmu_start(struct arm_pmu *cci_pmu)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
|
||||
|
||||
/* Enable all the PMU counters. */
|
||||
val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN;
|
||||
writel(val, cci_ctrl_base + CCI_PMCR);
|
||||
raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void pmu_stop(struct arm_pmu *cci_pmu)
|
||||
static void cci_pmu_disable(struct pmu *pmu)
|
||||
{
|
||||
u32 val;
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
|
||||
struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
|
||||
unsigned long flags;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
u32 val;
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
|
||||
|
||||
/* Disable all the PMU counters. */
|
||||
val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN;
|
||||
writel(val, cci_ctrl_base + CCI_PMCR);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static u32 pmu_read_counter(struct perf_event *event)
|
||||
static void cci_pmu_start(struct perf_event *event, int pmu_flags)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
u32 value;
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx = hwc->idx;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* To handle interrupt latency, we always reprogram the period
|
||||
* regardlesss of PERF_EF_RELOAD.
|
||||
*/
|
||||
if (pmu_flags & PERF_EF_RELOAD)
|
||||
WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
|
||||
|
||||
hwc->state = 0;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
value = pmu_read_register(idx, CCI_PMU_CNTR);
|
||||
|
||||
return value;
|
||||
raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
|
||||
|
||||
/* Configure the event to count, unless you are counting cycles */
|
||||
if (idx != CCI_PMU_CYCLE_CNTR_IDX)
|
||||
pmu_set_event(idx, hwc->config_base);
|
||||
|
||||
pmu_event_set_period(event);
|
||||
pmu_enable_counter(idx);
|
||||
|
||||
raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void pmu_write_counter(struct perf_event *event, u32 value)
|
||||
static void cci_pmu_stop(struct perf_event *event, int pmu_flags)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx = hwc->idx;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx)))
|
||||
if (hwc->state & PERF_HES_STOPPED)
|
||||
return;
|
||||
|
||||
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
else
|
||||
pmu_write_register(value, idx, CCI_PMU_CNTR);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We always reprogram the counter, so ignore PERF_EF_UPDATE. See
|
||||
* cci_pmu_start()
|
||||
*/
|
||||
pmu_disable_counter(idx);
|
||||
pmu_event_update(event);
|
||||
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
|
||||
}
|
||||
|
||||
static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev)
|
||||
static int cci_pmu_add(struct perf_event *event, int flags)
|
||||
{
|
||||
*cci_pmu = (struct arm_pmu){
|
||||
.name = pmu_names[probe_cci_revision()],
|
||||
.max_period = (1LLU << 32) - 1,
|
||||
.get_hw_events = pmu_get_hw_events,
|
||||
.get_event_idx = pmu_get_event_idx,
|
||||
.map_event = pmu_map_event,
|
||||
.request_irq = pmu_request_irq,
|
||||
.handle_irq = pmu_handle_irq,
|
||||
.free_irq = pmu_free_irq,
|
||||
.enable = pmu_enable_event,
|
||||
.disable = pmu_disable_event,
|
||||
.start = pmu_start,
|
||||
.stop = pmu_stop,
|
||||
.read_counter = pmu_read_counter,
|
||||
.write_counter = pmu_write_counter,
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx;
|
||||
int err = 0;
|
||||
|
||||
perf_pmu_disable(event->pmu);
|
||||
|
||||
/* If we don't have a space for the counter then finish early. */
|
||||
idx = pmu_get_event_idx(hw_events, event);
|
||||
if (idx < 0) {
|
||||
err = idx;
|
||||
goto out;
|
||||
}
|
||||
|
||||
event->hw.idx = idx;
|
||||
hw_events->events[idx] = event;
|
||||
|
||||
hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
|
||||
if (flags & PERF_EF_START)
|
||||
cci_pmu_start(event, PERF_EF_RELOAD);
|
||||
|
||||
/* Propagate our changes to the userspace mapping. */
|
||||
perf_event_update_userpage(event);
|
||||
|
||||
out:
|
||||
perf_pmu_enable(event->pmu);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void cci_pmu_del(struct perf_event *event, int flags)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int idx = hwc->idx;
|
||||
|
||||
cci_pmu_stop(event, PERF_EF_UPDATE);
|
||||
hw_events->events[idx] = NULL;
|
||||
clear_bit(idx, hw_events->used_mask);
|
||||
|
||||
perf_event_update_userpage(event);
|
||||
}
|
||||
|
||||
static int
|
||||
validate_event(struct cci_pmu_hw_events *hw_events,
|
||||
struct perf_event *event)
|
||||
{
|
||||
if (is_software_event(event))
|
||||
return 1;
|
||||
|
||||
if (event->state < PERF_EVENT_STATE_OFF)
|
||||
return 1;
|
||||
|
||||
if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
|
||||
return 1;
|
||||
|
||||
return pmu_get_event_idx(hw_events, event) >= 0;
|
||||
}
|
||||
|
||||
static int
|
||||
validate_group(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *sibling, *leader = event->group_leader;
|
||||
struct cci_pmu_hw_events fake_pmu = {
|
||||
/*
|
||||
* Initialise the fake PMU. We only need to populate the
|
||||
* used_mask for the purposes of validation.
|
||||
*/
|
||||
.used_mask = CPU_BITS_NONE,
|
||||
};
|
||||
|
||||
if (!validate_event(&fake_pmu, leader))
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
||||
if (!validate_event(&fake_pmu, sibling))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!validate_event(&fake_pmu, event))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
__hw_perf_event_init(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int mapping;
|
||||
|
||||
mapping = pmu_map_event(event);
|
||||
|
||||
if (mapping < 0) {
|
||||
pr_debug("event %x:%llx not supported\n", event->attr.type,
|
||||
event->attr.config);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't assign an index until we actually place the event onto
|
||||
* hardware. Use -1 to signify that we haven't decided where to put it
|
||||
* yet.
|
||||
*/
|
||||
hwc->idx = -1;
|
||||
hwc->config_base = 0;
|
||||
hwc->config = 0;
|
||||
hwc->event_base = 0;
|
||||
|
||||
/*
|
||||
* Store the event encoding into the config_base field.
|
||||
*/
|
||||
hwc->config_base |= (unsigned long)mapping;
|
||||
|
||||
/*
|
||||
* Limit the sample_period to half of the counter width. That way, the
|
||||
* new counter value is far less likely to overtake the previous one
|
||||
* unless you have some serious IRQ latency issues.
|
||||
*/
|
||||
hwc->sample_period = CCI_PMU_CNTR_MASK >> 1;
|
||||
hwc->last_period = hwc->sample_period;
|
||||
local64_set(&hwc->period_left, hwc->sample_period);
|
||||
|
||||
if (event->group_leader != event) {
|
||||
if (validate_group(event) != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cci_pmu_event_init(struct perf_event *event)
|
||||
{
|
||||
struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
|
||||
atomic_t *active_events = &cci_pmu->active_events;
|
||||
int err = 0;
|
||||
int cpu;
|
||||
|
||||
if (event->attr.type != event->pmu->type)
|
||||
return -ENOENT;
|
||||
|
||||
/* Shared by all CPUs, no meaningful state to sample */
|
||||
if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* We have no filtering of any kind */
|
||||
if (event->attr.exclude_user ||
|
||||
event->attr.exclude_kernel ||
|
||||
event->attr.exclude_hv ||
|
||||
event->attr.exclude_idle ||
|
||||
event->attr.exclude_host ||
|
||||
event->attr.exclude_guest)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Following the example set by other "uncore" PMUs, we accept any CPU
|
||||
* and rewrite its affinity dynamically rather than having perf core
|
||||
* handle cpu == -1 and pid == -1 for this case.
|
||||
*
|
||||
* The perf core will pin online CPUs for the duration of this call and
|
||||
* the event being installed into its context, so the PMU's CPU can't
|
||||
* change under our feet.
|
||||
*/
|
||||
cpu = cpumask_first(&cci_pmu->cpus);
|
||||
if (event->cpu < 0 || cpu < 0)
|
||||
return -EINVAL;
|
||||
event->cpu = cpu;
|
||||
|
||||
event->destroy = hw_perf_event_destroy;
|
||||
if (!atomic_inc_not_zero(active_events)) {
|
||||
mutex_lock(&cci_pmu->reserve_mutex);
|
||||
if (atomic_read(active_events) == 0)
|
||||
err = cci_pmu_get_hw(cci_pmu);
|
||||
if (!err)
|
||||
atomic_inc(active_events);
|
||||
mutex_unlock(&cci_pmu->reserve_mutex);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = __hw_perf_event_init(event);
|
||||
if (err)
|
||||
hw_perf_event_destroy(event);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t pmu_attr_cpumask_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &pmu->cpus);
|
||||
|
||||
buf[n++] = '\n';
|
||||
buf[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cpumask, S_IRUGO, pmu_attr_cpumask_show, NULL);
|
||||
|
||||
static struct attribute *pmu_attrs[] = {
|
||||
&dev_attr_cpumask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group pmu_attr_group = {
|
||||
.attrs = pmu_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *pmu_attr_groups[] = {
|
||||
&pmu_attr_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev)
|
||||
{
|
||||
char *name = pmu_names[probe_cci_revision()];
|
||||
cci_pmu->pmu = (struct pmu) {
|
||||
.name = pmu_names[probe_cci_revision()],
|
||||
.task_ctx_nr = perf_invalid_context,
|
||||
.pmu_enable = cci_pmu_enable,
|
||||
.pmu_disable = cci_pmu_disable,
|
||||
.event_init = cci_pmu_event_init,
|
||||
.add = cci_pmu_add,
|
||||
.del = cci_pmu_del,
|
||||
.start = cci_pmu_start,
|
||||
.stop = cci_pmu_stop,
|
||||
.read = pmu_read,
|
||||
.attr_groups = pmu_attr_groups,
|
||||
};
|
||||
|
||||
cci_pmu->plat_device = pdev;
|
||||
cci_pmu->num_events = pmu_get_max_counters();
|
||||
|
||||
return armpmu_register(cci_pmu, -1);
|
||||
return perf_pmu_register(&cci_pmu->pmu, name, -1);
|
||||
}
|
||||
|
||||
static int cci_pmu_cpu_notifier(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
unsigned int cpu = (long)hcpu;
|
||||
unsigned int target;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_DOWN_PREPARE:
|
||||
if (!cpumask_test_and_clear_cpu(cpu, &pmu->cpus))
|
||||
break;
|
||||
target = cpumask_any_but(cpu_online_mask, cpu);
|
||||
if (target < 0) // UP, last CPU
|
||||
break;
|
||||
/*
|
||||
* TODO: migrate context once core races on event->ctx have
|
||||
* been fixed.
|
||||
*/
|
||||
cpumask_set_cpu(target, &pmu->cpus);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block cci_pmu_cpu_nb = {
|
||||
.notifier_call = cci_pmu_cpu_notifier,
|
||||
/*
|
||||
* to migrate uncore events, our notifier should be executed
|
||||
* before perf core's notifier.
|
||||
*/
|
||||
.priority = CPU_PRI_PERF + 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id arm_cci_pmu_matches[] = {
|
||||
{
|
||||
.compatible = "arm,cci-400-pmu",
|
||||
@ -604,15 +939,16 @@ static int cci_pmu_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pmu->cci_pmu = devm_kzalloc(&pdev->dev, sizeof(*(pmu->cci_pmu)), GFP_KERNEL);
|
||||
if (!pmu->cci_pmu)
|
||||
return -ENOMEM;
|
||||
|
||||
pmu->hw_events.events = pmu->events;
|
||||
pmu->hw_events.used_mask = pmu->used_mask;
|
||||
raw_spin_lock_init(&pmu->hw_events.pmu_lock);
|
||||
mutex_init(&pmu->reserve_mutex);
|
||||
atomic_set(&pmu->active_events, 0);
|
||||
cpumask_set_cpu(smp_processor_id(), &pmu->cpus);
|
||||
|
||||
ret = cci_pmu_init(pmu->cci_pmu, pdev);
|
||||
ret = register_cpu_notifier(&cci_pmu_cpu_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cci_pmu_init(pmu, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -25,26 +25,72 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
#include <asm/bug.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_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_TEA (1 << 11)
|
||||
#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2)
|
||||
#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c
|
||||
#define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
|
||||
#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 {
|
||||
void __iomem *base;
|
||||
const int *gisb_offsets;
|
||||
struct mutex lock;
|
||||
struct list_head next;
|
||||
u32 valid_mask;
|
||||
@ -54,6 +100,26 @@ struct brcmstb_gisb_arb_device {
|
||||
|
||||
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,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -63,7 +129,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev,
|
||||
u32 timeout;
|
||||
|
||||
mutex_lock(&gdev->lock);
|
||||
timeout = ioread32(gdev->base + ARB_TIMER);
|
||||
timeout = gisb_read(gdev, ARB_TIMER);
|
||||
mutex_unlock(&gdev->lock);
|
||||
|
||||
return sprintf(buf, "%d", timeout);
|
||||
@ -85,7 +151,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&gdev->lock);
|
||||
iowrite32(val, gdev->base + ARB_TIMER);
|
||||
gisb_write(gdev, val, ARB_TIMER);
|
||||
mutex_unlock(&gdev->lock);
|
||||
|
||||
return count;
|
||||
@ -112,18 +178,18 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
|
||||
const char *m_name;
|
||||
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 */
|
||||
if (!(cap_status & ARB_ERR_CAP_STATUS_VALID))
|
||||
return 1;
|
||||
|
||||
/* 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))
|
||||
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
|
||||
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);
|
||||
if (!m_name) {
|
||||
@ -138,11 +204,12 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
|
||||
m_name);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
@ -161,6 +228,7 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 = {
|
||||
.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 brcmstb_gisb_arb_device *gdev;
|
||||
const struct of_device_id *of_id;
|
||||
struct resource *r;
|
||||
int err, timeout_irq, tea_irq;
|
||||
unsigned int num_masters, j = 0;
|
||||
@ -212,6 +290,13 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
|
||||
if (IS_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,
|
||||
brcmstb_gisb_timeout_handler, 0, pdev->name,
|
||||
gdev);
|
||||
@ -257,8 +342,10 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
|
||||
|
||||
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0,
|
||||
"imprecise external abort");
|
||||
#endif
|
||||
|
||||
dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n",
|
||||
gdev->base, timeout_irq, tea_irq);
|
||||
@ -272,7 +359,7 @@ static int brcmstb_gisb_arb_suspend(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
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;
|
||||
}
|
||||
@ -285,7 +372,7 @@ static int brcmstb_gisb_arb_resume_noirq(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
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;
|
||||
}
|
||||
@ -299,13 +386,7 @@ static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = {
|
||||
.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 = {
|
||||
.probe = brcmstb_gisb_arb_probe,
|
||||
.driver = {
|
||||
.name = "brcm-gisb-arb",
|
||||
.owner = THIS_MODULE,
|
||||
@ -316,7 +397,8 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
|
||||
|
||||
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);
|
||||
|
@ -222,10 +222,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
|
||||
}
|
||||
|
||||
/* 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[] = {
|
||||
@ -296,11 +300,66 @@ static int omap_l3_probe(struct platform_device *pdev)
|
||||
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 = {
|
||||
.probe = omap_l3_probe,
|
||||
.driver = {
|
||||
.name = "omap_l3_noc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = L3_DEV_PM_OPS,
|
||||
.of_match_table = of_match_ptr(l3_noc_match),
|
||||
},
|
||||
};
|
||||
|
@ -185,3 +185,16 @@ struct clk *tegra_clk_register_divider(const char *name,
|
||||
|
||||
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);
|
||||
static DEFINE_SPINLOCK(pll_d2_lock);
|
||||
static DEFINE_SPINLOCK(pll_u_lock);
|
||||
static DEFINE_SPINLOCK(pll_re_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
static struct div_nmp pllxc_nmp = {
|
||||
.divm_shift = 0,
|
||||
@ -1228,7 +1229,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
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++) {
|
||||
data = &tegra_periph_clk_list[i];
|
||||
|
@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(pll_d2_lock);
|
||||
static DEFINE_SPINLOCK(pll_e_lock);
|
||||
static DEFINE_SPINLOCK(pll_re_lock);
|
||||
static DEFINE_SPINLOCK(pll_u_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
/* possible OSC frequencies in Hz */
|
||||
static unsigned long tegra124_input_freq[] = {
|
||||
@ -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,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm), 0,
|
||||
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 */
|
||||
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
|
||||
|
@ -140,6 +140,8 @@ static struct cpu_clk_suspend_context {
|
||||
static void __iomem *clk_base;
|
||||
static void __iomem *pmc_base;
|
||||
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
|
||||
@ -819,11 +821,15 @@ static void __init tegra20_periph_clk_init(void)
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
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,
|
||||
57, periph_clk_enb_refcnt);
|
||||
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 */
|
||||
clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
|
||||
48, periph_clk_enb_refcnt);
|
||||
|
@ -177,6 +177,7 @@ static unsigned long input_freq;
|
||||
|
||||
static DEFINE_SPINLOCK(cml_lock);
|
||||
static DEFINE_SPINLOCK(pll_d_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
@ -1157,11 +1158,15 @@ static void __init tegra30_periph_clk_init(void)
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
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,
|
||||
57, periph_clk_enb_refcnt);
|
||||
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 */
|
||||
clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
|
||||
0, 0, &cml_lock);
|
||||
|
@ -86,6 +86,8 @@ struct clk *tegra_clk_register_divider(const char *name,
|
||||
const char *parent_name, void __iomem *reg,
|
||||
unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
|
||||
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:
|
||||
|
@ -462,7 +462,10 @@ static void __init arch_counter_register(unsigned type)
|
||||
|
||||
/* Register the CP15 based counter if we have one */
|
||||
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 {
|
||||
arch_timer_read_counter = arch_counter_get_cntvct_mem;
|
||||
|
||||
@ -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_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
|
||||
* has been configured to be accessible from PL1. Use it, so
|
||||
|
@ -163,14 +163,14 @@ config TEGRA_IOMMU_GART
|
||||
hardware included on Tegra SoCs.
|
||||
|
||||
config TEGRA_IOMMU_SMMU
|
||||
bool "Tegra SMMU IOMMU Support"
|
||||
depends on ARCH_TEGRA && TEGRA_AHB
|
||||
bool "NVIDIA Tegra SMMU Support"
|
||||
depends on ARCH_TEGRA
|
||||
depends on TEGRA_AHB
|
||||
depends on TEGRA_MC
|
||||
select IOMMU_API
|
||||
help
|
||||
Enables support for remapping discontiguous physical memory
|
||||
shared with the operating system into contiguous I/O virtual
|
||||
space through the SMMU (System Memory Management Unit)
|
||||
hardware included on Tegra SoCs.
|
||||
This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra
|
||||
SoCs (Tegra30 up to Tegra124).
|
||||
|
||||
config EXYNOS_IOMMU
|
||||
bool "Exynos IOMMU Support"
|
||||
|
@ -3424,6 +3424,7 @@ static const struct iommu_ops amd_iommu_ops = {
|
||||
.detach_dev = amd_iommu_detach_device,
|
||||
.map = amd_iommu_map,
|
||||
.unmap = amd_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = amd_iommu_iova_to_phys,
|
||||
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
|
||||
};
|
||||
|
@ -1652,6 +1652,7 @@ static const struct iommu_ops arm_smmu_ops = {
|
||||
.detach_dev = arm_smmu_detach_dev,
|
||||
.map = arm_smmu_map,
|
||||
.unmap = arm_smmu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.add_device = arm_smmu_add_device,
|
||||
.remove_device = arm_smmu_remove_device,
|
||||
|
@ -1178,6 +1178,7 @@ static const struct iommu_ops exynos_iommu_ops = {
|
||||
.detach_dev = exynos_iommu_detach_device,
|
||||
.map = exynos_iommu_map,
|
||||
.unmap = exynos_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = exynos_iommu_iova_to_phys,
|
||||
.add_device = exynos_iommu_add_device,
|
||||
.remove_device = exynos_iommu_remove_device,
|
||||
|
@ -4467,6 +4467,7 @@ static const struct iommu_ops intel_iommu_ops = {
|
||||
.detach_dev = intel_iommu_detach_device,
|
||||
.map = intel_iommu_map,
|
||||
.unmap = intel_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = intel_iommu_iova_to_phys,
|
||||
.add_device = intel_iommu_add_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)
|
||||
kfree(nb);
|
||||
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)
|
||||
*/
|
||||
int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (bus->iommu_ops != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
bus->iommu_ops = ops;
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -1124,6 +1138,38 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
||||
}
|
||||
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,
|
||||
phys_addr_t paddr, u64 size, int prot)
|
||||
|
@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = {
|
||||
.detach_dev = ipmmu_detach_device,
|
||||
.map = ipmmu_map,
|
||||
.unmap = ipmmu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = ipmmu_iova_to_phys,
|
||||
.add_device = ipmmu_add_device,
|
||||
.remove_device = ipmmu_remove_device,
|
||||
|
@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = {
|
||||
.detach_dev = msm_iommu_detach_dev,
|
||||
.map = msm_iommu_map,
|
||||
.unmap = msm_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = msm_iommu_iova_to_phys,
|
||||
.pgsize_bitmap = MSM_IOMMU_PGSIZES,
|
||||
};
|
||||
|
@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = {
|
||||
.detach_dev = omap_iommu_detach_dev,
|
||||
.map = omap_iommu_map,
|
||||
.unmap = omap_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = omap_iommu_iova_to_phys,
|
||||
.add_device = omap_iommu_add_device,
|
||||
.remove_device = omap_iommu_remove_device,
|
||||
|
@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = {
|
||||
.detach_dev = shmobile_iommu_detach_device,
|
||||
.map = shmobile_iommu_map,
|
||||
.unmap = shmobile_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = shmobile_iommu_iova_to_phys,
|
||||
.add_device = shmobile_iommu_add_device,
|
||||
.pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -61,16 +61,6 @@ config TEGRA20_MC
|
||||
analysis, especially for IOMMU/GART(Graphics Address
|
||||
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
|
||||
tristate "Freescale CoreNet Error Reporting"
|
||||
depends on FSL_SOC_BOOKE
|
||||
@ -85,4 +75,6 @@ config FSL_IFC
|
||||
bool
|
||||
depends on FSL_SOC
|
||||
|
||||
source "drivers/memory/tegra/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -12,4 +12,5 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
|
||||
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
|
||||
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
|
||||
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
|
||||
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra/
|
||||
|
7
drivers/memory/tegra/Kconfig
Normal file
7
drivers/memory/tegra/Kconfig
Normal file
@ -0,0 +1,7 @@
|
||||
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.
|
7
drivers/memory/tegra/Makefile
Normal file
7
drivers/memory/tegra/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
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
|
301
drivers/memory/tegra/mc.c
Normal file
301
drivers/memory/tegra/mc.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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");
|
40
drivers/memory/tegra/mc.h
Normal file
40
drivers/memory/tegra/mc.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MEMORY_TEGRA_MC_H
|
||||
#define MEMORY_TEGRA_MC_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <soc/tegra/mc.h>
|
||||
|
||||
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
|
||||
{
|
||||
return readl(mc->regs + offset);
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra_mc *mc, u32 value,
|
||||
unsigned long offset)
|
||||
{
|
||||
writel(value, mc->regs + offset);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
extern const struct tegra_mc_soc tegra30_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_114_SOC
|
||||
extern const struct tegra_mc_soc tegra114_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
||||
extern const struct tegra_mc_soc tegra124_mc_soc;
|
||||
#endif
|
||||
|
||||
#endif /* MEMORY_TEGRA_MC_H */
|
948
drivers/memory/tegra/tegra114.c
Normal file
948
drivers/memory/tegra/tegra114.c
Normal file
@ -0,0 +1,948 @@
|
||||
/*
|
||||
* 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/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra114-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static const struct tegra_mc_client tegra114_mc_clients[] = {
|
||||
{
|
||||
.id = 0x00,
|
||||
.name = "ptcr",
|
||||
.swgroup = TEGRA_SWGROUP_PTC,
|
||||
}, {
|
||||
.id = 0x01,
|
||||
.name = "display0a",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x02,
|
||||
.name = "display0ab",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x03,
|
||||
.name = "display0b",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x04,
|
||||
.name = "display0bb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x05,
|
||||
.name = "display0c",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2ec,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x06,
|
||||
.name = "display0cb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 6,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x09,
|
||||
.name = "eppup",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 9,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x300,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x33,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0a,
|
||||
.name = "g2pr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x308,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x09,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0b,
|
||||
.name = "g2sr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x308,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x09,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0f,
|
||||
.name = "avpcarm7r",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x10,
|
||||
.name = "displayhc",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x68,
|
||||
},
|
||||
}, {
|
||||
.id = 0x11,
|
||||
.name = "displayhcb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2fc,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x68,
|
||||
},
|
||||
}, {
|
||||
.id = 0x12,
|
||||
.name = "fdcdrd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x334,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x13,
|
||||
.name = "fdcdrd2",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 19,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x33c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x14,
|
||||
.name = "g2dr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x30c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x15,
|
||||
.name = "hdar",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x16,
|
||||
.name = "host1xdmar",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x17,
|
||||
.name = "host1xr",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x18,
|
||||
.name = "idxsrd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 24,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x334,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0b,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1c,
|
||||
.name = "msencsrd",
|
||||
.swgroup = TEGRA_SWGROUP_MSENC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1d,
|
||||
.name = "ppcsahbdmar",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 29,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1e,
|
||||
.name = "ppcsahbslvr",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xe8,
|
||||
},
|
||||
}, {
|
||||
.id = 0x20,
|
||||
.name = "texl2srd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x338,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x22,
|
||||
.name = "vdebsevr",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x23,
|
||||
.name = "vdember",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x24,
|
||||
.name = "vdemcer",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xb8,
|
||||
},
|
||||
}, {
|
||||
.id = 0x25,
|
||||
.name = "vdetper",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xee,
|
||||
},
|
||||
}, {
|
||||
.id = 0x26,
|
||||
.name = "mpcorelpr",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x27,
|
||||
.name = "mpcorer",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x28,
|
||||
.name = "eppu",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 8,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x300,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x33,
|
||||
},
|
||||
}, {
|
||||
.id = 0x29,
|
||||
.name = "eppv",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 9,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x304,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x6c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2a,
|
||||
.name = "eppy",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x304,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x6c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2b,
|
||||
.name = "msencswr",
|
||||
.swgroup = TEGRA_SWGROUP_MSENC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2c,
|
||||
.name = "viwsb",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x364,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x47,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2d,
|
||||
.name = "viwu",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x368,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2e,
|
||||
.name = "viwv",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x368,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2f,
|
||||
.name = "viwy",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x36c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x47,
|
||||
},
|
||||
}, {
|
||||
.id = 0x30,
|
||||
.name = "g2dw",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x30c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x9,
|
||||
},
|
||||
}, {
|
||||
.id = 0x32,
|
||||
.name = "avpcarm7w",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x33,
|
||||
.name = "fdcdwr",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 19,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x338,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x34,
|
||||
.name = "fdcwr2",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x340,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x35,
|
||||
.name = "hdaw",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x36,
|
||||
.name = "host1xw",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x314,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x25,
|
||||
},
|
||||
}, {
|
||||
.id = 0x37,
|
||||
.name = "ispw",
|
||||
.swgroup = TEGRA_SWGROUP_ISP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x31c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x38,
|
||||
.name = "mpcorelpw",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x39,
|
||||
.name = "mpcorew",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3b,
|
||||
.name = "ppcsahbdmaw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 27,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3c,
|
||||
.name = "ppcsahbslvw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xe8,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3e,
|
||||
.name = "vdebsevw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3f,
|
||||
.name = "vdedbgw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 31,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x40,
|
||||
.name = "vdembew",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x89,
|
||||
},
|
||||
}, {
|
||||
.id = 0x41,
|
||||
.name = "vdetpmw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x59,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4a,
|
||||
.name = "xusb_hostr",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x37c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4b,
|
||||
.name = "xusb_hostw",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x37c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4c,
|
||||
.name = "xusb_devr",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x380,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4d,
|
||||
.name = "xusb_devw",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x380,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4e,
|
||||
.name = "fdcdwr3",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x388,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4f,
|
||||
.name = "fdcdrd3",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x384,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x50,
|
||||
.name = "fdcwr4",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x388,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x51,
|
||||
.name = "fdcrd4",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x384,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x52,
|
||||
.name = "emucifr",
|
||||
.swgroup = TEGRA_SWGROUP_EMUCIF,
|
||||
.la = {
|
||||
.reg = 0x38c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x53,
|
||||
.name = "emucifw",
|
||||
.swgroup = TEGRA_SWGROUP_EMUCIF,
|
||||
.la = {
|
||||
.reg = 0x38c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x54,
|
||||
.name = "tsecsrd",
|
||||
.swgroup = TEGRA_SWGROUP_TSEC,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x390,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x55,
|
||||
.name = "tsecswr",
|
||||
.swgroup = TEGRA_SWGROUP_TSEC,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x390,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
|
||||
{ .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
|
||||
{ .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
|
||||
{ .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
|
||||
{ .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
|
||||
{ .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
|
||||
{ .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
|
||||
{ .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
|
||||
{ .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
|
||||
{ .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
|
||||
{ .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
|
||||
{ .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
|
||||
{ .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
|
||||
{ .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
|
||||
{ .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
|
||||
{ .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
|
||||
};
|
||||
|
||||
static void tegra114_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra114_smmu_ops = {
|
||||
.flush_dcache = tegra114_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra114_smmu_soc = {
|
||||
.clients = tegra114_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
|
||||
.swgroups = tegra114_swgroups,
|
||||
.num_swgroups = ARRAY_SIZE(tegra114_swgroups),
|
||||
.supports_round_robin_arbitration = false,
|
||||
.supports_request_limit = false,
|
||||
.num_asids = 4,
|
||||
.ops = &tegra114_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra114_mc_soc = {
|
||||
.clients = tegra114_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
|
||||
.num_address_bits = 32,
|
||||
.atom_size = 32,
|
||||
.smmu = &tegra114_smmu_soc,
|
||||
};
|
995
drivers/memory/tegra/tegra124.c
Normal file
995
drivers/memory/tegra/tegra124.c
Normal file
@ -0,0 +1,995 @@
|
||||
/*
|
||||
* 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/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra124-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static const struct tegra_mc_client tegra124_mc_clients[] = {
|
||||
{
|
||||
.id = 0x00,
|
||||
.name = "ptcr",
|
||||
.swgroup = TEGRA_SWGROUP_PTC,
|
||||
}, {
|
||||
.id = 0x01,
|
||||
.name = "display0a",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xc2,
|
||||
},
|
||||
}, {
|
||||
.id = 0x02,
|
||||
.name = "display0ab",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xc6,
|
||||
},
|
||||
}, {
|
||||
.id = 0x03,
|
||||
.name = "display0b",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x04,
|
||||
.name = "display0bb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x05,
|
||||
.name = "display0c",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2ec,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x06,
|
||||
.name = "display0cb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 6,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0e,
|
||||
.name = "afir",
|
||||
.swgroup = TEGRA_SWGROUP_AFI,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0f,
|
||||
.name = "avpcarm7r",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x10,
|
||||
.name = "displayhc",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x11,
|
||||
.name = "displayhcb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2fc,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x15,
|
||||
.name = "hdar",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x24,
|
||||
},
|
||||
}, {
|
||||
.id = 0x16,
|
||||
.name = "host1xdmar",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x1e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x17,
|
||||
.name = "host1xr",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1c,
|
||||
.name = "msencsrd",
|
||||
.swgroup = TEGRA_SWGROUP_MSENC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x23,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1d,
|
||||
.name = "ppcsahbdmar",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 29,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x49,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1e,
|
||||
.name = "ppcsahbslvr",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x1a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1f,
|
||||
.name = "satar",
|
||||
.swgroup = TEGRA_SWGROUP_SATA,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 31,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x350,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x65,
|
||||
},
|
||||
}, {
|
||||
.id = 0x22,
|
||||
.name = "vdebsevr",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4f,
|
||||
},
|
||||
}, {
|
||||
.id = 0x23,
|
||||
.name = "vdember",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x3d,
|
||||
},
|
||||
}, {
|
||||
.id = 0x24,
|
||||
.name = "vdemcer",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x66,
|
||||
},
|
||||
}, {
|
||||
.id = 0x25,
|
||||
.name = "vdetper",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xa5,
|
||||
},
|
||||
}, {
|
||||
.id = 0x26,
|
||||
.name = "mpcorelpr",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x27,
|
||||
.name = "mpcorer",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2b,
|
||||
.name = "msencswr",
|
||||
.swgroup = TEGRA_SWGROUP_MSENC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x31,
|
||||
.name = "afiw",
|
||||
.swgroup = TEGRA_SWGROUP_AFI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e0,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x32,
|
||||
.name = "avpcarm7w",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x35,
|
||||
.name = "hdaw",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x36,
|
||||
.name = "host1xw",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x314,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x38,
|
||||
.name = "mpcorelpw",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x39,
|
||||
.name = "mpcorew",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3b,
|
||||
.name = "ppcsahbdmaw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 27,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3c,
|
||||
.name = "ppcsahbslvw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3d,
|
||||
.name = "sataw",
|
||||
.swgroup = TEGRA_SWGROUP_SATA,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 29,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x350,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x65,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3e,
|
||||
.name = "vdebsevw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3f,
|
||||
.name = "vdedbgw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 31,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x40,
|
||||
.name = "vdembew",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x41,
|
||||
.name = "vdetpmw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x44,
|
||||
.name = "ispra",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x370,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x18,
|
||||
},
|
||||
}, {
|
||||
.id = 0x46,
|
||||
.name = "ispwa",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 6,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x374,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x47,
|
||||
.name = "ispwb",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 7,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x374,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4a,
|
||||
.name = "xusb_hostr",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x37c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x39,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4b,
|
||||
.name = "xusb_hostw",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x37c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4c,
|
||||
.name = "xusb_devr",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x380,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x39,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4d,
|
||||
.name = "xusb_devw",
|
||||
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x380,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x4e,
|
||||
.name = "isprab",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2B,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x384,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x18,
|
||||
},
|
||||
}, {
|
||||
.id = 0x50,
|
||||
.name = "ispwab",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2B,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x388,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x51,
|
||||
.name = "ispwbb",
|
||||
.swgroup = TEGRA_SWGROUP_ISP2B,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x388,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x54,
|
||||
.name = "tsecsrd",
|
||||
.swgroup = TEGRA_SWGROUP_TSEC,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x390,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x9b,
|
||||
},
|
||||
}, {
|
||||
.id = 0x55,
|
||||
.name = "tsecswr",
|
||||
.swgroup = TEGRA_SWGROUP_TSEC,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x390,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x56,
|
||||
.name = "a9avpscr",
|
||||
.swgroup = TEGRA_SWGROUP_A9AVP,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3a4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x57,
|
||||
.name = "a9avpscw",
|
||||
.swgroup = TEGRA_SWGROUP_A9AVP,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3a4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x58,
|
||||
.name = "gpusrd",
|
||||
.swgroup = TEGRA_SWGROUP_GPU,
|
||||
.smmu = {
|
||||
/* read-only */
|
||||
.reg = 0x230,
|
||||
.bit = 24,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x1a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x59,
|
||||
.name = "gpuswr",
|
||||
.swgroup = TEGRA_SWGROUP_GPU,
|
||||
.smmu = {
|
||||
/* read-only */
|
||||
.reg = 0x230,
|
||||
.bit = 25,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x5a,
|
||||
.name = "displayt",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 26,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f0,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x60,
|
||||
.name = "sdmmcra",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC1A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3b8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x49,
|
||||
},
|
||||
}, {
|
||||
.id = 0x61,
|
||||
.name = "sdmmcraa",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC2A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3bc,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x49,
|
||||
},
|
||||
}, {
|
||||
.id = 0x62,
|
||||
.name = "sdmmcr",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC3A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x49,
|
||||
},
|
||||
}, {
|
||||
.id = 0x63,
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC4A,
|
||||
.name = "sdmmcrab",
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x49,
|
||||
},
|
||||
}, {
|
||||
.id = 0x64,
|
||||
.name = "sdmmcwa",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC1A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3b8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x65,
|
||||
.name = "sdmmcwaa",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC2A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3bc,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x66,
|
||||
.name = "sdmmcw",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC3A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 6,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c0,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x67,
|
||||
.name = "sdmmcwab",
|
||||
.swgroup = TEGRA_SWGROUP_SDMMC4A,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 7,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x6c,
|
||||
.name = "vicsrd",
|
||||
.swgroup = TEGRA_SWGROUP_VIC,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x394,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x1a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x6d,
|
||||
.name = "vicswr",
|
||||
.swgroup = TEGRA_SWGROUP_VIC,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x394,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x72,
|
||||
.name = "viw",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x398,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x73,
|
||||
.name = "displayd",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x234,
|
||||
.bit = 19,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x3c8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
|
||||
{ .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
|
||||
{ .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
|
||||
{ .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
|
||||
{ .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
|
||||
{ .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
|
||||
{ .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
|
||||
{ .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
|
||||
{ .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
|
||||
{ .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
|
||||
{ .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
|
||||
{ .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
|
||||
{ .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
|
||||
{ .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 },
|
||||
{ .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
|
||||
{ .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 },
|
||||
{ .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac },
|
||||
{ .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 },
|
||||
{ .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
|
||||
{ .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
|
||||
{ .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
||||
static void tegra124_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra124_smmu_ops = {
|
||||
.flush_dcache = tegra124_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra124_smmu_soc = {
|
||||
.clients = tegra124_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
|
||||
.swgroups = tegra124_swgroups,
|
||||
.num_swgroups = ARRAY_SIZE(tegra124_swgroups),
|
||||
.supports_round_robin_arbitration = true,
|
||||
.supports_request_limit = true,
|
||||
.num_asids = 128,
|
||||
.ops = &tegra124_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra124_mc_soc = {
|
||||
.clients = tegra124_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
|
||||
.num_address_bits = 34,
|
||||
.atom_size = 32,
|
||||
.smmu = &tegra124_smmu_soc,
|
||||
};
|
||||
#endif /* CONFIG_ARCH_TEGRA_124_SOC */
|
970
drivers/memory/tegra/tegra30.c
Normal file
970
drivers/memory/tegra/tegra30.c
Normal file
@ -0,0 +1,970 @@
|
||||
/*
|
||||
* 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/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra30-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static const struct tegra_mc_client tegra30_mc_clients[] = {
|
||||
{
|
||||
.id = 0x00,
|
||||
.name = "ptcr",
|
||||
.swgroup = TEGRA_SWGROUP_PTC,
|
||||
}, {
|
||||
.id = 0x01,
|
||||
.name = "display0a",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x02,
|
||||
.name = "display0ab",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x03,
|
||||
.name = "display0b",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x04,
|
||||
.name = "display0bb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x05,
|
||||
.name = "display0c",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2ec,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x06,
|
||||
.name = "display0cb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 6,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f8,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x07,
|
||||
.name = "display1b",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 7,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2ec,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x08,
|
||||
.name = "display1bb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 8,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f8,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x4e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x09,
|
||||
.name = "eppup",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 9,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x300,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x17,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0a,
|
||||
.name = "g2pr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x308,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x09,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0b,
|
||||
.name = "g2sr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x308,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x09,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0c,
|
||||
.name = "mpeunifbr",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0d,
|
||||
.name = "viruv",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x364,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x2c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0e,
|
||||
.name = "afir",
|
||||
.swgroup = TEGRA_SWGROUP_AFI,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x0f,
|
||||
.name = "avpcarm7r",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x10,
|
||||
.name = "displayhc",
|
||||
.swgroup = TEGRA_SWGROUP_DC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2f0,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x11,
|
||||
.name = "displayhcb",
|
||||
.swgroup = TEGRA_SWGROUP_DCB,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2fc,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x12,
|
||||
.name = "fdcdrd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x334,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x13,
|
||||
.name = "fdcdrd2",
|
||||
.swgroup = TEGRA_SWGROUP_NV2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 19,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x33c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x14,
|
||||
.name = "g2dr",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x30c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x15,
|
||||
.name = "hdar",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x16,
|
||||
.name = "host1xdmar",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x05,
|
||||
},
|
||||
}, {
|
||||
.id = 0x17,
|
||||
.name = "host1xr",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x310,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x50,
|
||||
},
|
||||
}, {
|
||||
.id = 0x18,
|
||||
.name = "idxsrd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 24,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x334,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x19,
|
||||
.name = "idxsrd2",
|
||||
.swgroup = TEGRA_SWGROUP_NV2,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 25,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x33c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1a,
|
||||
.name = "mpe_ipred",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 26,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x328,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x80,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1b,
|
||||
.name = "mpeamemrd",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 27,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x32c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x42,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1c,
|
||||
.name = "mpecsrd",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x32c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1d,
|
||||
.name = "ppcsahbdmar",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 29,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1e,
|
||||
.name = "ppcsahbslvr",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x344,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x12,
|
||||
},
|
||||
}, {
|
||||
.id = 0x1f,
|
||||
.name = "satar",
|
||||
.swgroup = TEGRA_SWGROUP_SATA,
|
||||
.smmu = {
|
||||
.reg = 0x228,
|
||||
.bit = 31,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x350,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x33,
|
||||
},
|
||||
}, {
|
||||
.id = 0x20,
|
||||
.name = "texsrd",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x338,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x21,
|
||||
.name = "texsrd2",
|
||||
.swgroup = TEGRA_SWGROUP_NV2,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x340,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x22,
|
||||
.name = "vdebsevr",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 2,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x23,
|
||||
.name = "vdember",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 3,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x354,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xd0,
|
||||
},
|
||||
}, {
|
||||
.id = 0x24,
|
||||
.name = "vdemcer",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 4,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x2a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x25,
|
||||
.name = "vdetper",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 5,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x358,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x74,
|
||||
},
|
||||
}, {
|
||||
.id = 0x26,
|
||||
.name = "mpcorelpr",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x27,
|
||||
.name = "mpcorer",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x04,
|
||||
},
|
||||
}, {
|
||||
.id = 0x28,
|
||||
.name = "eppu",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 8,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x300,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x6c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x29,
|
||||
.name = "eppv",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 9,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x304,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x6c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2a,
|
||||
.name = "eppy",
|
||||
.swgroup = TEGRA_SWGROUP_EPP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 10,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x304,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x6c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2b,
|
||||
.name = "mpeunifbw",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 11,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x330,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x13,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2c,
|
||||
.name = "viwsb",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 12,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x364,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x12,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2d,
|
||||
.name = "viwu",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 13,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x368,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xb2,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2e,
|
||||
.name = "viwv",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 14,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x368,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xb2,
|
||||
},
|
||||
}, {
|
||||
.id = 0x2f,
|
||||
.name = "viwy",
|
||||
.swgroup = TEGRA_SWGROUP_VI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 15,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x36c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x12,
|
||||
},
|
||||
}, {
|
||||
.id = 0x30,
|
||||
.name = "g2dw",
|
||||
.swgroup = TEGRA_SWGROUP_G2,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 16,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x30c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x9,
|
||||
},
|
||||
}, {
|
||||
.id = 0x31,
|
||||
.name = "afiw",
|
||||
.swgroup = TEGRA_SWGROUP_AFI,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 17,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e0,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0c,
|
||||
},
|
||||
}, {
|
||||
.id = 0x32,
|
||||
.name = "avpcarm7w",
|
||||
.swgroup = TEGRA_SWGROUP_AVPC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 18,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x2e4,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x33,
|
||||
.name = "fdcdwr",
|
||||
.swgroup = TEGRA_SWGROUP_NV,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 19,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x338,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x34,
|
||||
.name = "fdcwr2",
|
||||
.swgroup = TEGRA_SWGROUP_NV2,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 20,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x340,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0a,
|
||||
},
|
||||
}, {
|
||||
.id = 0x35,
|
||||
.name = "hdaw",
|
||||
.swgroup = TEGRA_SWGROUP_HDA,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 21,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x318,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x36,
|
||||
.name = "host1xw",
|
||||
.swgroup = TEGRA_SWGROUP_HC,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 22,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x314,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x37,
|
||||
.name = "ispw",
|
||||
.swgroup = TEGRA_SWGROUP_ISP,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 23,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x31c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x38,
|
||||
.name = "mpcorelpw",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORELP,
|
||||
.la = {
|
||||
.reg = 0x324,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x39,
|
||||
.name = "mpcorew",
|
||||
.swgroup = TEGRA_SWGROUP_MPCORE,
|
||||
.la = {
|
||||
.reg = 0x320,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x0e,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3a,
|
||||
.name = "mpecswr",
|
||||
.swgroup = TEGRA_SWGROUP_MPE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 26,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x330,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3b,
|
||||
.name = "ppcsahbdmaw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 27,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x10,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3c,
|
||||
.name = "ppcsahbslvw",
|
||||
.swgroup = TEGRA_SWGROUP_PPCS,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 28,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x348,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x06,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3d,
|
||||
.name = "sataw",
|
||||
.swgroup = TEGRA_SWGROUP_SATA,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 29,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x350,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x33,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3e,
|
||||
.name = "vdebsevw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 30,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x3f,
|
||||
.name = "vdedbgw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x22c,
|
||||
.bit = 31,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x35c,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0xff,
|
||||
},
|
||||
}, {
|
||||
.id = 0x40,
|
||||
.name = "vdembew",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 0,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 0,
|
||||
.mask = 0xff,
|
||||
.def = 0x42,
|
||||
},
|
||||
}, {
|
||||
.id = 0x41,
|
||||
.name = "vdetpmw",
|
||||
.swgroup = TEGRA_SWGROUP_VDE,
|
||||
.smmu = {
|
||||
.reg = 0x230,
|
||||
.bit = 1,
|
||||
},
|
||||
.la = {
|
||||
.reg = 0x360,
|
||||
.shift = 16,
|
||||
.mask = 0xff,
|
||||
.def = 0x2a,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
|
||||
{ .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
|
||||
{ .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
|
||||
{ .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
|
||||
{ .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
|
||||
{ .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
|
||||
{ .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
|
||||
{ .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
|
||||
{ .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
|
||||
{ .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
|
||||
{ .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
|
||||
{ .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
|
||||
{ .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
|
||||
{ .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 },
|
||||
{ .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
|
||||
{ .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
|
||||
};
|
||||
|
||||
static void tegra30_flush_dcache(struct page *page, unsigned long offset,
|
||||
size_t size)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
void *virt = page_address(page) + offset;
|
||||
|
||||
__cpuc_flush_dcache_area(virt, size);
|
||||
outer_flush_range(phys, phys + size);
|
||||
}
|
||||
|
||||
static const struct tegra_smmu_ops tegra30_smmu_ops = {
|
||||
.flush_dcache = tegra30_flush_dcache,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_soc tegra30_smmu_soc = {
|
||||
.clients = tegra30_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra30_mc_clients),
|
||||
.swgroups = tegra30_swgroups,
|
||||
.num_swgroups = ARRAY_SIZE(tegra30_swgroups),
|
||||
.supports_round_robin_arbitration = false,
|
||||
.supports_request_limit = false,
|
||||
.num_asids = 4,
|
||||
.ops = &tegra30_smmu_ops,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra30_mc_soc = {
|
||||
.clients = tegra30_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra30_mc_clients),
|
||||
.num_address_bits = 32,
|
||||
.atom_size = 16,
|
||||
.smmu = &tegra30_smmu_soc,
|
||||
};
|
@ -1,378 +0,0 @@
|
||||
/*
|
||||
* Tegra30 Memory Controller
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra30-mc"
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
|
||||
#define MC_INT_ERR_SHIFT 6
|
||||
#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
|
||||
#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4)
|
||||
|
||||
#define MC_ERR_STATUS 0x8
|
||||
#define MC_ERR_ADR 0xc
|
||||
|
||||
#define MC_ERR_TYPE_SHIFT 28
|
||||
#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT)
|
||||
#define MC_ERR_TYPE_DECERR_EMEM 2
|
||||
#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
|
||||
#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
|
||||
#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
|
||||
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT)
|
||||
#define MC_ERR_RW_SHIFT 16
|
||||
#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT)
|
||||
#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1)
|
||||
|
||||
#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
|
||||
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
|
||||
#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
|
||||
#define MC_CLIENT_ID_MASK 0x7f
|
||||
|
||||
#define NUM_MC_REG_BANKS 4
|
||||
|
||||
struct tegra30_mc {
|
||||
void __iomem *regs[NUM_MC_REG_BANKS];
|
||||
struct device *dev;
|
||||
u32 ctx[0];
|
||||
};
|
||||
|
||||
static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (offs < 0x10)
|
||||
val = readl(mc->regs[0] + offs);
|
||||
else if (offs < 0x1f0)
|
||||
val = readl(mc->regs[1] + offs - 0x3c);
|
||||
else if (offs < 0x228)
|
||||
val = readl(mc->regs[2] + offs - 0x200);
|
||||
else if (offs < 0x400)
|
||||
val = readl(mc->regs[3] + offs - 0x284);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
|
||||
{
|
||||
if (offs < 0x10)
|
||||
writel(val, mc->regs[0] + offs);
|
||||
else if (offs < 0x1f0)
|
||||
writel(val, mc->regs[1] + offs - 0x3c);
|
||||
else if (offs < 0x228)
|
||||
writel(val, mc->regs[2] + offs - 0x200);
|
||||
else if (offs < 0x400)
|
||||
writel(val, mc->regs[3] + offs - 0x284);
|
||||
}
|
||||
|
||||
static const char * const tegra30_mc_client[] = {
|
||||
"csr_ptcr",
|
||||
"cbr_display0a",
|
||||
"cbr_display0ab",
|
||||
"cbr_display0b",
|
||||
"cbr_display0bb",
|
||||
"cbr_display0c",
|
||||
"cbr_display0cb",
|
||||
"cbr_display1b",
|
||||
"cbr_display1bb",
|
||||
"cbr_eppup",
|
||||
"cbr_g2pr",
|
||||
"cbr_g2sr",
|
||||
"cbr_mpeunifbr",
|
||||
"cbr_viruv",
|
||||
"csr_afir",
|
||||
"csr_avpcarm7r",
|
||||
"csr_displayhc",
|
||||
"csr_displayhcb",
|
||||
"csr_fdcdrd",
|
||||
"csr_fdcdrd2",
|
||||
"csr_g2dr",
|
||||
"csr_hdar",
|
||||
"csr_host1xdmar",
|
||||
"csr_host1xr",
|
||||
"csr_idxsrd",
|
||||
"csr_idxsrd2",
|
||||
"csr_mpe_ipred",
|
||||
"csr_mpeamemrd",
|
||||
"csr_mpecsrd",
|
||||
"csr_ppcsahbdmar",
|
||||
"csr_ppcsahbslvr",
|
||||
"csr_satar",
|
||||
"csr_texsrd",
|
||||
"csr_texsrd2",
|
||||
"csr_vdebsevr",
|
||||
"csr_vdember",
|
||||
"csr_vdemcer",
|
||||
"csr_vdetper",
|
||||
"csr_mpcorelpr",
|
||||
"csr_mpcorer",
|
||||
"cbw_eppu",
|
||||
"cbw_eppv",
|
||||
"cbw_eppy",
|
||||
"cbw_mpeunifbw",
|
||||
"cbw_viwsb",
|
||||
"cbw_viwu",
|
||||
"cbw_viwv",
|
||||
"cbw_viwy",
|
||||
"ccw_g2dw",
|
||||
"csw_afiw",
|
||||
"csw_avpcarm7w",
|
||||
"csw_fdcdwr",
|
||||
"csw_fdcdwr2",
|
||||
"csw_hdaw",
|
||||
"csw_host1xw",
|
||||
"csw_ispw",
|
||||
"csw_mpcorelpw",
|
||||
"csw_mpcorew",
|
||||
"csw_mpecswr",
|
||||
"csw_ppcsahbdmaw",
|
||||
"csw_ppcsahbslvw",
|
||||
"csw_sataw",
|
||||
"csw_vdebsevw",
|
||||
"csw_vdedbgw",
|
||||
"csw_vdembew",
|
||||
"csw_vdetpmw",
|
||||
};
|
||||
|
||||
static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
|
||||
{
|
||||
u32 err, addr;
|
||||
const char * const mc_int_err[] = {
|
||||
"MC_DECERR",
|
||||
"Unknown",
|
||||
"MC_SECURITY_ERR",
|
||||
"MC_ARBITRATION_EMEM",
|
||||
"MC_SMMU_ERR",
|
||||
};
|
||||
const char * const err_type[] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"DECERR_EMEM",
|
||||
"SECURITY_TRUSTZONE",
|
||||
"SECURITY_CARVEOUT",
|
||||
"Unknown",
|
||||
"INVALID_SMMU_PAGE",
|
||||
"Unknown",
|
||||
};
|
||||
char attr[6];
|
||||
int cid, perm, type, idx;
|
||||
const char *client = "Unknown";
|
||||
|
||||
idx = n - MC_INT_ERR_SHIFT;
|
||||
if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) {
|
||||
dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
|
||||
BIT(n));
|
||||
return;
|
||||
}
|
||||
|
||||
err = mc_readl(mc, MC_ERR_STATUS);
|
||||
|
||||
type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
|
||||
perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
|
||||
MC_ERR_INVALID_SMMU_PAGE_SHIFT;
|
||||
if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE)
|
||||
sprintf(attr, "%c-%c-%c",
|
||||
(perm & BIT(2)) ? 'R' : '-',
|
||||
(perm & BIT(1)) ? 'W' : '-',
|
||||
(perm & BIT(0)) ? 'S' : '-');
|
||||
else
|
||||
attr[0] = '\0';
|
||||
|
||||
cid = err & MC_CLIENT_ID_MASK;
|
||||
if (cid < ARRAY_SIZE(tegra30_mc_client))
|
||||
client = tegra30_mc_client[cid];
|
||||
|
||||
addr = mc_readl(mc, MC_ERR_ADR);
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
|
||||
mc_int_err[idx], err, addr, client,
|
||||
(err & MC_ERR_SECURITY) ? "secure" : "non-secure",
|
||||
(err & MC_ERR_RW) ? "write" : "read",
|
||||
err_type[type], attr);
|
||||
}
|
||||
|
||||
static const u32 tegra30_mc_ctx[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_MISC1,
|
||||
MC_EMEM_ARB_RING3_THROTTLE,
|
||||
MC_EMEM_ARB_OVERRIDE,
|
||||
MC_INTMASK,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tegra30_mc_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_mc_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]);
|
||||
|
||||
mc_writel(mc, 1, MC_TIMING_CONTROL);
|
||||
/* Read-back to ensure that write reached */
|
||||
mc_readl(mc, MC_TIMING_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
|
||||
tegra30_mc_suspend,
|
||||
tegra30_mc_resume, NULL);
|
||||
|
||||
static const struct of_device_id tegra30_mc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra30-mc", },
|
||||
{},
|
||||
};
|
||||
|
||||
static irqreturn_t tegra30_mc_isr(int irq, void *data)
|
||||
{
|
||||
u32 stat, mask, bit;
|
||||
struct tegra30_mc *mc = data;
|
||||
|
||||
stat = mc_readl(mc, MC_INTSTATUS);
|
||||
mask = mc_readl(mc, MC_INTMASK);
|
||||
mask &= stat;
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
while ((bit = ffs(mask)) != 0) {
|
||||
tegra30_mc_decode(mc, bit - 1);
|
||||
mask &= ~BIT(bit - 1);
|
||||
}
|
||||
|
||||
mc_writel(mc, stat, MC_INTSTATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tegra30_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *irq;
|
||||
struct tegra30_mc *mc;
|
||||
size_t bytes;
|
||||
int err, i;
|
||||
u32 intmask;
|
||||
|
||||
bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx);
|
||||
mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mc->regs[i]))
|
||||
return PTR_ERR(mc->regs[i]);
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), mc);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, mc);
|
||||
|
||||
intmask = MC_INT_INVALID_SMMU_PAGE |
|
||||
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
|
||||
mc_writel(mc, intmask, MC_INTMASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra30_mc_driver = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra30_mc_of_match,
|
||||
.pm = &tegra30_mc_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra30_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra30 MC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
@ -1,4 +1,5 @@
|
||||
obj-$(CONFIG_RESET_CONTROLLER) += core.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_STI) += sti/
|
||||
|
@ -125,6 +125,21 @@ int reset_control_deassert(struct reset_control *rstc)
|
||||
}
|
||||
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.
|
||||
* @node: device to be reset by the controller
|
||||
|
131
drivers/reset/reset-berlin.c
Normal file
131
drivers/reset/reset-berlin.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Tenart <antoine.tenart@free-electrons.com>
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BERLIN_MAX_RESETS 32
|
||||
|
||||
#define to_berlin_reset_priv(p) \
|
||||
container_of((p), struct berlin_reset_priv, rcdev)
|
||||
|
||||
struct berlin_reset_priv {
|
||||
void __iomem *base;
|
||||
unsigned int size;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static int berlin_reset_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
|
||||
int offset = id >> 8;
|
||||
int mask = BIT(id & 0x1f);
|
||||
|
||||
writel(mask, priv->base + offset);
|
||||
|
||||
/* let the reset be effective */
|
||||
udelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct reset_control_ops berlin_reset_ops = {
|
||||
.reset = berlin_reset_reset,
|
||||
};
|
||||
|
||||
static int berlin_reset_xlate(struct reset_controller_dev *rcdev,
|
||||
const struct of_phandle_args *reset_spec)
|
||||
{
|
||||
struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
|
||||
unsigned offset, bit;
|
||||
|
||||
if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
|
||||
return -EINVAL;
|
||||
|
||||
offset = reset_spec->args[0];
|
||||
bit = reset_spec->args[1];
|
||||
|
||||
if (offset >= priv->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (bit >= BERLIN_MAX_RESETS)
|
||||
return -EINVAL;
|
||||
|
||||
return (offset << 8) | bit;
|
||||
}
|
||||
|
||||
static int __berlin_reset_init(struct device_node *np)
|
||||
{
|
||||
struct berlin_reset_priv *priv;
|
||||
struct resource res;
|
||||
resource_size_t size;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
size = resource_size(&res);
|
||||
priv->base = ioremap(res.start, size);
|
||||
if (!priv->base) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
priv->size = size;
|
||||
|
||||
priv->rcdev.owner = THIS_MODULE;
|
||||
priv->rcdev.ops = &berlin_reset_ops;
|
||||
priv->rcdev.of_node = np;
|
||||
priv->rcdev.of_reset_n_cells = 2;
|
||||
priv->rcdev.of_xlate = berlin_reset_xlate;
|
||||
|
||||
reset_controller_register(&priv->rcdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id berlin_reset_of_match[] __initconst = {
|
||||
{ .compatible = "marvell,berlin2-chip-ctrl" },
|
||||
{ .compatible = "marvell,berlin2cd-chip-ctrl" },
|
||||
{ .compatible = "marvell,berlin2q-chip-ctrl" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int __init berlin_reset_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
for_each_matching_node(np, berlin_reset_of_match) {
|
||||
ret = __berlin_reset_init(np);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(berlin_reset_init);
|
@ -76,9 +76,24 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socfpga_reset_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct socfpga_reset_data *data = container_of(rcdev,
|
||||
struct socfpga_reset_data, rcdev);
|
||||
int bank = id / BITS_PER_LONG;
|
||||
int offset = id % BITS_PER_LONG;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS));
|
||||
|
||||
return !(reg & BIT(offset));
|
||||
}
|
||||
|
||||
static struct reset_control_ops socfpga_reset_ops = {
|
||||
.assert = socfpga_reset_assert,
|
||||
.deassert = socfpga_reset_deassert,
|
||||
.status = socfpga_reset_status,
|
||||
};
|
||||
|
||||
static int socfpga_reset_probe(struct platform_device *pdev)
|
||||
|
@ -12,4 +12,8 @@ config STIH416_RESET
|
||||
bool
|
||||
select STI_RESET_SYSCFG
|
||||
|
||||
config STIH407_RESET
|
||||
bool
|
||||
select STI_RESET_SYSCFG
|
||||
|
||||
endif
|
||||
|
@ -2,3 +2,4 @@ obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o
|
||||
|
||||
obj-$(CONFIG_STIH415_RESET) += reset-stih415.o
|
||||
obj-$(CONFIG_STIH416_RESET) += reset-stih416.o
|
||||
obj-$(CONFIG_STIH407_RESET) += reset-stih407.o
|
||||
|
158
drivers/reset/sti/reset-stih407.c
Normal file
158
drivers/reset/sti/reset-stih407.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics (R&D) Limited
|
||||
* Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <dt-bindings/reset-controller/stih407-resets.h>
|
||||
#include "reset-syscfg.h"
|
||||
|
||||
/* STiH407 Peripheral powerdown definitions. */
|
||||
static const char stih407_core[] = "st,stih407-core-syscfg";
|
||||
static const char stih407_sbc_reg[] = "st,stih407-sbc-reg-syscfg";
|
||||
static const char stih407_lpm[] = "st,stih407-lpm-syscfg";
|
||||
|
||||
#define STIH407_PDN_0(_bit) \
|
||||
_SYSCFG_RST_CH(stih407_core, SYSCFG_5000, _bit, SYSSTAT_5500, _bit)
|
||||
#define STIH407_PDN_1(_bit) \
|
||||
_SYSCFG_RST_CH(stih407_core, SYSCFG_5001, _bit, SYSSTAT_5501, _bit)
|
||||
#define STIH407_PDN_ETH(_bit, _stat) \
|
||||
_SYSCFG_RST_CH(stih407_sbc_reg, SYSCFG_4032, _bit, SYSSTAT_4520, _stat)
|
||||
|
||||
/* Powerdown requests control 0 */
|
||||
#define SYSCFG_5000 0x0
|
||||
#define SYSSTAT_5500 0x7d0
|
||||
/* Powerdown requests control 1 (High Speed Links) */
|
||||
#define SYSCFG_5001 0x4
|
||||
#define SYSSTAT_5501 0x7d4
|
||||
|
||||
/* Ethernet powerdown/status/reset */
|
||||
#define SYSCFG_4032 0x80
|
||||
#define SYSSTAT_4520 0x820
|
||||
#define SYSCFG_4002 0x8
|
||||
|
||||
static const struct syscfg_reset_channel_data stih407_powerdowns[] = {
|
||||
[STIH407_EMISS_POWERDOWN] = STIH407_PDN_0(1),
|
||||
[STIH407_NAND_POWERDOWN] = STIH407_PDN_0(0),
|
||||
[STIH407_USB3_POWERDOWN] = STIH407_PDN_1(6),
|
||||
[STIH407_USB2_PORT1_POWERDOWN] = STIH407_PDN_1(5),
|
||||
[STIH407_USB2_PORT0_POWERDOWN] = STIH407_PDN_1(4),
|
||||
[STIH407_PCIE1_POWERDOWN] = STIH407_PDN_1(3),
|
||||
[STIH407_PCIE0_POWERDOWN] = STIH407_PDN_1(2),
|
||||
[STIH407_SATA1_POWERDOWN] = STIH407_PDN_1(1),
|
||||
[STIH407_SATA0_POWERDOWN] = STIH407_PDN_1(0),
|
||||
[STIH407_ETH1_POWERDOWN] = STIH407_PDN_ETH(0, 2),
|
||||
};
|
||||
|
||||
/* Reset Generator control 0/1 */
|
||||
#define SYSCFG_5131 0x20c
|
||||
#define SYSCFG_5132 0x210
|
||||
|
||||
#define LPM_SYSCFG_1 0x4 /* Softreset IRB & SBC UART */
|
||||
|
||||
#define STIH407_SRST_CORE(_reg, _bit) \
|
||||
_SYSCFG_RST_CH_NO_ACK(stih407_core, _reg, _bit)
|
||||
|
||||
#define STIH407_SRST_SBC(_reg, _bit) \
|
||||
_SYSCFG_RST_CH_NO_ACK(stih407_sbc_reg, _reg, _bit)
|
||||
|
||||
#define STIH407_SRST_LPM(_reg, _bit) \
|
||||
_SYSCFG_RST_CH_NO_ACK(stih407_lpm, _reg, _bit)
|
||||
|
||||
static const struct syscfg_reset_channel_data stih407_softresets[] = {
|
||||
[STIH407_ETH1_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 4),
|
||||
[STIH407_MMC1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 3),
|
||||
[STIH407_USB2_PORT0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 28),
|
||||
[STIH407_USB2_PORT1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 29),
|
||||
[STIH407_PICOPHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 30),
|
||||
[STIH407_IRB_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 6),
|
||||
[STIH407_PCIE0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 6),
|
||||
[STIH407_PCIE1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 15),
|
||||
[STIH407_SATA0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 7),
|
||||
[STIH407_SATA1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 16),
|
||||
[STIH407_MIPHY0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 4),
|
||||
[STIH407_MIPHY1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 13),
|
||||
[STIH407_MIPHY2_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 22),
|
||||
[STIH407_SATA0_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 5),
|
||||
[STIH407_SATA1_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 14),
|
||||
[STIH407_DELTA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 3),
|
||||
[STIH407_BLITTER_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 10),
|
||||
[STIH407_HDTVOUT_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 11),
|
||||
[STIH407_HDQVDP_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 12),
|
||||
[STIH407_VDP_AUX_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 14),
|
||||
[STIH407_COMPO_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 15),
|
||||
[STIH407_HDMI_TX_PHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 21),
|
||||
[STIH407_JPEG_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 23),
|
||||
[STIH407_VP8_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 24),
|
||||
[STIH407_GPU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 30),
|
||||
[STIH407_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 0),
|
||||
[STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1),
|
||||
[STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2),
|
||||
[STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8),
|
||||
};
|
||||
|
||||
/* PicoPHY reset/control */
|
||||
#define SYSCFG_5061 0x0f4
|
||||
|
||||
static const struct syscfg_reset_channel_data stih407_picophyresets[] = {
|
||||
[STIH407_PICOPHY0_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 5),
|
||||
[STIH407_PICOPHY1_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 6),
|
||||
[STIH407_PICOPHY2_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 7),
|
||||
};
|
||||
|
||||
static const struct syscfg_reset_controller_data stih407_powerdown_controller = {
|
||||
.wait_for_ack = true,
|
||||
.nr_channels = ARRAY_SIZE(stih407_powerdowns),
|
||||
.channels = stih407_powerdowns,
|
||||
};
|
||||
|
||||
static const struct syscfg_reset_controller_data stih407_softreset_controller = {
|
||||
.wait_for_ack = false,
|
||||
.active_low = true,
|
||||
.nr_channels = ARRAY_SIZE(stih407_softresets),
|
||||
.channels = stih407_softresets,
|
||||
};
|
||||
|
||||
static const struct syscfg_reset_controller_data stih407_picophyreset_controller = {
|
||||
.wait_for_ack = false,
|
||||
.nr_channels = ARRAY_SIZE(stih407_picophyresets),
|
||||
.channels = stih407_picophyresets,
|
||||
};
|
||||
|
||||
static struct of_device_id stih407_reset_match[] = {
|
||||
{
|
||||
.compatible = "st,stih407-powerdown",
|
||||
.data = &stih407_powerdown_controller,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih407-softreset",
|
||||
.data = &stih407_softreset_controller,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih407-picophyreset",
|
||||
.data = &stih407_picophyreset_controller,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver stih407_reset_driver = {
|
||||
.probe = syscfg_reset_probe,
|
||||
.driver = {
|
||||
.name = "reset-stih407",
|
||||
.of_match_table = stih407_reset_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init stih407_reset_init(void)
|
||||
{
|
||||
return platform_driver_register(&stih407_reset_driver);
|
||||
}
|
||||
|
||||
arch_initcall(stih407_reset_init);
|
@ -1109,16 +1109,18 @@ config RTC_DRV_AT91RM9200
|
||||
this is powered by the backup power supply.
|
||||
|
||||
config RTC_DRV_AT91SAM9
|
||||
tristate "AT91SAM9x/AT91CAP9 RTT as RTC"
|
||||
tristate "AT91SAM9 RTT as RTC"
|
||||
depends on ARCH_AT91
|
||||
select MFD_SYSCON
|
||||
help
|
||||
RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT
|
||||
(Real Time Timer). These timers are powered by the backup power
|
||||
supply (such as a small coin cell battery), but do not need to
|
||||
be used as RTCs.
|
||||
|
||||
(On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the
|
||||
dedicated RTC module and leave the RTT available for other uses.)
|
||||
Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
|
||||
can be used as an RTC thanks to the backup power supply (e.g. a
|
||||
small coin cell battery) which keeps this block and the GPBR
|
||||
(General Purpose Backup Registers) block powered when the device
|
||||
is shutdown.
|
||||
Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd
|
||||
probably want to use the real RTC block instead of the "RTT as an
|
||||
RTC" driver.
|
||||
|
||||
config RTC_DRV_AT91SAM9_RTT
|
||||
int
|
||||
@ -1126,6 +1128,9 @@ config RTC_DRV_AT91SAM9_RTT
|
||||
default 0
|
||||
depends on RTC_DRV_AT91SAM9
|
||||
help
|
||||
This option is only relevant for legacy board support and
|
||||
won't be used when booting a DT board.
|
||||
|
||||
More than one RTT module is available. You can choose which
|
||||
one will be used as an RTC. The default of zero is normally
|
||||
OK to use, though some systems use that for non-RTC purposes.
|
||||
@ -1137,6 +1142,9 @@ config RTC_DRV_AT91SAM9_GPBR
|
||||
prompt "Backup Register Number"
|
||||
depends on RTC_DRV_AT91SAM9
|
||||
help
|
||||
This option is only relevant for legacy board support and
|
||||
won't be used when booting a DT board.
|
||||
|
||||
The RTC driver needs to use one of the General Purpose Backup
|
||||
Registers (GPBRs) as well as the RTT. You can choose which one
|
||||
will be used. The default of zero is normally OK to use, but
|
||||
|
@ -21,10 +21,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/at91_rtt.h>
|
||||
#include <mach/cpu.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/*
|
||||
* This driver uses two configurable hardware resources that live in the
|
||||
@ -47,6 +46,22 @@
|
||||
* registers available, likewise usable for more than "RTC" support.
|
||||
*/
|
||||
|
||||
#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
|
||||
#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */
|
||||
#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */
|
||||
#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */
|
||||
#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */
|
||||
|
||||
#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
|
||||
#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
|
||||
|
||||
#define AT91_RTT_VR 0x08 /* Real-time Value Register */
|
||||
#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
|
||||
|
||||
#define AT91_RTT_SR 0x0c /* Real-time Status Register */
|
||||
#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */
|
||||
#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */
|
||||
|
||||
/*
|
||||
* We store ALARM_DISABLED in ALMV to record that no alarm is set.
|
||||
* It's also the reset value for that field.
|
||||
@ -58,19 +73,30 @@ struct sam9_rtc {
|
||||
void __iomem *rtt;
|
||||
struct rtc_device *rtcdev;
|
||||
u32 imr;
|
||||
void __iomem *gpbr;
|
||||
struct regmap *gpbr;
|
||||
unsigned int gpbr_offset;
|
||||
int irq;
|
||||
struct clk *sclk;
|
||||
};
|
||||
|
||||
#define rtt_readl(rtc, field) \
|
||||
__raw_readl((rtc)->rtt + AT91_RTT_ ## field)
|
||||
readl((rtc)->rtt + AT91_RTT_ ## field)
|
||||
#define rtt_writel(rtc, field, val) \
|
||||
__raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field)
|
||||
writel((val), (rtc)->rtt + AT91_RTT_ ## field)
|
||||
|
||||
#define gpbr_readl(rtc) \
|
||||
__raw_readl((rtc)->gpbr)
|
||||
#define gpbr_writel(rtc, val) \
|
||||
__raw_writel((val), (rtc)->gpbr)
|
||||
static inline unsigned int gpbr_readl(struct sam9_rtc *rtc)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(rtc->gpbr, rtc->gpbr_offset, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val)
|
||||
{
|
||||
regmap_write(rtc->gpbr, rtc->gpbr_offset, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read current time and date in RTC
|
||||
@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = {
|
||||
.alarm_irq_enable = at91_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static struct regmap_config gpbr_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize and install RTC driver
|
||||
*/
|
||||
static int at91_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *r, *r_gpbr;
|
||||
struct resource *r;
|
||||
struct sam9_rtc *rtc;
|
||||
int ret, irq;
|
||||
u32 mr;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!r || !r_gpbr) {
|
||||
dev_err(&pdev->dev, "need 2 ressources\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
unsigned int sclk_rate;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev)
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (!rtc->rtt) {
|
||||
dev_err(&pdev->dev, "failed to map registers, aborting.\n");
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rtc->rtt = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(rtc->rtt))
|
||||
return PTR_ERR(rtc->rtt);
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
/*
|
||||
* TODO: Remove this code chunk when removing non DT board
|
||||
* support. Remember to remove the gpbr_regmap_config
|
||||
* variable too.
|
||||
*/
|
||||
void __iomem *gpbr;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
gpbr = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(gpbr))
|
||||
return PTR_ERR(gpbr);
|
||||
|
||||
rtc->gpbr = regmap_init_mmio(NULL, gpbr,
|
||||
&gpbr_regmap_config);
|
||||
} else {
|
||||
struct of_phandle_args args;
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
|
||||
"atmel,rtt-rtc-time-reg", 1, 0,
|
||||
&args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc->gpbr = syscon_node_to_regmap(args.np);
|
||||
rtc->gpbr_offset = args.args[0];
|
||||
}
|
||||
|
||||
if (IS_ERR(rtc->gpbr)) {
|
||||
dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start,
|
||||
resource_size(r_gpbr));
|
||||
if (!rtc->gpbr) {
|
||||
dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n");
|
||||
return -ENOMEM;
|
||||
rtc->sclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(rtc->sclk))
|
||||
return PTR_ERR(rtc->sclk);
|
||||
|
||||
sclk_rate = clk_get_rate(rtc->sclk);
|
||||
if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) {
|
||||
dev_err(&pdev->dev, "Invalid slow clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rtc->sclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not enable slow clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mr = rtt_readl(rtc, MR);
|
||||
|
||||
/* unless RTT is counting at 1 Hz, re-initialize it */
|
||||
if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) {
|
||||
mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES);
|
||||
if ((mr & AT91_RTT_RTPRES) != sclk_rate) {
|
||||
mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES);
|
||||
gpbr_writel(rtc, 0);
|
||||
}
|
||||
|
||||
@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev)
|
||||
/* disable all interrupts */
|
||||
rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
|
||||
|
||||
if (!IS_ERR(rtc->sclk))
|
||||
clk_disable_unprepare(rtc->sclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id at91_rtc_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91sam9260-rtt" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver at91_rtc_driver = {
|
||||
.probe = at91_rtc_probe,
|
||||
.remove = at91_rtc_remove,
|
||||
@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = {
|
||||
.name = "rtc-at91sam9",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &at91_rtc_pm_ops,
|
||||
.of_match_table = of_match_ptr(at91_rtc_dt_ids),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -348,15 +348,15 @@ struct knav_range_info {
|
||||
list_for_each_entry(region, &kdev->regions, list)
|
||||
|
||||
#define first_region(kdev) \
|
||||
list_first_entry(&kdev->regions, \
|
||||
struct knav_region, list)
|
||||
list_first_entry_or_null(&kdev->regions, \
|
||||
struct knav_region, list)
|
||||
|
||||
#define for_each_queue_range(kdev, range) \
|
||||
list_for_each_entry(range, &kdev->queue_ranges, list)
|
||||
|
||||
#define first_queue_range(kdev) \
|
||||
list_first_entry(&kdev->queue_ranges, \
|
||||
struct knav_range_info, list)
|
||||
list_first_entry_or_null(&kdev->queue_ranges, \
|
||||
struct knav_range_info, list)
|
||||
|
||||
#define for_each_pool(kdev, pool) \
|
||||
list_for_each_entry(pool, &kdev->pools, list)
|
||||
|
@ -785,7 +785,7 @@ void *knav_pool_create(const char *name,
|
||||
dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n",
|
||||
region_id, name);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* Region maintains a sorted (by region offset) list of pools
|
||||
@ -815,15 +815,16 @@ void *knav_pool_create(const char *name,
|
||||
dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n",
|
||||
name, region_id);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
mutex_unlock(&knav_dev_lock);
|
||||
kdesc_fill_pool(pool);
|
||||
return pool;
|
||||
|
||||
err:
|
||||
err_unlock:
|
||||
mutex_unlock(&knav_dev_lock);
|
||||
err:
|
||||
kfree(pool->name);
|
||||
devm_kfree(kdev->dev, pool);
|
||||
return ERR_PTR(ret);
|
||||
@ -1305,14 +1306,14 @@ static void knav_free_queue_ranges(struct knav_device *kdev)
|
||||
static void knav_queue_free_regions(struct knav_device *kdev)
|
||||
{
|
||||
struct knav_region *region;
|
||||
struct knav_pool *pool;
|
||||
struct knav_pool *pool, *tmp;
|
||||
unsigned size;
|
||||
|
||||
for (;;) {
|
||||
region = first_region(kdev);
|
||||
if (!region)
|
||||
break;
|
||||
list_for_each_entry(pool, ®ion->pools, region_inst)
|
||||
list_for_each_entry_safe(pool, tmp, ®ion->pools, region_inst)
|
||||
knav_pool_destroy(pool);
|
||||
|
||||
size = region->virt_end - region->virt_start;
|
||||
@ -1639,7 +1640,7 @@ static int knav_queue_init_queues(struct knav_device *kdev)
|
||||
size = (1 << kdev->inst_shift) * kdev->num_queues_in_use;
|
||||
kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL);
|
||||
if (!kdev->instances)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_queue_range(kdev, range) {
|
||||
if (range->ops && range->ops->init_range)
|
||||
|
@ -249,14 +249,14 @@ config SERIAL_SAMSUNG
|
||||
|
||||
config SERIAL_SAMSUNG_UARTS_4
|
||||
bool
|
||||
depends on PLAT_SAMSUNG
|
||||
depends on SERIAL_SAMSUNG
|
||||
default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442)
|
||||
help
|
||||
Internal node for the common case of 4 Samsung compatible UARTs
|
||||
|
||||
config SERIAL_SAMSUNG_UARTS
|
||||
int
|
||||
depends on PLAT_SAMSUNG
|
||||
depends on SERIAL_SAMSUNG
|
||||
default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416
|
||||
default 3
|
||||
help
|
||||
|
@ -72,11 +72,18 @@ struct hdq_data {
|
||||
static int omap_hdq_probe(struct platform_device *pdev);
|
||||
static int omap_hdq_remove(struct platform_device *pdev);
|
||||
|
||||
static struct of_device_id omap_hdq_dt_ids[] = {
|
||||
{ .compatible = "ti,omap3-1w" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_hdq_dt_ids);
|
||||
|
||||
static struct platform_driver omap_hdq_driver = {
|
||||
.probe = omap_hdq_probe,
|
||||
.remove = omap_hdq_remove,
|
||||
.driver = {
|
||||
.name = "omap_hdq",
|
||||
.of_match_table = omap_hdq_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
#define TEGRA114_CLK_I2S0 30
|
||||
/* 31 */
|
||||
|
||||
/* 32 */
|
||||
#define TEGRA114_CLK_MC 32
|
||||
/* 33 */
|
||||
#define TEGRA114_CLK_APBDMA 34
|
||||
/* 35 */
|
||||
|
@ -48,7 +48,7 @@
|
||||
#define TEGRA124_CLK_I2S0 30
|
||||
/* 31 */
|
||||
|
||||
/* 32 */
|
||||
#define TEGRA124_CLK_MC 32
|
||||
/* 33 */
|
||||
#define TEGRA124_CLK_APBDMA 34
|
||||
/* 35 */
|
||||
|
@ -49,7 +49,7 @@
|
||||
/* 30 */
|
||||
#define TEGRA20_CLK_CACHE2 31
|
||||
|
||||
#define TEGRA20_CLK_MEM 32
|
||||
#define TEGRA20_CLK_MC 32
|
||||
#define TEGRA20_CLK_AHBDMA 33
|
||||
#define TEGRA20_CLK_APBDMA 34
|
||||
/* 35 */
|
||||
|
25
include/dt-bindings/memory/tegra114-mc.h
Normal file
25
include/dt-bindings/memory/tegra114-mc.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef DT_BINDINGS_MEMORY_TEGRA114_MC_H
|
||||
#define DT_BINDINGS_MEMORY_TEGRA114_MC_H
|
||||
|
||||
#define TEGRA_SWGROUP_PTC 0
|
||||
#define TEGRA_SWGROUP_DC 1
|
||||
#define TEGRA_SWGROUP_DCB 2
|
||||
#define TEGRA_SWGROUP_EPP 3
|
||||
#define TEGRA_SWGROUP_G2 4
|
||||
#define TEGRA_SWGROUP_AVPC 5
|
||||
#define TEGRA_SWGROUP_NV 6
|
||||
#define TEGRA_SWGROUP_HDA 7
|
||||
#define TEGRA_SWGROUP_HC 8
|
||||
#define TEGRA_SWGROUP_MSENC 9
|
||||
#define TEGRA_SWGROUP_PPCS 10
|
||||
#define TEGRA_SWGROUP_VDE 11
|
||||
#define TEGRA_SWGROUP_MPCORELP 12
|
||||
#define TEGRA_SWGROUP_MPCORE 13
|
||||
#define TEGRA_SWGROUP_VI 14
|
||||
#define TEGRA_SWGROUP_ISP 15
|
||||
#define TEGRA_SWGROUP_XUSB_HOST 16
|
||||
#define TEGRA_SWGROUP_XUSB_DEV 17
|
||||
#define TEGRA_SWGROUP_EMUCIF 18
|
||||
#define TEGRA_SWGROUP_TSEC 19
|
||||
|
||||
#endif
|
31
include/dt-bindings/memory/tegra124-mc.h
Normal file
31
include/dt-bindings/memory/tegra124-mc.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef DT_BINDINGS_MEMORY_TEGRA124_MC_H
|
||||
#define DT_BINDINGS_MEMORY_TEGRA124_MC_H
|
||||
|
||||
#define TEGRA_SWGROUP_PTC 0
|
||||
#define TEGRA_SWGROUP_DC 1
|
||||
#define TEGRA_SWGROUP_DCB 2
|
||||
#define TEGRA_SWGROUP_AFI 3
|
||||
#define TEGRA_SWGROUP_AVPC 4
|
||||
#define TEGRA_SWGROUP_HDA 5
|
||||
#define TEGRA_SWGROUP_HC 6
|
||||
#define TEGRA_SWGROUP_MSENC 7
|
||||
#define TEGRA_SWGROUP_PPCS 8
|
||||
#define TEGRA_SWGROUP_SATA 9
|
||||
#define TEGRA_SWGROUP_VDE 10
|
||||
#define TEGRA_SWGROUP_MPCORELP 11
|
||||
#define TEGRA_SWGROUP_MPCORE 12
|
||||
#define TEGRA_SWGROUP_ISP2 13
|
||||
#define TEGRA_SWGROUP_XUSB_HOST 14
|
||||
#define TEGRA_SWGROUP_XUSB_DEV 15
|
||||
#define TEGRA_SWGROUP_ISP2B 16
|
||||
#define TEGRA_SWGROUP_TSEC 17
|
||||
#define TEGRA_SWGROUP_A9AVP 18
|
||||
#define TEGRA_SWGROUP_GPU 19
|
||||
#define TEGRA_SWGROUP_SDMMC1A 20
|
||||
#define TEGRA_SWGROUP_SDMMC2A 21
|
||||
#define TEGRA_SWGROUP_SDMMC3A 22
|
||||
#define TEGRA_SWGROUP_SDMMC4A 23
|
||||
#define TEGRA_SWGROUP_VIC 24
|
||||
#define TEGRA_SWGROUP_VI 25
|
||||
|
||||
#endif
|
24
include/dt-bindings/memory/tegra30-mc.h
Normal file
24
include/dt-bindings/memory/tegra30-mc.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef DT_BINDINGS_MEMORY_TEGRA30_MC_H
|
||||
#define DT_BINDINGS_MEMORY_TEGRA30_MC_H
|
||||
|
||||
#define TEGRA_SWGROUP_PTC 0
|
||||
#define TEGRA_SWGROUP_DC 1
|
||||
#define TEGRA_SWGROUP_DCB 2
|
||||
#define TEGRA_SWGROUP_EPP 3
|
||||
#define TEGRA_SWGROUP_G2 4
|
||||
#define TEGRA_SWGROUP_MPE 5
|
||||
#define TEGRA_SWGROUP_VI 6
|
||||
#define TEGRA_SWGROUP_AFI 7
|
||||
#define TEGRA_SWGROUP_AVPC 8
|
||||
#define TEGRA_SWGROUP_NV 9
|
||||
#define TEGRA_SWGROUP_NV2 10
|
||||
#define TEGRA_SWGROUP_HDA 11
|
||||
#define TEGRA_SWGROUP_HC 12
|
||||
#define TEGRA_SWGROUP_PPCS 13
|
||||
#define TEGRA_SWGROUP_SATA 14
|
||||
#define TEGRA_SWGROUP_VDE 15
|
||||
#define TEGRA_SWGROUP_MPCORELP 16
|
||||
#define TEGRA_SWGROUP_MPCORE 17
|
||||
#define TEGRA_SWGROUP_ISP 18
|
||||
|
||||
#endif
|
61
include/dt-bindings/reset-controller/stih407-resets.h
Normal file
61
include/dt-bindings/reset-controller/stih407-resets.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This header provides constants for the reset controller
|
||||
* based peripheral powerdown requests on the STMicroelectronics
|
||||
* STiH407 SoC.
|
||||
*/
|
||||
#ifndef _DT_BINDINGS_RESET_CONTROLLER_STIH407
|
||||
#define _DT_BINDINGS_RESET_CONTROLLER_STIH407
|
||||
|
||||
/* Powerdown requests control 0 */
|
||||
#define STIH407_EMISS_POWERDOWN 0
|
||||
#define STIH407_NAND_POWERDOWN 1
|
||||
|
||||
/* Synp GMAC PowerDown */
|
||||
#define STIH407_ETH1_POWERDOWN 2
|
||||
|
||||
/* Powerdown requests control 1 */
|
||||
#define STIH407_USB3_POWERDOWN 3
|
||||
#define STIH407_USB2_PORT1_POWERDOWN 4
|
||||
#define STIH407_USB2_PORT0_POWERDOWN 5
|
||||
#define STIH407_PCIE1_POWERDOWN 6
|
||||
#define STIH407_PCIE0_POWERDOWN 7
|
||||
#define STIH407_SATA1_POWERDOWN 8
|
||||
#define STIH407_SATA0_POWERDOWN 9
|
||||
|
||||
/* Reset defines */
|
||||
#define STIH407_ETH1_SOFTRESET 0
|
||||
#define STIH407_MMC1_SOFTRESET 1
|
||||
#define STIH407_PICOPHY_SOFTRESET 2
|
||||
#define STIH407_IRB_SOFTRESET 3
|
||||
#define STIH407_PCIE0_SOFTRESET 4
|
||||
#define STIH407_PCIE1_SOFTRESET 5
|
||||
#define STIH407_SATA0_SOFTRESET 6
|
||||
#define STIH407_SATA1_SOFTRESET 7
|
||||
#define STIH407_MIPHY0_SOFTRESET 8
|
||||
#define STIH407_MIPHY1_SOFTRESET 9
|
||||
#define STIH407_MIPHY2_SOFTRESET 10
|
||||
#define STIH407_SATA0_PWR_SOFTRESET 11
|
||||
#define STIH407_SATA1_PWR_SOFTRESET 12
|
||||
#define STIH407_DELTA_SOFTRESET 13
|
||||
#define STIH407_BLITTER_SOFTRESET 14
|
||||
#define STIH407_HDTVOUT_SOFTRESET 15
|
||||
#define STIH407_HDQVDP_SOFTRESET 16
|
||||
#define STIH407_VDP_AUX_SOFTRESET 17
|
||||
#define STIH407_COMPO_SOFTRESET 18
|
||||
#define STIH407_HDMI_TX_PHY_SOFTRESET 19
|
||||
#define STIH407_JPEG_DEC_SOFTRESET 20
|
||||
#define STIH407_VP8_DEC_SOFTRESET 21
|
||||
#define STIH407_GPU_SOFTRESET 22
|
||||
#define STIH407_HVA_SOFTRESET 23
|
||||
#define STIH407_ERAM_HVA_SOFTRESET 24
|
||||
#define STIH407_LPM_SOFTRESET 25
|
||||
#define STIH407_KEYSCAN_SOFTRESET 26
|
||||
#define STIH407_USB2_PORT0_SOFTRESET 27
|
||||
#define STIH407_USB2_PORT1_SOFTRESET 28
|
||||
|
||||
/* Picophy reset defines */
|
||||
#define STIH407_PICOPHY0_RESET 0
|
||||
#define STIH407_PICOPHY1_RESET 1
|
||||
#define STIH407_PICOPHY2_RESET 2
|
||||
|
||||
#endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH407 */
|
@ -22,6 +22,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <trace/events/iommu.h>
|
||||
|
||||
#define IOMMU_READ (1 << 0)
|
||||
@ -97,6 +98,8 @@ enum iommu_attr {
|
||||
* @detach_dev: detach device from an iommu domain
|
||||
* @map: map a physically contiguous memory region to an iommu domain
|
||||
* @unmap: unmap a physically contiguous memory region from an iommu domain
|
||||
* @map_sg: map a scatter-gather list of physically contiguous memory chunks
|
||||
* to an iommu domain
|
||||
* @iova_to_phys: translate iova to physical address
|
||||
* @add_device: add device to iommu grouping
|
||||
* @remove_device: remove device from iommu grouping
|
||||
@ -114,6 +117,8 @@ struct iommu_ops {
|
||||
phys_addr_t paddr, size_t size, int prot);
|
||||
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot);
|
||||
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
|
||||
int (*add_device)(struct device *dev);
|
||||
void (*remove_device)(struct device *dev);
|
||||
@ -156,6 +161,9 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot);
|
||||
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg,unsigned int nents,
|
||||
int prot);
|
||||
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
|
||||
extern void iommu_set_fault_handler(struct iommu_domain *domain,
|
||||
iommu_fault_handler_t handler, void *token);
|
||||
@ -241,6 +249,13 @@ static inline int report_iommu_fault(struct iommu_domain *domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline size_t iommu_map_sg(struct iommu_domain *domain,
|
||||
unsigned long iova, struct scatterlist *sg,
|
||||
unsigned int nents, int prot)
|
||||
{
|
||||
return domain->ops->map_sg(domain, iova, sg, nents, prot);
|
||||
}
|
||||
|
||||
#else /* CONFIG_IOMMU_API */
|
||||
|
||||
struct iommu_ops {};
|
||||
@ -293,6 +308,13 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline size_t iommu_map_sg(struct iommu_domain *domain,
|
||||
unsigned long iova, struct scatterlist *sg,
|
||||
unsigned int nents, int prot)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int iommu_domain_window_enable(struct iommu_domain *domain,
|
||||
u32 wnd_nr, phys_addr_t paddr,
|
||||
u64 size, int prot)
|
||||
|
@ -12,11 +12,13 @@ struct reset_controller_dev;
|
||||
* things to reset the device
|
||||
* @assert: manually assert the reset line, if supported
|
||||
* @deassert: manually deassert the reset line, if supported
|
||||
* @status: return the status of the reset line, if supported
|
||||
*/
|
||||
struct reset_control_ops {
|
||||
int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);
|
||||
int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);
|
||||
int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);
|
||||
int (*status)(struct reset_controller_dev *rcdev, unsigned long id);
|
||||
};
|
||||
|
||||
struct module;
|
||||
|
@ -10,6 +10,7 @@ struct reset_control;
|
||||
int reset_control_reset(struct reset_control *rstc);
|
||||
int reset_control_assert(struct reset_control *rstc);
|
||||
int reset_control_deassert(struct reset_control *rstc);
|
||||
int reset_control_status(struct reset_control *rstc);
|
||||
|
||||
struct reset_control *reset_control_get(struct device *dev, const char *id);
|
||||
void reset_control_put(struct reset_control *rstc);
|
||||
@ -57,6 +58,12 @@ static inline int reset_control_deassert(struct reset_control *rstc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int reset_control_status(struct reset_control *rstc)
|
||||
{
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void reset_control_put(struct reset_control *rstc)
|
||||
{
|
||||
WARN_ON(1);
|
||||
|
107
include/soc/tegra/mc.h
Normal file
107
include/soc/tegra/mc.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014 NVIDIA Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SOC_TEGRA_MC_H__
|
||||
#define __SOC_TEGRA_MC_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct clk;
|
||||
struct device;
|
||||
struct page;
|
||||
|
||||
struct tegra_smmu_enable {
|
||||
unsigned int reg;
|
||||
unsigned int bit;
|
||||
};
|
||||
|
||||
/* latency allowance */
|
||||
struct tegra_mc_la {
|
||||
unsigned int reg;
|
||||
unsigned int shift;
|
||||
unsigned int mask;
|
||||
unsigned int def;
|
||||
};
|
||||
|
||||
struct tegra_mc_client {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
unsigned int swgroup;
|
||||
|
||||
unsigned int fifo_size;
|
||||
|
||||
struct tegra_smmu_enable smmu;
|
||||
struct tegra_mc_la la;
|
||||
};
|
||||
|
||||
struct tegra_smmu_swgroup {
|
||||
unsigned int swgroup;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
struct tegra_smmu_ops {
|
||||
void (*flush_dcache)(struct page *page, unsigned long offset,
|
||||
size_t size);
|
||||
};
|
||||
|
||||
struct tegra_smmu_soc {
|
||||
const struct tegra_mc_client *clients;
|
||||
unsigned int num_clients;
|
||||
|
||||
const struct tegra_smmu_swgroup *swgroups;
|
||||
unsigned int num_swgroups;
|
||||
|
||||
bool supports_round_robin_arbitration;
|
||||
bool supports_request_limit;
|
||||
|
||||
unsigned int num_asids;
|
||||
|
||||
const struct tegra_smmu_ops *ops;
|
||||
};
|
||||
|
||||
struct tegra_mc;
|
||||
struct tegra_smmu;
|
||||
|
||||
#ifdef CONFIG_TEGRA_IOMMU_SMMU
|
||||
struct tegra_smmu *tegra_smmu_probe(struct device *dev,
|
||||
const struct tegra_smmu_soc *soc,
|
||||
struct tegra_mc *mc);
|
||||
#else
|
||||
static inline struct tegra_smmu *
|
||||
tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
|
||||
struct tegra_mc *mc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct tegra_mc_soc {
|
||||
const struct tegra_mc_client *clients;
|
||||
unsigned int num_clients;
|
||||
|
||||
const unsigned int *emem_regs;
|
||||
unsigned int num_emem_regs;
|
||||
|
||||
unsigned int num_address_bits;
|
||||
unsigned int atom_size;
|
||||
|
||||
const struct tegra_smmu_soc *smmu;
|
||||
};
|
||||
|
||||
struct tegra_mc {
|
||||
struct device *dev;
|
||||
struct tegra_smmu *smmu;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
|
||||
const struct tegra_mc_soc *soc;
|
||||
unsigned long tick;
|
||||
};
|
||||
|
||||
#endif /* __SOC_TEGRA_MC_H__ */
|
Loading…
Reference in New Issue
Block a user