diff options
83 files changed, 5846 insertions, 2010 deletions
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt index 37b2cafa4e52..256b4d8bab7b 100644 --- a/Documentation/devicetree/bindings/arm/arch_timer.txt +++ b/Documentation/devicetree/bindings/arm/arch_timer.txt | |||
| @@ -22,6 +22,14 @@ to deliver its interrupts via SPIs. | |||
| 22 | - always-on : a boolean property. If present, the timer is powered through an | 22 | - always-on : a boolean property. If present, the timer is powered through an |
| 23 | always-on power domain, therefore it never loses context. | 23 | always-on power domain, therefore it never loses context. |
| 24 | 24 | ||
| 25 | ** Optional properties: | ||
| 26 | |||
| 27 | - arm,cpu-registers-not-fw-configured : Firmware does not initialize | ||
| 28 | any of the generic timer CPU registers, which contain their | ||
| 29 | architecturally-defined reset values. Only supported for 32-bit | ||
| 30 | systems which follow the ARMv7 architected reset values. | ||
| 31 | |||
| 32 | |||
| 25 | Example: | 33 | Example: |
| 26 | 34 | ||
| 27 | timer { | 35 | timer { |
diff --git a/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt index e2d501d20c9a..1eceefb20f01 100644 --- a/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt +++ b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt | |||
| @@ -2,7 +2,11 @@ Broadcom GISB bus Arbiter controller | |||
| 2 | 2 | ||
| 3 | Required properties: | 3 | Required properties: |
| 4 | 4 | ||
| 5 | - compatible: should be "brcm,gisb-arb" | 5 | - compatible: |
| 6 | "brcm,gisb-arb" or "brcm,bcm7445-gisb-arb" for 28nm chips | ||
| 7 | "brcm,bcm7435-gisb-arb" for newer 40nm chips | ||
| 8 | "brcm,bcm7400-gisb-arb" for older 40nm chips and all 65nm chips | ||
| 9 | "brcm,bcm7038-gisb-arb" for 130nm chips | ||
| 6 | - reg: specifies the base physical address and size of the registers | 10 | - reg: specifies the base physical address and size of the registers |
| 7 | - interrupt-parent: specifies the phandle to the parent interrupt controller | 11 | - interrupt-parent: specifies the phandle to the parent interrupt controller |
| 8 | this arbiter gets interrupt line from | 12 | this arbiter gets interrupt line from |
diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra-mc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra-mc.txt new file mode 100644 index 000000000000..f3db93c85eea --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra-mc.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | NVIDIA Tegra Memory Controller device tree bindings | ||
| 2 | =================================================== | ||
| 3 | |||
| 4 | Required properties: | ||
| 5 | - compatible: Should be "nvidia,tegra<chip>-mc" | ||
| 6 | - reg: Physical base address and length of the controller's registers. | ||
| 7 | - clocks: Must contain an entry for each entry in clock-names. | ||
| 8 | See ../clocks/clock-bindings.txt for details. | ||
| 9 | - clock-names: Must include the following entries: | ||
| 10 | - mc: the module's clock input | ||
| 11 | - interrupts: The interrupt outputs from the controller. | ||
| 12 | - #iommu-cells: Should be 1. The single cell of the IOMMU specifier defines | ||
| 13 | the SWGROUP of the master. | ||
| 14 | |||
| 15 | This device implements an IOMMU that complies with the generic IOMMU binding. | ||
| 16 | See ../iommu/iommu.txt for details. | ||
| 17 | |||
| 18 | Example: | ||
| 19 | -------- | ||
| 20 | |||
| 21 | mc: memory-controller@0,70019000 { | ||
| 22 | compatible = "nvidia,tegra124-mc"; | ||
| 23 | reg = <0x0 0x70019000 0x0 0x1000>; | ||
| 24 | clocks = <&tegra_car TEGRA124_CLK_MC>; | ||
| 25 | clock-names = "mc"; | ||
| 26 | |||
| 27 | interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>; | ||
| 28 | |||
| 29 | #iommu-cells = <1>; | ||
| 30 | }; | ||
| 31 | |||
| 32 | sdhci@0,700b0000 { | ||
| 33 | compatible = "nvidia,tegra124-sdhci"; | ||
| 34 | ... | ||
| 35 | iommus = <&mc TEGRA_SWGROUP_SDMMC1A>; | ||
| 36 | }; | ||
diff --git a/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt b/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt new file mode 100644 index 000000000000..54ae9f747e45 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | STMicroelectronics STi family Sysconfig Picophy SoftReset Controller | ||
| 2 | ============================================================================= | ||
| 3 | |||
| 4 | This binding describes a reset controller device that is used to enable and | ||
| 5 | disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in | ||
| 6 | the STi family SoC system configuration registers. | ||
| 7 | |||
| 8 | The actual action taken when softreset is asserted is hardware dependent. | ||
| 9 | However, when asserted it may not be possible to access the hardware's | ||
| 10 | registers and after an assert/deassert sequence the hardware's previous state | ||
| 11 | may no longer be valid. | ||
| 12 | |||
| 13 | Please refer to Documentation/devicetree/bindings/reset/reset.txt | ||
| 14 | for common reset controller binding usage. | ||
| 15 | |||
| 16 | Required properties: | ||
| 17 | - compatible: Should be "st,stih407-picophyreset" | ||
| 18 | - #reset-cells: 1, see below | ||
| 19 | |||
| 20 | Example: | ||
| 21 | |||
| 22 | picophyreset: picophyreset-controller { | ||
| 23 | compatible = "st,stih407-picophyreset"; | ||
| 24 | #reset-cells = <1>; | ||
| 25 | }; | ||
| 26 | |||
| 27 | Specifying picophyreset control of devices | ||
| 28 | ======================================= | ||
| 29 | |||
| 30 | Device nodes should specify the reset channel required in their "resets" | ||
| 31 | property, containing a phandle to the picophyreset device node and an | ||
| 32 | index specifying which channel to use, as described in | ||
| 33 | Documentation/devicetree/bindings/reset/reset.txt. | ||
| 34 | |||
| 35 | Example: | ||
| 36 | |||
| 37 | usb2_picophy0: usbpicophy@0 { | ||
| 38 | resets = <&picophyreset STIH407_PICOPHY0_RESET>; | ||
| 39 | }; | ||
| 40 | |||
| 41 | Macro definitions for the supported reset channels can be found in: | ||
| 42 | include/dt-bindings/reset-controller/stih407-resets.h | ||
diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt b/Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt new file mode 100644 index 000000000000..6ae79d1843f3 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/atmel,at91sam9-rtc.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | Atmel AT91SAM9260 Real Time Timer | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be: "atmel,at91sam9260-rtt" | ||
| 5 | - reg: should encode the memory region of the RTT controller | ||
| 6 | - interrupts: rtt alarm/event interrupt | ||
| 7 | - clocks: should contain the 32 KHz slow clk that will drive the RTT block. | ||
| 8 | - atmel,rtt-rtc-time-reg: should encode the GPBR register used to store | ||
| 9 | the time base when the RTT is used as an RTC. | ||
| 10 | The first cell should point to the GPBR node and the second one | ||
| 11 | encode the offset within the GPBR block (or in other words, the | ||
| 12 | GPBR register used to store the time base). | ||
| 13 | |||
| 14 | |||
| 15 | Example: | ||
| 16 | |||
| 17 | rtt@fffffd20 { | ||
| 18 | compatible = "atmel,at91sam9260-rtt"; | ||
| 19 | reg = <0xfffffd20 0x10>; | ||
| 20 | interrupts = <1 4 7>; | ||
| 21 | clocks = <&clk32k>; | ||
| 22 | atmel,rtt-rtc-time-reg = <&gpbr 0x0>; | ||
| 23 | }; | ||
diff --git a/Documentation/devicetree/bindings/w1/omap-hdq.txt b/Documentation/devicetree/bindings/w1/omap-hdq.txt new file mode 100644 index 000000000000..fef794741bd1 --- /dev/null +++ b/Documentation/devicetree/bindings/w1/omap-hdq.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | * OMAP HDQ One wire bus master controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : should be "ti,omap3-1w" | ||
| 5 | - reg : Address and length of the register set for the device | ||
| 6 | - interrupts : interrupt line. | ||
| 7 | - ti,hwmods : "hdq1w" | ||
| 8 | |||
| 9 | Example: | ||
| 10 | |||
| 11 | - From omap3.dtsi | ||
| 12 | hdqw1w: 1w@480b2000 { | ||
| 13 | compatible = "ti,omap3-1w"; | ||
| 14 | reg = <0x480b2000 0x1000>; | ||
| 15 | interrupts = <58>; | ||
| 16 | ti,hwmods = "hdq1w"; | ||
| 17 | }; | ||
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8db9dc07f8ac..c8424a85bc04 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -1246,9 +1246,6 @@ source "arch/arm/common/Kconfig" | |||
| 1246 | 1246 | ||
| 1247 | menu "Bus support" | 1247 | menu "Bus support" |
| 1248 | 1248 | ||
| 1249 | config ARM_AMBA | ||
| 1250 | bool | ||
| 1251 | |||
| 1252 | config ISA | 1249 | config ISA |
| 1253 | bool | 1250 | bool |
| 1254 | help | 1251 | help |
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 72041f002b7e..5662a872689b 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c | |||
| @@ -245,6 +245,8 @@ struct edma { | |||
| 245 | /* list of channels with no even trigger; terminated by "-1" */ | 245 | /* list of channels with no even trigger; terminated by "-1" */ |
| 246 | const s8 *noevent; | 246 | const s8 *noevent; |
| 247 | 247 | ||
| 248 | struct edma_soc_info *info; | ||
| 249 | |||
| 248 | /* The edma_inuse bit for each PaRAM slot is clear unless the | 250 | /* The edma_inuse bit for each PaRAM slot is clear unless the |
| 249 | * channel is in use ... by ARM or DSP, for QDMA, or whatever. | 251 | * channel is in use ... by ARM or DSP, for QDMA, or whatever. |
| 250 | */ | 252 | */ |
| @@ -296,7 +298,7 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no, | |||
| 296 | ~(0x7 << bit), queue_no << bit); | 298 | ~(0x7 << bit), queue_no << bit); |
| 297 | } | 299 | } |
| 298 | 300 | ||
| 299 | static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, | 301 | static void assign_priority_to_queue(unsigned ctlr, int queue_no, |
| 300 | int priority) | 302 | int priority) |
| 301 | { | 303 | { |
| 302 | int bit = queue_no * 4; | 304 | int bit = queue_no * 4; |
| @@ -315,7 +317,7 @@ static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, | |||
| 315 | * included in that particular EDMA variant (Eg : dm646x) | 317 | * included in that particular EDMA variant (Eg : dm646x) |
| 316 | * | 318 | * |
| 317 | */ | 319 | */ |
| 318 | static void __init map_dmach_param(unsigned ctlr) | 320 | static void map_dmach_param(unsigned ctlr) |
| 319 | { | 321 | { |
| 320 | int i; | 322 | int i; |
| 321 | for (i = 0; i < EDMA_MAX_DMACH; i++) | 323 | for (i = 0; i < EDMA_MAX_DMACH; i++) |
| @@ -1798,6 +1800,7 @@ static int edma_probe(struct platform_device *pdev) | |||
| 1798 | edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); | 1800 | edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); |
| 1799 | edma_write_array(j, EDMA_QRAE, i, 0x0); | 1801 | edma_write_array(j, EDMA_QRAE, i, 0x0); |
| 1800 | } | 1802 | } |
| 1803 | edma_cc[j]->info = info[j]; | ||
| 1801 | arch_num_cc++; | 1804 | arch_num_cc++; |
| 1802 | 1805 | ||
| 1803 | edma_dev_info.id = j; | 1806 | edma_dev_info.id = j; |
| @@ -1807,9 +1810,56 @@ static int edma_probe(struct platform_device *pdev) | |||
| 1807 | return 0; | 1810 | return 0; |
| 1808 | } | 1811 | } |
| 1809 | 1812 | ||
| 1813 | #ifdef CONFIG_PM_SLEEP | ||
| 1814 | static int edma_pm_resume(struct device *dev) | ||
| 1815 | { | ||
| 1816 | int i, j; | ||
| 1817 | |||
| 1818 | for (j = 0; j < arch_num_cc; j++) { | ||
| 1819 | struct edma *cc = edma_cc[j]; | ||
| 1820 | |||
| 1821 | s8 (*queue_priority_mapping)[2]; | ||
| 1822 | |||
| 1823 | queue_priority_mapping = cc->info->queue_priority_mapping; | ||
| 1824 | |||
| 1825 | /* Event queue priority mapping */ | ||
| 1826 | for (i = 0; queue_priority_mapping[i][0] != -1; i++) | ||
| 1827 | assign_priority_to_queue(j, | ||
| 1828 | queue_priority_mapping[i][0], | ||
| 1829 | queue_priority_mapping[i][1]); | ||
| 1830 | |||
| 1831 | /* | ||
| 1832 | * Map the channel to param entry if channel mapping logic | ||
| 1833 | * exist | ||
| 1834 | */ | ||
| 1835 | if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) | ||
| 1836 | map_dmach_param(j); | ||
| 1837 | |||
| 1838 | for (i = 0; i < cc->num_channels; i++) { | ||
| 1839 | if (test_bit(i, cc->edma_inuse)) { | ||
| 1840 | /* ensure access through shadow region 0 */ | ||
| 1841 | edma_or_array2(j, EDMA_DRAE, 0, i >> 5, | ||
| 1842 | BIT(i & 0x1f)); | ||
| 1843 | |||
| 1844 | setup_dma_interrupt(i, | ||
| 1845 | cc->intr_data[i].callback, | ||
| 1846 | cc->intr_data[i].data); | ||
| 1847 | } | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | return 0; | ||
| 1852 | } | ||
| 1853 | #endif | ||
| 1854 | |||
| 1855 | static const struct dev_pm_ops edma_pm_ops = { | ||
| 1856 | SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, edma_pm_resume) | ||
| 1857 | }; | ||
| 1858 | |||
| 1810 | static struct platform_driver edma_driver = { | 1859 | static struct platform_driver edma_driver = { |
| 1811 | .driver = { | 1860 | .driver = { |
| 1812 | .name = "edma", | 1861 | .name = "edma", |
| 1862 | .pm = &edma_pm_ops, | ||
| 1813 | .of_match_table = edma_of_ids, | 1863 | .of_match_table = edma_of_ids, |
| 1814 | }, | 1864 | }, |
| 1815 | .probe = edma_probe, | 1865 | .probe = edma_probe, |
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 92793ba69c40..d4ebf5679f1f 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h | |||
| @@ -78,6 +78,15 @@ static inline u32 arch_timer_get_cntfrq(void) | |||
| 78 | return val; | 78 | return val; |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | static inline u64 arch_counter_get_cntpct(void) | ||
| 82 | { | ||
| 83 | u64 cval; | ||
| 84 | |||
| 85 | isb(); | ||
| 86 | asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); | ||
| 87 | return cval; | ||
| 88 | } | ||
| 89 | |||
| 81 | static inline u64 arch_counter_get_cntvct(void) | 90 | static inline u64 arch_counter_get_cntvct(void) |
| 82 | { | 91 | { |
| 83 | u64 cval; | 92 | u64 cval; |
diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h index c3a83691af8e..d9cf138fd7d4 100644 --- a/arch/arm/include/asm/perf_event.h +++ b/arch/arm/include/asm/perf_event.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #ifndef __ARM_PERF_EVENT_H__ | 12 | #ifndef __ARM_PERF_EVENT_H__ |
| 13 | #define __ARM_PERF_EVENT_H__ | 13 | #define __ARM_PERF_EVENT_H__ |
| 14 | 14 | ||
| 15 | #ifdef CONFIG_HW_PERF_EVENTS | 15 | #ifdef CONFIG_PERF_EVENTS |
| 16 | struct pt_regs; | 16 | struct pt_regs; |
| 17 | extern unsigned long perf_instruction_pointer(struct pt_regs *regs); | 17 | extern unsigned long perf_instruction_pointer(struct pt_regs *regs); |
| 18 | extern unsigned long perf_misc_flags(struct pt_regs *regs); | 18 | extern unsigned long perf_misc_flags(struct pt_regs *regs); |
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 0b648c541293..b1596bd59129 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
| 16 | #include <linux/perf_event.h> | 16 | #include <linux/perf_event.h> |
| 17 | 17 | ||
| 18 | #include <asm/cputype.h> | ||
| 19 | |||
| 18 | /* | 20 | /* |
| 19 | * struct arm_pmu_platdata - ARM PMU platform data | 21 | * struct arm_pmu_platdata - ARM PMU platform data |
| 20 | * | 22 | * |
| @@ -66,19 +68,25 @@ struct pmu_hw_events { | |||
| 66 | /* | 68 | /* |
| 67 | * The events that are active on the PMU for the given index. | 69 | * The events that are active on the PMU for the given index. |
| 68 | */ | 70 | */ |
| 69 | struct perf_event **events; | 71 | struct perf_event *events[ARMPMU_MAX_HWEVENTS]; |
| 70 | 72 | ||
| 71 | /* | 73 | /* |
| 72 | * A 1 bit for an index indicates that the counter is being used for | 74 | * A 1 bit for an index indicates that the counter is being used for |
| 73 | * an event. A 0 means that the counter can be used. | 75 | * an event. A 0 means that the counter can be used. |
| 74 | */ | 76 | */ |
| 75 | unsigned long *used_mask; | 77 | DECLARE_BITMAP(used_mask, ARMPMU_MAX_HWEVENTS); |
| 76 | 78 | ||
| 77 | /* | 79 | /* |
| 78 | * Hardware lock to serialize accesses to PMU registers. Needed for the | 80 | * Hardware lock to serialize accesses to PMU registers. Needed for the |
| 79 | * read/modify/write sequences. | 81 | * read/modify/write sequences. |
| 80 | */ | 82 | */ |
| 81 | raw_spinlock_t pmu_lock; | 83 | raw_spinlock_t pmu_lock; |
| 84 | |||
| 85 | /* | ||
| 86 | * When using percpu IRQs, we need a percpu dev_id. Place it here as we | ||
| 87 | * already have to allocate this struct per cpu. | ||
| 88 | */ | ||
| 89 | struct arm_pmu *percpu_pmu; | ||
| 82 | }; | 90 | }; |
| 83 | 91 | ||
| 84 | struct arm_pmu { | 92 | struct arm_pmu { |
| @@ -107,7 +115,8 @@ struct arm_pmu { | |||
| 107 | struct mutex reserve_mutex; | 115 | struct mutex reserve_mutex; |
| 108 | u64 max_period; | 116 | u64 max_period; |
| 109 | struct platform_device *plat_device; | 117 | struct platform_device *plat_device; |
| 110 | struct pmu_hw_events *(*get_hw_events)(void); | 118 | struct pmu_hw_events __percpu *hw_events; |
| 119 | struct notifier_block hotplug_nb; | ||
| 111 | }; | 120 | }; |
| 112 | 121 | ||
| 113 | #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) | 122 | #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) |
| @@ -127,6 +136,27 @@ int armpmu_map_event(struct perf_event *event, | |||
| 127 | [PERF_COUNT_HW_CACHE_RESULT_MAX], | 136 | [PERF_COUNT_HW_CACHE_RESULT_MAX], |
| 128 | u32 raw_event_mask); | 137 | u32 raw_event_mask); |
| 129 | 138 | ||
| 139 | struct pmu_probe_info { | ||
| 140 | unsigned int cpuid; | ||
| 141 | unsigned int mask; | ||
| 142 | int (*init)(struct arm_pmu *); | ||
| 143 | }; | ||
| 144 | |||
| 145 | #define PMU_PROBE(_cpuid, _mask, _fn) \ | ||
| 146 | { \ | ||
| 147 | .cpuid = (_cpuid), \ | ||
| 148 | .mask = (_mask), \ | ||
| 149 | .init = (_fn), \ | ||
| 150 | } | ||
| 151 | |||
| 152 | #define ARM_PMU_PROBE(_cpuid, _fn) \ | ||
| 153 | PMU_PROBE(_cpuid, ARM_CPU_PART_MASK, _fn) | ||
| 154 | |||
| 155 | #define ARM_PMU_XSCALE_MASK ((0xff << 24) | ARM_CPU_XSCALE_ARCH_MASK) | ||
| 156 | |||
| 157 | #define XSCALE_PMU_PROBE(_version, _fn) \ | ||
| 158 | PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn) | ||
| 159 | |||
| 130 | #endif /* CONFIG_HW_PERF_EVENTS */ | 160 | #endif /* CONFIG_HW_PERF_EVENTS */ |
| 131 | 161 | ||
| 132 | #endif /* __ARM_PMU_H__ */ | 162 | #endif /* __ARM_PMU_H__ */ |
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 38ddd9f83d0e..8dcbed5016ac 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o | |||
| 82 | obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o | 82 | obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o |
| 83 | obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o | 83 | obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o |
| 84 | obj-$(CONFIG_IWMMXT) += iwmmxt.o | 84 | obj-$(CONFIG_IWMMXT) += iwmmxt.o |
| 85 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o | 85 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o |
| 86 | obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o | 86 | obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o |
| 87 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt | 87 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt |
| 88 | obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o | 88 | obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o |
diff --git a/arch/arm/kernel/perf_callchain.c b/arch/arm/kernel/perf_callchain.c new file mode 100644 index 000000000000..4e02ae5950ff --- /dev/null +++ b/arch/arm/kernel/perf_callchain.c | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | /* | ||
| 2 | * ARM callchain support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles | ||
| 5 | * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> | ||
| 6 | * | ||
| 7 | * This code is based on the ARM OProfile backtrace code. | ||
| 8 | */ | ||
| 9 | #include <linux/perf_event.h> | ||
| 10 | #include <linux/uaccess.h> | ||
| 11 | |||
| 12 | #include <asm/stacktrace.h> | ||
| 13 | |||
| 14 | /* | ||
| 15 | * The registers we're interested in are at the end of the variable | ||
| 16 | * length saved register structure. The fp points at the end of this | ||
| 17 | * structure so the address of this struct is: | ||
| 18 | * (struct frame_tail *)(xxx->fp)-1 | ||
| 19 | * | ||
| 20 | * This code has been adapted from the ARM OProfile support. | ||
| 21 | */ | ||
| 22 | struct frame_tail { | ||
| 23 | struct frame_tail __user *fp; | ||
| 24 | unsigned long sp; | ||
| 25 | unsigned long lr; | ||
| 26 | } __attribute__((packed)); | ||
| 27 | |||
| 28 | /* | ||
| 29 | * Get the return address for a single stackframe and return a pointer to the | ||
| 30 | * next frame tail. | ||
| 31 | */ | ||
| 32 | static struct frame_tail __user * | ||
| 33 | user_backtrace(struct frame_tail __user *tail, | ||
| 34 | struct perf_callchain_entry *entry) | ||
| 35 | { | ||
| 36 | struct frame_tail buftail; | ||
| 37 | unsigned long err; | ||
| 38 | |||
| 39 | if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) | ||
| 40 | return NULL; | ||
| 41 | |||
| 42 | pagefault_disable(); | ||
| 43 | err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); | ||
| 44 | pagefault_enable(); | ||
| 45 | |||
| 46 | if (err) | ||
| 47 | return NULL; | ||
| 48 | |||
| 49 | perf_callchain_store(entry, buftail.lr); | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Frame pointers should strictly progress back up the stack | ||
| 53 | * (towards higher addresses). | ||
| 54 | */ | ||
| 55 | if (tail + 1 >= buftail.fp) | ||
| 56 | return NULL; | ||
| 57 | |||
| 58 | return buftail.fp - 1; | ||
| 59 | } | ||
| 60 | |||
| 61 | void | ||
| 62 | perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) | ||
| 63 | { | ||
| 64 | struct frame_tail __user *tail; | ||
| 65 | |||
| 66 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 67 | /* We don't support guest os callchain now */ | ||
| 68 | return; | ||
| 69 | } | ||
| 70 | |||
| 71 | perf_callchain_store(entry, regs->ARM_pc); | ||
| 72 | |||
| 73 | if (!current->mm) | ||
| 74 | return; | ||
| 75 | |||
| 76 | tail = (struct frame_tail __user *)regs->ARM_fp - 1; | ||
| 77 | |||
| 78 | while ((entry->nr < PERF_MAX_STACK_DEPTH) && | ||
| 79 | tail && !((unsigned long)tail & 0x3)) | ||
| 80 | tail = user_backtrace(tail, entry); | ||
| 81 | } | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Gets called by walk_stackframe() for every stackframe. This will be called | ||
| 85 | * whist unwinding the stackframe and is like a subroutine return so we use | ||
| 86 | * the PC. | ||
| 87 | */ | ||
| 88 | static int | ||
| 89 | callchain_trace(struct stackframe *fr, | ||
| 90 | void *data) | ||
| 91 | { | ||
| 92 | struct perf_callchain_entry *entry = data; | ||
| 93 | perf_callchain_store(entry, fr->pc); | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | void | ||
| 98 | perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) | ||
| 99 | { | ||
| 100 | struct stackframe fr; | ||
| 101 | |||
| 102 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 103 | /* We don't support guest os callchain now */ | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | |||
| 107 | arm_get_current_stackframe(regs, &fr); | ||
| 108 | walk_stackframe(&fr, callchain_trace, entry); | ||
| 109 | } | ||
| 110 | |||
| 111 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | ||
| 112 | { | ||
| 113 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | ||
| 114 | return perf_guest_cbs->get_guest_ip(); | ||
| 115 | |||
| 116 | return instruction_pointer(regs); | ||
| 117 | } | ||
| 118 | |||
| 119 | unsigned long perf_misc_flags(struct pt_regs *regs) | ||
| 120 | { | ||
| 121 | int misc = 0; | ||
| 122 | |||
| 123 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 124 | if (perf_guest_cbs->is_user_mode()) | ||
| 125 | misc |= PERF_RECORD_MISC_GUEST_USER; | ||
| 126 | else | ||
| 127 | misc |= PERF_RECORD_MISC_GUEST_KERNEL; | ||
| 128 | } else { | ||
| 129 | if (user_mode(regs)) | ||
| 130 | misc |= PERF_RECORD_MISC_USER; | ||
| 131 | else | ||
| 132 | misc |= PERF_RECORD_MISC_KERNEL; | ||
| 133 | } | ||
| 134 | |||
| 135 | return misc; | ||
| 136 | } | ||
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 266cba46db3e..e34934f63a49 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c | |||
| @@ -7,21 +7,18 @@ | |||
| 7 | * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> | 7 | * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> |
| 8 | * | 8 | * |
| 9 | * This code is based on the sparc64 perf event code, which is in turn based | 9 | * This code is based on the sparc64 perf event code, which is in turn based |
| 10 | * on the x86 code. Callchain code is based on the ARM OProfile backtrace | 10 | * on the x86 code. |
| 11 | * code. | ||
| 12 | */ | 11 | */ |
| 13 | #define pr_fmt(fmt) "hw perfevents: " fmt | 12 | #define pr_fmt(fmt) "hw perfevents: " fmt |
| 14 | 13 | ||
| 15 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 16 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
| 17 | #include <linux/pm_runtime.h> | 16 | #include <linux/pm_runtime.h> |
| 18 | #include <linux/uaccess.h> | ||
| 19 | #include <linux/irq.h> | 17 | #include <linux/irq.h> |
| 20 | #include <linux/irqdesc.h> | 18 | #include <linux/irqdesc.h> |
| 21 | 19 | ||
| 22 | #include <asm/irq_regs.h> | 20 | #include <asm/irq_regs.h> |
| 23 | #include <asm/pmu.h> | 21 | #include <asm/pmu.h> |
| 24 | #include <asm/stacktrace.h> | ||
| 25 | 22 | ||
| 26 | static int | 23 | static int |
| 27 | armpmu_map_cache_event(const unsigned (*cache_map) | 24 | armpmu_map_cache_event(const unsigned (*cache_map) |
| @@ -80,8 +77,12 @@ armpmu_map_event(struct perf_event *event, | |||
| 80 | u32 raw_event_mask) | 77 | u32 raw_event_mask) |
| 81 | { | 78 | { |
| 82 | u64 config = event->attr.config; | 79 | u64 config = event->attr.config; |
| 80 | int type = event->attr.type; | ||
| 83 | 81 | ||
| 84 | switch (event->attr.type) { | 82 | if (type == event->pmu->type) |
| 83 | return armpmu_map_raw_event(raw_event_mask, config); | ||
| 84 | |||
| 85 | switch (type) { | ||
| 85 | case PERF_TYPE_HARDWARE: | 86 | case PERF_TYPE_HARDWARE: |
| 86 | return armpmu_map_hw_event(event_map, config); | 87 | return armpmu_map_hw_event(event_map, config); |
| 87 | case PERF_TYPE_HW_CACHE: | 88 | case PERF_TYPE_HW_CACHE: |
| @@ -200,7 +201,7 @@ static void | |||
| 200 | armpmu_del(struct perf_event *event, int flags) | 201 | armpmu_del(struct perf_event *event, int flags) |
| 201 | { | 202 | { |
| 202 | struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | 203 | struct arm_pmu *armpmu = to_arm_pmu(event->pmu); |
| 203 | struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | 204 | struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); |
| 204 | struct hw_perf_event *hwc = &event->hw; | 205 | struct hw_perf_event *hwc = &event->hw; |
| 205 | int idx = hwc->idx; | 206 | int idx = hwc->idx; |
| 206 | 207 | ||
| @@ -217,7 +218,7 @@ static int | |||
| 217 | armpmu_add(struct perf_event *event, int flags) | 218 | armpmu_add(struct perf_event *event, int flags) |
| 218 | { | 219 | { |
| 219 | struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | 220 | struct arm_pmu *armpmu = to_arm_pmu(event->pmu); |
| 220 | struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | 221 | struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); |
| 221 | struct hw_perf_event *hwc = &event->hw; | 222 | struct hw_perf_event *hwc = &event->hw; |
| 222 | int idx; | 223 | int idx; |
| 223 | int err = 0; | 224 | int err = 0; |
| @@ -274,14 +275,12 @@ validate_group(struct perf_event *event) | |||
| 274 | { | 275 | { |
| 275 | struct perf_event *sibling, *leader = event->group_leader; | 276 | struct perf_event *sibling, *leader = event->group_leader; |
| 276 | struct pmu_hw_events fake_pmu; | 277 | struct pmu_hw_events fake_pmu; |
| 277 | DECLARE_BITMAP(fake_used_mask, ARMPMU_MAX_HWEVENTS); | ||
| 278 | 278 | ||
| 279 | /* | 279 | /* |
| 280 | * Initialise the fake PMU. We only need to populate the | 280 | * Initialise the fake PMU. We only need to populate the |
| 281 | * used_mask for the purposes of validation. | 281 | * used_mask for the purposes of validation. |
| 282 | */ | 282 | */ |
| 283 | memset(fake_used_mask, 0, sizeof(fake_used_mask)); | 283 | memset(&fake_pmu.used_mask, 0, sizeof(fake_pmu.used_mask)); |
| 284 | fake_pmu.used_mask = fake_used_mask; | ||
| 285 | 284 | ||
| 286 | if (!validate_event(&fake_pmu, leader)) | 285 | if (!validate_event(&fake_pmu, leader)) |
| 287 | return -EINVAL; | 286 | return -EINVAL; |
| @@ -305,17 +304,21 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) | |||
| 305 | int ret; | 304 | int ret; |
| 306 | u64 start_clock, finish_clock; | 305 | u64 start_clock, finish_clock; |
| 307 | 306 | ||
| 308 | if (irq_is_percpu(irq)) | 307 | /* |
| 309 | dev = *(void **)dev; | 308 | * we request the IRQ with a (possibly percpu) struct arm_pmu**, but |
| 310 | armpmu = dev; | 309 | * the handlers expect a struct arm_pmu*. The percpu_irq framework will |
| 310 | * do any necessary shifting, we just need to perform the first | ||
| 311 | * dereference. | ||
| 312 | */ | ||
| 313 | armpmu = *(void **)dev; | ||
| 311 | plat_device = armpmu->plat_device; | 314 | plat_device = armpmu->plat_device; |
| 312 | plat = dev_get_platdata(&plat_device->dev); | 315 | plat = dev_get_platdata(&plat_device->dev); |
| 313 | 316 | ||
| 314 | start_clock = sched_clock(); | 317 | start_clock = sched_clock(); |
| 315 | if (plat && plat->handle_irq) | 318 | if (plat && plat->handle_irq) |
| 316 | ret = plat->handle_irq(irq, dev, armpmu->handle_irq); | 319 | ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq); |
| 317 | else | 320 | else |
| 318 | ret = armpmu->handle_irq(irq, dev); | 321 | ret = armpmu->handle_irq(irq, armpmu); |
| 319 | finish_clock = sched_clock(); | 322 | finish_clock = sched_clock(); |
| 320 | 323 | ||
| 321 | perf_sample_event_took(finish_clock - start_clock); | 324 | perf_sample_event_took(finish_clock - start_clock); |
| @@ -468,7 +471,7 @@ static int armpmu_event_init(struct perf_event *event) | |||
| 468 | static void armpmu_enable(struct pmu *pmu) | 471 | static void armpmu_enable(struct pmu *pmu) |
| 469 | { | 472 | { |
| 470 | struct arm_pmu *armpmu = to_arm_pmu(pmu); | 473 | struct arm_pmu *armpmu = to_arm_pmu(pmu); |
| 471 | struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | 474 | struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); |
| 472 | int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); | 475 | int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); |
| 473 | 476 | ||
| 474 | if (enabled) | 477 | if (enabled) |
| @@ -533,130 +536,3 @@ int armpmu_register(struct arm_pmu *armpmu, int type) | |||
| 533 | return perf_pmu_register(&armpmu->pmu, armpmu->name, type); | 536 | return perf_pmu_register(&armpmu->pmu, armpmu->name, type); |
| 534 | } | 537 | } |
| 535 | 538 | ||
| 536 | /* | ||
| 537 | * Callchain handling code. | ||
| 538 | */ | ||
| 539 | |||
| 540 | /* | ||
| 541 | * The registers we're interested in are at the end of the variable | ||
| 542 | * length saved register structure. The fp points at the end of this | ||
| 543 | * structure so the address of this struct is: | ||
| 544 | * (struct frame_tail *)(xxx->fp)-1 | ||
| 545 | * | ||
| 546 | * This code has been adapted from the ARM OProfile support. | ||
| 547 | */ | ||
| 548 | struct frame_tail { | ||
| 549 | struct frame_tail __user *fp; | ||
| 550 | unsigned long sp; | ||
| 551 | unsigned long lr; | ||
| 552 | } __attribute__((packed)); | ||
| 553 | |||
| 554 | /* | ||
| 555 | * Get the return address for a single stackframe and return a pointer to the | ||
| 556 | * next frame tail. | ||
| 557 | */ | ||
| 558 | static struct frame_tail __user * | ||
| 559 | user_backtrace(struct frame_tail __user *tail, | ||
| 560 | struct perf_callchain_entry *entry) | ||
| 561 | { | ||
| 562 | struct frame_tail buftail; | ||
| 563 | unsigned long err; | ||
| 564 | |||
| 565 | if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) | ||
| 566 | return NULL; | ||
| 567 | |||
| 568 | pagefault_disable(); | ||
| 569 | err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); | ||
| 570 | pagefault_enable(); | ||
| 571 | |||
| 572 | if (err) | ||
| 573 | return NULL; | ||
| 574 | |||
| 575 | perf_callchain_store(entry, buftail.lr); | ||
| 576 | |||
| 577 | /* | ||
| 578 | * Frame pointers should strictly progress back up the stack | ||
| 579 | * (towards higher addresses). | ||
| 580 | */ | ||
| 581 | if (tail + 1 >= buftail.fp) | ||
| 582 | return NULL; | ||
| 583 | |||
| 584 | return buftail.fp - 1; | ||
| 585 | } | ||
| 586 | |||
| 587 | void | ||
| 588 | perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) | ||
| 589 | { | ||
| 590 | struct frame_tail __user *tail; | ||
| 591 | |||
| 592 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 593 | /* We don't support guest os callchain now */ | ||
| 594 | return; | ||
| 595 | } | ||
| 596 | |||
| 597 | perf_callchain_store(entry, regs->ARM_pc); | ||
| 598 | |||
| 599 | if (!current->mm) | ||
| 600 | return; | ||
| 601 | |||
| 602 | tail = (struct frame_tail __user *)regs->ARM_fp - 1; | ||
| 603 | |||
| 604 | while ((entry->nr < PERF_MAX_STACK_DEPTH) && | ||
| 605 | tail && !((unsigned long)tail & 0x3)) | ||
| 606 | tail = user_backtrace(tail, entry); | ||
| 607 | } | ||
| 608 | |||
| 609 | /* | ||
| 610 | * Gets called by walk_stackframe() for every stackframe. This will be called | ||
| 611 | * whist unwinding the stackframe and is like a subroutine return so we use | ||
| 612 | * the PC. | ||
| 613 | */ | ||
| 614 | static int | ||
| 615 | callchain_trace(struct stackframe *fr, | ||
| 616 | void *data) | ||
| 617 | { | ||
| 618 | struct perf_callchain_entry *entry = data; | ||
| 619 | perf_callchain_store(entry, fr->pc); | ||
| 620 | return 0; | ||
| 621 | } | ||
| 622 | |||
| 623 | void | ||
| 624 | perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) | ||
| 625 | { | ||
| 626 | struct stackframe fr; | ||
| 627 | |||
| 628 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 629 | /* We don't support guest os callchain now */ | ||
| 630 | return; | ||
| 631 | } | ||
| 632 | |||
| 633 | arm_get_current_stackframe(regs, &fr); | ||
| 634 | walk_stackframe(&fr, callchain_trace, entry); | ||
| 635 | } | ||
| 636 | |||
| 637 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | ||
| 638 | { | ||
| 639 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | ||
| 640 | return perf_guest_cbs->get_guest_ip(); | ||
| 641 | |||
| 642 | return instruction_pointer(regs); | ||
| 643 | } | ||
| 644 | |||
| 645 | unsigned long perf_misc_flags(struct pt_regs *regs) | ||
| 646 | { | ||
| 647 | int misc = 0; | ||
| 648 | |||
| 649 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
| 650 | if (perf_guest_cbs->is_user_mode()) | ||
| 651 | misc |= PERF_RECORD_MISC_GUEST_USER; | ||
| 652 | else | ||
| 653 | misc |= PERF_RECORD_MISC_GUEST_KERNEL; | ||
| 654 | } else { | ||
| 655 | if (user_mode(regs)) | ||
| 656 | misc |= PERF_RECORD_MISC_USER; | ||
| 657 | else | ||
| 658 | misc |= PERF_RECORD_MISC_KERNEL; | ||
| 659 | } | ||
| 660 | |||
| 661 | return misc; | ||
| 662 | } | ||
diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c index eb2c4d55666b..dd9acc95ebc0 100644 --- a/arch/arm/kernel/perf_event_cpu.c +++ b/arch/arm/kernel/perf_event_cpu.c | |||
| @@ -35,11 +35,6 @@ | |||
| 35 | /* Set at runtime when we know what CPU type we are. */ | 35 | /* Set at runtime when we know what CPU type we are. */ |
| 36 | static struct arm_pmu *cpu_pmu; | 36 | static struct arm_pmu *cpu_pmu; |
| 37 | 37 | ||
| 38 | static DEFINE_PER_CPU(struct arm_pmu *, percpu_pmu); | ||
| 39 | static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); | ||
| 40 | static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); | ||
| 41 | static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); | ||
| 42 | |||
| 43 | /* | 38 | /* |
| 44 | * Despite the names, these two functions are CPU-specific and are used | 39 | * Despite the names, these two functions are CPU-specific and are used |
| 45 | * by the OProfile/perf code. | 40 | * by the OProfile/perf code. |
| @@ -69,11 +64,6 @@ EXPORT_SYMBOL_GPL(perf_num_counters); | |||
| 69 | #include "perf_event_v6.c" | 64 | #include "perf_event_v6.c" |
| 70 | #include "perf_event_v7.c" | 65 | #include "perf_event_v7.c" |
| 71 | 66 | ||
| 72 | static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) | ||
| 73 | { | ||
| 74 | return this_cpu_ptr(&cpu_hw_events); | ||
| 75 | } | ||
| 76 | |||
| 77 | static void cpu_pmu_enable_percpu_irq(void *data) | 67 | static void cpu_pmu_enable_percpu_irq(void *data) |
| 78 | { | 68 | { |
| 79 | int irq = *(int *)data; | 69 | int irq = *(int *)data; |
| @@ -92,20 +82,21 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) | |||
| 92 | { | 82 | { |
| 93 | int i, irq, irqs; | 83 | int i, irq, irqs; |
| 94 | struct platform_device *pmu_device = cpu_pmu->plat_device; | 84 | struct platform_device *pmu_device = cpu_pmu->plat_device; |
| 85 | struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events; | ||
| 95 | 86 | ||
| 96 | irqs = min(pmu_device->num_resources, num_possible_cpus()); | 87 | irqs = min(pmu_device->num_resources, num_possible_cpus()); |
| 97 | 88 | ||
| 98 | irq = platform_get_irq(pmu_device, 0); | 89 | irq = platform_get_irq(pmu_device, 0); |
| 99 | if (irq >= 0 && irq_is_percpu(irq)) { | 90 | if (irq >= 0 && irq_is_percpu(irq)) { |
| 100 | on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); | 91 | on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); |
| 101 | free_percpu_irq(irq, &percpu_pmu); | 92 | free_percpu_irq(irq, &hw_events->percpu_pmu); |
| 102 | } else { | 93 | } else { |
| 103 | for (i = 0; i < irqs; ++i) { | 94 | for (i = 0; i < irqs; ++i) { |
| 104 | if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) | 95 | if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) |
| 105 | continue; | 96 | continue; |
| 106 | irq = platform_get_irq(pmu_device, i); | 97 | irq = platform_get_irq(pmu_device, i); |
| 107 | if (irq >= 0) | 98 | if (irq >= 0) |
| 108 | free_irq(irq, cpu_pmu); | 99 | free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, i)); |
| 109 | } | 100 | } |
| 110 | } | 101 | } |
| 111 | } | 102 | } |
| @@ -114,19 +105,21 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) | |||
| 114 | { | 105 | { |
| 115 | int i, err, irq, irqs; | 106 | int i, err, irq, irqs; |
| 116 | struct platform_device *pmu_device = cpu_pmu->plat_device; | 107 | struct platform_device *pmu_device = cpu_pmu->plat_device; |
| 108 | struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events; | ||
| 117 | 109 | ||
| 118 | if (!pmu_device) | 110 | if (!pmu_device) |
| 119 | return -ENODEV; | 111 | return -ENODEV; |
| 120 | 112 | ||
| 121 | irqs = min(pmu_device->num_resources, num_possible_cpus()); | 113 | irqs = min(pmu_device->num_resources, num_possible_cpus()); |
| 122 | if (irqs < 1) { | 114 | if (irqs < 1) { |
| 123 | printk_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n"); | 115 | pr_warn_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n"); |
| 124 | return 0; | 116 | return 0; |
| 125 | } | 117 | } |
| 126 | 118 | ||
| 127 | irq = platform_get_irq(pmu_device, 0); | 119 | irq = platform_get_irq(pmu_device, 0); |
| 128 | if (irq >= 0 && irq_is_percpu(irq)) { | 120 | if (irq >= 0 && irq_is_percpu(irq)) { |
| 129 | err = request_percpu_irq(irq, handler, "arm-pmu", &percpu_pmu); | 121 | err = request_percpu_irq(irq, handler, "arm-pmu", |
| 122 | &hw_events->percpu_pmu); | ||
| 130 | if (err) { | 123 | if (err) { |
| 131 | pr_err("unable to request IRQ%d for ARM PMU counters\n", | 124 | pr_err("unable to request IRQ%d for ARM PMU counters\n", |
| 132 | irq); | 125 | irq); |
| @@ -153,7 +146,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) | |||
| 153 | 146 | ||
| 154 | err = request_irq(irq, handler, | 147 | err = request_irq(irq, handler, |
| 155 | IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", | 148 | IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", |
| 156 | cpu_pmu); | 149 | per_cpu_ptr(&hw_events->percpu_pmu, i)); |
| 157 | if (err) { | 150 | if (err) { |
| 158 | pr_err("unable to request IRQ%d for ARM PMU counters\n", | 151 | pr_err("unable to request IRQ%d for ARM PMU counters\n", |
| 159 | irq); | 152 | irq); |
| @@ -167,18 +160,50 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) | |||
| 167 | return 0; | 160 | return 0; |
| 168 | } | 161 | } |
| 169 | 162 | ||
| 170 | static void cpu_pmu_init(struct arm_pmu *cpu_pmu) | 163 | /* |
| 164 | * PMU hardware loses all context when a CPU goes offline. | ||
| 165 | * When a CPU is hotplugged back in, since some hardware registers are | ||
| 166 | * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading | ||
| 167 | * junk values out of them. | ||
| 168 | */ | ||
| 169 | static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, | ||
| 170 | void *hcpu) | ||
| 171 | { | ||
| 172 | struct arm_pmu *pmu = container_of(b, struct arm_pmu, hotplug_nb); | ||
| 173 | |||
| 174 | if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) | ||
| 175 | return NOTIFY_DONE; | ||
| 176 | |||
| 177 | if (pmu->reset) | ||
| 178 | pmu->reset(pmu); | ||
| 179 | else | ||
| 180 | return NOTIFY_DONE; | ||
| 181 | |||
| 182 | return NOTIFY_OK; | ||
| 183 | } | ||
| 184 | |||
| 185 | static int cpu_pmu_init(struct arm_pmu *cpu_pmu) | ||
| 171 | { | 186 | { |
| 187 | int err; | ||
| 172 | int cpu; | 188 | int cpu; |
| 189 | struct pmu_hw_events __percpu *cpu_hw_events; | ||
| 190 | |||
| 191 | cpu_hw_events = alloc_percpu(struct pmu_hw_events); | ||
| 192 | if (!cpu_hw_events) | ||
| 193 | return -ENOMEM; | ||
| 194 | |||
| 195 | cpu_pmu->hotplug_nb.notifier_call = cpu_pmu_notify; | ||
| 196 | err = register_cpu_notifier(&cpu_pmu->hotplug_nb); | ||
| 197 | if (err) | ||
| 198 | goto out_hw_events; | ||
| 199 | |||
| 173 | for_each_possible_cpu(cpu) { | 200 | for_each_possible_cpu(cpu) { |
| 174 | struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); | 201 | struct pmu_hw_events *events = per_cpu_ptr(cpu_hw_events, cpu); |
| 175 | events->events = per_cpu(hw_events, cpu); | ||
| 176 | events->used_mask = per_cpu(used_mask, cpu); | ||
| 177 | raw_spin_lock_init(&events->pmu_lock); | 202 | raw_spin_lock_init(&events->pmu_lock); |
| 178 | per_cpu(percpu_pmu, cpu) = cpu_pmu; | 203 | events->percpu_pmu = cpu_pmu; |
| 179 | } | 204 | } |
| 180 | 205 | ||
| 181 | cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; | 206 | cpu_pmu->hw_events = cpu_hw_events; |
| 182 | cpu_pmu->request_irq = cpu_pmu_request_irq; | 207 | cpu_pmu->request_irq = cpu_pmu_request_irq; |
| 183 | cpu_pmu->free_irq = cpu_pmu_free_irq; | 208 | cpu_pmu->free_irq = cpu_pmu_free_irq; |
| 184 | 209 | ||
| @@ -189,31 +214,19 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu) | |||
| 189 | /* If no interrupts available, set the corresponding capability flag */ | 214 | /* If no interrupts available, set the corresponding capability flag */ |
| 190 | if (!platform_get_irq(cpu_pmu->plat_device, 0)) | 215 | if (!platform_get_irq(cpu_pmu->plat_device, 0)) |
| 191 | cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; | 216 | cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; |
| 192 | } | ||
| 193 | |||
| 194 | /* | ||
| 195 | * PMU hardware loses all context when a CPU goes offline. | ||
| 196 | * When a CPU is hotplugged back in, since some hardware registers are | ||
| 197 | * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading | ||
| 198 | * junk values out of them. | ||
| 199 | */ | ||
| 200 | static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, | ||
| 201 | void *hcpu) | ||
| 202 | { | ||
| 203 | if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) | ||
| 204 | return NOTIFY_DONE; | ||
| 205 | 217 | ||
| 206 | if (cpu_pmu && cpu_pmu->reset) | 218 | return 0; |
| 207 | cpu_pmu->reset(cpu_pmu); | ||
| 208 | else | ||
| 209 | return NOTIFY_DONE; | ||
| 210 | 219 | ||
| 211 | return NOTIFY_OK; | 220 | out_hw_events: |
| 221 | free_percpu(cpu_hw_events); | ||
| 222 | return err; | ||
| 212 | } | 223 | } |
| 213 | 224 | ||
| 214 | static struct notifier_block cpu_pmu_hotplug_notifier = { | 225 | static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu) |
| 215 | .notifier_call = cpu_pmu_notify, | 226 | { |
| 216 | }; | 227 | unregister_cpu_notifier(&cpu_pmu->hotplug_nb); |
| 228 | free_percpu(cpu_pmu->hw_events); | ||
| 229 | } | ||
| 217 | 230 | ||
| 218 | /* | 231 | /* |
| 219 | * PMU platform driver and devicetree bindings. | 232 | * PMU platform driver and devicetree bindings. |
| @@ -241,48 +254,34 @@ static struct platform_device_id cpu_pmu_plat_device_ids[] = { | |||
| 241 | {}, | 254 | {}, |
| 242 | }; | 255 | }; |
| 243 | 256 | ||
| 257 | static const struct pmu_probe_info pmu_probe_table[] = { | ||
| 258 | ARM_PMU_PROBE(ARM_CPU_PART_ARM1136, armv6_1136_pmu_init), | ||
| 259 | ARM_PMU_PROBE(ARM_CPU_PART_ARM1156, armv6_1156_pmu_init), | ||
| 260 | ARM_PMU_PROBE(ARM_CPU_PART_ARM1176, armv6_1176_pmu_init), | ||
| 261 | ARM_PMU_PROBE(ARM_CPU_PART_ARM11MPCORE, armv6mpcore_pmu_init), | ||
| 262 | ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A8, armv7_a8_pmu_init), | ||
| 263 | ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A9, armv7_a9_pmu_init), | ||
| 264 | XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V1, xscale1pmu_init), | ||
| 265 | XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V2, xscale2pmu_init), | ||
| 266 | { /* sentinel value */ } | ||
| 267 | }; | ||
| 268 | |||
| 244 | /* | 269 | /* |
| 245 | * CPU PMU identification and probing. | 270 | * CPU PMU identification and probing. |
| 246 | */ | 271 | */ |
| 247 | static int probe_current_pmu(struct arm_pmu *pmu) | 272 | static int probe_current_pmu(struct arm_pmu *pmu) |
| 248 | { | 273 | { |
| 249 | int cpu = get_cpu(); | 274 | int cpu = get_cpu(); |
| 275 | unsigned int cpuid = read_cpuid_id(); | ||
| 250 | int ret = -ENODEV; | 276 | int ret = -ENODEV; |
| 277 | const struct pmu_probe_info *info; | ||
| 251 | 278 | ||
| 252 | pr_info("probing PMU on CPU %d\n", cpu); | 279 | pr_info("probing PMU on CPU %d\n", cpu); |
| 253 | 280 | ||
| 254 | switch (read_cpuid_part()) { | 281 | for (info = pmu_probe_table; info->init != NULL; info++) { |
| 255 | /* ARM Ltd CPUs. */ | 282 | if ((cpuid & info->mask) != info->cpuid) |
| 256 | case ARM_CPU_PART_ARM1136: | 283 | continue; |
| 257 | ret = armv6_1136_pmu_init(pmu); | 284 | ret = info->init(pmu); |
| 258 | break; | ||
| 259 | case ARM_CPU_PART_ARM1156: | ||
| 260 | ret = armv6_1156_pmu_init(pmu); | ||
| 261 | break; | ||
| 262 | case ARM_CPU_PART_ARM1176: | ||
| 263 | ret = armv6_1176_pmu_init(pmu); | ||
| 264 | break; | ||
| 265 | case ARM_CPU_PART_ARM11MPCORE: | ||
| 266 | ret = armv6mpcore_pmu_init(pmu); | ||
| 267 | break; | ||
| 268 | case ARM_CPU_PART_CORTEX_A8: | ||
| 269 | ret = armv7_a8_pmu_init(pmu); | ||
| 270 | break; | ||
| 271 | case ARM_CPU_PART_CORTEX_A9: | ||
| 272 | ret = armv7_a9_pmu_init(pmu); | ||
| 273 | break; | ||
| 274 | |||
| 275 | default: | ||
| 276 | if (read_cpuid_implementor() == ARM_CPU_IMP_INTEL) { | ||
| 277 | switch (xscale_cpu_arch_version()) { | ||
| 278 | case ARM_CPU_XSCALE_ARCH_V1: | ||
| 279 | ret = xscale1pmu_init(pmu); | ||
| 280 | break; | ||
| 281 | case ARM_CPU_XSCALE_ARCH_V2: | ||
| 282 | ret = xscale2pmu_init(pmu); | ||
| 283 | break; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | break; | 285 | break; |
| 287 | } | 286 | } |
| 288 | 287 | ||
| @@ -299,13 +298,13 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) | |||
| 299 | int ret = -ENODEV; | 298 | int ret = -ENODEV; |
| 300 | 299 | ||
| 301 | if (cpu_pmu) { | 300 | if (cpu_pmu) { |
| 302 | pr_info("attempt to register multiple PMU devices!"); | 301 | pr_info("attempt to register multiple PMU devices!\n"); |
| 303 | return -ENOSPC; | 302 | return -ENOSPC; |
| 304 | } | 303 | } |
| 305 | 304 | ||
| 306 | pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL); | 305 | pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL); |
| 307 | if (!pmu) { | 306 | if (!pmu) { |
| 308 | pr_info("failed to allocate PMU device!"); | 307 | pr_info("failed to allocate PMU device!\n"); |
| 309 | return -ENOMEM; | 308 | return -ENOMEM; |
| 310 | } | 309 | } |
| 311 | 310 | ||
| @@ -320,18 +319,24 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) | |||
| 320 | } | 319 | } |
| 321 | 320 | ||
| 322 | if (ret) { | 321 | if (ret) { |
| 323 | pr_info("failed to probe PMU!"); | 322 | pr_info("failed to probe PMU!\n"); |
| 324 | goto out_free; | 323 | goto out_free; |
| 325 | } | 324 | } |
| 326 | 325 | ||
| 327 | cpu_pmu_init(cpu_pmu); | 326 | ret = cpu_pmu_init(cpu_pmu); |
| 328 | ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW); | 327 | if (ret) |
| 328 | goto out_free; | ||
| 329 | 329 | ||
| 330 | if (!ret) | 330 | ret = armpmu_register(cpu_pmu, -1); |
| 331 | return 0; | 331 | if (ret) |
| 332 | goto out_destroy; | ||
| 332 | 333 | ||
| 334 | return 0; | ||
| 335 | |||
| 336 | out_destroy: | ||
| 337 | cpu_pmu_destroy(cpu_pmu); | ||
| 333 | out_free: | 338 | out_free: |
| 334 | pr_info("failed to register PMU devices!"); | 339 | pr_info("failed to register PMU devices!\n"); |
| 335 | kfree(pmu); | 340 | kfree(pmu); |
| 336 | return ret; | 341 | return ret; |
| 337 | } | 342 | } |
| @@ -348,16 +353,6 @@ static struct platform_driver cpu_pmu_driver = { | |||
| 348 | 353 | ||
| 349 | static int __init register_pmu_driver(void) | 354 | static int __init register_pmu_driver(void) |
| 350 | { | 355 | { |
| 351 | int err; | 356 | return platform_driver_register(&cpu_pmu_driver); |
| 352 | |||
| 353 | err = register_cpu_notifier(&cpu_pmu_hotplug_notifier); | ||
| 354 | if (err) | ||
| 355 | return err; | ||
| 356 | |||
| 357 | err = platform_driver_register(&cpu_pmu_driver); | ||
| 358 | if (err) | ||
| 359 | unregister_cpu_notifier(&cpu_pmu_hotplug_notifier); | ||
| 360 | |||
| 361 | return err; | ||
| 362 | } | 357 | } |
| 363 | device_initcall(register_pmu_driver); | 358 | device_initcall(register_pmu_driver); |
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index abfeb04f3213..f2ffd5c542ed 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c | |||
| @@ -262,7 +262,7 @@ static void armv6pmu_enable_event(struct perf_event *event) | |||
| 262 | unsigned long val, mask, evt, flags; | 262 | unsigned long val, mask, evt, flags; |
| 263 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 263 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 264 | struct hw_perf_event *hwc = &event->hw; | 264 | struct hw_perf_event *hwc = &event->hw; |
| 265 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 265 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 266 | int idx = hwc->idx; | 266 | int idx = hwc->idx; |
| 267 | 267 | ||
| 268 | if (ARMV6_CYCLE_COUNTER == idx) { | 268 | if (ARMV6_CYCLE_COUNTER == idx) { |
| @@ -300,7 +300,7 @@ armv6pmu_handle_irq(int irq_num, | |||
| 300 | unsigned long pmcr = armv6_pmcr_read(); | 300 | unsigned long pmcr = armv6_pmcr_read(); |
| 301 | struct perf_sample_data data; | 301 | struct perf_sample_data data; |
| 302 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; | 302 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; |
| 303 | struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); | 303 | struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); |
| 304 | struct pt_regs *regs; | 304 | struct pt_regs *regs; |
| 305 | int idx; | 305 | int idx; |
| 306 | 306 | ||
| @@ -356,7 +356,7 @@ armv6pmu_handle_irq(int irq_num, | |||
| 356 | static void armv6pmu_start(struct arm_pmu *cpu_pmu) | 356 | static void armv6pmu_start(struct arm_pmu *cpu_pmu) |
| 357 | { | 357 | { |
| 358 | unsigned long flags, val; | 358 | unsigned long flags, val; |
| 359 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 359 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 360 | 360 | ||
| 361 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 361 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 362 | val = armv6_pmcr_read(); | 362 | val = armv6_pmcr_read(); |
| @@ -368,7 +368,7 @@ static void armv6pmu_start(struct arm_pmu *cpu_pmu) | |||
| 368 | static void armv6pmu_stop(struct arm_pmu *cpu_pmu) | 368 | static void armv6pmu_stop(struct arm_pmu *cpu_pmu) |
| 369 | { | 369 | { |
| 370 | unsigned long flags, val; | 370 | unsigned long flags, val; |
| 371 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 371 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 372 | 372 | ||
| 373 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 373 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 374 | val = armv6_pmcr_read(); | 374 | val = armv6_pmcr_read(); |
| @@ -409,7 +409,7 @@ static void armv6pmu_disable_event(struct perf_event *event) | |||
| 409 | unsigned long val, mask, evt, flags; | 409 | unsigned long val, mask, evt, flags; |
| 410 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 410 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 411 | struct hw_perf_event *hwc = &event->hw; | 411 | struct hw_perf_event *hwc = &event->hw; |
| 412 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 412 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 413 | int idx = hwc->idx; | 413 | int idx = hwc->idx; |
| 414 | 414 | ||
| 415 | if (ARMV6_CYCLE_COUNTER == idx) { | 415 | if (ARMV6_CYCLE_COUNTER == idx) { |
| @@ -444,7 +444,7 @@ static void armv6mpcore_pmu_disable_event(struct perf_event *event) | |||
| 444 | unsigned long val, mask, flags, evt = 0; | 444 | unsigned long val, mask, flags, evt = 0; |
| 445 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 445 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 446 | struct hw_perf_event *hwc = &event->hw; | 446 | struct hw_perf_event *hwc = &event->hw; |
| 447 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 447 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 448 | int idx = hwc->idx; | 448 | int idx = hwc->idx; |
| 449 | 449 | ||
| 450 | if (ARMV6_CYCLE_COUNTER == idx) { | 450 | if (ARMV6_CYCLE_COUNTER == idx) { |
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 116758b77f93..8993770c47de 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c | |||
| @@ -564,13 +564,11 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx) | |||
| 564 | return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx)); | 564 | return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx)); |
| 565 | } | 565 | } |
| 566 | 566 | ||
| 567 | static inline int armv7_pmnc_select_counter(int idx) | 567 | static inline void armv7_pmnc_select_counter(int idx) |
| 568 | { | 568 | { |
| 569 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); | 569 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); |
| 570 | asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); | 570 | asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); |
| 571 | isb(); | 571 | isb(); |
| 572 | |||
| 573 | return idx; | ||
| 574 | } | 572 | } |
| 575 | 573 | ||
| 576 | static inline u32 armv7pmu_read_counter(struct perf_event *event) | 574 | static inline u32 armv7pmu_read_counter(struct perf_event *event) |
| @@ -580,13 +578,15 @@ static inline u32 armv7pmu_read_counter(struct perf_event *event) | |||
| 580 | int idx = hwc->idx; | 578 | int idx = hwc->idx; |
| 581 | u32 value = 0; | 579 | u32 value = 0; |
| 582 | 580 | ||
| 583 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) | 581 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { |
| 584 | pr_err("CPU%u reading wrong counter %d\n", | 582 | pr_err("CPU%u reading wrong counter %d\n", |
| 585 | smp_processor_id(), idx); | 583 | smp_processor_id(), idx); |
| 586 | else if (idx == ARMV7_IDX_CYCLE_COUNTER) | 584 | } else if (idx == ARMV7_IDX_CYCLE_COUNTER) { |
| 587 | asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); | 585 | asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); |
| 588 | else if (armv7_pmnc_select_counter(idx) == idx) | 586 | } else { |
| 587 | armv7_pmnc_select_counter(idx); | ||
| 589 | asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); | 588 | asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); |
| 589 | } | ||
| 590 | 590 | ||
| 591 | return value; | 591 | return value; |
| 592 | } | 592 | } |
| @@ -597,45 +597,43 @@ static inline void armv7pmu_write_counter(struct perf_event *event, u32 value) | |||
| 597 | struct hw_perf_event *hwc = &event->hw; | 597 | struct hw_perf_event *hwc = &event->hw; |
| 598 | int idx = hwc->idx; | 598 | int idx = hwc->idx; |
| 599 | 599 | ||
| 600 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) | 600 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { |
| 601 | pr_err("CPU%u writing wrong counter %d\n", | 601 | pr_err("CPU%u writing wrong counter %d\n", |
| 602 | smp_processor_id(), idx); | 602 | smp_processor_id(), idx); |
| 603 | else if (idx == ARMV7_IDX_CYCLE_COUNTER) | 603 | } else if (idx == ARMV7_IDX_CYCLE_COUNTER) { |
| 604 | asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); | 604 | asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); |
| 605 | else if (armv7_pmnc_select_counter(idx) == idx) | 605 | } else { |
| 606 | armv7_pmnc_select_counter(idx); | ||
| 606 | asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value)); | 607 | asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value)); |
| 608 | } | ||
| 607 | } | 609 | } |
| 608 | 610 | ||
| 609 | static inline void armv7_pmnc_write_evtsel(int idx, u32 val) | 611 | static inline void armv7_pmnc_write_evtsel(int idx, u32 val) |
| 610 | { | 612 | { |
| 611 | if (armv7_pmnc_select_counter(idx) == idx) { | 613 | armv7_pmnc_select_counter(idx); |
| 612 | val &= ARMV7_EVTYPE_MASK; | 614 | val &= ARMV7_EVTYPE_MASK; |
| 613 | asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); | 615 | asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); |
| 614 | } | ||
| 615 | } | 616 | } |
| 616 | 617 | ||
| 617 | static inline int armv7_pmnc_enable_counter(int idx) | 618 | static inline void armv7_pmnc_enable_counter(int idx) |
| 618 | { | 619 | { |
| 619 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); | 620 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); |
| 620 | asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter))); | 621 | asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter))); |
| 621 | return idx; | ||
| 622 | } | 622 | } |
| 623 | 623 | ||
| 624 | static inline int armv7_pmnc_disable_counter(int idx) | 624 | static inline void armv7_pmnc_disable_counter(int idx) |
| 625 | { | 625 | { |
| 626 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); | 626 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); |
| 627 | asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter))); | 627 | asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter))); |
| 628 | return idx; | ||
| 629 | } | 628 | } |
| 630 | 629 | ||
| 631 | static inline int armv7_pmnc_enable_intens(int idx) | 630 | static inline void armv7_pmnc_enable_intens(int idx) |
| 632 | { | 631 | { |
| 633 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); | 632 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); |
| 634 | asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter))); | 633 | asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter))); |
| 635 | return idx; | ||
| 636 | } | 634 | } |
| 637 | 635 | ||
| 638 | static inline int armv7_pmnc_disable_intens(int idx) | 636 | static inline void armv7_pmnc_disable_intens(int idx) |
| 639 | { | 637 | { |
| 640 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); | 638 | u32 counter = ARMV7_IDX_TO_COUNTER(idx); |
| 641 | asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter))); | 639 | asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter))); |
| @@ -643,8 +641,6 @@ static inline int armv7_pmnc_disable_intens(int idx) | |||
| 643 | /* Clear the overflow flag in case an interrupt is pending. */ | 641 | /* Clear the overflow flag in case an interrupt is pending. */ |
| 644 | asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter))); | 642 | asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter))); |
| 645 | isb(); | 643 | isb(); |
| 646 | |||
| 647 | return idx; | ||
| 648 | } | 644 | } |
| 649 | 645 | ||
| 650 | static inline u32 armv7_pmnc_getreset_flags(void) | 646 | static inline u32 armv7_pmnc_getreset_flags(void) |
| @@ -667,34 +663,34 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu) | |||
| 667 | u32 val; | 663 | u32 val; |
| 668 | unsigned int cnt; | 664 | unsigned int cnt; |
| 669 | 665 | ||
| 670 | printk(KERN_INFO "PMNC registers dump:\n"); | 666 | pr_info("PMNC registers dump:\n"); |
| 671 | 667 | ||
| 672 | asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); | 668 | asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); |
| 673 | printk(KERN_INFO "PMNC =0x%08x\n", val); | 669 | pr_info("PMNC =0x%08x\n", val); |
| 674 | 670 | ||
| 675 | asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val)); | 671 | asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val)); |
| 676 | printk(KERN_INFO "CNTENS=0x%08x\n", val); | 672 | pr_info("CNTENS=0x%08x\n", val); |
| 677 | 673 | ||
| 678 | asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val)); | 674 | asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val)); |
| 679 | printk(KERN_INFO "INTENS=0x%08x\n", val); | 675 | pr_info("INTENS=0x%08x\n", val); |
| 680 | 676 | ||
| 681 | asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); | 677 | asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); |
| 682 | printk(KERN_INFO "FLAGS =0x%08x\n", val); | 678 | pr_info("FLAGS =0x%08x\n", val); |
| 683 | 679 | ||
| 684 | asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val)); | 680 | asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val)); |
| 685 | printk(KERN_INFO "SELECT=0x%08x\n", val); | 681 | pr_info("SELECT=0x%08x\n", val); |
| 686 | 682 | ||
| 687 | asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); | 683 | asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); |
| 688 | printk(KERN_INFO "CCNT =0x%08x\n", val); | 684 | pr_info("CCNT =0x%08x\n", val); |
| 689 | 685 | ||
| 690 | for (cnt = ARMV7_IDX_COUNTER0; | 686 | for (cnt = ARMV7_IDX_COUNTER0; |
| 691 | cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { | 687 | cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { |
| 692 | armv7_pmnc_select_counter(cnt); | 688 | armv7_pmnc_select_counter(cnt); |
| 693 | asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); | 689 | asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); |
| 694 | printk(KERN_INFO "CNT[%d] count =0x%08x\n", | 690 | pr_info("CNT[%d] count =0x%08x\n", |
| 695 | ARMV7_IDX_TO_COUNTER(cnt), val); | 691 | ARMV7_IDX_TO_COUNTER(cnt), val); |
| 696 | asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); | 692 | asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); |
| 697 | printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", | 693 | pr_info("CNT[%d] evtsel=0x%08x\n", |
| 698 | ARMV7_IDX_TO_COUNTER(cnt), val); | 694 | ARMV7_IDX_TO_COUNTER(cnt), val); |
| 699 | } | 695 | } |
| 700 | } | 696 | } |
| @@ -705,7 +701,7 @@ static void armv7pmu_enable_event(struct perf_event *event) | |||
| 705 | unsigned long flags; | 701 | unsigned long flags; |
| 706 | struct hw_perf_event *hwc = &event->hw; | 702 | struct hw_perf_event *hwc = &event->hw; |
| 707 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 703 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 708 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 704 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 709 | int idx = hwc->idx; | 705 | int idx = hwc->idx; |
| 710 | 706 | ||
| 711 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { | 707 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { |
| @@ -751,7 +747,7 @@ static void armv7pmu_disable_event(struct perf_event *event) | |||
| 751 | unsigned long flags; | 747 | unsigned long flags; |
| 752 | struct hw_perf_event *hwc = &event->hw; | 748 | struct hw_perf_event *hwc = &event->hw; |
| 753 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 749 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 754 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 750 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 755 | int idx = hwc->idx; | 751 | int idx = hwc->idx; |
| 756 | 752 | ||
| 757 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { | 753 | if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) { |
| @@ -783,7 +779,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | |||
| 783 | u32 pmnc; | 779 | u32 pmnc; |
| 784 | struct perf_sample_data data; | 780 | struct perf_sample_data data; |
| 785 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; | 781 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; |
| 786 | struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); | 782 | struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); |
| 787 | struct pt_regs *regs; | 783 | struct pt_regs *regs; |
| 788 | int idx; | 784 | int idx; |
| 789 | 785 | ||
| @@ -843,7 +839,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | |||
| 843 | static void armv7pmu_start(struct arm_pmu *cpu_pmu) | 839 | static void armv7pmu_start(struct arm_pmu *cpu_pmu) |
| 844 | { | 840 | { |
| 845 | unsigned long flags; | 841 | unsigned long flags; |
| 846 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 842 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 847 | 843 | ||
| 848 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 844 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 849 | /* Enable all counters */ | 845 | /* Enable all counters */ |
| @@ -854,7 +850,7 @@ static void armv7pmu_start(struct arm_pmu *cpu_pmu) | |||
| 854 | static void armv7pmu_stop(struct arm_pmu *cpu_pmu) | 850 | static void armv7pmu_stop(struct arm_pmu *cpu_pmu) |
| 855 | { | 851 | { |
| 856 | unsigned long flags; | 852 | unsigned long flags; |
| 857 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 853 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 858 | 854 | ||
| 859 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 855 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 860 | /* Disable all counters */ | 856 | /* Disable all counters */ |
| @@ -1287,7 +1283,7 @@ static void krait_pmu_disable_event(struct perf_event *event) | |||
| 1287 | struct hw_perf_event *hwc = &event->hw; | 1283 | struct hw_perf_event *hwc = &event->hw; |
| 1288 | int idx = hwc->idx; | 1284 | int idx = hwc->idx; |
| 1289 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 1285 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 1290 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 1286 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 1291 | 1287 | ||
| 1292 | /* Disable counter and interrupt */ | 1288 | /* Disable counter and interrupt */ |
| 1293 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 1289 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| @@ -1313,7 +1309,7 @@ static void krait_pmu_enable_event(struct perf_event *event) | |||
| 1313 | struct hw_perf_event *hwc = &event->hw; | 1309 | struct hw_perf_event *hwc = &event->hw; |
| 1314 | int idx = hwc->idx; | 1310 | int idx = hwc->idx; |
| 1315 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 1311 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 1316 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 1312 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 1317 | 1313 | ||
| 1318 | /* | 1314 | /* |
| 1319 | * Enable counter and interrupt, and set the counter to count | 1315 | * Enable counter and interrupt, and set the counter to count |
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 08da0af550b7..8af9f1f82c68 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c | |||
| @@ -138,7 +138,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) | |||
| 138 | unsigned long pmnc; | 138 | unsigned long pmnc; |
| 139 | struct perf_sample_data data; | 139 | struct perf_sample_data data; |
| 140 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; | 140 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; |
| 141 | struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); | 141 | struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); |
| 142 | struct pt_regs *regs; | 142 | struct pt_regs *regs; |
| 143 | int idx; | 143 | int idx; |
| 144 | 144 | ||
| @@ -198,7 +198,7 @@ static void xscale1pmu_enable_event(struct perf_event *event) | |||
| 198 | unsigned long val, mask, evt, flags; | 198 | unsigned long val, mask, evt, flags; |
| 199 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 199 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 200 | struct hw_perf_event *hwc = &event->hw; | 200 | struct hw_perf_event *hwc = &event->hw; |
| 201 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 201 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 202 | int idx = hwc->idx; | 202 | int idx = hwc->idx; |
| 203 | 203 | ||
| 204 | switch (idx) { | 204 | switch (idx) { |
| @@ -234,7 +234,7 @@ static void xscale1pmu_disable_event(struct perf_event *event) | |||
| 234 | unsigned long val, mask, evt, flags; | 234 | unsigned long val, mask, evt, flags; |
| 235 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 235 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 236 | struct hw_perf_event *hwc = &event->hw; | 236 | struct hw_perf_event *hwc = &event->hw; |
| 237 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 237 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 238 | int idx = hwc->idx; | 238 | int idx = hwc->idx; |
| 239 | 239 | ||
| 240 | switch (idx) { | 240 | switch (idx) { |
| @@ -287,7 +287,7 @@ xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc, | |||
| 287 | static void xscale1pmu_start(struct arm_pmu *cpu_pmu) | 287 | static void xscale1pmu_start(struct arm_pmu *cpu_pmu) |
| 288 | { | 288 | { |
| 289 | unsigned long flags, val; | 289 | unsigned long flags, val; |
| 290 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 290 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 291 | 291 | ||
| 292 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 292 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 293 | val = xscale1pmu_read_pmnc(); | 293 | val = xscale1pmu_read_pmnc(); |
| @@ -299,7 +299,7 @@ static void xscale1pmu_start(struct arm_pmu *cpu_pmu) | |||
| 299 | static void xscale1pmu_stop(struct arm_pmu *cpu_pmu) | 299 | static void xscale1pmu_stop(struct arm_pmu *cpu_pmu) |
| 300 | { | 300 | { |
| 301 | unsigned long flags, val; | 301 | unsigned long flags, val; |
| 302 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 302 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 303 | 303 | ||
| 304 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 304 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 305 | val = xscale1pmu_read_pmnc(); | 305 | val = xscale1pmu_read_pmnc(); |
| @@ -485,7 +485,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) | |||
| 485 | unsigned long pmnc, of_flags; | 485 | unsigned long pmnc, of_flags; |
| 486 | struct perf_sample_data data; | 486 | struct perf_sample_data data; |
| 487 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; | 487 | struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev; |
| 488 | struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events(); | 488 | struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); |
| 489 | struct pt_regs *regs; | 489 | struct pt_regs *regs; |
| 490 | int idx; | 490 | int idx; |
| 491 | 491 | ||
| @@ -539,7 +539,7 @@ static void xscale2pmu_enable_event(struct perf_event *event) | |||
| 539 | unsigned long flags, ien, evtsel; | 539 | unsigned long flags, ien, evtsel; |
| 540 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 540 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 541 | struct hw_perf_event *hwc = &event->hw; | 541 | struct hw_perf_event *hwc = &event->hw; |
| 542 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 542 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 543 | int idx = hwc->idx; | 543 | int idx = hwc->idx; |
| 544 | 544 | ||
| 545 | ien = xscale2pmu_read_int_enable(); | 545 | ien = xscale2pmu_read_int_enable(); |
| @@ -585,7 +585,7 @@ static void xscale2pmu_disable_event(struct perf_event *event) | |||
| 585 | unsigned long flags, ien, evtsel, of_flags; | 585 | unsigned long flags, ien, evtsel, of_flags; |
| 586 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); | 586 | struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); |
| 587 | struct hw_perf_event *hwc = &event->hw; | 587 | struct hw_perf_event *hwc = &event->hw; |
| 588 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 588 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 589 | int idx = hwc->idx; | 589 | int idx = hwc->idx; |
| 590 | 590 | ||
| 591 | ien = xscale2pmu_read_int_enable(); | 591 | ien = xscale2pmu_read_int_enable(); |
| @@ -651,7 +651,7 @@ out: | |||
| 651 | static void xscale2pmu_start(struct arm_pmu *cpu_pmu) | 651 | static void xscale2pmu_start(struct arm_pmu *cpu_pmu) |
| 652 | { | 652 | { |
| 653 | unsigned long flags, val; | 653 | unsigned long flags, val; |
| 654 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 654 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 655 | 655 | ||
| 656 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 656 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 657 | val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; | 657 | val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; |
| @@ -663,7 +663,7 @@ static void xscale2pmu_start(struct arm_pmu *cpu_pmu) | |||
| 663 | static void xscale2pmu_stop(struct arm_pmu *cpu_pmu) | 663 | static void xscale2pmu_stop(struct arm_pmu *cpu_pmu) |
| 664 | { | 664 | { |
| 665 | unsigned long flags, val; | 665 | unsigned long flags, val; |
| 666 | struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | 666 | struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); |
| 667 | 667 | ||
| 668 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 668 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 669 | val = xscale2pmu_read_pmnc(); | 669 | val = xscale2pmu_read_pmnc(); |
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 095399618ca5..d0be9a1ef6b8 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig | |||
| @@ -2,6 +2,7 @@ menuconfig ARCH_TEGRA | |||
| 2 | bool "NVIDIA Tegra" if ARCH_MULTI_V7 | 2 | bool "NVIDIA Tegra" if ARCH_MULTI_V7 |
| 3 | select ARCH_REQUIRE_GPIOLIB | 3 | select ARCH_REQUIRE_GPIOLIB |
| 4 | select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS | 4 | select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS |
| 5 | select ARM_AMBA | ||
| 5 | select ARM_GIC | 6 | select ARM_GIC |
| 6 | select CLKSRC_MMIO | 7 | select CLKSRC_MMIO |
| 7 | select HAVE_ARM_SCU if SMP | 8 | select HAVE_ARM_SCU if SMP |
| @@ -59,12 +60,4 @@ config ARCH_TEGRA_124_SOC | |||
| 59 | Support for NVIDIA Tegra T124 processor family, based on the | 60 | Support for NVIDIA Tegra T124 processor family, based on the |
| 60 | ARM CortexA15MP CPU | 61 | ARM CortexA15MP CPU |
| 61 | 62 | ||
| 62 | config TEGRA_AHB | ||
| 63 | bool "Enable AHB driver for NVIDIA Tegra SoCs" | ||
| 64 | default y | ||
| 65 | help | ||
| 66 | Adds AHB configuration functionality for NVIDIA Tegra SoCs, | ||
| 67 | which controls AHB bus master arbitration and some | ||
| 68 | performance parameters(priority, prefech size). | ||
| 69 | |||
| 70 | endif | 63 | endif |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7c79c6494379..6caad5428178 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
| @@ -169,9 +169,6 @@ endmenu | |||
| 169 | 169 | ||
| 170 | menu "Bus support" | 170 | menu "Bus support" |
| 171 | 171 | ||
| 172 | config ARM_AMBA | ||
| 173 | bool | ||
| 174 | |||
| 175 | config PCI | 172 | config PCI |
| 176 | bool "PCI support" | 173 | bool "PCI support" |
| 177 | help | 174 | help |
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index f19097134b02..b1fa4e614718 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h | |||
| @@ -104,6 +104,15 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl) | |||
| 104 | asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); | 104 | asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | static inline u64 arch_counter_get_cntpct(void) | ||
| 108 | { | ||
| 109 | /* | ||
| 110 | * AArch64 kernel and user space mandate the use of CNTVCT. | ||
| 111 | */ | ||
| 112 | BUG(); | ||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 107 | static inline u64 arch_counter_get_cntvct(void) | 116 | static inline u64 arch_counter_get_cntvct(void) |
| 108 | { | 117 | { |
| 109 | u64 cval; | 118 | u64 cval; |
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h index 42632c7a2a4e..9cfa3706a1b8 100644 --- a/arch/powerpc/include/asm/iommu.h +++ b/arch/powerpc/include/asm/iommu.h | |||
| @@ -137,13 +137,16 @@ static inline void set_iommu_table_base_and_group(struct device *dev, | |||
| 137 | iommu_add_device(dev); | 137 | iommu_add_device(dev); |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | 140 | extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl, |
| 141 | struct scatterlist *sglist, int nelems, | 141 | struct scatterlist *sglist, int nelems, |
| 142 | unsigned long mask, enum dma_data_direction direction, | 142 | unsigned long mask, |
| 143 | struct dma_attrs *attrs); | 143 | enum dma_data_direction direction, |
| 144 | extern void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, | 144 | struct dma_attrs *attrs); |
| 145 | int nelems, enum dma_data_direction direction, | 145 | extern void ppc_iommu_unmap_sg(struct iommu_table *tbl, |
| 146 | struct dma_attrs *attrs); | 146 | struct scatterlist *sglist, |
| 147 | int nelems, | ||
| 148 | enum dma_data_direction direction, | ||
| 149 | struct dma_attrs *attrs); | ||
| 147 | 150 | ||
| 148 | extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, | 151 | extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, |
| 149 | size_t size, dma_addr_t *dma_handle, | 152 | size_t size, dma_addr_t *dma_handle, |
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 54d0116256f7..4c68bfe4108a 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c | |||
| @@ -60,16 +60,16 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist, | |||
| 60 | int nelems, enum dma_data_direction direction, | 60 | int nelems, enum dma_data_direction direction, |
| 61 | struct dma_attrs *attrs) | 61 | struct dma_attrs *attrs) |
| 62 | { | 62 | { |
| 63 | return iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems, | 63 | return ppc_iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems, |
| 64 | device_to_mask(dev), direction, attrs); | 64 | device_to_mask(dev), direction, attrs); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist, | 67 | static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist, |
| 68 | int nelems, enum dma_data_direction direction, | 68 | int nelems, enum dma_data_direction direction, |
| 69 | struct dma_attrs *attrs) | 69 | struct dma_attrs *attrs) |
| 70 | { | 70 | { |
| 71 | iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems, direction, | 71 | ppc_iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems, |
| 72 | attrs); | 72 | direction, attrs); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | /* We support DMA to/from any memory page via the iommu */ | 75 | /* We support DMA to/from any memory page via the iommu */ |
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index a10642a0d861..a83cf5ef6488 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c | |||
| @@ -428,10 +428,10 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, | |||
| 428 | ppc_md.tce_flush(tbl); | 428 | ppc_md.tce_flush(tbl); |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | 431 | int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl, |
| 432 | struct scatterlist *sglist, int nelems, | 432 | struct scatterlist *sglist, int nelems, |
| 433 | unsigned long mask, enum dma_data_direction direction, | 433 | unsigned long mask, enum dma_data_direction direction, |
| 434 | struct dma_attrs *attrs) | 434 | struct dma_attrs *attrs) |
| 435 | { | 435 | { |
| 436 | dma_addr_t dma_next = 0, dma_addr; | 436 | dma_addr_t dma_next = 0, dma_addr; |
| 437 | struct scatterlist *s, *outs, *segstart; | 437 | struct scatterlist *s, *outs, *segstart; |
| @@ -539,7 +539,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | |||
| 539 | 539 | ||
| 540 | DBG("mapped %d elements:\n", outcount); | 540 | DBG("mapped %d elements:\n", outcount); |
| 541 | 541 | ||
| 542 | /* For the sake of iommu_unmap_sg, we clear out the length in the | 542 | /* For the sake of ppc_iommu_unmap_sg, we clear out the length in the |
| 543 | * next entry of the sglist if we didn't fill the list completely | 543 | * next entry of the sglist if we didn't fill the list completely |
| 544 | */ | 544 | */ |
| 545 | if (outcount < incount) { | 545 | if (outcount < incount) { |
| @@ -572,9 +572,9 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | |||
| 572 | } | 572 | } |
| 573 | 573 | ||
| 574 | 574 | ||
| 575 | void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, | 575 | void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, |
| 576 | int nelems, enum dma_data_direction direction, | 576 | int nelems, enum dma_data_direction direction, |
| 577 | struct dma_attrs *attrs) | 577 | struct dma_attrs *attrs) |
| 578 | { | 578 | { |
| 579 | struct scatterlist *sg; | 579 | struct scatterlist *sg; |
| 580 | 580 | ||
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 2b90ff8a93be..c7c8720aa39f 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c | |||
| @@ -621,8 +621,9 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, | |||
| 621 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | 621 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) |
| 622 | return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); | 622 | return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); |
| 623 | else | 623 | else |
| 624 | return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, | 624 | return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg, |
| 625 | device_to_mask(dev), direction, attrs); | 625 | nents, device_to_mask(dev), |
| 626 | direction, attrs); | ||
| 626 | } | 627 | } |
| 627 | 628 | ||
| 628 | static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, | 629 | static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, |
| @@ -632,8 +633,8 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, | |||
| 632 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | 633 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) |
| 633 | dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); | 634 | dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); |
| 634 | else | 635 | else |
| 635 | iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, | 636 | ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, |
| 636 | attrs); | 637 | direction, attrs); |
| 637 | } | 638 | } |
| 638 | 639 | ||
| 639 | static int dma_fixed_dma_supported(struct device *dev, u64 mask) | 640 | static int dma_fixed_dma_supported(struct device *dev, u64 mask) |
diff --git a/drivers/Kconfig b/drivers/Kconfig index 1a693d3f9d51..af02a8a8ec4a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | menu "Device Drivers" | 1 | menu "Device Drivers" |
| 2 | 2 | ||
| 3 | source "drivers/amba/Kconfig" | ||
| 4 | |||
| 3 | source "drivers/base/Kconfig" | 5 | source "drivers/base/Kconfig" |
| 4 | 6 | ||
| 5 | source "drivers/bus/Kconfig" | 7 | source "drivers/bus/Kconfig" |
diff --git a/drivers/amba/Kconfig b/drivers/amba/Kconfig new file mode 100644 index 000000000000..4a5c9d279059 --- /dev/null +++ b/drivers/amba/Kconfig | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | config ARM_AMBA | ||
| 2 | bool | ||
| 3 | |||
| 4 | if ARM_AMBA | ||
| 5 | |||
| 6 | config TEGRA_AHB | ||
| 7 | bool "Enable AHB driver for NVIDIA Tegra SoCs" | ||
| 8 | default y if ARCH_TEGRA | ||
| 9 | help | ||
| 10 | Adds AHB configuration functionality for NVIDIA Tegra SoCs, | ||
| 11 | which controls AHB bus master arbitration and some performance | ||
| 12 | parameters (priority, prefetch size). | ||
| 13 | |||
| 14 | endif | ||
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 603eb1be4f6a..b99729e36860 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig | |||
| @@ -6,7 +6,7 @@ menu "Bus devices" | |||
| 6 | 6 | ||
| 7 | config BRCMSTB_GISB_ARB | 7 | config BRCMSTB_GISB_ARB |
| 8 | bool "Broadcom STB GISB bus arbiter" | 8 | bool "Broadcom STB GISB bus arbiter" |
| 9 | depends on ARM | 9 | depends on ARM || MIPS |
| 10 | help | 10 | help |
| 11 | Driver for the Broadcom Set Top Box System-on-a-chip internal bus | 11 | Driver for the Broadcom Set Top Box System-on-a-chip internal bus |
| 12 | arbiter. This driver provides timeout and target abort error handling | 12 | arbiter. This driver provides timeout and target abort error handling |
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index 7af78df241f2..860da40b78ef 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c | |||
| @@ -16,17 +16,17 @@ | |||
| 16 | 16 | ||
| 17 | #include <linux/arm-cci.h> | 17 | #include <linux/arm-cci.h> |
| 18 | #include <linux/io.h> | 18 | #include <linux/io.h> |
| 19 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 20 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
| 21 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
| 22 | #include <linux/of_platform.h> | 23 | #include <linux/of_platform.h> |
| 24 | #include <linux/perf_event.h> | ||
| 23 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
| 24 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
| 25 | #include <linux/spinlock.h> | 27 | #include <linux/spinlock.h> |
| 26 | 28 | ||
| 27 | #include <asm/cacheflush.h> | 29 | #include <asm/cacheflush.h> |
| 28 | #include <asm/irq_regs.h> | ||
| 29 | #include <asm/pmu.h> | ||
| 30 | #include <asm/smp_plat.h> | 30 | #include <asm/smp_plat.h> |
| 31 | 31 | ||
| 32 | #define DRIVER_NAME "CCI-400" | 32 | #define DRIVER_NAME "CCI-400" |
| @@ -98,6 +98,8 @@ static unsigned long cci_ctrl_phys; | |||
| 98 | 98 | ||
| 99 | #define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K) | 99 | #define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K) |
| 100 | 100 | ||
| 101 | #define CCI_PMU_CNTR_MASK ((1ULL << 32) -1) | ||
| 102 | |||
| 101 | /* | 103 | /* |
| 102 | * Instead of an event id to monitor CCI cycles, a dedicated counter is | 104 | * Instead of an event id to monitor CCI cycles, a dedicated counter is |
| 103 | * provided. Use 0xff to represent CCI cycles and hope that no future revisions | 105 | * provided. Use 0xff to represent CCI cycles and hope that no future revisions |
| @@ -170,18 +172,29 @@ static char *const pmu_names[] = { | |||
| 170 | [CCI_REV_R1] = "CCI_400_r1", | 172 | [CCI_REV_R1] = "CCI_400_r1", |
| 171 | }; | 173 | }; |
| 172 | 174 | ||
| 173 | struct cci_pmu_drv_data { | 175 | struct cci_pmu_hw_events { |
| 176 | struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; | ||
| 177 | unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; | ||
| 178 | raw_spinlock_t pmu_lock; | ||
| 179 | }; | ||
| 180 | |||
| 181 | struct cci_pmu { | ||
| 174 | void __iomem *base; | 182 | void __iomem *base; |
| 175 | struct arm_pmu *cci_pmu; | 183 | struct pmu pmu; |
| 176 | int nr_irqs; | 184 | int nr_irqs; |
| 177 | int irqs[CCI_PMU_MAX_HW_EVENTS]; | 185 | int irqs[CCI_PMU_MAX_HW_EVENTS]; |
| 178 | unsigned long active_irqs; | 186 | unsigned long active_irqs; |
| 179 | struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; | ||
| 180 | unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; | ||
| 181 | struct pmu_port_event_ranges *port_ranges; | 187 | struct pmu_port_event_ranges *port_ranges; |
| 182 | struct pmu_hw_events hw_events; | 188 | struct cci_pmu_hw_events hw_events; |
| 189 | struct platform_device *plat_device; | ||
| 190 | int num_events; | ||
| 191 | atomic_t active_events; | ||
| 192 | struct mutex reserve_mutex; | ||
| 193 | cpumask_t cpus; | ||
| 183 | }; | 194 | }; |
| 184 | static struct cci_pmu_drv_data *pmu; | 195 | static struct cci_pmu *pmu; |
| 196 | |||
| 197 | #define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu)) | ||
| 185 | 198 | ||
| 186 | static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) | 199 | static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) |
| 187 | { | 200 | { |
| @@ -252,7 +265,7 @@ static int pmu_validate_hw_event(u8 hw_event) | |||
| 252 | return -ENOENT; | 265 | return -ENOENT; |
| 253 | } | 266 | } |
| 254 | 267 | ||
| 255 | static int pmu_is_valid_counter(struct arm_pmu *cci_pmu, int idx) | 268 | static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) |
| 256 | { | 269 | { |
| 257 | return CCI_PMU_CYCLE_CNTR_IDX <= idx && | 270 | return CCI_PMU_CYCLE_CNTR_IDX <= idx && |
| 258 | idx <= CCI_PMU_CNTR_LAST(cci_pmu); | 271 | idx <= CCI_PMU_CNTR_LAST(cci_pmu); |
| @@ -293,14 +306,9 @@ static u32 pmu_get_max_counters(void) | |||
| 293 | return n_cnts + 1; | 306 | return n_cnts + 1; |
| 294 | } | 307 | } |
| 295 | 308 | ||
| 296 | static struct pmu_hw_events *pmu_get_hw_events(void) | 309 | static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event) |
| 297 | { | ||
| 298 | return &pmu->hw_events; | ||
| 299 | } | ||
| 300 | |||
| 301 | static int pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event) | ||
| 302 | { | 310 | { |
| 303 | struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); | 311 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); |
| 304 | struct hw_perf_event *hw_event = &event->hw; | 312 | struct hw_perf_event *hw_event = &event->hw; |
| 305 | unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK; | 313 | unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK; |
| 306 | int idx; | 314 | int idx; |
| @@ -336,7 +344,7 @@ static int pmu_map_event(struct perf_event *event) | |||
| 336 | return mapping; | 344 | return mapping; |
| 337 | } | 345 | } |
| 338 | 346 | ||
| 339 | static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler) | 347 | static int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler) |
| 340 | { | 348 | { |
| 341 | int i; | 349 | int i; |
| 342 | struct platform_device *pmu_device = cci_pmu->plat_device; | 350 | 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) | |||
| 371 | return 0; | 379 | return 0; |
| 372 | } | 380 | } |
| 373 | 381 | ||
| 382 | static void pmu_free_irq(struct cci_pmu *cci_pmu) | ||
| 383 | { | ||
| 384 | int i; | ||
| 385 | |||
| 386 | for (i = 0; i < pmu->nr_irqs; i++) { | ||
| 387 | if (!test_and_clear_bit(i, &pmu->active_irqs)) | ||
| 388 | continue; | ||
| 389 | |||
| 390 | free_irq(pmu->irqs[i], cci_pmu); | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | static u32 pmu_read_counter(struct perf_event *event) | ||
| 395 | { | ||
| 396 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); | ||
| 397 | struct hw_perf_event *hw_counter = &event->hw; | ||
| 398 | int idx = hw_counter->idx; | ||
| 399 | u32 value; | ||
| 400 | |||
| 401 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { | ||
| 402 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | ||
| 403 | return 0; | ||
| 404 | } | ||
| 405 | value = pmu_read_register(idx, CCI_PMU_CNTR); | ||
| 406 | |||
| 407 | return value; | ||
| 408 | } | ||
| 409 | |||
| 410 | static void pmu_write_counter(struct perf_event *event, u32 value) | ||
| 411 | { | ||
| 412 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); | ||
| 413 | struct hw_perf_event *hw_counter = &event->hw; | ||
| 414 | int idx = hw_counter->idx; | ||
| 415 | |||
| 416 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) | ||
| 417 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | ||
| 418 | else | ||
| 419 | pmu_write_register(value, idx, CCI_PMU_CNTR); | ||
| 420 | } | ||
| 421 | |||
| 422 | static u64 pmu_event_update(struct perf_event *event) | ||
| 423 | { | ||
| 424 | struct hw_perf_event *hwc = &event->hw; | ||
| 425 | u64 delta, prev_raw_count, new_raw_count; | ||
| 426 | |||
| 427 | do { | ||
| 428 | prev_raw_count = local64_read(&hwc->prev_count); | ||
| 429 | new_raw_count = pmu_read_counter(event); | ||
| 430 | } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, | ||
| 431 | new_raw_count) != prev_raw_count); | ||
| 432 | |||
| 433 | delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK; | ||
| 434 | |||
| 435 | local64_add(delta, &event->count); | ||
| 436 | |||
| 437 | return new_raw_count; | ||
| 438 | } | ||
| 439 | |||
| 440 | static void pmu_read(struct perf_event *event) | ||
| 441 | { | ||
| 442 | pmu_event_update(event); | ||
| 443 | } | ||
| 444 | |||
| 445 | void pmu_event_set_period(struct perf_event *event) | ||
| 446 | { | ||
| 447 | struct hw_perf_event *hwc = &event->hw; | ||
| 448 | /* | ||
| 449 | * The CCI PMU counters have a period of 2^32. To account for the | ||
| 450 | * possiblity of extreme interrupt latency we program for a period of | ||
| 451 | * half that. Hopefully we can handle the interrupt before another 2^31 | ||
| 452 | * events occur and the counter overtakes its previous value. | ||
| 453 | */ | ||
| 454 | u64 val = 1ULL << 31; | ||
| 455 | local64_set(&hwc->prev_count, val); | ||
| 456 | pmu_write_counter(event, val); | ||
| 457 | } | ||
| 458 | |||
| 374 | static irqreturn_t pmu_handle_irq(int irq_num, void *dev) | 459 | static irqreturn_t pmu_handle_irq(int irq_num, void *dev) |
| 375 | { | 460 | { |
| 376 | unsigned long flags; | 461 | unsigned long flags; |
| 377 | struct arm_pmu *cci_pmu = (struct arm_pmu *)dev; | 462 | struct cci_pmu *cci_pmu = dev; |
| 378 | struct pmu_hw_events *events = cci_pmu->get_hw_events(); | 463 | struct cci_pmu_hw_events *events = &pmu->hw_events; |
| 379 | struct perf_sample_data data; | ||
| 380 | struct pt_regs *regs; | ||
| 381 | int idx, handled = IRQ_NONE; | 464 | int idx, handled = IRQ_NONE; |
| 382 | 465 | ||
| 383 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 466 | raw_spin_lock_irqsave(&events->pmu_lock, flags); |
| 384 | regs = get_irq_regs(); | ||
| 385 | /* | 467 | /* |
| 386 | * Iterate over counters and update the corresponding perf events. | 468 | * Iterate over counters and update the corresponding perf events. |
| 387 | * This should work regardless of whether we have per-counter overflow | 469 | * 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) | |||
| 403 | 485 | ||
| 404 | pmu_write_register(CCI_PMU_OVRFLW_FLAG, idx, CCI_PMU_OVRFLW); | 486 | pmu_write_register(CCI_PMU_OVRFLW_FLAG, idx, CCI_PMU_OVRFLW); |
| 405 | 487 | ||
| 488 | pmu_event_update(event); | ||
| 489 | pmu_event_set_period(event); | ||
| 406 | handled = IRQ_HANDLED; | 490 | handled = IRQ_HANDLED; |
| 407 | |||
| 408 | armpmu_event_update(event); | ||
| 409 | perf_sample_data_init(&data, 0, hw_counter->last_period); | ||
| 410 | if (!armpmu_event_set_period(event)) | ||
| 411 | continue; | ||
| 412 | |||
| 413 | if (perf_event_overflow(event, &data, regs)) | ||
| 414 | cci_pmu->disable(event); | ||
| 415 | } | 491 | } |
| 416 | raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | 492 | raw_spin_unlock_irqrestore(&events->pmu_lock, flags); |
| 417 | 493 | ||
| 418 | return IRQ_RETVAL(handled); | 494 | return IRQ_RETVAL(handled); |
| 419 | } | 495 | } |
| 420 | 496 | ||
| 421 | static void pmu_free_irq(struct arm_pmu *cci_pmu) | 497 | static int cci_pmu_get_hw(struct cci_pmu *cci_pmu) |
| 422 | { | 498 | { |
| 423 | int i; | 499 | int ret = pmu_request_irq(cci_pmu, pmu_handle_irq); |
| 500 | if (ret) { | ||
| 501 | pmu_free_irq(cci_pmu); | ||
| 502 | return ret; | ||
| 503 | } | ||
| 504 | return 0; | ||
| 505 | } | ||
| 424 | 506 | ||
| 425 | for (i = 0; i < pmu->nr_irqs; i++) { | 507 | static void cci_pmu_put_hw(struct cci_pmu *cci_pmu) |
| 426 | if (!test_and_clear_bit(i, &pmu->active_irqs)) | 508 | { |
| 427 | continue; | 509 | pmu_free_irq(cci_pmu); |
| 510 | } | ||
| 428 | 511 | ||
| 429 | free_irq(pmu->irqs[i], cci_pmu); | 512 | static void hw_perf_event_destroy(struct perf_event *event) |
| 513 | { | ||
| 514 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); | ||
| 515 | atomic_t *active_events = &cci_pmu->active_events; | ||
| 516 | struct mutex *reserve_mutex = &cci_pmu->reserve_mutex; | ||
| 517 | |||
| 518 | if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) { | ||
| 519 | cci_pmu_put_hw(cci_pmu); | ||
| 520 | mutex_unlock(reserve_mutex); | ||
| 430 | } | 521 | } |
| 431 | } | 522 | } |
| 432 | 523 | ||
| 433 | static void pmu_enable_event(struct perf_event *event) | 524 | static void cci_pmu_enable(struct pmu *pmu) |
| 434 | { | 525 | { |
| 526 | struct cci_pmu *cci_pmu = to_cci_pmu(pmu); | ||
| 527 | struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; | ||
| 528 | int enabled = bitmap_weight(hw_events->used_mask, cci_pmu->num_events); | ||
| 435 | unsigned long flags; | 529 | unsigned long flags; |
| 436 | struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); | 530 | u32 val; |
| 437 | struct pmu_hw_events *events = cci_pmu->get_hw_events(); | 531 | |
| 438 | struct hw_perf_event *hw_counter = &event->hw; | 532 | if (!enabled) |
| 439 | int idx = hw_counter->idx; | 533 | return; |
| 534 | |||
| 535 | raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); | ||
| 536 | |||
| 537 | /* Enable all the PMU counters. */ | ||
| 538 | val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN; | ||
| 539 | writel(val, cci_ctrl_base + CCI_PMCR); | ||
| 540 | raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); | ||
| 541 | |||
| 542 | } | ||
| 543 | |||
| 544 | static void cci_pmu_disable(struct pmu *pmu) | ||
| 545 | { | ||
| 546 | struct cci_pmu *cci_pmu = to_cci_pmu(pmu); | ||
| 547 | struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; | ||
| 548 | unsigned long flags; | ||
| 549 | u32 val; | ||
| 550 | |||
| 551 | raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); | ||
| 552 | |||
| 553 | /* Disable all the PMU counters. */ | ||
| 554 | val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN; | ||
| 555 | writel(val, cci_ctrl_base + CCI_PMCR); | ||
| 556 | raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); | ||
| 557 | } | ||
| 558 | |||
| 559 | static void cci_pmu_start(struct perf_event *event, int pmu_flags) | ||
| 560 | { | ||
| 561 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); | ||
| 562 | struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; | ||
| 563 | struct hw_perf_event *hwc = &event->hw; | ||
| 564 | int idx = hwc->idx; | ||
| 565 | unsigned long flags; | ||
| 566 | |||
| 567 | /* | ||
| 568 | * To handle interrupt latency, we always reprogram the period | ||
| 569 | * regardlesss of PERF_EF_RELOAD. | ||
| 570 | */ | ||
| 571 | if (pmu_flags & PERF_EF_RELOAD) | ||
| 572 | WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); | ||
| 573 | |||
| 574 | hwc->state = 0; | ||
| 440 | 575 | ||
| 441 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { | 576 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { |
| 442 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | 577 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); |
| 443 | return; | 578 | return; |
| 444 | } | 579 | } |
| 445 | 580 | ||
| 446 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 581 | raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); |
| 447 | 582 | ||
| 448 | /* Configure the event to count, unless you are counting cycles */ | 583 | /* Configure the event to count, unless you are counting cycles */ |
| 449 | if (idx != CCI_PMU_CYCLE_CNTR_IDX) | 584 | if (idx != CCI_PMU_CYCLE_CNTR_IDX) |
| 450 | pmu_set_event(idx, hw_counter->config_base); | 585 | pmu_set_event(idx, hwc->config_base); |
| 451 | 586 | ||
| 587 | pmu_event_set_period(event); | ||
| 452 | pmu_enable_counter(idx); | 588 | pmu_enable_counter(idx); |
| 453 | 589 | ||
| 454 | raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | 590 | raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); |
| 455 | } | 591 | } |
| 456 | 592 | ||
| 457 | static void pmu_disable_event(struct perf_event *event) | 593 | static void cci_pmu_stop(struct perf_event *event, int pmu_flags) |
| 458 | { | 594 | { |
| 459 | struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); | 595 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); |
| 460 | struct hw_perf_event *hw_counter = &event->hw; | 596 | struct hw_perf_event *hwc = &event->hw; |
| 461 | int idx = hw_counter->idx; | 597 | int idx = hwc->idx; |
| 598 | |||
| 599 | if (hwc->state & PERF_HES_STOPPED) | ||
| 600 | return; | ||
| 462 | 601 | ||
| 463 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { | 602 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { |
| 464 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | 603 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); |
| 465 | return; | 604 | return; |
| 466 | } | 605 | } |
| 467 | 606 | ||
| 607 | /* | ||
| 608 | * We always reprogram the counter, so ignore PERF_EF_UPDATE. See | ||
| 609 | * cci_pmu_start() | ||
| 610 | */ | ||
| 468 | pmu_disable_counter(idx); | 611 | pmu_disable_counter(idx); |
| 612 | pmu_event_update(event); | ||
| 613 | hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; | ||
| 469 | } | 614 | } |
| 470 | 615 | ||
| 471 | static void pmu_start(struct arm_pmu *cci_pmu) | 616 | static int cci_pmu_add(struct perf_event *event, int flags) |
| 472 | { | 617 | { |
| 473 | u32 val; | 618 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); |
| 474 | unsigned long flags; | 619 | struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; |
| 475 | struct pmu_hw_events *events = cci_pmu->get_hw_events(); | 620 | struct hw_perf_event *hwc = &event->hw; |
| 621 | int idx; | ||
| 622 | int err = 0; | ||
| 476 | 623 | ||
| 477 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 624 | perf_pmu_disable(event->pmu); |
| 478 | 625 | ||
| 479 | /* Enable all the PMU counters. */ | 626 | /* If we don't have a space for the counter then finish early. */ |
| 480 | val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN; | 627 | idx = pmu_get_event_idx(hw_events, event); |
| 481 | writel(val, cci_ctrl_base + CCI_PMCR); | 628 | if (idx < 0) { |
| 629 | err = idx; | ||
| 630 | goto out; | ||
| 631 | } | ||
| 482 | 632 | ||
| 483 | raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | 633 | event->hw.idx = idx; |
| 634 | hw_events->events[idx] = event; | ||
| 635 | |||
| 636 | hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; | ||
| 637 | if (flags & PERF_EF_START) | ||
| 638 | cci_pmu_start(event, PERF_EF_RELOAD); | ||
| 639 | |||
| 640 | /* Propagate our changes to the userspace mapping. */ | ||
| 641 | perf_event_update_userpage(event); | ||
| 642 | |||
| 643 | out: | ||
| 644 | perf_pmu_enable(event->pmu); | ||
| 645 | return err; | ||
| 484 | } | 646 | } |
| 485 | 647 | ||
| 486 | static void pmu_stop(struct arm_pmu *cci_pmu) | 648 | static void cci_pmu_del(struct perf_event *event, int flags) |
| 487 | { | 649 | { |
| 488 | u32 val; | 650 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); |
| 489 | unsigned long flags; | 651 | struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; |
| 490 | struct pmu_hw_events *events = cci_pmu->get_hw_events(); | 652 | struct hw_perf_event *hwc = &event->hw; |
| 653 | int idx = hwc->idx; | ||
| 491 | 654 | ||
| 492 | raw_spin_lock_irqsave(&events->pmu_lock, flags); | 655 | cci_pmu_stop(event, PERF_EF_UPDATE); |
| 656 | hw_events->events[idx] = NULL; | ||
| 657 | clear_bit(idx, hw_events->used_mask); | ||
| 493 | 658 | ||
| 494 | /* Disable all the PMU counters. */ | 659 | perf_event_update_userpage(event); |
| 495 | val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN; | 660 | } |
| 496 | writel(val, cci_ctrl_base + CCI_PMCR); | ||
| 497 | 661 | ||
| 498 | raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | 662 | static int |
| 663 | validate_event(struct cci_pmu_hw_events *hw_events, | ||
| 664 | struct perf_event *event) | ||
| 665 | { | ||
| 666 | if (is_software_event(event)) | ||
| 667 | return 1; | ||
| 668 | |||
| 669 | if (event->state < PERF_EVENT_STATE_OFF) | ||
| 670 | return 1; | ||
| 671 | |||
| 672 | if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) | ||
| 673 | return 1; | ||
| 674 | |||
| 675 | return pmu_get_event_idx(hw_events, event) >= 0; | ||
| 499 | } | 676 | } |
| 500 | 677 | ||
| 501 | static u32 pmu_read_counter(struct perf_event *event) | 678 | static int |
| 679 | validate_group(struct perf_event *event) | ||
| 502 | { | 680 | { |
| 503 | struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); | 681 | struct perf_event *sibling, *leader = event->group_leader; |
| 504 | struct hw_perf_event *hw_counter = &event->hw; | 682 | struct cci_pmu_hw_events fake_pmu = { |
| 505 | int idx = hw_counter->idx; | 683 | /* |
| 506 | u32 value; | 684 | * Initialise the fake PMU. We only need to populate the |
| 685 | * used_mask for the purposes of validation. | ||
| 686 | */ | ||
| 687 | .used_mask = CPU_BITS_NONE, | ||
| 688 | }; | ||
| 507 | 689 | ||
| 508 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { | 690 | if (!validate_event(&fake_pmu, leader)) |
| 509 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | 691 | return -EINVAL; |
| 510 | return 0; | 692 | |
| 693 | list_for_each_entry(sibling, &leader->sibling_list, group_entry) { | ||
| 694 | if (!validate_event(&fake_pmu, sibling)) | ||
| 695 | return -EINVAL; | ||
| 511 | } | 696 | } |
| 512 | value = pmu_read_register(idx, CCI_PMU_CNTR); | ||
| 513 | 697 | ||
| 514 | return value; | 698 | if (!validate_event(&fake_pmu, event)) |
| 699 | return -EINVAL; | ||
| 700 | |||
| 701 | return 0; | ||
| 515 | } | 702 | } |
| 516 | 703 | ||
| 517 | static void pmu_write_counter(struct perf_event *event, u32 value) | 704 | static int |
| 705 | __hw_perf_event_init(struct perf_event *event) | ||
| 518 | { | 706 | { |
| 519 | struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); | 707 | struct hw_perf_event *hwc = &event->hw; |
| 520 | struct hw_perf_event *hw_counter = &event->hw; | 708 | int mapping; |
| 521 | int idx = hw_counter->idx; | ||
| 522 | 709 | ||
| 523 | if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) | 710 | mapping = pmu_map_event(event); |
| 524 | dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); | 711 | |
| 525 | else | 712 | if (mapping < 0) { |
| 526 | pmu_write_register(value, idx, CCI_PMU_CNTR); | 713 | pr_debug("event %x:%llx not supported\n", event->attr.type, |
| 714 | event->attr.config); | ||
| 715 | return mapping; | ||
| 716 | } | ||
| 717 | |||
| 718 | /* | ||
| 719 | * We don't assign an index until we actually place the event onto | ||
| 720 | * hardware. Use -1 to signify that we haven't decided where to put it | ||
| 721 | * yet. | ||
| 722 | */ | ||
| 723 | hwc->idx = -1; | ||
| 724 | hwc->config_base = 0; | ||
| 725 | hwc->config = 0; | ||
| 726 | hwc->event_base = 0; | ||
| 727 | |||
| 728 | /* | ||
| 729 | * Store the event encoding into the config_base field. | ||
| 730 | */ | ||
| 731 | hwc->config_base |= (unsigned long)mapping; | ||
| 732 | |||
| 733 | /* | ||
| 734 | * Limit the sample_period to half of the counter width. That way, the | ||
| 735 | * new counter value is far less likely to overtake the previous one | ||
| 736 | * unless you have some serious IRQ latency issues. | ||
| 737 | */ | ||
| 738 | hwc->sample_period = CCI_PMU_CNTR_MASK >> 1; | ||
| 739 | hwc->last_period = hwc->sample_period; | ||
| 740 | local64_set(&hwc->period_left, hwc->sample_period); | ||
| 741 | |||
| 742 | if (event->group_leader != event) { | ||
| 743 | if (validate_group(event) != 0) | ||
| 744 | return -EINVAL; | ||
| 745 | } | ||
| 746 | |||
| 747 | return 0; | ||
| 748 | } | ||
| 749 | |||
| 750 | static int cci_pmu_event_init(struct perf_event *event) | ||
| 751 | { | ||
| 752 | struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); | ||
| 753 | atomic_t *active_events = &cci_pmu->active_events; | ||
| 754 | int err = 0; | ||
| 755 | int cpu; | ||
| 756 | |||
| 757 | if (event->attr.type != event->pmu->type) | ||
| 758 | return -ENOENT; | ||
| 759 | |||
| 760 | /* Shared by all CPUs, no meaningful state to sample */ | ||
| 761 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) | ||
| 762 | return -EOPNOTSUPP; | ||
| 763 | |||
| 764 | /* We have no filtering of any kind */ | ||
| 765 | if (event->attr.exclude_user || | ||
| 766 | event->attr.exclude_kernel || | ||
| 767 | event->attr.exclude_hv || | ||
| 768 | event->attr.exclude_idle || | ||
| 769 | event->attr.exclude_host || | ||
| 770 | event->attr.exclude_guest) | ||
| 771 | return -EINVAL; | ||
| 772 | |||
| 773 | /* | ||
| 774 | * Following the example set by other "uncore" PMUs, we accept any CPU | ||
| 775 | * and rewrite its affinity dynamically rather than having perf core | ||
| 776 | * handle cpu == -1 and pid == -1 for this case. | ||
| 777 | * | ||
| 778 | * The perf core will pin online CPUs for the duration of this call and | ||
| 779 | * the event being installed into its context, so the PMU's CPU can't | ||
| 780 | * change under our feet. | ||
| 781 | */ | ||
| 782 | cpu = cpumask_first(&cci_pmu->cpus); | ||
| 783 | if (event->cpu < 0 || cpu < 0) | ||
| 784 | return -EINVAL; | ||
| 785 | event->cpu = cpu; | ||
| 786 | |||
| 787 | event->destroy = hw_perf_event_destroy; | ||
| 788 | if (!atomic_inc_not_zero(active_events)) { | ||
| 789 | mutex_lock(&cci_pmu->reserve_mutex); | ||
| 790 | if (atomic_read(active_events) == 0) | ||
| 791 | err = cci_pmu_get_hw(cci_pmu); | ||
| 792 | if (!err) | ||
| 793 | atomic_inc(active_events); | ||
| 794 | mutex_unlock(&cci_pmu->reserve_mutex); | ||
| 795 | } | ||
| 796 | if (err) | ||
| 797 | return err; | ||
| 798 | |||
| 799 | err = __hw_perf_event_init(event); | ||
| 800 | if (err) | ||
| 801 | hw_perf_event_destroy(event); | ||
| 802 | |||
| 803 | return err; | ||
| 527 | } | 804 | } |
| 528 | 805 | ||
| 529 | static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev) | 806 | static ssize_t pmu_attr_cpumask_show(struct device *dev, |
| 807 | struct device_attribute *attr, char *buf) | ||
| 530 | { | 808 | { |
| 531 | *cci_pmu = (struct arm_pmu){ | 809 | int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &pmu->cpus); |
| 532 | .name = pmu_names[probe_cci_revision()], | 810 | |
| 533 | .max_period = (1LLU << 32) - 1, | 811 | buf[n++] = '\n'; |
| 534 | .get_hw_events = pmu_get_hw_events, | 812 | buf[n] = '\0'; |
| 535 | .get_event_idx = pmu_get_event_idx, | 813 | return n; |
| 536 | .map_event = pmu_map_event, | 814 | } |
| 537 | .request_irq = pmu_request_irq, | 815 | |
| 538 | .handle_irq = pmu_handle_irq, | 816 | static DEVICE_ATTR(cpumask, S_IRUGO, pmu_attr_cpumask_show, NULL); |
| 539 | .free_irq = pmu_free_irq, | 817 | |
| 540 | .enable = pmu_enable_event, | 818 | static struct attribute *pmu_attrs[] = { |
| 541 | .disable = pmu_disable_event, | 819 | &dev_attr_cpumask.attr, |
| 542 | .start = pmu_start, | 820 | NULL, |
| 543 | .stop = pmu_stop, | 821 | }; |
| 544 | .read_counter = pmu_read_counter, | 822 | |
| 545 | .write_counter = pmu_write_counter, | 823 | static struct attribute_group pmu_attr_group = { |
| 824 | .attrs = pmu_attrs, | ||
| 825 | }; | ||
| 826 | |||
| 827 | static const struct attribute_group *pmu_attr_groups[] = { | ||
| 828 | &pmu_attr_group, | ||
| 829 | NULL | ||
| 830 | }; | ||
| 831 | |||
| 832 | static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) | ||
| 833 | { | ||
| 834 | char *name = pmu_names[probe_cci_revision()]; | ||
| 835 | cci_pmu->pmu = (struct pmu) { | ||
| 836 | .name = pmu_names[probe_cci_revision()], | ||
| 837 | .task_ctx_nr = perf_invalid_context, | ||
| 838 | .pmu_enable = cci_pmu_enable, | ||
| 839 | .pmu_disable = cci_pmu_disable, | ||
| 840 | .event_init = cci_pmu_event_init, | ||
| 841 | .add = cci_pmu_add, | ||
| 842 | .del = cci_pmu_del, | ||
| 843 | .start = cci_pmu_start, | ||
| 844 | .stop = cci_pmu_stop, | ||
| 845 | .read = pmu_read, | ||
| 846 | .attr_groups = pmu_attr_groups, | ||
| 546 | }; | 847 | }; |
| 547 | 848 | ||
| 548 | cci_pmu->plat_device = pdev; | 849 | cci_pmu->plat_device = pdev; |
| 549 | cci_pmu->num_events = pmu_get_max_counters(); | 850 | cci_pmu->num_events = pmu_get_max_counters(); |
| 550 | 851 | ||
| 551 | return armpmu_register(cci_pmu, -1); | 852 | return perf_pmu_register(&cci_pmu->pmu, name, -1); |
| 552 | } | 853 | } |
| 553 | 854 | ||
| 855 | static int cci_pmu_cpu_notifier(struct notifier_block *self, | ||
| 856 | unsigned long action, void *hcpu) | ||
| 857 | { | ||
| 858 | unsigned int cpu = (long)hcpu; | ||
| 859 | unsigned int target; | ||
| 860 | |||
| 861 | switch (action & ~CPU_TASKS_FROZEN) { | ||
| 862 | case CPU_DOWN_PREPARE: | ||
| 863 | if (!cpumask_test_and_clear_cpu(cpu, &pmu->cpus)) | ||
| 864 | break; | ||
| 865 | target = cpumask_any_but(cpu_online_mask, cpu); | ||
| 866 | if (target < 0) // UP, last CPU | ||
| 867 | break; | ||
| 868 | /* | ||
| 869 | * TODO: migrate context once core races on event->ctx have | ||
| 870 | * been fixed. | ||
| 871 | */ | ||
| 872 | cpumask_set_cpu(target, &pmu->cpus); | ||
| 873 | default: | ||
| 874 | break; | ||
| 875 | } | ||
| 876 | |||
| 877 | return NOTIFY_OK; | ||
| 878 | } | ||
| 879 | |||
| 880 | static struct notifier_block cci_pmu_cpu_nb = { | ||
| 881 | .notifier_call = cci_pmu_cpu_notifier, | ||
| 882 | /* | ||
| 883 | * to migrate uncore events, our notifier should be executed | ||
| 884 | * before perf core's notifier. | ||
| 885 | */ | ||
| 886 | .priority = CPU_PRI_PERF + 1, | ||
| 887 | }; | ||
| 888 | |||
| 554 | static const struct of_device_id arm_cci_pmu_matches[] = { | 889 | static const struct of_device_id arm_cci_pmu_matches[] = { |
| 555 | { | 890 | { |
| 556 | .compatible = "arm,cci-400-pmu", | 891 | .compatible = "arm,cci-400-pmu", |
| @@ -604,15 +939,16 @@ static int cci_pmu_probe(struct platform_device *pdev) | |||
| 604 | return -EINVAL; | 939 | return -EINVAL; |
| 605 | } | 940 | } |
| 606 | 941 | ||
| 607 | pmu->cci_pmu = devm_kzalloc(&pdev->dev, sizeof(*(pmu->cci_pmu)), GFP_KERNEL); | ||
| 608 | if (!pmu->cci_pmu) | ||
| 609 | return -ENOMEM; | ||
| 610 | |||
| 611 | pmu->hw_events.events = pmu->events; | ||
| 612 | pmu->hw_events.used_mask = pmu->used_mask; | ||
| 613 | raw_spin_lock_init(&pmu->hw_events.pmu_lock); | 942 | raw_spin_lock_init(&pmu->hw_events.pmu_lock); |
| 943 | mutex_init(&pmu->reserve_mutex); | ||
| 944 | atomic_set(&pmu->active_events, 0); | ||
| 945 | cpumask_set_cpu(smp_processor_id(), &pmu->cpus); | ||
| 946 | |||
| 947 | ret = register_cpu_notifier(&cci_pmu_cpu_nb); | ||
| 948 | if (ret) | ||
| 949 | return ret; | ||
| 614 | 950 | ||
| 615 | ret = cci_pmu_init(pmu->cci_pmu, pdev); | 951 | ret = cci_pmu_init(pmu, pdev); |
| 616 | if (ret) | 952 | if (ret) |
| 617 | return ret; | 953 | return ret; |
| 618 | 954 | ||
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index e7ccd21a45c9..46de8dc39eb4 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c | |||
| @@ -25,26 +25,72 @@ | |||
| 25 | #include <linux/bitops.h> | 25 | #include <linux/bitops.h> |
| 26 | #include <linux/pm.h> | 26 | #include <linux/pm.h> |
| 27 | 27 | ||
| 28 | #ifdef CONFIG_ARM | ||
| 28 | #include <asm/bug.h> | 29 | #include <asm/bug.h> |
| 29 | #include <asm/signal.h> | 30 | #include <asm/signal.h> |
| 31 | #endif | ||
| 30 | 32 | ||
| 31 | #define ARB_TIMER 0x008 | ||
| 32 | #define ARB_ERR_CAP_CLR 0x7e4 | ||
| 33 | #define ARB_ERR_CAP_CLEAR (1 << 0) | 33 | #define ARB_ERR_CAP_CLEAR (1 << 0) |
| 34 | #define ARB_ERR_CAP_HI_ADDR 0x7e8 | ||
| 35 | #define ARB_ERR_CAP_ADDR 0x7ec | ||
| 36 | #define ARB_ERR_CAP_DATA 0x7f0 | ||
| 37 | #define ARB_ERR_CAP_STATUS 0x7f4 | ||
| 38 | #define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) | 34 | #define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) |
| 39 | #define ARB_ERR_CAP_STATUS_TEA (1 << 11) | 35 | #define ARB_ERR_CAP_STATUS_TEA (1 << 11) |
| 40 | #define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) | 36 | #define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) |
| 41 | #define ARB_ERR_CAP_STATUS_BS_MASK 0x3c | 37 | #define ARB_ERR_CAP_STATUS_BS_MASK 0x3c |
| 42 | #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) | 38 | #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) |
| 43 | #define ARB_ERR_CAP_STATUS_VALID (1 << 0) | 39 | #define ARB_ERR_CAP_STATUS_VALID (1 << 0) |
| 44 | #define ARB_ERR_CAP_MASTER 0x7f8 | 40 | |
| 41 | enum { | ||
| 42 | ARB_TIMER, | ||
| 43 | ARB_ERR_CAP_CLR, | ||
| 44 | ARB_ERR_CAP_HI_ADDR, | ||
| 45 | ARB_ERR_CAP_ADDR, | ||
| 46 | ARB_ERR_CAP_DATA, | ||
| 47 | ARB_ERR_CAP_STATUS, | ||
| 48 | ARB_ERR_CAP_MASTER, | ||
| 49 | }; | ||
| 50 | |||
| 51 | static const int gisb_offsets_bcm7038[] = { | ||
| 52 | [ARB_TIMER] = 0x00c, | ||
| 53 | [ARB_ERR_CAP_CLR] = 0x0c4, | ||
| 54 | [ARB_ERR_CAP_HI_ADDR] = -1, | ||
| 55 | [ARB_ERR_CAP_ADDR] = 0x0c8, | ||
| 56 | [ARB_ERR_CAP_DATA] = 0x0cc, | ||
| 57 | [ARB_ERR_CAP_STATUS] = 0x0d0, | ||
| 58 | [ARB_ERR_CAP_MASTER] = -1, | ||
| 59 | }; | ||
| 60 | |||
| 61 | static const int gisb_offsets_bcm7400[] = { | ||
| 62 | [ARB_TIMER] = 0x00c, | ||
| 63 | [ARB_ERR_CAP_CLR] = 0x0c8, | ||
| 64 | [ARB_ERR_CAP_HI_ADDR] = -1, | ||
| 65 | [ARB_ERR_CAP_ADDR] = 0x0cc, | ||
| 66 | [ARB_ERR_CAP_DATA] = 0x0d0, | ||
| 67 | [ARB_ERR_CAP_STATUS] = 0x0d4, | ||
| 68 | [ARB_ERR_CAP_MASTER] = 0x0d8, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const int gisb_offsets_bcm7435[] = { | ||
| 72 | [ARB_TIMER] = 0x00c, | ||
| 73 | [ARB_ERR_CAP_CLR] = 0x168, | ||
| 74 | [ARB_ERR_CAP_HI_ADDR] = -1, | ||
| 75 | [ARB_ERR_CAP_ADDR] = 0x16c, | ||
| 76 | [ARB_ERR_CAP_DATA] = 0x170, | ||
| 77 | [ARB_ERR_CAP_STATUS] = 0x174, | ||
| 78 | [ARB_ERR_CAP_MASTER] = 0x178, | ||
| 79 | }; | ||
| 80 | |||
| 81 | static const int gisb_offsets_bcm7445[] = { | ||
| 82 | [ARB_TIMER] = 0x008, | ||
| 83 | [ARB_ERR_CAP_CLR] = 0x7e4, | ||
| 84 | [ARB_ERR_CAP_HI_ADDR] = 0x7e8, | ||
| 85 | [ARB_ERR_CAP_ADDR] = 0x7ec, | ||
| 86 | [ARB_ERR_CAP_DATA] = 0x7f0, | ||
| 87 | [ARB_ERR_CAP_STATUS] = 0x7f4, | ||
| 88 | [ARB_ERR_CAP_MASTER] = 0x7f8, | ||
| 89 | }; | ||
| 45 | 90 | ||
| 46 | struct brcmstb_gisb_arb_device { | 91 | struct brcmstb_gisb_arb_device { |
| 47 | void __iomem *base; | 92 | void __iomem *base; |
| 93 | const int *gisb_offsets; | ||
| 48 | struct mutex lock; | 94 | struct mutex lock; |
| 49 | struct list_head next; | 95 | struct list_head next; |
| 50 | u32 valid_mask; | 96 | u32 valid_mask; |
| @@ -54,6 +100,26 @@ struct brcmstb_gisb_arb_device { | |||
| 54 | 100 | ||
| 55 | static LIST_HEAD(brcmstb_gisb_arb_device_list); | 101 | static LIST_HEAD(brcmstb_gisb_arb_device_list); |
| 56 | 102 | ||
| 103 | static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg) | ||
| 104 | { | ||
| 105 | int offset = gdev->gisb_offsets[reg]; | ||
| 106 | |||
| 107 | /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */ | ||
| 108 | if (offset == -1) | ||
| 109 | return 1; | ||
| 110 | |||
| 111 | return ioread32(gdev->base + offset); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg) | ||
| 115 | { | ||
| 116 | int offset = gdev->gisb_offsets[reg]; | ||
| 117 | |||
| 118 | if (offset == -1) | ||
| 119 | return; | ||
| 120 | iowrite32(val, gdev->base + reg); | ||
| 121 | } | ||
| 122 | |||
| 57 | static ssize_t gisb_arb_get_timeout(struct device *dev, | 123 | static ssize_t gisb_arb_get_timeout(struct device *dev, |
| 58 | struct device_attribute *attr, | 124 | struct device_attribute *attr, |
| 59 | char *buf) | 125 | char *buf) |
| @@ -63,7 +129,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev, | |||
| 63 | u32 timeout; | 129 | u32 timeout; |
| 64 | 130 | ||
| 65 | mutex_lock(&gdev->lock); | 131 | mutex_lock(&gdev->lock); |
| 66 | timeout = ioread32(gdev->base + ARB_TIMER); | 132 | timeout = gisb_read(gdev, ARB_TIMER); |
| 67 | mutex_unlock(&gdev->lock); | 133 | mutex_unlock(&gdev->lock); |
| 68 | 134 | ||
| 69 | return sprintf(buf, "%d", timeout); | 135 | return sprintf(buf, "%d", timeout); |
| @@ -85,7 +151,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev, | |||
| 85 | return -EINVAL; | 151 | return -EINVAL; |
| 86 | 152 | ||
| 87 | mutex_lock(&gdev->lock); | 153 | mutex_lock(&gdev->lock); |
| 88 | iowrite32(val, gdev->base + ARB_TIMER); | 154 | gisb_write(gdev, val, ARB_TIMER); |
| 89 | mutex_unlock(&gdev->lock); | 155 | mutex_unlock(&gdev->lock); |
| 90 | 156 | ||
| 91 | return count; | 157 | return count; |
| @@ -112,18 +178,18 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, | |||
| 112 | const char *m_name; | 178 | const char *m_name; |
| 113 | char m_fmt[11]; | 179 | char m_fmt[11]; |
| 114 | 180 | ||
| 115 | cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS); | 181 | cap_status = gisb_read(gdev, ARB_ERR_CAP_STATUS); |
| 116 | 182 | ||
| 117 | /* Invalid captured address, bail out */ | 183 | /* Invalid captured address, bail out */ |
| 118 | if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) | 184 | if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) |
| 119 | return 1; | 185 | return 1; |
| 120 | 186 | ||
| 121 | /* Read the address and master */ | 187 | /* Read the address and master */ |
| 122 | arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff; | 188 | arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff; |
| 123 | #if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) | 189 | #if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) |
| 124 | arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32; | 190 | arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32; |
| 125 | #endif | 191 | #endif |
| 126 | master = ioread32(gdev->base + ARB_ERR_CAP_MASTER); | 192 | master = gisb_read(gdev, ARB_ERR_CAP_MASTER); |
| 127 | 193 | ||
| 128 | m_name = brcmstb_gisb_master_to_str(gdev, master); | 194 | m_name = brcmstb_gisb_master_to_str(gdev, master); |
| 129 | if (!m_name) { | 195 | if (!m_name) { |
| @@ -138,11 +204,12 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, | |||
| 138 | m_name); | 204 | m_name); |
| 139 | 205 | ||
| 140 | /* clear the GISB error */ | 206 | /* clear the GISB error */ |
| 141 | iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR); | 207 | gisb_write(gdev, ARB_ERR_CAP_CLEAR, ARB_ERR_CAP_CLR); |
| 142 | 208 | ||
| 143 | return 0; | 209 | return 0; |
| 144 | } | 210 | } |
| 145 | 211 | ||
| 212 | #ifdef CONFIG_ARM | ||
| 146 | static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, | 213 | static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, |
| 147 | struct pt_regs *regs) | 214 | struct pt_regs *regs) |
| 148 | { | 215 | { |
| @@ -161,6 +228,7 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, | |||
| 161 | 228 | ||
| 162 | return ret; | 229 | return ret; |
| 163 | } | 230 | } |
| 231 | #endif | ||
| 164 | 232 | ||
| 165 | static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) | 233 | static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) |
| 166 | { | 234 | { |
| @@ -188,10 +256,20 @@ static struct attribute_group gisb_arb_sysfs_attr_group = { | |||
| 188 | .attrs = gisb_arb_sysfs_attrs, | 256 | .attrs = gisb_arb_sysfs_attrs, |
| 189 | }; | 257 | }; |
| 190 | 258 | ||
| 191 | static int brcmstb_gisb_arb_probe(struct platform_device *pdev) | 259 | static const struct of_device_id brcmstb_gisb_arb_of_match[] = { |
| 260 | { .compatible = "brcm,gisb-arb", .data = gisb_offsets_bcm7445 }, | ||
| 261 | { .compatible = "brcm,bcm7445-gisb-arb", .data = gisb_offsets_bcm7445 }, | ||
| 262 | { .compatible = "brcm,bcm7435-gisb-arb", .data = gisb_offsets_bcm7435 }, | ||
| 263 | { .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 }, | ||
| 264 | { .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 }, | ||
| 265 | { }, | ||
| 266 | }; | ||
| 267 | |||
| 268 | static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) | ||
| 192 | { | 269 | { |
| 193 | struct device_node *dn = pdev->dev.of_node; | 270 | struct device_node *dn = pdev->dev.of_node; |
| 194 | struct brcmstb_gisb_arb_device *gdev; | 271 | struct brcmstb_gisb_arb_device *gdev; |
| 272 | const struct of_device_id *of_id; | ||
| 195 | struct resource *r; | 273 | struct resource *r; |
| 196 | int err, timeout_irq, tea_irq; | 274 | int err, timeout_irq, tea_irq; |
| 197 | unsigned int num_masters, j = 0; | 275 | unsigned int num_masters, j = 0; |
| @@ -212,6 +290,13 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) | |||
| 212 | if (IS_ERR(gdev->base)) | 290 | if (IS_ERR(gdev->base)) |
| 213 | return PTR_ERR(gdev->base); | 291 | return PTR_ERR(gdev->base); |
| 214 | 292 | ||
| 293 | of_id = of_match_node(brcmstb_gisb_arb_of_match, dn); | ||
| 294 | if (!of_id) { | ||
| 295 | pr_err("failed to look up compatible string\n"); | ||
| 296 | return -EINVAL; | ||
| 297 | } | ||
| 298 | gdev->gisb_offsets = of_id->data; | ||
| 299 | |||
| 215 | err = devm_request_irq(&pdev->dev, timeout_irq, | 300 | err = devm_request_irq(&pdev->dev, timeout_irq, |
| 216 | brcmstb_gisb_timeout_handler, 0, pdev->name, | 301 | brcmstb_gisb_timeout_handler, 0, pdev->name, |
| 217 | gdev); | 302 | gdev); |
| @@ -257,8 +342,10 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) | |||
| 257 | 342 | ||
| 258 | list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); | 343 | list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); |
| 259 | 344 | ||
| 345 | #ifdef CONFIG_ARM | ||
| 260 | hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, | 346 | hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, |
| 261 | "imprecise external abort"); | 347 | "imprecise external abort"); |
| 348 | #endif | ||
| 262 | 349 | ||
| 263 | dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", | 350 | dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", |
| 264 | gdev->base, timeout_irq, tea_irq); | 351 | gdev->base, timeout_irq, tea_irq); |
| @@ -272,7 +359,7 @@ static int brcmstb_gisb_arb_suspend(struct device *dev) | |||
| 272 | struct platform_device *pdev = to_platform_device(dev); | 359 | struct platform_device *pdev = to_platform_device(dev); |
| 273 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); | 360 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); |
| 274 | 361 | ||
| 275 | gdev->saved_timeout = ioread32(gdev->base + ARB_TIMER); | 362 | gdev->saved_timeout = gisb_read(gdev, ARB_TIMER); |
| 276 | 363 | ||
| 277 | return 0; | 364 | return 0; |
| 278 | } | 365 | } |
| @@ -285,7 +372,7 @@ static int brcmstb_gisb_arb_resume_noirq(struct device *dev) | |||
| 285 | struct platform_device *pdev = to_platform_device(dev); | 372 | struct platform_device *pdev = to_platform_device(dev); |
| 286 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); | 373 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); |
| 287 | 374 | ||
| 288 | iowrite32(gdev->saved_timeout, gdev->base + ARB_TIMER); | 375 | gisb_write(gdev, gdev->saved_timeout, ARB_TIMER); |
| 289 | 376 | ||
| 290 | return 0; | 377 | return 0; |
| 291 | } | 378 | } |
| @@ -299,13 +386,7 @@ static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = { | |||
| 299 | .resume_noirq = brcmstb_gisb_arb_resume_noirq, | 386 | .resume_noirq = brcmstb_gisb_arb_resume_noirq, |
| 300 | }; | 387 | }; |
| 301 | 388 | ||
| 302 | static const struct of_device_id brcmstb_gisb_arb_of_match[] = { | ||
| 303 | { .compatible = "brcm,gisb-arb" }, | ||
| 304 | { }, | ||
| 305 | }; | ||
| 306 | |||
| 307 | static struct platform_driver brcmstb_gisb_arb_driver = { | 389 | static struct platform_driver brcmstb_gisb_arb_driver = { |
| 308 | .probe = brcmstb_gisb_arb_probe, | ||
| 309 | .driver = { | 390 | .driver = { |
| 310 | .name = "brcm-gisb-arb", | 391 | .name = "brcm-gisb-arb", |
| 311 | .owner = THIS_MODULE, | 392 | .owner = THIS_MODULE, |
| @@ -316,7 +397,8 @@ static struct platform_driver brcmstb_gisb_arb_driver = { | |||
| 316 | 397 | ||
| 317 | static int __init brcm_gisb_driver_init(void) | 398 | static int __init brcm_gisb_driver_init(void) |
| 318 | { | 399 | { |
| 319 | return platform_driver_register(&brcmstb_gisb_arb_driver); | 400 | return platform_driver_probe(&brcmstb_gisb_arb_driver, |
| 401 | brcmstb_gisb_arb_probe); | ||
| 320 | } | 402 | } |
| 321 | 403 | ||
| 322 | module_init(brcm_gisb_driver_init); | 404 | module_init(brcm_gisb_driver_init); |
diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 531ae591783b..17d86595951c 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c | |||
| @@ -222,10 +222,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) | |||
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | /* Error found so break the for loop */ | 224 | /* Error found so break the for loop */ |
| 225 | break; | 225 | return IRQ_HANDLED; |
| 226 | } | 226 | } |
| 227 | } | 227 | } |
| 228 | return IRQ_HANDLED; | 228 | |
| 229 | dev_err(l3->dev, "L3 %s IRQ not handled!!\n", | ||
| 230 | inttype ? "debug" : "application"); | ||
| 231 | |||
| 232 | return IRQ_NONE; | ||
| 229 | } | 233 | } |
| 230 | 234 | ||
| 231 | static const struct of_device_id l3_noc_match[] = { | 235 | static const struct of_device_id l3_noc_match[] = { |
| @@ -296,11 +300,66 @@ static int omap_l3_probe(struct platform_device *pdev) | |||
| 296 | return ret; | 300 | return ret; |
| 297 | } | 301 | } |
| 298 | 302 | ||
| 303 | #ifdef CONFIG_PM | ||
| 304 | |||
| 305 | /** | ||
| 306 | * l3_resume_noirq() - resume function for l3_noc | ||
| 307 | * @dev: pointer to l3_noc device structure | ||
| 308 | * | ||
| 309 | * We only have the resume handler only since we | ||
| 310 | * have already maintained the delta register | ||
| 311 | * configuration as part of configuring the system | ||
| 312 | */ | ||
| 313 | static int l3_resume_noirq(struct device *dev) | ||
| 314 | { | ||
| 315 | struct omap_l3 *l3 = dev_get_drvdata(dev); | ||
| 316 | int i; | ||
| 317 | struct l3_flagmux_data *flag_mux; | ||
| 318 | void __iomem *base, *mask_regx = NULL; | ||
| 319 | u32 mask_val; | ||
| 320 | |||
| 321 | for (i = 0; i < l3->num_modules; i++) { | ||
| 322 | base = l3->l3_base[i]; | ||
| 323 | flag_mux = l3->l3_flagmux[i]; | ||
| 324 | if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits) | ||
| 325 | continue; | ||
| 326 | |||
| 327 | mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + | ||
| 328 | (L3_APPLICATION_ERROR << 3); | ||
| 329 | mask_val = readl_relaxed(mask_regx); | ||
| 330 | mask_val &= ~(flag_mux->mask_app_bits); | ||
| 331 | |||
| 332 | writel_relaxed(mask_val, mask_regx); | ||
| 333 | mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + | ||
| 334 | (L3_DEBUG_ERROR << 3); | ||
| 335 | mask_val = readl_relaxed(mask_regx); | ||
| 336 | mask_val &= ~(flag_mux->mask_dbg_bits); | ||
| 337 | |||
| 338 | writel_relaxed(mask_val, mask_regx); | ||
| 339 | } | ||
| 340 | |||
| 341 | /* Dummy read to force OCP barrier */ | ||
| 342 | if (mask_regx) | ||
| 343 | (void)readl(mask_regx); | ||
| 344 | |||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | |||
| 348 | static const struct dev_pm_ops l3_dev_pm_ops = { | ||
| 349 | .resume_noirq = l3_resume_noirq, | ||
| 350 | }; | ||
| 351 | |||
| 352 | #define L3_DEV_PM_OPS (&l3_dev_pm_ops) | ||
| 353 | #else | ||
| 354 | #define L3_DEV_PM_OPS NULL | ||
| 355 | #endif | ||
| 356 | |||
| 299 | static struct platform_driver omap_l3_driver = { | 357 | static struct platform_driver omap_l3_driver = { |
| 300 | .probe = omap_l3_probe, | 358 | .probe = omap_l3_probe, |
| 301 | .driver = { | 359 | .driver = { |
| 302 | .name = "omap_l3_noc", | 360 | .name = "omap_l3_noc", |
| 303 | .owner = THIS_MODULE, | 361 | .owner = THIS_MODULE, |
| 362 | .pm = L3_DEV_PM_OPS, | ||
| 304 | .of_match_table = of_match_ptr(l3_noc_match), | 363 | .of_match_table = of_match_ptr(l3_noc_match), |
| 305 | }, | 364 | }, |
| 306 | }; | 365 | }; |
diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 290f9c1a3749..59a5714dfe18 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c | |||
| @@ -185,3 +185,16 @@ struct clk *tegra_clk_register_divider(const char *name, | |||
| 185 | 185 | ||
| 186 | return clk; | 186 | return clk; |
| 187 | } | 187 | } |
| 188 | |||
| 189 | static const struct clk_div_table mc_div_table[] = { | ||
| 190 | { .val = 0, .div = 2 }, | ||
| 191 | { .val = 1, .div = 1 }, | ||
| 192 | { .val = 0, .div = 0 }, | ||
| 193 | }; | ||
| 194 | |||
| 195 | struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, | ||
| 196 | void __iomem *reg, spinlock_t *lock) | ||
| 197 | { | ||
| 198 | return clk_register_divider_table(NULL, name, parent_name, 0, reg, | ||
| 199 | 16, 1, 0, mc_div_table, lock); | ||
| 200 | } | ||
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index f760f31d05c4..0b03d2cf7264 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c | |||
| @@ -173,6 +173,7 @@ static DEFINE_SPINLOCK(pll_d_lock); | |||
| 173 | static DEFINE_SPINLOCK(pll_d2_lock); | 173 | static DEFINE_SPINLOCK(pll_d2_lock); |
| 174 | static DEFINE_SPINLOCK(pll_u_lock); | 174 | static DEFINE_SPINLOCK(pll_u_lock); |
| 175 | static DEFINE_SPINLOCK(pll_re_lock); | 175 | static DEFINE_SPINLOCK(pll_re_lock); |
| 176 | static DEFINE_SPINLOCK(emc_lock); | ||
| 176 | 177 | ||
| 177 | static struct div_nmp pllxc_nmp = { | 178 | static struct div_nmp pllxc_nmp = { |
| 178 | .divm_shift = 0, | 179 | .divm_shift = 0, |
| @@ -1228,7 +1229,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, | |||
| 1228 | ARRAY_SIZE(mux_pllmcp_clkm), | 1229 | ARRAY_SIZE(mux_pllmcp_clkm), |
| 1229 | CLK_SET_RATE_NO_REPARENT, | 1230 | CLK_SET_RATE_NO_REPARENT, |
| 1230 | clk_base + CLK_SOURCE_EMC, | 1231 | clk_base + CLK_SOURCE_EMC, |
| 1231 | 29, 3, 0, NULL); | 1232 | 29, 3, 0, &emc_lock); |
| 1233 | |||
| 1234 | clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, | ||
| 1235 | &emc_lock); | ||
| 1236 | clks[TEGRA114_CLK_MC] = clk; | ||
| 1232 | 1237 | ||
| 1233 | for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { | 1238 | for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { |
| 1234 | data = &tegra_periph_clk_list[i]; | 1239 | data = &tegra_periph_clk_list[i]; |
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index e3a85842ce0c..f5f9baca7bb6 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c | |||
| @@ -132,6 +132,7 @@ static DEFINE_SPINLOCK(pll_d2_lock); | |||
| 132 | static DEFINE_SPINLOCK(pll_e_lock); | 132 | static DEFINE_SPINLOCK(pll_e_lock); |
| 133 | static DEFINE_SPINLOCK(pll_re_lock); | 133 | static DEFINE_SPINLOCK(pll_re_lock); |
| 134 | static DEFINE_SPINLOCK(pll_u_lock); | 134 | static DEFINE_SPINLOCK(pll_u_lock); |
| 135 | static DEFINE_SPINLOCK(emc_lock); | ||
| 135 | 136 | ||
| 136 | /* possible OSC frequencies in Hz */ | 137 | /* possible OSC frequencies in Hz */ |
| 137 | static unsigned long tegra124_input_freq[] = { | 138 | static unsigned long tegra124_input_freq[] = { |
| @@ -1127,7 +1128,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, | |||
| 1127 | clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, | 1128 | clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, |
| 1128 | ARRAY_SIZE(mux_pllmcp_clkm), 0, | 1129 | ARRAY_SIZE(mux_pllmcp_clkm), 0, |
| 1129 | clk_base + CLK_SOURCE_EMC, | 1130 | clk_base + CLK_SOURCE_EMC, |
| 1130 | 29, 3, 0, NULL); | 1131 | 29, 3, 0, &emc_lock); |
| 1132 | |||
| 1133 | clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, | ||
| 1134 | &emc_lock); | ||
| 1135 | clks[TEGRA124_CLK_MC] = clk; | ||
| 1131 | 1136 | ||
| 1132 | /* cml0 */ | 1137 | /* cml0 */ |
| 1133 | clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, | 1138 | clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, |
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index dace2b1b5ae6..41272dcc9e22 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c | |||
| @@ -140,6 +140,8 @@ static struct cpu_clk_suspend_context { | |||
| 140 | static void __iomem *clk_base; | 140 | static void __iomem *clk_base; |
| 141 | static void __iomem *pmc_base; | 141 | static void __iomem *pmc_base; |
| 142 | 142 | ||
| 143 | static DEFINE_SPINLOCK(emc_lock); | ||
| 144 | |||
| 143 | #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ | 145 | #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ |
| 144 | _clk_num, _gate_flags, _clk_id) \ | 146 | _clk_num, _gate_flags, _clk_id) \ |
| 145 | TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \ | 147 | TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \ |
| @@ -819,11 +821,15 @@ static void __init tegra20_periph_clk_init(void) | |||
| 819 | ARRAY_SIZE(mux_pllmcp_clkm), | 821 | ARRAY_SIZE(mux_pllmcp_clkm), |
| 820 | CLK_SET_RATE_NO_REPARENT, | 822 | CLK_SET_RATE_NO_REPARENT, |
| 821 | clk_base + CLK_SOURCE_EMC, | 823 | clk_base + CLK_SOURCE_EMC, |
| 822 | 30, 2, 0, NULL); | 824 | 30, 2, 0, &emc_lock); |
| 823 | clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, | 825 | clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, |
| 824 | 57, periph_clk_enb_refcnt); | 826 | 57, periph_clk_enb_refcnt); |
| 825 | clks[TEGRA20_CLK_EMC] = clk; | 827 | clks[TEGRA20_CLK_EMC] = clk; |
| 826 | 828 | ||
| 829 | clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, | ||
| 830 | &emc_lock); | ||
| 831 | clks[TEGRA20_CLK_MC] = clk; | ||
| 832 | |||
| 827 | /* dsi */ | 833 | /* dsi */ |
| 828 | clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, | 834 | clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, |
| 829 | 48, periph_clk_enb_refcnt); | 835 | 48, periph_clk_enb_refcnt); |
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 5bbacd01094f..4b9d8bd3d0bf 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c | |||
| @@ -177,6 +177,7 @@ static unsigned long input_freq; | |||
| 177 | 177 | ||
| 178 | static DEFINE_SPINLOCK(cml_lock); | 178 | static DEFINE_SPINLOCK(cml_lock); |
| 179 | static DEFINE_SPINLOCK(pll_d_lock); | 179 | static DEFINE_SPINLOCK(pll_d_lock); |
| 180 | static DEFINE_SPINLOCK(emc_lock); | ||
| 180 | 181 | ||
| 181 | #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ | 182 | #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ |
| 182 | _clk_num, _gate_flags, _clk_id) \ | 183 | _clk_num, _gate_flags, _clk_id) \ |
| @@ -1157,11 +1158,15 @@ static void __init tegra30_periph_clk_init(void) | |||
| 1157 | ARRAY_SIZE(mux_pllmcp_clkm), | 1158 | ARRAY_SIZE(mux_pllmcp_clkm), |
| 1158 | CLK_SET_RATE_NO_REPARENT, | 1159 | CLK_SET_RATE_NO_REPARENT, |
| 1159 | clk_base + CLK_SOURCE_EMC, | 1160 | clk_base + CLK_SOURCE_EMC, |
| 1160 | 30, 2, 0, NULL); | 1161 | 30, 2, 0, &emc_lock); |
| 1161 | clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, | 1162 | clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, |
| 1162 | 57, periph_clk_enb_refcnt); | 1163 | 57, periph_clk_enb_refcnt); |
| 1163 | clks[TEGRA30_CLK_EMC] = clk; | 1164 | clks[TEGRA30_CLK_EMC] = clk; |
| 1164 | 1165 | ||
| 1166 | clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, | ||
| 1167 | &emc_lock); | ||
| 1168 | clks[TEGRA30_CLK_MC] = clk; | ||
| 1169 | |||
| 1165 | /* cml0 */ | 1170 | /* cml0 */ |
| 1166 | clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, | 1171 | clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX, |
| 1167 | 0, 0, &cml_lock); | 1172 | 0, 0, &cml_lock); |
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 16ec8d6bb87f..4e458aa8d45c 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h | |||
| @@ -86,6 +86,8 @@ struct clk *tegra_clk_register_divider(const char *name, | |||
| 86 | const char *parent_name, void __iomem *reg, | 86 | const char *parent_name, void __iomem *reg, |
| 87 | unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, | 87 | unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, |
| 88 | u8 frac_width, spinlock_t *lock); | 88 | u8 frac_width, spinlock_t *lock); |
| 89 | struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, | ||
| 90 | void __iomem *reg, spinlock_t *lock); | ||
| 89 | 91 | ||
| 90 | /* | 92 | /* |
| 91 | * Tegra PLL: | 93 | * Tegra PLL: |
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 43005d4d3348..6a79fc4f900c 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
| @@ -462,7 +462,10 @@ static void __init arch_counter_register(unsigned type) | |||
| 462 | 462 | ||
| 463 | /* Register the CP15 based counter if we have one */ | 463 | /* Register the CP15 based counter if we have one */ |
| 464 | if (type & ARCH_CP15_TIMER) { | 464 | if (type & ARCH_CP15_TIMER) { |
| 465 | arch_timer_read_counter = arch_counter_get_cntvct; | 465 | if (arch_timer_use_virtual) |
| 466 | arch_timer_read_counter = arch_counter_get_cntvct; | ||
| 467 | else | ||
| 468 | arch_timer_read_counter = arch_counter_get_cntpct; | ||
| 466 | } else { | 469 | } else { |
| 467 | arch_timer_read_counter = arch_counter_get_cntvct_mem; | 470 | arch_timer_read_counter = arch_counter_get_cntvct_mem; |
| 468 | 471 | ||
| @@ -702,6 +705,14 @@ static void __init arch_timer_init(struct device_node *np) | |||
| 702 | arch_timer_detect_rate(NULL, np); | 705 | arch_timer_detect_rate(NULL, np); |
| 703 | 706 | ||
| 704 | /* | 707 | /* |
| 708 | * If we cannot rely on firmware initializing the timer registers then | ||
| 709 | * we should use the physical timers instead. | ||
| 710 | */ | ||
| 711 | if (IS_ENABLED(CONFIG_ARM) && | ||
| 712 | of_property_read_bool(np, "arm,cpu-registers-not-fw-configured")) | ||
| 713 | arch_timer_use_virtual = false; | ||
| 714 | |||
| 715 | /* | ||
| 705 | * If HYP mode is available, we know that the physical timer | 716 | * If HYP mode is available, we know that the physical timer |
| 706 | * has been configured to be accessible from PL1. Use it, so | 717 | * has been configured to be accessible from PL1. Use it, so |
| 707 | * that a guest can use the virtual timer instead. | 718 | * that a guest can use the virtual timer instead. |
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index dd5112265cc9..6dbfbc209491 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
| @@ -163,14 +163,14 @@ config TEGRA_IOMMU_GART | |||
| 163 | hardware included on Tegra SoCs. | 163 | hardware included on Tegra SoCs. |
| 164 | 164 | ||
| 165 | config TEGRA_IOMMU_SMMU | 165 | config TEGRA_IOMMU_SMMU |
| 166 | bool "Tegra SMMU IOMMU Support" | 166 | bool "NVIDIA Tegra SMMU Support" |
| 167 | depends on ARCH_TEGRA && TEGRA_AHB | 167 | depends on ARCH_TEGRA |
| 168 | depends on TEGRA_AHB | ||
| 169 | depends on TEGRA_MC | ||
| 168 | select IOMMU_API | 170 | select IOMMU_API |
| 169 | help | 171 | help |
| 170 | Enables support for remapping discontiguous physical memory | 172 | This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra |
| 171 | shared with the operating system into contiguous I/O virtual | 173 | SoCs (Tegra30 up to Tegra124). |
| 172 | space through the SMMU (System Memory Management Unit) | ||
| 173 | hardware included on Tegra SoCs. | ||
| 174 | 174 | ||
| 175 | config EXYNOS_IOMMU | 175 | config EXYNOS_IOMMU |
| 176 | bool "Exynos IOMMU Support" | 176 | bool "Exynos IOMMU Support" |
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 505a9adac2d5..2d84c9edf3b8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c | |||
| @@ -3424,6 +3424,7 @@ static const struct iommu_ops amd_iommu_ops = { | |||
| 3424 | .detach_dev = amd_iommu_detach_device, | 3424 | .detach_dev = amd_iommu_detach_device, |
| 3425 | .map = amd_iommu_map, | 3425 | .map = amd_iommu_map, |
| 3426 | .unmap = amd_iommu_unmap, | 3426 | .unmap = amd_iommu_unmap, |
| 3427 | .map_sg = default_iommu_map_sg, | ||
| 3427 | .iova_to_phys = amd_iommu_iova_to_phys, | 3428 | .iova_to_phys = amd_iommu_iova_to_phys, |
| 3428 | .pgsize_bitmap = AMD_IOMMU_PGSIZES, | 3429 | .pgsize_bitmap = AMD_IOMMU_PGSIZES, |
| 3429 | }; | 3430 | }; |
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 60558f794922..e393ae01b5d2 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c | |||
| @@ -1652,6 +1652,7 @@ static const struct iommu_ops arm_smmu_ops = { | |||
| 1652 | .detach_dev = arm_smmu_detach_dev, | 1652 | .detach_dev = arm_smmu_detach_dev, |
| 1653 | .map = arm_smmu_map, | 1653 | .map = arm_smmu_map, |
| 1654 | .unmap = arm_smmu_unmap, | 1654 | .unmap = arm_smmu_unmap, |
| 1655 | .map_sg = default_iommu_map_sg, | ||
| 1655 | .iova_to_phys = arm_smmu_iova_to_phys, | 1656 | .iova_to_phys = arm_smmu_iova_to_phys, |
| 1656 | .add_device = arm_smmu_add_device, | 1657 | .add_device = arm_smmu_add_device, |
| 1657 | .remove_device = arm_smmu_remove_device, | 1658 | .remove_device = arm_smmu_remove_device, |
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 74233186f6f7..28372b85d8da 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c | |||
| @@ -1178,6 +1178,7 @@ static const struct iommu_ops exynos_iommu_ops = { | |||
| 1178 | .detach_dev = exynos_iommu_detach_device, | 1178 | .detach_dev = exynos_iommu_detach_device, |
| 1179 | .map = exynos_iommu_map, | 1179 | .map = exynos_iommu_map, |
| 1180 | .unmap = exynos_iommu_unmap, | 1180 | .unmap = exynos_iommu_unmap, |
| 1181 | .map_sg = default_iommu_map_sg, | ||
| 1181 | .iova_to_phys = exynos_iommu_iova_to_phys, | 1182 | .iova_to_phys = exynos_iommu_iova_to_phys, |
| 1182 | .add_device = exynos_iommu_add_device, | 1183 | .add_device = exynos_iommu_add_device, |
| 1183 | .remove_device = exynos_iommu_remove_device, | 1184 | .remove_device = exynos_iommu_remove_device, |
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a27d6cb1a793..02cd26a17fe0 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
| @@ -4467,6 +4467,7 @@ static const struct iommu_ops intel_iommu_ops = { | |||
| 4467 | .detach_dev = intel_iommu_detach_device, | 4467 | .detach_dev = intel_iommu_detach_device, |
| 4468 | .map = intel_iommu_map, | 4468 | .map = intel_iommu_map, |
| 4469 | .unmap = intel_iommu_unmap, | 4469 | .unmap = intel_iommu_unmap, |
| 4470 | .map_sg = default_iommu_map_sg, | ||
| 4470 | .iova_to_phys = intel_iommu_iova_to_phys, | 4471 | .iova_to_phys = intel_iommu_iova_to_phys, |
| 4471 | .add_device = intel_iommu_add_device, | 4472 | .add_device = intel_iommu_add_device, |
| 4472 | .remove_device = intel_iommu_remove_device, | 4473 | .remove_device = intel_iommu_remove_device, |
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index ed8b04867b1f..02e4313e937c 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c | |||
| @@ -818,7 +818,15 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) | |||
| 818 | kfree(nb); | 818 | kfree(nb); |
| 819 | return err; | 819 | return err; |
| 820 | } | 820 | } |
| 821 | return bus_for_each_dev(bus, NULL, &cb, add_iommu_group); | 821 | |
| 822 | err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group); | ||
| 823 | if (err) { | ||
| 824 | bus_unregister_notifier(bus, nb); | ||
| 825 | kfree(nb); | ||
| 826 | return err; | ||
| 827 | } | ||
| 828 | |||
| 829 | return 0; | ||
| 822 | } | 830 | } |
| 823 | 831 | ||
| 824 | /** | 832 | /** |
| @@ -836,13 +844,19 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) | |||
| 836 | */ | 844 | */ |
| 837 | int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) | 845 | int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) |
| 838 | { | 846 | { |
| 847 | int err; | ||
| 848 | |||
| 839 | if (bus->iommu_ops != NULL) | 849 | if (bus->iommu_ops != NULL) |
| 840 | return -EBUSY; | 850 | return -EBUSY; |
| 841 | 851 | ||
| 842 | bus->iommu_ops = ops; | 852 | bus->iommu_ops = ops; |
| 843 | 853 | ||
| 844 | /* Do IOMMU specific setup for this bus-type */ | 854 | /* Do IOMMU specific setup for this bus-type */ |
| 845 | return iommu_bus_init(bus, ops); | 855 | err = iommu_bus_init(bus, ops); |
| 856 | if (err) | ||
| 857 | bus->iommu_ops = NULL; | ||
| 858 | |||
| 859 | return err; | ||
| 846 | } | 860 | } |
| 847 | EXPORT_SYMBOL_GPL(bus_set_iommu); | 861 | EXPORT_SYMBOL_GPL(bus_set_iommu); |
| 848 | 862 | ||
| @@ -1124,6 +1138,38 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) | |||
| 1124 | } | 1138 | } |
| 1125 | EXPORT_SYMBOL_GPL(iommu_unmap); | 1139 | EXPORT_SYMBOL_GPL(iommu_unmap); |
| 1126 | 1140 | ||
| 1141 | size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, | ||
| 1142 | struct scatterlist *sg, unsigned int nents, int prot) | ||
| 1143 | { | ||
| 1144 | struct scatterlist *s; | ||
| 1145 | size_t mapped = 0; | ||
| 1146 | unsigned int i; | ||
| 1147 | int ret; | ||
| 1148 | |||
| 1149 | for_each_sg(sg, s, nents, i) { | ||
| 1150 | phys_addr_t phys = page_to_phys(sg_page(s)); | ||
| 1151 | |||
| 1152 | /* We are mapping on page boundarys, so offset must be 0 */ | ||
| 1153 | if (s->offset) | ||
| 1154 | goto out_err; | ||
| 1155 | |||
| 1156 | ret = iommu_map(domain, iova + mapped, phys, s->length, prot); | ||
| 1157 | if (ret) | ||
| 1158 | goto out_err; | ||
| 1159 | |||
| 1160 | mapped += s->length; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | return mapped; | ||
| 1164 | |||
| 1165 | out_err: | ||
| 1166 | /* undo mappings already done */ | ||
| 1167 | iommu_unmap(domain, iova, mapped); | ||
| 1168 | |||
| 1169 | return 0; | ||
| 1170 | |||
| 1171 | } | ||
| 1172 | EXPORT_SYMBOL_GPL(default_iommu_map_sg); | ||
| 1127 | 1173 | ||
| 1128 | int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, | 1174 | int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, |
| 1129 | phys_addr_t paddr, u64 size, int prot) | 1175 | phys_addr_t paddr, u64 size, int prot) |
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 7dab5cbcc775..e509c58eee92 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c | |||
| @@ -1127,6 +1127,7 @@ static const struct iommu_ops ipmmu_ops = { | |||
| 1127 | .detach_dev = ipmmu_detach_device, | 1127 | .detach_dev = ipmmu_detach_device, |
| 1128 | .map = ipmmu_map, | 1128 | .map = ipmmu_map, |
| 1129 | .unmap = ipmmu_unmap, | 1129 | .unmap = ipmmu_unmap, |
| 1130 | .map_sg = default_iommu_map_sg, | ||
| 1130 | .iova_to_phys = ipmmu_iova_to_phys, | 1131 | .iova_to_phys = ipmmu_iova_to_phys, |
| 1131 | .add_device = ipmmu_add_device, | 1132 | .add_device = ipmmu_add_device, |
| 1132 | .remove_device = ipmmu_remove_device, | 1133 | .remove_device = ipmmu_remove_device, |
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 6e3dcc289d59..1c7b78ecf3e3 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c | |||
| @@ -681,6 +681,7 @@ static const struct iommu_ops msm_iommu_ops = { | |||
| 681 | .detach_dev = msm_iommu_detach_dev, | 681 | .detach_dev = msm_iommu_detach_dev, |
| 682 | .map = msm_iommu_map, | 682 | .map = msm_iommu_map, |
| 683 | .unmap = msm_iommu_unmap, | 683 | .unmap = msm_iommu_unmap, |
| 684 | .map_sg = default_iommu_map_sg, | ||
| 684 | .iova_to_phys = msm_iommu_iova_to_phys, | 685 | .iova_to_phys = msm_iommu_iova_to_phys, |
| 685 | .pgsize_bitmap = MSM_IOMMU_PGSIZES, | 686 | .pgsize_bitmap = MSM_IOMMU_PGSIZES, |
| 686 | }; | 687 | }; |
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 36278870e84a..18003c044454 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c | |||
| @@ -1288,6 +1288,7 @@ static const struct iommu_ops omap_iommu_ops = { | |||
| 1288 | .detach_dev = omap_iommu_detach_dev, | 1288 | .detach_dev = omap_iommu_detach_dev, |
| 1289 | .map = omap_iommu_map, | 1289 | .map = omap_iommu_map, |
| 1290 | .unmap = omap_iommu_unmap, | 1290 | .unmap = omap_iommu_unmap, |
| 1291 | .map_sg = default_iommu_map_sg, | ||
| 1291 | .iova_to_phys = omap_iommu_iova_to_phys, | 1292 | .iova_to_phys = omap_iommu_iova_to_phys, |
| 1292 | .add_device = omap_iommu_add_device, | 1293 | .add_device = omap_iommu_add_device, |
| 1293 | .remove_device = omap_iommu_remove_device, | 1294 | .remove_device = omap_iommu_remove_device, |
diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c index 1333e6fb3405..f1b00774e4de 100644 --- a/drivers/iommu/shmobile-iommu.c +++ b/drivers/iommu/shmobile-iommu.c | |||
| @@ -361,6 +361,7 @@ static const struct iommu_ops shmobile_iommu_ops = { | |||
| 361 | .detach_dev = shmobile_iommu_detach_device, | 361 | .detach_dev = shmobile_iommu_detach_device, |
| 362 | .map = shmobile_iommu_map, | 362 | .map = shmobile_iommu_map, |
| 363 | .unmap = shmobile_iommu_unmap, | 363 | .unmap = shmobile_iommu_unmap, |
| 364 | .map_sg = default_iommu_map_sg, | ||
| 364 | .iova_to_phys = shmobile_iommu_iova_to_phys, | 365 | .iova_to_phys = shmobile_iommu_iova_to_phys, |
| 365 | .add_device = shmobile_iommu_add_device, | 366 | .add_device = shmobile_iommu_add_device, |
| 366 | .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, | 367 | .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, |
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3afdf43f732a..6e134c7c227f 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c | |||
| @@ -1,1295 +1,732 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * IOMMU API for SMMU in Tegra30 | 2 | * Copyright (C) 2011-2014 NVIDIA CORPORATION. All rights reserved. |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2011-2013, NVIDIA CORPORATION. All rights reserved. | 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * | 5 | * it under the terms of the GNU General Public License version 2 as |
| 6 | * This program is free software; you can redistribute it and/or modify it | 6 | * published by the Free Software Foundation. |
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 18 | */ | 7 | */ |
| 19 | 8 | ||
| 20 | #define pr_fmt(fmt) "%s(): " fmt, __func__ | ||
| 21 | |||
| 22 | #include <linux/err.h> | 9 | #include <linux/err.h> |
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/spinlock.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/vmalloc.h> | ||
| 28 | #include <linux/mm.h> | ||
| 29 | #include <linux/pagemap.h> | ||
| 30 | #include <linux/device.h> | ||
| 31 | #include <linux/sched.h> | ||
| 32 | #include <linux/iommu.h> | 10 | #include <linux/iommu.h> |
| 33 | #include <linux/io.h> | 11 | #include <linux/kernel.h> |
| 34 | #include <linux/of.h> | 12 | #include <linux/of.h> |
| 35 | #include <linux/of_iommu.h> | 13 | #include <linux/of_device.h> |
| 36 | #include <linux/debugfs.h> | 14 | #include <linux/platform_device.h> |
| 37 | #include <linux/seq_file.h> | 15 | #include <linux/slab.h> |
| 38 | 16 | ||
| 39 | #include <soc/tegra/ahb.h> | 17 | #include <soc/tegra/ahb.h> |
| 18 | #include <soc/tegra/mc.h> | ||
| 40 | 19 | ||
| 41 | #include <asm/page.h> | 20 | struct tegra_smmu { |
| 42 | #include <asm/cacheflush.h> | 21 | void __iomem *regs; |
| 43 | 22 | struct device *dev; | |
| 44 | enum smmu_hwgrp { | ||
| 45 | HWGRP_AFI, | ||
| 46 | HWGRP_AVPC, | ||
| 47 | HWGRP_DC, | ||
| 48 | HWGRP_DCB, | ||
| 49 | HWGRP_EPP, | ||
| 50 | HWGRP_G2, | ||
| 51 | HWGRP_HC, | ||
| 52 | HWGRP_HDA, | ||
| 53 | HWGRP_ISP, | ||
| 54 | HWGRP_MPE, | ||
| 55 | HWGRP_NV, | ||
| 56 | HWGRP_NV2, | ||
| 57 | HWGRP_PPCS, | ||
| 58 | HWGRP_SATA, | ||
| 59 | HWGRP_VDE, | ||
| 60 | HWGRP_VI, | ||
| 61 | |||
| 62 | HWGRP_COUNT, | ||
| 63 | |||
| 64 | HWGRP_END = ~0, | ||
| 65 | }; | ||
| 66 | 23 | ||
| 67 | #define HWG_AFI (1 << HWGRP_AFI) | 24 | struct tegra_mc *mc; |
| 68 | #define HWG_AVPC (1 << HWGRP_AVPC) | 25 | const struct tegra_smmu_soc *soc; |
| 69 | #define HWG_DC (1 << HWGRP_DC) | ||
| 70 | #define HWG_DCB (1 << HWGRP_DCB) | ||
| 71 | #define HWG_EPP (1 << HWGRP_EPP) | ||
| 72 | #define HWG_G2 (1 << HWGRP_G2) | ||
| 73 | #define HWG_HC (1 << HWGRP_HC) | ||
| 74 | #define HWG_HDA (1 << HWGRP_HDA) | ||
| 75 | #define HWG_ISP (1 << HWGRP_ISP) | ||
| 76 | #define HWG_MPE (1 << HWGRP_MPE) | ||
| 77 | #define HWG_NV (1 << HWGRP_NV) | ||
| 78 | #define HWG_NV2 (1 << HWGRP_NV2) | ||
| 79 | #define HWG_PPCS (1 << HWGRP_PPCS) | ||
| 80 | #define HWG_SATA (1 << HWGRP_SATA) | ||
| 81 | #define HWG_VDE (1 << HWGRP_VDE) | ||
| 82 | #define HWG_VI (1 << HWGRP_VI) | ||
| 83 | |||
| 84 | /* bitmap of the page sizes currently supported */ | ||
| 85 | #define SMMU_IOMMU_PGSIZES (SZ_4K) | ||
| 86 | |||
| 87 | #define SMMU_CONFIG 0x10 | ||
| 88 | #define SMMU_CONFIG_DISABLE 0 | ||
| 89 | #define SMMU_CONFIG_ENABLE 1 | ||
| 90 | |||
| 91 | /* REVISIT: To support multiple MCs */ | ||
| 92 | enum { | ||
| 93 | _MC = 0, | ||
| 94 | }; | ||
| 95 | 26 | ||
| 96 | enum { | 27 | unsigned long *asids; |
| 97 | _TLB = 0, | 28 | struct mutex lock; |
| 98 | _PTC, | ||
| 99 | }; | ||
| 100 | 29 | ||
| 101 | #define SMMU_CACHE_CONFIG_BASE 0x14 | 30 | struct list_head list; |
| 102 | #define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache) | ||
| 103 | #define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache) | ||
| 104 | |||
| 105 | #define SMMU_CACHE_CONFIG_STATS_SHIFT 31 | ||
| 106 | #define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT) | ||
| 107 | #define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30 | ||
| 108 | #define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT) | ||
| 109 | |||
| 110 | #define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) | ||
| 111 | #define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 | ||
| 112 | #define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 | ||
| 113 | |||
| 114 | #define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) | ||
| 115 | #define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f | ||
| 116 | #define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f | ||
| 117 | |||
| 118 | #define SMMU_PTB_ASID 0x1c | ||
| 119 | #define SMMU_PTB_ASID_CURRENT_SHIFT 0 | ||
| 120 | |||
| 121 | #define SMMU_PTB_DATA 0x20 | ||
| 122 | #define SMMU_PTB_DATA_RESET_VAL 0 | ||
| 123 | #define SMMU_PTB_DATA_ASID_NONSECURE_SHIFT 29 | ||
| 124 | #define SMMU_PTB_DATA_ASID_WRITABLE_SHIFT 30 | ||
| 125 | #define SMMU_PTB_DATA_ASID_READABLE_SHIFT 31 | ||
| 126 | |||
| 127 | #define SMMU_TLB_FLUSH 0x30 | ||
| 128 | #define SMMU_TLB_FLUSH_VA_MATCH_ALL 0 | ||
| 129 | #define SMMU_TLB_FLUSH_VA_MATCH_SECTION 2 | ||
| 130 | #define SMMU_TLB_FLUSH_VA_MATCH_GROUP 3 | ||
| 131 | #define SMMU_TLB_FLUSH_ASID_SHIFT 29 | ||
| 132 | #define SMMU_TLB_FLUSH_ASID_MATCH_DISABLE 0 | ||
| 133 | #define SMMU_TLB_FLUSH_ASID_MATCH_ENABLE 1 | ||
| 134 | #define SMMU_TLB_FLUSH_ASID_MATCH_SHIFT 31 | ||
| 135 | |||
| 136 | #define SMMU_PTC_FLUSH 0x34 | ||
| 137 | #define SMMU_PTC_FLUSH_TYPE_ALL 0 | ||
| 138 | #define SMMU_PTC_FLUSH_TYPE_ADR 1 | ||
| 139 | #define SMMU_PTC_FLUSH_ADR_SHIFT 4 | ||
| 140 | |||
| 141 | #define SMMU_ASID_SECURITY 0x38 | ||
| 142 | |||
| 143 | #define SMMU_STATS_CACHE_COUNT_BASE 0x1f0 | ||
| 144 | |||
| 145 | #define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \ | ||
| 146 | (SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss) | ||
| 147 | |||
| 148 | #define SMMU_TRANSLATION_ENABLE_0 0x228 | ||
| 149 | #define SMMU_TRANSLATION_ENABLE_1 0x22c | ||
| 150 | #define SMMU_TRANSLATION_ENABLE_2 0x230 | ||
| 151 | |||
| 152 | #define SMMU_AFI_ASID 0x238 /* PCIE */ | ||
| 153 | #define SMMU_AVPC_ASID 0x23c /* AVP */ | ||
| 154 | #define SMMU_DC_ASID 0x240 /* Display controller */ | ||
| 155 | #define SMMU_DCB_ASID 0x244 /* Display controller B */ | ||
| 156 | #define SMMU_EPP_ASID 0x248 /* Encoder pre-processor */ | ||
| 157 | #define SMMU_G2_ASID 0x24c /* 2D engine */ | ||
| 158 | #define SMMU_HC_ASID 0x250 /* Host1x */ | ||
| 159 | #define SMMU_HDA_ASID 0x254 /* High-def audio */ | ||
| 160 | #define SMMU_ISP_ASID 0x258 /* Image signal processor */ | ||
| 161 | #define SMMU_MPE_ASID 0x264 /* MPEG encoder */ | ||
| 162 | #define SMMU_NV_ASID 0x268 /* (3D) */ | ||
| 163 | #define SMMU_NV2_ASID 0x26c /* (3D) */ | ||
| 164 | #define SMMU_PPCS_ASID 0x270 /* AHB */ | ||
| 165 | #define SMMU_SATA_ASID 0x278 /* SATA */ | ||
| 166 | #define SMMU_VDE_ASID 0x27c /* Video decoder */ | ||
| 167 | #define SMMU_VI_ASID 0x280 /* Video input */ | ||
| 168 | |||
| 169 | #define SMMU_PDE_NEXT_SHIFT 28 | ||
| 170 | |||
| 171 | #define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 | ||
| 172 | #define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ | ||
| 173 | #define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 | ||
| 174 | #define SMMU_TLB_FLUSH_VA_GROUP__SHIFT 12 /* right shift */ | ||
| 175 | #define SMMU_TLB_FLUSH_VA(iova, which) \ | ||
| 176 | ((((iova) & SMMU_TLB_FLUSH_VA_##which##__MASK) >> \ | ||
| 177 | SMMU_TLB_FLUSH_VA_##which##__SHIFT) | \ | ||
| 178 | SMMU_TLB_FLUSH_VA_MATCH_##which) | ||
| 179 | #define SMMU_PTB_ASID_CUR(n) \ | ||
| 180 | ((n) << SMMU_PTB_ASID_CURRENT_SHIFT) | ||
| 181 | #define SMMU_TLB_FLUSH_ASID_MATCH_disable \ | ||
| 182 | (SMMU_TLB_FLUSH_ASID_MATCH_DISABLE << \ | ||
| 183 | SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) | ||
| 184 | #define SMMU_TLB_FLUSH_ASID_MATCH__ENABLE \ | ||
| 185 | (SMMU_TLB_FLUSH_ASID_MATCH_ENABLE << \ | ||
| 186 | SMMU_TLB_FLUSH_ASID_MATCH_SHIFT) | ||
| 187 | |||
| 188 | #define SMMU_PAGE_SHIFT 12 | ||
| 189 | #define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) | ||
| 190 | #define SMMU_PAGE_MASK ((1 << SMMU_PAGE_SHIFT) - 1) | ||
| 191 | |||
| 192 | #define SMMU_PDIR_COUNT 1024 | ||
| 193 | #define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) | ||
| 194 | #define SMMU_PTBL_COUNT 1024 | ||
| 195 | #define SMMU_PTBL_SIZE (sizeof(unsigned long) * SMMU_PTBL_COUNT) | ||
| 196 | #define SMMU_PDIR_SHIFT 12 | ||
| 197 | #define SMMU_PDE_SHIFT 12 | ||
| 198 | #define SMMU_PTE_SHIFT 12 | ||
| 199 | #define SMMU_PFN_MASK 0x000fffff | ||
| 200 | |||
| 201 | #define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12) | ||
| 202 | #define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22) | ||
| 203 | #define SMMU_PDN_TO_ADDR(pdn) ((pdn) << 22) | ||
| 204 | |||
| 205 | #define _READABLE (1 << SMMU_PTB_DATA_ASID_READABLE_SHIFT) | ||
| 206 | #define _WRITABLE (1 << SMMU_PTB_DATA_ASID_WRITABLE_SHIFT) | ||
| 207 | #define _NONSECURE (1 << SMMU_PTB_DATA_ASID_NONSECURE_SHIFT) | ||
| 208 | #define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT) | ||
| 209 | #define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE) | ||
| 210 | |||
| 211 | #define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE) | ||
| 212 | |||
| 213 | #define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE) | ||
| 214 | #define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT) | ||
| 215 | #define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR) | ||
| 216 | |||
| 217 | #define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE) | ||
| 218 | #define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR) | ||
| 219 | |||
| 220 | #define SMMU_MK_PDIR(page, attr) \ | ||
| 221 | ((page_to_phys(page) >> SMMU_PDIR_SHIFT) | (attr)) | ||
| 222 | #define SMMU_MK_PDE(page, attr) \ | ||
| 223 | (unsigned long)((page_to_phys(page) >> SMMU_PDE_SHIFT) | (attr)) | ||
| 224 | #define SMMU_EX_PTBL_PAGE(pde) \ | ||
| 225 | pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK) | ||
| 226 | #define SMMU_PFN_TO_PTE(pfn, attr) (unsigned long)((pfn) | (attr)) | ||
| 227 | |||
| 228 | #define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31)) | ||
| 229 | #define SMMU_ASID_DISABLE 0 | ||
| 230 | #define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) | ||
| 231 | |||
| 232 | #define NUM_SMMU_REG_BANKS 3 | ||
| 233 | |||
| 234 | #define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) | ||
| 235 | #define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) | ||
| 236 | #define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) | ||
| 237 | #define __smmu_client_disable_hwgrp(c) __smmu_client_set_hwgrp(c, 0, 0) | ||
| 238 | |||
| 239 | #define HWGRP_INIT(client) [HWGRP_##client] = SMMU_##client##_ASID | ||
| 240 | |||
| 241 | static const u32 smmu_hwgrp_asid_reg[] = { | ||
| 242 | HWGRP_INIT(AFI), | ||
| 243 | HWGRP_INIT(AVPC), | ||
| 244 | HWGRP_INIT(DC), | ||
| 245 | HWGRP_INIT(DCB), | ||
| 246 | HWGRP_INIT(EPP), | ||
| 247 | HWGRP_INIT(G2), | ||
| 248 | HWGRP_INIT(HC), | ||
| 249 | HWGRP_INIT(HDA), | ||
| 250 | HWGRP_INIT(ISP), | ||
| 251 | HWGRP_INIT(MPE), | ||
| 252 | HWGRP_INIT(NV), | ||
| 253 | HWGRP_INIT(NV2), | ||
| 254 | HWGRP_INIT(PPCS), | ||
| 255 | HWGRP_INIT(SATA), | ||
| 256 | HWGRP_INIT(VDE), | ||
| 257 | HWGRP_INIT(VI), | ||
| 258 | }; | 31 | }; |
| 259 | #define HWGRP_ASID_REG(x) (smmu_hwgrp_asid_reg[x]) | ||
| 260 | 32 | ||
| 261 | /* | 33 | struct tegra_smmu_as { |
| 262 | * Per client for address space | 34 | struct iommu_domain *domain; |
| 263 | */ | 35 | struct tegra_smmu *smmu; |
| 264 | struct smmu_client { | 36 | unsigned int use_count; |
| 265 | struct device *dev; | 37 | struct page *count; |
| 266 | struct list_head list; | 38 | struct page *pd; |
| 267 | struct smmu_as *as; | 39 | unsigned id; |
| 268 | u32 hwgrp; | 40 | u32 attr; |
| 269 | }; | 41 | }; |
| 270 | 42 | ||
| 271 | /* | 43 | static inline void smmu_writel(struct tegra_smmu *smmu, u32 value, |
| 272 | * Per address space | 44 | unsigned long offset) |
| 273 | */ | 45 | { |
| 274 | struct smmu_as { | 46 | writel(value, smmu->regs + offset); |
| 275 | struct smmu_device *smmu; /* back pointer to container */ | 47 | } |
| 276 | unsigned int asid; | ||
| 277 | spinlock_t lock; /* for pagetable */ | ||
| 278 | struct page *pdir_page; | ||
| 279 | unsigned long pdir_attr; | ||
| 280 | unsigned long pde_attr; | ||
| 281 | unsigned long pte_attr; | ||
| 282 | unsigned int *pte_count; | ||
| 283 | |||
| 284 | struct list_head client; | ||
| 285 | spinlock_t client_lock; /* for client list */ | ||
| 286 | }; | ||
| 287 | 48 | ||
| 288 | struct smmu_debugfs_info { | 49 | static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) |
| 289 | struct smmu_device *smmu; | 50 | { |
| 290 | int mc; | 51 | return readl(smmu->regs + offset); |
| 291 | int cache; | 52 | } |
| 292 | }; | ||
| 293 | 53 | ||
| 294 | /* | 54 | #define SMMU_CONFIG 0x010 |
| 295 | * Per SMMU device - IOMMU device | 55 | #define SMMU_CONFIG_ENABLE (1 << 0) |
| 296 | */ | ||
| 297 | struct smmu_device { | ||
| 298 | void __iomem *regbase; /* register offset base */ | ||
| 299 | void __iomem **regs; /* register block start address array */ | ||
| 300 | void __iomem **rege; /* register block end address array */ | ||
| 301 | int nregs; /* number of register blocks */ | ||
| 302 | |||
| 303 | unsigned long iovmm_base; /* remappable base address */ | ||
| 304 | unsigned long page_count; /* total remappable size */ | ||
| 305 | spinlock_t lock; | ||
| 306 | char *name; | ||
| 307 | struct device *dev; | ||
| 308 | struct page *avp_vector_page; /* dummy page shared by all AS's */ | ||
| 309 | 56 | ||
| 310 | /* | 57 | #define SMMU_TLB_CONFIG 0x14 |
| 311 | * Register image savers for suspend/resume | 58 | #define SMMU_TLB_CONFIG_HIT_UNDER_MISS (1 << 29) |
| 312 | */ | 59 | #define SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION (1 << 28) |
| 313 | unsigned long translation_enable_0; | 60 | #define SMMU_TLB_CONFIG_ACTIVE_LINES(x) ((x) & 0x3f) |
| 314 | unsigned long translation_enable_1; | ||
| 315 | unsigned long translation_enable_2; | ||
| 316 | unsigned long asid_security; | ||
| 317 | 61 | ||
| 318 | struct dentry *debugfs_root; | 62 | #define SMMU_PTC_CONFIG 0x18 |
| 319 | struct smmu_debugfs_info *debugfs_info; | 63 | #define SMMU_PTC_CONFIG_ENABLE (1 << 29) |
| 64 | #define SMMU_PTC_CONFIG_REQ_LIMIT(x) (((x) & 0x0f) << 24) | ||
| 65 | #define SMMU_PTC_CONFIG_INDEX_MAP(x) ((x) & 0x3f) | ||
| 320 | 66 | ||
| 321 | struct device_node *ahb; | 67 | #define SMMU_PTB_ASID 0x01c |
| 68 | #define SMMU_PTB_ASID_VALUE(x) ((x) & 0x7f) | ||
| 322 | 69 | ||
| 323 | int num_as; | 70 | #define SMMU_PTB_DATA 0x020 |
| 324 | struct smmu_as as[0]; /* Run-time allocated array */ | 71 | #define SMMU_PTB_DATA_VALUE(page, attr) (page_to_phys(page) >> 12 | (attr)) |
| 325 | }; | ||
| 326 | 72 | ||
| 327 | static struct smmu_device *smmu_handle; /* unique for a system */ | 73 | #define SMMU_MK_PDE(page, attr) (page_to_phys(page) >> SMMU_PTE_SHIFT | (attr)) |
| 328 | 74 | ||
| 329 | /* | 75 | #define SMMU_TLB_FLUSH 0x030 |
| 330 | * SMMU register accessors | 76 | #define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0) |
| 331 | */ | 77 | #define SMMU_TLB_FLUSH_VA_MATCH_SECTION (2 << 0) |
| 332 | static bool inline smmu_valid_reg(struct smmu_device *smmu, | 78 | #define SMMU_TLB_FLUSH_VA_MATCH_GROUP (3 << 0) |
| 333 | void __iomem *addr) | 79 | #define SMMU_TLB_FLUSH_ASID(x) (((x) & 0x7f) << 24) |
| 334 | { | 80 | #define SMMU_TLB_FLUSH_VA_SECTION(addr) ((((addr) & 0xffc00000) >> 12) | \ |
| 335 | int i; | 81 | SMMU_TLB_FLUSH_VA_MATCH_SECTION) |
| 82 | #define SMMU_TLB_FLUSH_VA_GROUP(addr) ((((addr) & 0xffffc000) >> 12) | \ | ||
| 83 | SMMU_TLB_FLUSH_VA_MATCH_GROUP) | ||
| 84 | #define SMMU_TLB_FLUSH_ASID_MATCH (1 << 31) | ||
| 336 | 85 | ||
| 337 | for (i = 0; i < smmu->nregs; i++) { | 86 | #define SMMU_PTC_FLUSH 0x034 |
| 338 | if (addr < smmu->regs[i]) | 87 | #define SMMU_PTC_FLUSH_TYPE_ALL (0 << 0) |
| 339 | break; | 88 | #define SMMU_PTC_FLUSH_TYPE_ADR (1 << 0) |
| 340 | if (addr <= smmu->rege[i]) | ||
| 341 | return true; | ||
| 342 | } | ||
| 343 | 89 | ||
| 344 | return false; | 90 | #define SMMU_PTC_FLUSH_HI 0x9b8 |
| 345 | } | 91 | #define SMMU_PTC_FLUSH_HI_MASK 0x3 |
| 346 | 92 | ||
| 347 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) | 93 | /* per-SWGROUP SMMU_*_ASID register */ |
| 348 | { | 94 | #define SMMU_ASID_ENABLE (1 << 31) |
| 349 | void __iomem *addr = smmu->regbase + offs; | 95 | #define SMMU_ASID_MASK 0x7f |
| 96 | #define SMMU_ASID_VALUE(x) ((x) & SMMU_ASID_MASK) | ||
| 350 | 97 | ||
| 351 | BUG_ON(!smmu_valid_reg(smmu, addr)); | 98 | /* page table definitions */ |
| 99 | #define SMMU_NUM_PDE 1024 | ||
| 100 | #define SMMU_NUM_PTE 1024 | ||
| 352 | 101 | ||
| 353 | return readl(addr); | 102 | #define SMMU_SIZE_PD (SMMU_NUM_PDE * 4) |
| 354 | } | 103 | #define SMMU_SIZE_PT (SMMU_NUM_PTE * 4) |
| 355 | 104 | ||
| 356 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) | 105 | #define SMMU_PDE_SHIFT 22 |
| 357 | { | 106 | #define SMMU_PTE_SHIFT 12 |
| 358 | void __iomem *addr = smmu->regbase + offs; | ||
| 359 | 107 | ||
| 360 | BUG_ON(!smmu_valid_reg(smmu, addr)); | 108 | #define SMMU_PFN_MASK 0x000fffff |
| 361 | 109 | ||
| 362 | writel(val, addr); | 110 | #define SMMU_PD_READABLE (1 << 31) |
| 363 | } | 111 | #define SMMU_PD_WRITABLE (1 << 30) |
| 112 | #define SMMU_PD_NONSECURE (1 << 29) | ||
| 364 | 113 | ||
| 365 | #define VA_PAGE_TO_PA(va, page) \ | 114 | #define SMMU_PDE_READABLE (1 << 31) |
| 366 | (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK)) | 115 | #define SMMU_PDE_WRITABLE (1 << 30) |
| 116 | #define SMMU_PDE_NONSECURE (1 << 29) | ||
| 117 | #define SMMU_PDE_NEXT (1 << 28) | ||
| 367 | 118 | ||
| 368 | #define FLUSH_CPU_DCACHE(va, page, size) \ | 119 | #define SMMU_PTE_READABLE (1 << 31) |
| 369 | do { \ | 120 | #define SMMU_PTE_WRITABLE (1 << 30) |
| 370 | unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \ | 121 | #define SMMU_PTE_NONSECURE (1 << 29) |
| 371 | __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \ | ||
| 372 | outer_flush_range(_pa_, _pa_+(size_t)(size)); \ | ||
| 373 | } while (0) | ||
| 374 | 122 | ||
| 375 | /* | 123 | #define SMMU_PDE_ATTR (SMMU_PDE_READABLE | SMMU_PDE_WRITABLE | \ |
| 376 | * Any interaction between any block on PPSB and a block on APB or AHB | 124 | SMMU_PDE_NONSECURE) |
| 377 | * must have these read-back barriers to ensure the APB/AHB bus | 125 | #define SMMU_PTE_ATTR (SMMU_PTE_READABLE | SMMU_PTE_WRITABLE | \ |
| 378 | * transaction is complete before initiating activity on the PPSB | 126 | SMMU_PTE_NONSECURE) |
| 379 | * block. | ||
| 380 | */ | ||
| 381 | #define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG) | ||
| 382 | 127 | ||
| 383 | #define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data) | 128 | static inline void smmu_flush_ptc(struct tegra_smmu *smmu, struct page *page, |
| 384 | 129 | unsigned long offset) | |
| 385 | static int __smmu_client_set_hwgrp(struct smmu_client *c, | ||
| 386 | unsigned long map, int on) | ||
| 387 | { | 130 | { |
| 388 | int i; | 131 | phys_addr_t phys = page ? page_to_phys(page) : 0; |
| 389 | struct smmu_as *as = c->as; | 132 | u32 value; |
| 390 | u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid); | 133 | |
| 391 | struct smmu_device *smmu = as->smmu; | 134 | if (page) { |
| 392 | 135 | offset &= ~(smmu->mc->soc->atom_size - 1); | |
| 393 | WARN_ON(!on && map); | 136 | |
| 394 | if (on && !map) | 137 | if (smmu->mc->soc->num_address_bits > 32) { |
| 395 | return -EINVAL; | 138 | #ifdef CONFIG_PHYS_ADDR_T_64BIT |
| 396 | if (!on) | 139 | value = (phys >> 32) & SMMU_PTC_FLUSH_HI_MASK; |
| 397 | map = smmu_client_hwgrp(c); | 140 | #else |
| 398 | 141 | value = 0; | |
| 399 | for_each_set_bit(i, &map, HWGRP_COUNT) { | 142 | #endif |
| 400 | offs = HWGRP_ASID_REG(i); | 143 | smmu_writel(smmu, value, SMMU_PTC_FLUSH_HI); |
| 401 | val = smmu_read(smmu, offs); | ||
| 402 | if (on) { | ||
| 403 | if (WARN_ON(val & mask)) | ||
| 404 | goto err_hw_busy; | ||
| 405 | val |= mask; | ||
| 406 | } else { | ||
| 407 | WARN_ON((val & mask) == mask); | ||
| 408 | val &= ~mask; | ||
| 409 | } | 144 | } |
| 410 | smmu_write(smmu, val, offs); | ||
| 411 | } | ||
| 412 | FLUSH_SMMU_REGS(smmu); | ||
| 413 | c->hwgrp = map; | ||
| 414 | return 0; | ||
| 415 | 145 | ||
| 416 | err_hw_busy: | 146 | value = (phys + offset) | SMMU_PTC_FLUSH_TYPE_ADR; |
| 417 | for_each_set_bit(i, &map, HWGRP_COUNT) { | 147 | } else { |
| 418 | offs = HWGRP_ASID_REG(i); | 148 | value = SMMU_PTC_FLUSH_TYPE_ALL; |
| 419 | val = smmu_read(smmu, offs); | ||
| 420 | val &= ~mask; | ||
| 421 | smmu_write(smmu, val, offs); | ||
| 422 | } | 149 | } |
| 423 | return -EBUSY; | 150 | |
| 151 | smmu_writel(smmu, value, SMMU_PTC_FLUSH); | ||
| 424 | } | 152 | } |
| 425 | 153 | ||
| 426 | static int smmu_client_set_hwgrp(struct smmu_client *c, u32 map, int on) | 154 | static inline void smmu_flush_tlb(struct tegra_smmu *smmu) |
| 427 | { | 155 | { |
| 428 | u32 val; | 156 | smmu_writel(smmu, SMMU_TLB_FLUSH_VA_MATCH_ALL, SMMU_TLB_FLUSH); |
| 429 | unsigned long flags; | ||
| 430 | struct smmu_as *as = c->as; | ||
| 431 | struct smmu_device *smmu = as->smmu; | ||
| 432 | |||
| 433 | spin_lock_irqsave(&smmu->lock, flags); | ||
| 434 | val = __smmu_client_set_hwgrp(c, map, on); | ||
| 435 | spin_unlock_irqrestore(&smmu->lock, flags); | ||
| 436 | return val; | ||
| 437 | } | 157 | } |
| 438 | 158 | ||
| 439 | /* | 159 | static inline void smmu_flush_tlb_asid(struct tegra_smmu *smmu, |
| 440 | * Flush all TLB entries and all PTC entries | 160 | unsigned long asid) |
| 441 | * Caller must lock smmu | ||
| 442 | */ | ||
| 443 | static void smmu_flush_regs(struct smmu_device *smmu, int enable) | ||
| 444 | { | 161 | { |
| 445 | u32 val; | 162 | u32 value; |
| 446 | |||
| 447 | smmu_write(smmu, SMMU_PTC_FLUSH_TYPE_ALL, SMMU_PTC_FLUSH); | ||
| 448 | FLUSH_SMMU_REGS(smmu); | ||
| 449 | val = SMMU_TLB_FLUSH_VA_MATCH_ALL | | ||
| 450 | SMMU_TLB_FLUSH_ASID_MATCH_disable; | ||
| 451 | smmu_write(smmu, val, SMMU_TLB_FLUSH); | ||
| 452 | 163 | ||
| 453 | if (enable) | 164 | value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | |
| 454 | smmu_write(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); | 165 | SMMU_TLB_FLUSH_VA_MATCH_ALL; |
| 455 | FLUSH_SMMU_REGS(smmu); | 166 | smmu_writel(smmu, value, SMMU_TLB_FLUSH); |
| 456 | } | 167 | } |
| 457 | 168 | ||
| 458 | static int smmu_setup_regs(struct smmu_device *smmu) | 169 | static inline void smmu_flush_tlb_section(struct tegra_smmu *smmu, |
| 170 | unsigned long asid, | ||
| 171 | unsigned long iova) | ||
| 459 | { | 172 | { |
| 460 | int i; | 173 | u32 value; |
| 461 | u32 val; | ||
| 462 | 174 | ||
| 463 | for (i = 0; i < smmu->num_as; i++) { | 175 | value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | |
| 464 | struct smmu_as *as = &smmu->as[i]; | 176 | SMMU_TLB_FLUSH_VA_SECTION(iova); |
| 465 | struct smmu_client *c; | 177 | smmu_writel(smmu, value, SMMU_TLB_FLUSH); |
| 466 | |||
| 467 | smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); | ||
| 468 | val = as->pdir_page ? | ||
| 469 | SMMU_MK_PDIR(as->pdir_page, as->pdir_attr) : | ||
| 470 | SMMU_PTB_DATA_RESET_VAL; | ||
| 471 | smmu_write(smmu, val, SMMU_PTB_DATA); | ||
| 472 | |||
| 473 | list_for_each_entry(c, &as->client, list) | ||
| 474 | __smmu_client_set_hwgrp(c, c->hwgrp, 1); | ||
| 475 | } | ||
| 476 | |||
| 477 | smmu_write(smmu, smmu->translation_enable_0, SMMU_TRANSLATION_ENABLE_0); | ||
| 478 | smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); | ||
| 479 | smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); | ||
| 480 | smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); | ||
| 481 | smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB)); | ||
| 482 | smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC)); | ||
| 483 | |||
| 484 | smmu_flush_regs(smmu, 1); | ||
| 485 | |||
| 486 | return tegra_ahb_enable_smmu(smmu->ahb); | ||
| 487 | } | 178 | } |
| 488 | 179 | ||
| 489 | static void flush_ptc_and_tlb(struct smmu_device *smmu, | 180 | static inline void smmu_flush_tlb_group(struct tegra_smmu *smmu, |
| 490 | struct smmu_as *as, dma_addr_t iova, | 181 | unsigned long asid, |
| 491 | unsigned long *pte, struct page *page, int is_pde) | 182 | unsigned long iova) |
| 492 | { | 183 | { |
| 493 | u32 val; | 184 | u32 value; |
| 494 | unsigned long tlb_flush_va = is_pde | ||
| 495 | ? SMMU_TLB_FLUSH_VA(iova, SECTION) | ||
| 496 | : SMMU_TLB_FLUSH_VA(iova, GROUP); | ||
| 497 | |||
| 498 | val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page); | ||
| 499 | smmu_write(smmu, val, SMMU_PTC_FLUSH); | ||
| 500 | FLUSH_SMMU_REGS(smmu); | ||
| 501 | val = tlb_flush_va | | ||
| 502 | SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | | ||
| 503 | (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); | ||
| 504 | smmu_write(smmu, val, SMMU_TLB_FLUSH); | ||
| 505 | FLUSH_SMMU_REGS(smmu); | ||
| 506 | } | ||
| 507 | 185 | ||
| 508 | static void free_ptbl(struct smmu_as *as, dma_addr_t iova) | 186 | value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | |
| 509 | { | 187 | SMMU_TLB_FLUSH_VA_GROUP(iova); |
| 510 | unsigned long pdn = SMMU_ADDR_TO_PDN(iova); | 188 | smmu_writel(smmu, value, SMMU_TLB_FLUSH); |
| 511 | unsigned long *pdir = (unsigned long *)page_address(as->pdir_page); | ||
| 512 | |||
| 513 | if (pdir[pdn] != _PDE_VACANT(pdn)) { | ||
| 514 | dev_dbg(as->smmu->dev, "pdn: %lx\n", pdn); | ||
| 515 | |||
| 516 | ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn])); | ||
| 517 | __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn])); | ||
| 518 | pdir[pdn] = _PDE_VACANT(pdn); | ||
| 519 | FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); | ||
| 520 | flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], | ||
| 521 | as->pdir_page, 1); | ||
| 522 | } | ||
| 523 | } | 189 | } |
| 524 | 190 | ||
| 525 | static void free_pdir(struct smmu_as *as) | 191 | static inline void smmu_flush(struct tegra_smmu *smmu) |
| 526 | { | 192 | { |
| 527 | unsigned addr; | 193 | smmu_readl(smmu, SMMU_CONFIG); |
| 528 | int count; | ||
| 529 | struct device *dev = as->smmu->dev; | ||
| 530 | |||
| 531 | if (!as->pdir_page) | ||
| 532 | return; | ||
| 533 | |||
| 534 | addr = as->smmu->iovmm_base; | ||
| 535 | count = as->smmu->page_count; | ||
| 536 | while (count-- > 0) { | ||
| 537 | free_ptbl(as, addr); | ||
| 538 | addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT; | ||
| 539 | } | ||
| 540 | ClearPageReserved(as->pdir_page); | ||
| 541 | __free_page(as->pdir_page); | ||
| 542 | as->pdir_page = NULL; | ||
| 543 | devm_kfree(dev, as->pte_count); | ||
| 544 | as->pte_count = NULL; | ||
| 545 | } | 194 | } |
| 546 | 195 | ||
| 547 | /* | 196 | static int tegra_smmu_alloc_asid(struct tegra_smmu *smmu, unsigned int *idp) |
| 548 | * Maps PTBL for given iova and returns the PTE address | ||
| 549 | * Caller must unmap the mapped PTBL returned in *ptbl_page_p | ||
| 550 | */ | ||
| 551 | static unsigned long *locate_pte(struct smmu_as *as, | ||
| 552 | dma_addr_t iova, bool allocate, | ||
| 553 | struct page **ptbl_page_p, | ||
| 554 | unsigned int **count) | ||
| 555 | { | 197 | { |
| 556 | unsigned long ptn = SMMU_ADDR_TO_PFN(iova); | 198 | unsigned long id; |
| 557 | unsigned long pdn = SMMU_ADDR_TO_PDN(iova); | ||
| 558 | unsigned long *pdir = page_address(as->pdir_page); | ||
| 559 | unsigned long *ptbl; | ||
| 560 | |||
| 561 | if (pdir[pdn] != _PDE_VACANT(pdn)) { | ||
| 562 | /* Mapped entry table already exists */ | ||
| 563 | *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]); | ||
| 564 | ptbl = page_address(*ptbl_page_p); | ||
| 565 | } else if (!allocate) { | ||
| 566 | return NULL; | ||
| 567 | } else { | ||
| 568 | int pn; | ||
| 569 | unsigned long addr = SMMU_PDN_TO_ADDR(pdn); | ||
| 570 | 199 | ||
| 571 | /* Vacant - allocate a new page table */ | 200 | mutex_lock(&smmu->lock); |
| 572 | dev_dbg(as->smmu->dev, "New PTBL pdn: %lx\n", pdn); | ||
| 573 | 201 | ||
| 574 | *ptbl_page_p = alloc_page(GFP_ATOMIC); | 202 | id = find_first_zero_bit(smmu->asids, smmu->soc->num_asids); |
| 575 | if (!*ptbl_page_p) { | 203 | if (id >= smmu->soc->num_asids) { |
| 576 | dev_err(as->smmu->dev, | 204 | mutex_unlock(&smmu->lock); |
| 577 | "failed to allocate smmu_device page table\n"); | 205 | return -ENOSPC; |
| 578 | return NULL; | ||
| 579 | } | ||
| 580 | SetPageReserved(*ptbl_page_p); | ||
| 581 | ptbl = (unsigned long *)page_address(*ptbl_page_p); | ||
| 582 | for (pn = 0; pn < SMMU_PTBL_COUNT; | ||
| 583 | pn++, addr += SMMU_PAGE_SIZE) { | ||
| 584 | ptbl[pn] = _PTE_VACANT(addr); | ||
| 585 | } | ||
| 586 | FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE); | ||
| 587 | pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p, | ||
| 588 | as->pde_attr | _PDE_NEXT); | ||
| 589 | FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]); | ||
| 590 | flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn], | ||
| 591 | as->pdir_page, 1); | ||
| 592 | } | 206 | } |
| 593 | *count = &as->pte_count[pdn]; | ||
| 594 | 207 | ||
| 595 | return &ptbl[ptn % SMMU_PTBL_COUNT]; | 208 | set_bit(id, smmu->asids); |
| 209 | *idp = id; | ||
| 210 | |||
| 211 | mutex_unlock(&smmu->lock); | ||
| 212 | return 0; | ||
| 596 | } | 213 | } |
| 597 | 214 | ||
| 598 | #ifdef CONFIG_SMMU_SIG_DEBUG | 215 | static void tegra_smmu_free_asid(struct tegra_smmu *smmu, unsigned int id) |
| 599 | static void put_signature(struct smmu_as *as, | ||
| 600 | dma_addr_t iova, unsigned long pfn) | ||
| 601 | { | 216 | { |
| 602 | struct page *page; | 217 | mutex_lock(&smmu->lock); |
| 603 | unsigned long *vaddr; | 218 | clear_bit(id, smmu->asids); |
| 604 | 219 | mutex_unlock(&smmu->lock); | |
| 605 | page = pfn_to_page(pfn); | ||
| 606 | vaddr = page_address(page); | ||
| 607 | if (!vaddr) | ||
| 608 | return; | ||
| 609 | |||
| 610 | vaddr[0] = iova; | ||
| 611 | vaddr[1] = pfn << PAGE_SHIFT; | ||
| 612 | FLUSH_CPU_DCACHE(vaddr, page, sizeof(vaddr[0]) * 2); | ||
| 613 | } | 220 | } |
| 614 | #else | 221 | |
| 615 | static inline void put_signature(struct smmu_as *as, | 222 | static bool tegra_smmu_capable(enum iommu_cap cap) |
| 616 | unsigned long addr, unsigned long pfn) | ||
| 617 | { | 223 | { |
| 224 | return false; | ||
| 618 | } | 225 | } |
| 619 | #endif | ||
| 620 | 226 | ||
| 621 | /* | 227 | static int tegra_smmu_domain_init(struct iommu_domain *domain) |
| 622 | * Caller must not hold as->lock | ||
| 623 | */ | ||
| 624 | static int alloc_pdir(struct smmu_as *as) | ||
| 625 | { | 228 | { |
| 626 | unsigned long *pdir, flags; | 229 | struct tegra_smmu_as *as; |
| 627 | int pdn, err = 0; | 230 | unsigned int i; |
| 628 | u32 val; | 231 | uint32_t *pd; |
| 629 | struct smmu_device *smmu = as->smmu; | ||
| 630 | struct page *page; | ||
| 631 | unsigned int *cnt; | ||
| 632 | 232 | ||
| 633 | /* | 233 | as = kzalloc(sizeof(*as), GFP_KERNEL); |
| 634 | * do the allocation, then grab as->lock | 234 | if (!as) |
| 635 | */ | 235 | return -ENOMEM; |
| 636 | cnt = devm_kzalloc(smmu->dev, | ||
| 637 | sizeof(cnt[0]) * SMMU_PDIR_COUNT, | ||
| 638 | GFP_KERNEL); | ||
| 639 | page = alloc_page(GFP_KERNEL | __GFP_DMA); | ||
| 640 | 236 | ||
| 641 | spin_lock_irqsave(&as->lock, flags); | 237 | as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE; |
| 238 | as->domain = domain; | ||
| 642 | 239 | ||
| 643 | if (as->pdir_page) { | 240 | as->pd = alloc_page(GFP_KERNEL | __GFP_DMA); |
| 644 | /* We raced, free the redundant */ | 241 | if (!as->pd) { |
| 645 | err = -EAGAIN; | 242 | kfree(as); |
| 646 | goto err_out; | 243 | return -ENOMEM; |
| 647 | } | 244 | } |
| 648 | 245 | ||
| 649 | if (!page || !cnt) { | 246 | as->count = alloc_page(GFP_KERNEL); |
| 650 | dev_err(smmu->dev, "failed to allocate at %s\n", __func__); | 247 | if (!as->count) { |
| 651 | err = -ENOMEM; | 248 | __free_page(as->pd); |
| 652 | goto err_out; | 249 | kfree(as); |
| 250 | return -ENOMEM; | ||
| 653 | } | 251 | } |
| 654 | 252 | ||
| 655 | as->pdir_page = page; | 253 | /* clear PDEs */ |
| 656 | as->pte_count = cnt; | 254 | pd = page_address(as->pd); |
| 255 | SetPageReserved(as->pd); | ||
| 657 | 256 | ||
| 658 | SetPageReserved(as->pdir_page); | 257 | for (i = 0; i < SMMU_NUM_PDE; i++) |
| 659 | pdir = page_address(as->pdir_page); | 258 | pd[i] = 0; |
| 660 | 259 | ||
| 661 | for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++) | 260 | /* clear PDE usage counters */ |
| 662 | pdir[pdn] = _PDE_VACANT(pdn); | 261 | pd = page_address(as->count); |
| 663 | FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE); | 262 | SetPageReserved(as->count); |
| 664 | val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pdir, as->pdir_page); | ||
| 665 | smmu_write(smmu, val, SMMU_PTC_FLUSH); | ||
| 666 | FLUSH_SMMU_REGS(as->smmu); | ||
| 667 | val = SMMU_TLB_FLUSH_VA_MATCH_ALL | | ||
| 668 | SMMU_TLB_FLUSH_ASID_MATCH__ENABLE | | ||
| 669 | (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT); | ||
| 670 | smmu_write(smmu, val, SMMU_TLB_FLUSH); | ||
| 671 | FLUSH_SMMU_REGS(as->smmu); | ||
| 672 | 263 | ||
| 673 | spin_unlock_irqrestore(&as->lock, flags); | 264 | for (i = 0; i < SMMU_NUM_PDE; i++) |
| 674 | 265 | pd[i] = 0; | |
| 675 | return 0; | ||
| 676 | 266 | ||
| 677 | err_out: | 267 | domain->priv = as; |
| 678 | spin_unlock_irqrestore(&as->lock, flags); | ||
| 679 | 268 | ||
| 680 | devm_kfree(smmu->dev, cnt); | 269 | return 0; |
| 681 | if (page) | ||
| 682 | __free_page(page); | ||
| 683 | return err; | ||
| 684 | } | 270 | } |
| 685 | 271 | ||
| 686 | static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) | 272 | static void tegra_smmu_domain_destroy(struct iommu_domain *domain) |
| 687 | { | 273 | { |
| 688 | unsigned long *pte; | 274 | struct tegra_smmu_as *as = domain->priv; |
| 689 | struct page *page; | ||
| 690 | unsigned int *count; | ||
| 691 | 275 | ||
| 692 | pte = locate_pte(as, iova, false, &page, &count); | 276 | /* TODO: free page directory and page tables */ |
| 693 | if (WARN_ON(!pte)) | 277 | ClearPageReserved(as->pd); |
| 694 | return; | ||
| 695 | 278 | ||
| 696 | if (WARN_ON(*pte == _PTE_VACANT(iova))) | 279 | kfree(as); |
| 697 | return; | ||
| 698 | |||
| 699 | *pte = _PTE_VACANT(iova); | ||
| 700 | FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); | ||
| 701 | flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); | ||
| 702 | if (!--(*count)) | ||
| 703 | free_ptbl(as, iova); | ||
| 704 | } | 280 | } |
| 705 | 281 | ||
| 706 | static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, | 282 | static const struct tegra_smmu_swgroup * |
| 707 | unsigned long pfn) | 283 | tegra_smmu_find_swgroup(struct tegra_smmu *smmu, unsigned int swgroup) |
| 708 | { | 284 | { |
| 709 | struct smmu_device *smmu = as->smmu; | 285 | const struct tegra_smmu_swgroup *group = NULL; |
| 710 | unsigned long *pte; | 286 | unsigned int i; |
| 711 | unsigned int *count; | ||
| 712 | struct page *page; | ||
| 713 | 287 | ||
| 714 | pte = locate_pte(as, iova, true, &page, &count); | 288 | for (i = 0; i < smmu->soc->num_swgroups; i++) { |
| 715 | if (WARN_ON(!pte)) | 289 | if (smmu->soc->swgroups[i].swgroup == swgroup) { |
| 716 | return; | 290 | group = &smmu->soc->swgroups[i]; |
| 291 | break; | ||
| 292 | } | ||
| 293 | } | ||
| 717 | 294 | ||
| 718 | if (*pte == _PTE_VACANT(iova)) | 295 | return group; |
| 719 | (*count)++; | ||
| 720 | *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr); | ||
| 721 | if (unlikely((*pte == _PTE_VACANT(iova)))) | ||
| 722 | (*count)--; | ||
| 723 | FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); | ||
| 724 | flush_ptc_and_tlb(smmu, as, iova, pte, page, 0); | ||
| 725 | put_signature(as, iova, pfn); | ||
| 726 | } | 296 | } |
| 727 | 297 | ||
| 728 | static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova, | 298 | static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup, |
| 729 | phys_addr_t pa, size_t bytes, int prot) | 299 | unsigned int asid) |
| 730 | { | 300 | { |
| 731 | struct smmu_as *as = domain->priv; | 301 | const struct tegra_smmu_swgroup *group; |
| 732 | unsigned long pfn = __phys_to_pfn(pa); | 302 | unsigned int i; |
| 733 | unsigned long flags; | 303 | u32 value; |
| 734 | 304 | ||
| 735 | dev_dbg(as->smmu->dev, "[%d] %08lx:%pa\n", as->asid, iova, &pa); | 305 | for (i = 0; i < smmu->soc->num_clients; i++) { |
| 306 | const struct tegra_mc_client *client = &smmu->soc->clients[i]; | ||
| 736 | 307 | ||
| 737 | if (!pfn_valid(pfn)) | 308 | if (client->swgroup != swgroup) |
| 738 | return -ENOMEM; | 309 | continue; |
| 739 | |||
| 740 | spin_lock_irqsave(&as->lock, flags); | ||
| 741 | __smmu_iommu_map_pfn(as, iova, pfn); | ||
| 742 | spin_unlock_irqrestore(&as->lock, flags); | ||
| 743 | return 0; | ||
| 744 | } | ||
| 745 | |||
| 746 | static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova, | ||
| 747 | size_t bytes) | ||
| 748 | { | ||
| 749 | struct smmu_as *as = domain->priv; | ||
| 750 | unsigned long flags; | ||
| 751 | 310 | ||
| 752 | dev_dbg(as->smmu->dev, "[%d] %08lx\n", as->asid, iova); | 311 | value = smmu_readl(smmu, client->smmu.reg); |
| 312 | value |= BIT(client->smmu.bit); | ||
| 313 | smmu_writel(smmu, value, client->smmu.reg); | ||
| 314 | } | ||
| 753 | 315 | ||
| 754 | spin_lock_irqsave(&as->lock, flags); | 316 | group = tegra_smmu_find_swgroup(smmu, swgroup); |
| 755 | __smmu_iommu_unmap(as, iova); | 317 | if (group) { |
| 756 | spin_unlock_irqrestore(&as->lock, flags); | 318 | value = smmu_readl(smmu, group->reg); |
| 757 | return SMMU_PAGE_SIZE; | 319 | value &= ~SMMU_ASID_MASK; |
| 320 | value |= SMMU_ASID_VALUE(asid); | ||
| 321 | value |= SMMU_ASID_ENABLE; | ||
| 322 | smmu_writel(smmu, value, group->reg); | ||
| 323 | } | ||
| 758 | } | 324 | } |
| 759 | 325 | ||
| 760 | static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, | 326 | static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup, |
| 761 | dma_addr_t iova) | 327 | unsigned int asid) |
| 762 | { | 328 | { |
| 763 | struct smmu_as *as = domain->priv; | 329 | const struct tegra_smmu_swgroup *group; |
| 764 | unsigned long *pte; | 330 | unsigned int i; |
| 765 | unsigned int *count; | 331 | u32 value; |
| 766 | struct page *page; | ||
| 767 | unsigned long pfn; | ||
| 768 | unsigned long flags; | ||
| 769 | 332 | ||
| 770 | spin_lock_irqsave(&as->lock, flags); | 333 | group = tegra_smmu_find_swgroup(smmu, swgroup); |
| 334 | if (group) { | ||
| 335 | value = smmu_readl(smmu, group->reg); | ||
| 336 | value &= ~SMMU_ASID_MASK; | ||
| 337 | value |= SMMU_ASID_VALUE(asid); | ||
| 338 | value &= ~SMMU_ASID_ENABLE; | ||
| 339 | smmu_writel(smmu, value, group->reg); | ||
| 340 | } | ||
| 771 | 341 | ||
| 772 | pte = locate_pte(as, iova, true, &page, &count); | 342 | for (i = 0; i < smmu->soc->num_clients; i++) { |
| 773 | pfn = *pte & SMMU_PFN_MASK; | 343 | const struct tegra_mc_client *client = &smmu->soc->clients[i]; |
| 774 | WARN_ON(!pfn_valid(pfn)); | ||
| 775 | dev_dbg(as->smmu->dev, | ||
| 776 | "iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova, | ||
| 777 | pfn, as->asid); | ||
| 778 | 344 | ||
| 779 | spin_unlock_irqrestore(&as->lock, flags); | 345 | if (client->swgroup != swgroup) |
| 780 | return PFN_PHYS(pfn); | 346 | continue; |
| 781 | } | ||
| 782 | 347 | ||
| 783 | static bool smmu_iommu_capable(enum iommu_cap cap) | 348 | value = smmu_readl(smmu, client->smmu.reg); |
| 784 | { | 349 | value &= ~BIT(client->smmu.bit); |
| 785 | return false; | 350 | smmu_writel(smmu, value, client->smmu.reg); |
| 351 | } | ||
| 786 | } | 352 | } |
| 787 | 353 | ||
| 788 | static int smmu_iommu_attach_dev(struct iommu_domain *domain, | 354 | static int tegra_smmu_as_prepare(struct tegra_smmu *smmu, |
| 789 | struct device *dev) | 355 | struct tegra_smmu_as *as) |
| 790 | { | 356 | { |
| 791 | struct smmu_as *as = domain->priv; | 357 | u32 value; |
| 792 | struct smmu_device *smmu = as->smmu; | ||
| 793 | struct smmu_client *client, *c; | ||
| 794 | u32 map; | ||
| 795 | int err; | 358 | int err; |
| 796 | 359 | ||
| 797 | client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL); | 360 | if (as->use_count > 0) { |
| 798 | if (!client) | 361 | as->use_count++; |
| 799 | return -ENOMEM; | 362 | return 0; |
| 800 | client->dev = dev; | ||
| 801 | client->as = as; | ||
| 802 | map = (unsigned long)dev->platform_data; | ||
| 803 | if (!map) | ||
| 804 | return -EINVAL; | ||
| 805 | |||
| 806 | err = smmu_client_enable_hwgrp(client, map); | ||
| 807 | if (err) | ||
| 808 | goto err_hwgrp; | ||
| 809 | |||
| 810 | spin_lock(&as->client_lock); | ||
| 811 | list_for_each_entry(c, &as->client, list) { | ||
| 812 | if (c->dev == dev) { | ||
| 813 | dev_err(smmu->dev, | ||
| 814 | "%s is already attached\n", dev_name(c->dev)); | ||
| 815 | err = -EINVAL; | ||
| 816 | goto err_client; | ||
| 817 | } | ||
| 818 | } | 363 | } |
| 819 | list_add(&client->list, &as->client); | ||
| 820 | spin_unlock(&as->client_lock); | ||
| 821 | 364 | ||
| 822 | /* | 365 | err = tegra_smmu_alloc_asid(smmu, &as->id); |
| 823 | * Reserve "page zero" for AVP vectors using a common dummy | 366 | if (err < 0) |
| 824 | * page. | 367 | return err; |
| 825 | */ | ||
| 826 | if (map & HWG_AVPC) { | ||
| 827 | struct page *page; | ||
| 828 | 368 | ||
| 829 | page = as->smmu->avp_vector_page; | 369 | smmu->soc->ops->flush_dcache(as->pd, 0, SMMU_SIZE_PD); |
| 830 | __smmu_iommu_map_pfn(as, 0, page_to_pfn(page)); | 370 | smmu_flush_ptc(smmu, as->pd, 0); |
| 371 | smmu_flush_tlb_asid(smmu, as->id); | ||
| 831 | 372 | ||
| 832 | pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n"); | 373 | smmu_writel(smmu, as->id & 0x7f, SMMU_PTB_ASID); |
| 833 | } | 374 | value = SMMU_PTB_DATA_VALUE(as->pd, as->attr); |
| 375 | smmu_writel(smmu, value, SMMU_PTB_DATA); | ||
| 376 | smmu_flush(smmu); | ||
| 834 | 377 | ||
| 835 | dev_dbg(smmu->dev, "%s is attached\n", dev_name(dev)); | 378 | as->smmu = smmu; |
| 836 | return 0; | 379 | as->use_count++; |
| 837 | 380 | ||
| 838 | err_client: | 381 | return 0; |
| 839 | smmu_client_disable_hwgrp(client); | ||
| 840 | spin_unlock(&as->client_lock); | ||
| 841 | err_hwgrp: | ||
| 842 | devm_kfree(smmu->dev, client); | ||
| 843 | return err; | ||
| 844 | } | 382 | } |
| 845 | 383 | ||
| 846 | static void smmu_iommu_detach_dev(struct iommu_domain *domain, | 384 | static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu, |
| 847 | struct device *dev) | 385 | struct tegra_smmu_as *as) |
| 848 | { | 386 | { |
| 849 | struct smmu_as *as = domain->priv; | 387 | if (--as->use_count > 0) |
| 850 | struct smmu_device *smmu = as->smmu; | 388 | return; |
| 851 | struct smmu_client *c; | 389 | |
| 852 | 390 | tegra_smmu_free_asid(smmu, as->id); | |
| 853 | spin_lock(&as->client_lock); | 391 | as->smmu = NULL; |
| 854 | |||
| 855 | list_for_each_entry(c, &as->client, list) { | ||
| 856 | if (c->dev == dev) { | ||
| 857 | smmu_client_disable_hwgrp(c); | ||
| 858 | list_del(&c->list); | ||
| 859 | devm_kfree(smmu->dev, c); | ||
| 860 | c->as = NULL; | ||
| 861 | dev_dbg(smmu->dev, | ||
| 862 | "%s is detached\n", dev_name(c->dev)); | ||
| 863 | goto out; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev)); | ||
| 867 | out: | ||
| 868 | spin_unlock(&as->client_lock); | ||
| 869 | } | 392 | } |
| 870 | 393 | ||
| 871 | static int smmu_iommu_domain_init(struct iommu_domain *domain) | 394 | static int tegra_smmu_attach_dev(struct iommu_domain *domain, |
| 395 | struct device *dev) | ||
| 872 | { | 396 | { |
| 873 | int i, err = -EAGAIN; | 397 | struct tegra_smmu *smmu = dev->archdata.iommu; |
| 874 | unsigned long flags; | 398 | struct tegra_smmu_as *as = domain->priv; |
| 875 | struct smmu_as *as; | 399 | struct device_node *np = dev->of_node; |
| 876 | struct smmu_device *smmu = smmu_handle; | 400 | struct of_phandle_args args; |
| 401 | unsigned int index = 0; | ||
| 402 | int err = 0; | ||
| 877 | 403 | ||
| 878 | /* Look for a free AS with lock held */ | 404 | while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, |
| 879 | for (i = 0; i < smmu->num_as; i++) { | 405 | &args)) { |
| 880 | as = &smmu->as[i]; | 406 | unsigned int swgroup = args.args[0]; |
| 881 | 407 | ||
| 882 | if (as->pdir_page) | 408 | if (args.np != smmu->dev->of_node) { |
| 409 | of_node_put(args.np); | ||
| 883 | continue; | 410 | continue; |
| 411 | } | ||
| 884 | 412 | ||
| 885 | err = alloc_pdir(as); | 413 | of_node_put(args.np); |
| 886 | if (!err) | ||
| 887 | goto found; | ||
| 888 | 414 | ||
| 889 | if (err != -EAGAIN) | 415 | err = tegra_smmu_as_prepare(smmu, as); |
| 890 | break; | 416 | if (err < 0) |
| 417 | return err; | ||
| 418 | |||
| 419 | tegra_smmu_enable(smmu, swgroup, as->id); | ||
| 420 | index++; | ||
| 891 | } | 421 | } |
| 892 | if (i == smmu->num_as) | ||
| 893 | dev_err(smmu->dev, "no free AS\n"); | ||
| 894 | return err; | ||
| 895 | 422 | ||
| 896 | found: | 423 | if (index == 0) |
| 897 | spin_lock_irqsave(&smmu->lock, flags); | 424 | return -ENODEV; |
| 898 | 425 | ||
| 899 | /* Update PDIR register */ | 426 | return 0; |
| 900 | smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); | 427 | } |
| 901 | smmu_write(smmu, | ||
| 902 | SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); | ||
| 903 | FLUSH_SMMU_REGS(smmu); | ||
| 904 | 428 | ||
| 905 | spin_unlock_irqrestore(&smmu->lock, flags); | 429 | static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) |
| 430 | { | ||
| 431 | struct tegra_smmu_as *as = domain->priv; | ||
| 432 | struct device_node *np = dev->of_node; | ||
| 433 | struct tegra_smmu *smmu = as->smmu; | ||
| 434 | struct of_phandle_args args; | ||
| 435 | unsigned int index = 0; | ||
| 906 | 436 | ||
| 907 | domain->priv = as; | 437 | while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, |
| 438 | &args)) { | ||
| 439 | unsigned int swgroup = args.args[0]; | ||
| 908 | 440 | ||
| 909 | domain->geometry.aperture_start = smmu->iovmm_base; | 441 | if (args.np != smmu->dev->of_node) { |
| 910 | domain->geometry.aperture_end = smmu->iovmm_base + | 442 | of_node_put(args.np); |
| 911 | smmu->page_count * SMMU_PAGE_SIZE - 1; | 443 | continue; |
| 912 | domain->geometry.force_aperture = true; | 444 | } |
| 913 | 445 | ||
| 914 | dev_dbg(smmu->dev, "smmu_as@%p\n", as); | 446 | of_node_put(args.np); |
| 915 | 447 | ||
| 916 | return 0; | 448 | tegra_smmu_disable(smmu, swgroup, as->id); |
| 449 | tegra_smmu_as_unprepare(smmu, as); | ||
| 450 | index++; | ||
| 451 | } | ||
| 917 | } | 452 | } |
| 918 | 453 | ||
| 919 | static void smmu_iommu_domain_destroy(struct iommu_domain *domain) | 454 | static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, |
| 455 | struct page **pagep) | ||
| 920 | { | 456 | { |
| 921 | struct smmu_as *as = domain->priv; | 457 | u32 *pd = page_address(as->pd), *pt, *count; |
| 922 | struct smmu_device *smmu = as->smmu; | 458 | u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff; |
| 923 | unsigned long flags; | 459 | u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff; |
| 460 | struct tegra_smmu *smmu = as->smmu; | ||
| 461 | struct page *page; | ||
| 462 | unsigned int i; | ||
| 463 | |||
| 464 | if (pd[pde] == 0) { | ||
| 465 | page = alloc_page(GFP_KERNEL | __GFP_DMA); | ||
| 466 | if (!page) | ||
| 467 | return NULL; | ||
| 924 | 468 | ||
| 925 | spin_lock_irqsave(&as->lock, flags); | 469 | pt = page_address(page); |
| 470 | SetPageReserved(page); | ||
| 926 | 471 | ||
| 927 | if (as->pdir_page) { | 472 | for (i = 0; i < SMMU_NUM_PTE; i++) |
| 928 | spin_lock(&smmu->lock); | 473 | pt[i] = 0; |
| 929 | smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); | ||
| 930 | smmu_write(smmu, SMMU_PTB_DATA_RESET_VAL, SMMU_PTB_DATA); | ||
| 931 | FLUSH_SMMU_REGS(smmu); | ||
| 932 | spin_unlock(&smmu->lock); | ||
| 933 | 474 | ||
| 934 | free_pdir(as); | 475 | smmu->soc->ops->flush_dcache(page, 0, SMMU_SIZE_PT); |
| 935 | } | ||
| 936 | 476 | ||
| 937 | if (!list_empty(&as->client)) { | 477 | pd[pde] = SMMU_MK_PDE(page, SMMU_PDE_ATTR | SMMU_PDE_NEXT); |
| 938 | struct smmu_client *c; | ||
| 939 | 478 | ||
| 940 | list_for_each_entry(c, &as->client, list) | 479 | smmu->soc->ops->flush_dcache(as->pd, pde << 2, 4); |
| 941 | smmu_iommu_detach_dev(domain, c->dev); | 480 | smmu_flush_ptc(smmu, as->pd, pde << 2); |
| 481 | smmu_flush_tlb_section(smmu, as->id, iova); | ||
| 482 | smmu_flush(smmu); | ||
| 483 | } else { | ||
| 484 | page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); | ||
| 485 | pt = page_address(page); | ||
| 942 | } | 486 | } |
| 943 | 487 | ||
| 944 | spin_unlock_irqrestore(&as->lock, flags); | 488 | *pagep = page; |
| 945 | 489 | ||
| 946 | domain->priv = NULL; | 490 | /* Keep track of entries in this page table. */ |
| 947 | dev_dbg(smmu->dev, "smmu_as@%p\n", as); | 491 | count = page_address(as->count); |
| 948 | } | 492 | if (pt[pte] == 0) |
| 493 | count[pde]++; | ||
| 949 | 494 | ||
| 950 | static const struct iommu_ops smmu_iommu_ops = { | 495 | return &pt[pte]; |
| 951 | .capable = smmu_iommu_capable, | 496 | } |
| 952 | .domain_init = smmu_iommu_domain_init, | ||
| 953 | .domain_destroy = smmu_iommu_domain_destroy, | ||
| 954 | .attach_dev = smmu_iommu_attach_dev, | ||
| 955 | .detach_dev = smmu_iommu_detach_dev, | ||
| 956 | .map = smmu_iommu_map, | ||
| 957 | .unmap = smmu_iommu_unmap, | ||
| 958 | .iova_to_phys = smmu_iommu_iova_to_phys, | ||
| 959 | .pgsize_bitmap = SMMU_IOMMU_PGSIZES, | ||
| 960 | }; | ||
| 961 | |||
| 962 | /* Should be in the order of enum */ | ||
| 963 | static const char * const smmu_debugfs_mc[] = { "mc", }; | ||
| 964 | static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", }; | ||
| 965 | 497 | ||
| 966 | static ssize_t smmu_debugfs_stats_write(struct file *file, | 498 | static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) |
| 967 | const char __user *buffer, | ||
| 968 | size_t count, loff_t *pos) | ||
| 969 | { | 499 | { |
| 970 | struct smmu_debugfs_info *info; | 500 | u32 pde = (iova >> SMMU_PDE_SHIFT) & 0x3ff; |
| 971 | struct smmu_device *smmu; | 501 | u32 pte = (iova >> SMMU_PTE_SHIFT) & 0x3ff; |
| 972 | int i; | 502 | u32 *count = page_address(as->count); |
| 973 | enum { | 503 | u32 *pd = page_address(as->pd), *pt; |
| 974 | _OFF = 0, | 504 | struct page *page; |
| 975 | _ON, | ||
| 976 | _RESET, | ||
| 977 | }; | ||
| 978 | const char * const command[] = { | ||
| 979 | [_OFF] = "off", | ||
| 980 | [_ON] = "on", | ||
| 981 | [_RESET] = "reset", | ||
| 982 | }; | ||
| 983 | char str[] = "reset"; | ||
| 984 | u32 val; | ||
| 985 | size_t offs; | ||
| 986 | 505 | ||
| 987 | count = min_t(size_t, count, sizeof(str)); | 506 | page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); |
| 988 | if (copy_from_user(str, buffer, count)) | 507 | pt = page_address(page); |
| 989 | return -EINVAL; | ||
| 990 | 508 | ||
| 991 | for (i = 0; i < ARRAY_SIZE(command); i++) | 509 | /* |
| 992 | if (strncmp(str, command[i], | 510 | * When no entries in this page table are used anymore, return the |
| 993 | strlen(command[i])) == 0) | 511 | * memory page to the system. |
| 994 | break; | 512 | */ |
| 513 | if (pt[pte] != 0) { | ||
| 514 | if (--count[pde] == 0) { | ||
| 515 | ClearPageReserved(page); | ||
| 516 | __free_page(page); | ||
| 517 | pd[pde] = 0; | ||
| 518 | } | ||
| 995 | 519 | ||
| 996 | if (i == ARRAY_SIZE(command)) | 520 | pt[pte] = 0; |
| 997 | return -EINVAL; | ||
| 998 | |||
| 999 | info = file_inode(file)->i_private; | ||
| 1000 | smmu = info->smmu; | ||
| 1001 | |||
| 1002 | offs = SMMU_CACHE_CONFIG(info->cache); | ||
| 1003 | val = smmu_read(smmu, offs); | ||
| 1004 | switch (i) { | ||
| 1005 | case _OFF: | ||
| 1006 | val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE; | ||
| 1007 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
| 1008 | smmu_write(smmu, val, offs); | ||
| 1009 | break; | ||
| 1010 | case _ON: | ||
| 1011 | val |= SMMU_CACHE_CONFIG_STATS_ENABLE; | ||
| 1012 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
| 1013 | smmu_write(smmu, val, offs); | ||
| 1014 | break; | ||
| 1015 | case _RESET: | ||
| 1016 | val |= SMMU_CACHE_CONFIG_STATS_TEST; | ||
| 1017 | smmu_write(smmu, val, offs); | ||
| 1018 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
| 1019 | smmu_write(smmu, val, offs); | ||
| 1020 | break; | ||
| 1021 | default: | ||
| 1022 | BUG(); | ||
| 1023 | break; | ||
| 1024 | } | 521 | } |
| 1025 | |||
| 1026 | dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__, | ||
| 1027 | val, smmu_read(smmu, offs), offs); | ||
| 1028 | |||
| 1029 | return count; | ||
| 1030 | } | 522 | } |
| 1031 | 523 | ||
| 1032 | static int smmu_debugfs_stats_show(struct seq_file *s, void *v) | 524 | static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, |
| 525 | phys_addr_t paddr, size_t size, int prot) | ||
| 1033 | { | 526 | { |
| 1034 | struct smmu_debugfs_info *info = s->private; | 527 | struct tegra_smmu_as *as = domain->priv; |
| 1035 | struct smmu_device *smmu = info->smmu; | 528 | struct tegra_smmu *smmu = as->smmu; |
| 1036 | int i; | 529 | unsigned long offset; |
| 1037 | const char * const stats[] = { "hit", "miss", }; | 530 | struct page *page; |
| 531 | u32 *pte; | ||
| 1038 | 532 | ||
| 533 | pte = as_get_pte(as, iova, &page); | ||
| 534 | if (!pte) | ||
| 535 | return -ENOMEM; | ||
| 1039 | 536 | ||
| 1040 | for (i = 0; i < ARRAY_SIZE(stats); i++) { | 537 | *pte = __phys_to_pfn(paddr) | SMMU_PTE_ATTR; |
| 1041 | u32 val; | 538 | offset = offset_in_page(pte); |
| 1042 | size_t offs; | ||
| 1043 | 539 | ||
| 1044 | offs = SMMU_STATS_CACHE_COUNT(info->mc, info->cache, i); | 540 | smmu->soc->ops->flush_dcache(page, offset, 4); |
| 1045 | val = smmu_read(smmu, offs); | 541 | smmu_flush_ptc(smmu, page, offset); |
| 1046 | seq_printf(s, "%s:%08x ", stats[i], val); | 542 | smmu_flush_tlb_group(smmu, as->id, iova); |
| 543 | smmu_flush(smmu); | ||
| 1047 | 544 | ||
| 1048 | dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__, | ||
| 1049 | stats[i], val, offs); | ||
| 1050 | } | ||
| 1051 | seq_printf(s, "\n"); | ||
| 1052 | return 0; | 545 | return 0; |
| 1053 | } | 546 | } |
| 1054 | 547 | ||
| 1055 | static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) | 548 | static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, |
| 549 | size_t size) | ||
| 1056 | { | 550 | { |
| 1057 | return single_open(file, smmu_debugfs_stats_show, inode->i_private); | 551 | struct tegra_smmu_as *as = domain->priv; |
| 1058 | } | 552 | struct tegra_smmu *smmu = as->smmu; |
| 553 | unsigned long offset; | ||
| 554 | struct page *page; | ||
| 555 | u32 *pte; | ||
| 1059 | 556 | ||
| 1060 | static const struct file_operations smmu_debugfs_stats_fops = { | 557 | pte = as_get_pte(as, iova, &page); |
| 1061 | .open = smmu_debugfs_stats_open, | 558 | if (!pte) |
| 1062 | .read = seq_read, | 559 | return 0; |
| 1063 | .llseek = seq_lseek, | ||
| 1064 | .release = single_release, | ||
| 1065 | .write = smmu_debugfs_stats_write, | ||
| 1066 | }; | ||
| 1067 | 560 | ||
| 1068 | static void smmu_debugfs_delete(struct smmu_device *smmu) | 561 | offset = offset_in_page(pte); |
| 1069 | { | 562 | as_put_pte(as, iova); |
| 1070 | debugfs_remove_recursive(smmu->debugfs_root); | 563 | |
| 1071 | kfree(smmu->debugfs_info); | 564 | smmu->soc->ops->flush_dcache(page, offset, 4); |
| 565 | smmu_flush_ptc(smmu, page, offset); | ||
| 566 | smmu_flush_tlb_group(smmu, as->id, iova); | ||
| 567 | smmu_flush(smmu); | ||
| 568 | |||
| 569 | return size; | ||
| 1072 | } | 570 | } |
| 1073 | 571 | ||
| 1074 | static void smmu_debugfs_create(struct smmu_device *smmu) | 572 | static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, |
| 573 | dma_addr_t iova) | ||
| 1075 | { | 574 | { |
| 1076 | int i; | 575 | struct tegra_smmu_as *as = domain->priv; |
| 1077 | size_t bytes; | 576 | struct page *page; |
| 1078 | struct dentry *root; | 577 | unsigned long pfn; |
| 1079 | 578 | u32 *pte; | |
| 1080 | bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) * | ||
| 1081 | sizeof(*smmu->debugfs_info); | ||
| 1082 | smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL); | ||
| 1083 | if (!smmu->debugfs_info) | ||
| 1084 | return; | ||
| 1085 | |||
| 1086 | root = debugfs_create_dir(dev_name(smmu->dev), NULL); | ||
| 1087 | if (!root) | ||
| 1088 | goto err_out; | ||
| 1089 | smmu->debugfs_root = root; | ||
| 1090 | |||
| 1091 | for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { | ||
| 1092 | int j; | ||
| 1093 | struct dentry *mc; | ||
| 1094 | |||
| 1095 | mc = debugfs_create_dir(smmu_debugfs_mc[i], root); | ||
| 1096 | if (!mc) | ||
| 1097 | goto err_out; | ||
| 1098 | |||
| 1099 | for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { | ||
| 1100 | struct dentry *cache; | ||
| 1101 | struct smmu_debugfs_info *info; | ||
| 1102 | |||
| 1103 | info = smmu->debugfs_info; | ||
| 1104 | info += i * ARRAY_SIZE(smmu_debugfs_mc) + j; | ||
| 1105 | info->smmu = smmu; | ||
| 1106 | info->mc = i; | ||
| 1107 | info->cache = j; | ||
| 1108 | |||
| 1109 | cache = debugfs_create_file(smmu_debugfs_cache[j], | ||
| 1110 | S_IWUGO | S_IRUGO, mc, | ||
| 1111 | (void *)info, | ||
| 1112 | &smmu_debugfs_stats_fops); | ||
| 1113 | if (!cache) | ||
| 1114 | goto err_out; | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | 579 | ||
| 1118 | return; | 580 | pte = as_get_pte(as, iova, &page); |
| 581 | pfn = *pte & SMMU_PFN_MASK; | ||
| 1119 | 582 | ||
| 1120 | err_out: | 583 | return PFN_PHYS(pfn); |
| 1121 | smmu_debugfs_delete(smmu); | ||
| 1122 | } | 584 | } |
| 1123 | 585 | ||
| 1124 | static int tegra_smmu_suspend(struct device *dev) | 586 | static struct tegra_smmu *tegra_smmu_find(struct device_node *np) |
| 1125 | { | 587 | { |
| 1126 | struct smmu_device *smmu = dev_get_drvdata(dev); | 588 | struct platform_device *pdev; |
| 589 | struct tegra_mc *mc; | ||
| 1127 | 590 | ||
| 1128 | smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0); | 591 | pdev = of_find_device_by_node(np); |
| 1129 | smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1); | 592 | if (!pdev) |
| 1130 | smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2); | 593 | return NULL; |
| 1131 | smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY); | 594 | |
| 1132 | return 0; | 595 | mc = platform_get_drvdata(pdev); |
| 596 | if (!mc) | ||
| 597 | return NULL; | ||
| 598 | |||
| 599 | return mc->smmu; | ||
| 1133 | } | 600 | } |
| 1134 | 601 | ||
| 1135 | static int tegra_smmu_resume(struct device *dev) | 602 | static int tegra_smmu_add_device(struct device *dev) |
| 1136 | { | 603 | { |
| 1137 | struct smmu_device *smmu = dev_get_drvdata(dev); | 604 | struct device_node *np = dev->of_node; |
| 1138 | unsigned long flags; | 605 | struct of_phandle_args args; |
| 1139 | int err; | 606 | unsigned int index = 0; |
| 1140 | 607 | ||
| 1141 | spin_lock_irqsave(&smmu->lock, flags); | 608 | while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, |
| 1142 | err = smmu_setup_regs(smmu); | 609 | &args) == 0) { |
| 1143 | spin_unlock_irqrestore(&smmu->lock, flags); | 610 | struct tegra_smmu *smmu; |
| 1144 | return err; | 611 | |
| 612 | smmu = tegra_smmu_find(args.np); | ||
| 613 | if (smmu) { | ||
| 614 | /* | ||
| 615 | * Only a single IOMMU master interface is currently | ||
| 616 | * supported by the Linux kernel, so abort after the | ||
| 617 | * first match. | ||
| 618 | */ | ||
| 619 | dev->archdata.iommu = smmu; | ||
| 620 | break; | ||
| 621 | } | ||
| 622 | |||
| 623 | index++; | ||
| 624 | } | ||
| 625 | |||
| 626 | return 0; | ||
| 1145 | } | 627 | } |
| 1146 | 628 | ||
| 1147 | static int tegra_smmu_probe(struct platform_device *pdev) | 629 | static void tegra_smmu_remove_device(struct device *dev) |
| 1148 | { | 630 | { |
| 1149 | struct smmu_device *smmu; | 631 | dev->archdata.iommu = NULL; |
| 1150 | struct device *dev = &pdev->dev; | 632 | } |
| 1151 | int i, asids, err = 0; | ||
| 1152 | dma_addr_t uninitialized_var(base); | ||
| 1153 | size_t bytes, uninitialized_var(size); | ||
| 1154 | 633 | ||
| 1155 | if (smmu_handle) | 634 | static const struct iommu_ops tegra_smmu_ops = { |
| 1156 | return -EIO; | 635 | .capable = tegra_smmu_capable, |
| 636 | .domain_init = tegra_smmu_domain_init, | ||
| 637 | .domain_destroy = tegra_smmu_domain_destroy, | ||
| 638 | .attach_dev = tegra_smmu_attach_dev, | ||
| 639 | .detach_dev = tegra_smmu_detach_dev, | ||
| 640 | .add_device = tegra_smmu_add_device, | ||
| 641 | .remove_device = tegra_smmu_remove_device, | ||
| 642 | .map = tegra_smmu_map, | ||
| 643 | .unmap = tegra_smmu_unmap, | ||
| 644 | .map_sg = default_iommu_map_sg, | ||
| 645 | .iova_to_phys = tegra_smmu_iova_to_phys, | ||
| 1157 | 646 | ||
| 1158 | BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); | 647 | .pgsize_bitmap = SZ_4K, |
| 648 | }; | ||
| 1159 | 649 | ||
| 1160 | if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) | 650 | static void tegra_smmu_ahb_enable(void) |
| 1161 | return -ENODEV; | 651 | { |
| 652 | static const struct of_device_id ahb_match[] = { | ||
| 653 | { .compatible = "nvidia,tegra30-ahb", }, | ||
| 654 | { } | ||
| 655 | }; | ||
| 656 | struct device_node *ahb; | ||
| 1162 | 657 | ||
| 1163 | bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); | 658 | ahb = of_find_matching_node(NULL, ahb_match); |
| 1164 | smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); | 659 | if (ahb) { |
| 1165 | if (!smmu) { | 660 | tegra_ahb_enable_smmu(ahb); |
| 1166 | dev_err(dev, "failed to allocate smmu_device\n"); | 661 | of_node_put(ahb); |
| 1167 | return -ENOMEM; | ||
| 1168 | } | 662 | } |
| 663 | } | ||
| 1169 | 664 | ||
| 1170 | smmu->nregs = pdev->num_resources; | 665 | struct tegra_smmu *tegra_smmu_probe(struct device *dev, |
| 1171 | smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs), | 666 | const struct tegra_smmu_soc *soc, |
| 1172 | GFP_KERNEL); | 667 | struct tegra_mc *mc) |
| 1173 | smmu->rege = smmu->regs + smmu->nregs; | 668 | { |
| 1174 | if (!smmu->regs) | 669 | struct tegra_smmu *smmu; |
| 1175 | return -ENOMEM; | 670 | size_t size; |
| 1176 | for (i = 0; i < smmu->nregs; i++) { | 671 | u32 value; |
| 1177 | struct resource *res; | 672 | int err; |
| 1178 | |||
| 1179 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
| 1180 | smmu->regs[i] = devm_ioremap_resource(&pdev->dev, res); | ||
| 1181 | if (IS_ERR(smmu->regs[i])) | ||
| 1182 | return PTR_ERR(smmu->regs[i]); | ||
| 1183 | smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; | ||
| 1184 | } | ||
| 1185 | /* Same as "mc" 1st regiter block start address */ | ||
| 1186 | smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); | ||
| 1187 | 673 | ||
| 1188 | err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); | 674 | /* This can happen on Tegra20 which doesn't have an SMMU */ |
| 1189 | if (err) | 675 | if (!soc) |
| 1190 | return -ENODEV; | 676 | return NULL; |
| 1191 | 677 | ||
| 1192 | if (size & SMMU_PAGE_MASK) | 678 | smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); |
| 1193 | return -EINVAL; | 679 | if (!smmu) |
| 680 | return ERR_PTR(-ENOMEM); | ||
| 1194 | 681 | ||
| 1195 | size >>= SMMU_PAGE_SHIFT; | 682 | /* |
| 1196 | if (!size) | 683 | * This is a bit of a hack. Ideally we'd want to simply return this |
| 1197 | return -EINVAL; | 684 | * value. However the IOMMU registration process will attempt to add |
| 685 | * all devices to the IOMMU when bus_set_iommu() is called. In order | ||
| 686 | * not to rely on global variables to track the IOMMU instance, we | ||
| 687 | * set it here so that it can be looked up from the .add_device() | ||
| 688 | * callback via the IOMMU device's .drvdata field. | ||
| 689 | */ | ||
| 690 | mc->smmu = smmu; | ||
| 1198 | 691 | ||
| 1199 | smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); | 692 | size = BITS_TO_LONGS(soc->num_asids) * sizeof(long); |
| 1200 | if (!smmu->ahb) | ||
| 1201 | return -ENODEV; | ||
| 1202 | 693 | ||
| 1203 | smmu->dev = dev; | 694 | smmu->asids = devm_kzalloc(dev, size, GFP_KERNEL); |
| 1204 | smmu->num_as = asids; | 695 | if (!smmu->asids) |
| 1205 | smmu->iovmm_base = base; | 696 | return ERR_PTR(-ENOMEM); |
| 1206 | smmu->page_count = size; | ||
| 1207 | |||
| 1208 | smmu->translation_enable_0 = ~0; | ||
| 1209 | smmu->translation_enable_1 = ~0; | ||
| 1210 | smmu->translation_enable_2 = ~0; | ||
| 1211 | smmu->asid_security = 0; | ||
| 1212 | |||
| 1213 | for (i = 0; i < smmu->num_as; i++) { | ||
| 1214 | struct smmu_as *as = &smmu->as[i]; | ||
| 1215 | |||
| 1216 | as->smmu = smmu; | ||
| 1217 | as->asid = i; | ||
| 1218 | as->pdir_attr = _PDIR_ATTR; | ||
| 1219 | as->pde_attr = _PDE_ATTR; | ||
| 1220 | as->pte_attr = _PTE_ATTR; | ||
| 1221 | |||
| 1222 | spin_lock_init(&as->lock); | ||
| 1223 | spin_lock_init(&as->client_lock); | ||
| 1224 | INIT_LIST_HEAD(&as->client); | ||
| 1225 | } | ||
| 1226 | spin_lock_init(&smmu->lock); | ||
| 1227 | err = smmu_setup_regs(smmu); | ||
| 1228 | if (err) | ||
| 1229 | return err; | ||
| 1230 | platform_set_drvdata(pdev, smmu); | ||
| 1231 | 697 | ||
| 1232 | smmu->avp_vector_page = alloc_page(GFP_KERNEL); | 698 | mutex_init(&smmu->lock); |
| 1233 | if (!smmu->avp_vector_page) | ||
| 1234 | return -ENOMEM; | ||
| 1235 | 699 | ||
| 1236 | smmu_debugfs_create(smmu); | 700 | smmu->regs = mc->regs; |
| 1237 | smmu_handle = smmu; | 701 | smmu->soc = soc; |
| 1238 | bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); | 702 | smmu->dev = dev; |
| 1239 | return 0; | 703 | smmu->mc = mc; |
| 1240 | } | ||
| 1241 | 704 | ||
| 1242 | static int tegra_smmu_remove(struct platform_device *pdev) | 705 | value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); |
| 1243 | { | ||
| 1244 | struct smmu_device *smmu = platform_get_drvdata(pdev); | ||
| 1245 | int i; | ||
| 1246 | 706 | ||
| 1247 | smmu_debugfs_delete(smmu); | 707 | if (soc->supports_request_limit) |
| 708 | value |= SMMU_PTC_CONFIG_REQ_LIMIT(8); | ||
| 1248 | 709 | ||
| 1249 | smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); | 710 | smmu_writel(smmu, value, SMMU_PTC_CONFIG); |
| 1250 | for (i = 0; i < smmu->num_as; i++) | ||
| 1251 | free_pdir(&smmu->as[i]); | ||
| 1252 | __free_page(smmu->avp_vector_page); | ||
| 1253 | smmu_handle = NULL; | ||
| 1254 | return 0; | ||
| 1255 | } | ||
| 1256 | 711 | ||
| 1257 | static const struct dev_pm_ops tegra_smmu_pm_ops = { | 712 | value = SMMU_TLB_CONFIG_HIT_UNDER_MISS | |
| 1258 | .suspend = tegra_smmu_suspend, | 713 | SMMU_TLB_CONFIG_ACTIVE_LINES(0x20); |
| 1259 | .resume = tegra_smmu_resume, | ||
| 1260 | }; | ||
| 1261 | 714 | ||
| 1262 | static const struct of_device_id tegra_smmu_of_match[] = { | 715 | if (soc->supports_round_robin_arbitration) |
| 1263 | { .compatible = "nvidia,tegra30-smmu", }, | 716 | value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION; |
| 1264 | { }, | ||
| 1265 | }; | ||
| 1266 | MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); | ||
| 1267 | |||
| 1268 | static struct platform_driver tegra_smmu_driver = { | ||
| 1269 | .probe = tegra_smmu_probe, | ||
| 1270 | .remove = tegra_smmu_remove, | ||
| 1271 | .driver = { | ||
| 1272 | .owner = THIS_MODULE, | ||
| 1273 | .name = "tegra-smmu", | ||
| 1274 | .pm = &tegra_smmu_pm_ops, | ||
| 1275 | .of_match_table = tegra_smmu_of_match, | ||
| 1276 | }, | ||
| 1277 | }; | ||
| 1278 | 717 | ||
| 1279 | static int tegra_smmu_init(void) | 718 | smmu_writel(smmu, value, SMMU_TLB_CONFIG); |
| 1280 | { | ||
| 1281 | return platform_driver_register(&tegra_smmu_driver); | ||
| 1282 | } | ||
| 1283 | 719 | ||
| 1284 | static void __exit tegra_smmu_exit(void) | 720 | smmu_flush_ptc(smmu, NULL, 0); |
| 1285 | { | 721 | smmu_flush_tlb(smmu); |
| 1286 | platform_driver_unregister(&tegra_smmu_driver); | 722 | smmu_writel(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); |
| 1287 | } | 723 | smmu_flush(smmu); |
| 724 | |||
| 725 | tegra_smmu_ahb_enable(); | ||
| 1288 | 726 | ||
| 1289 | subsys_initcall(tegra_smmu_init); | 727 | err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops); |
| 1290 | module_exit(tegra_smmu_exit); | 728 | if (err < 0) |
| 729 | return ERR_PTR(err); | ||
| 1291 | 730 | ||
| 1292 | MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); | 731 | return smmu; |
| 1293 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); | 732 | } |
| 1294 | MODULE_ALIAS("platform:tegra-smmu"); | ||
| 1295 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 6d91c27fd4c8..08bd4cfca2a4 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig | |||
| @@ -61,16 +61,6 @@ config TEGRA20_MC | |||
| 61 | analysis, especially for IOMMU/GART(Graphics Address | 61 | analysis, especially for IOMMU/GART(Graphics Address |
| 62 | Relocation Table) module. | 62 | Relocation Table) module. |
| 63 | 63 | ||
| 64 | config TEGRA30_MC | ||
| 65 | bool "Tegra30 Memory Controller(MC) driver" | ||
| 66 | default y | ||
| 67 | depends on ARCH_TEGRA_3x_SOC | ||
| 68 | help | ||
| 69 | This driver is for the Memory Controller(MC) module available | ||
| 70 | in Tegra30 SoCs, mainly for a address translation fault | ||
| 71 | analysis, especially for IOMMU/SMMU(System Memory Management | ||
| 72 | Unit) module. | ||
| 73 | |||
| 74 | config FSL_CORENET_CF | 64 | config FSL_CORENET_CF |
| 75 | tristate "Freescale CoreNet Error Reporting" | 65 | tristate "Freescale CoreNet Error Reporting" |
| 76 | depends on FSL_SOC_BOOKE | 66 | depends on FSL_SOC_BOOKE |
| @@ -85,4 +75,6 @@ config FSL_IFC | |||
| 85 | bool | 75 | bool |
| 86 | depends on FSL_SOC | 76 | depends on FSL_SOC |
| 87 | 77 | ||
| 78 | source "drivers/memory/tegra/Kconfig" | ||
| 79 | |||
| 88 | endif | 80 | endif |
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index c32d31981be3..ad98bb232623 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile | |||
| @@ -12,4 +12,5 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o | |||
| 12 | obj-$(CONFIG_FSL_IFC) += fsl_ifc.o | 12 | obj-$(CONFIG_FSL_IFC) += fsl_ifc.o |
| 13 | obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o | 13 | obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o |
| 14 | obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o | 14 | obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o |
| 15 | obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o | 15 | |
| 16 | obj-$(CONFIG_TEGRA_MC) += tegra/ | ||
diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig new file mode 100644 index 000000000000..571087621827 --- /dev/null +++ b/drivers/memory/tegra/Kconfig | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | config TEGRA_MC | ||
| 2 | bool "NVIDIA Tegra Memory Controller support" | ||
| 3 | default y | ||
| 4 | depends on ARCH_TEGRA | ||
| 5 | help | ||
| 6 | This driver supports the Memory Controller (MC) hardware found on | ||
| 7 | NVIDIA Tegra SoCs. | ||
diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile new file mode 100644 index 000000000000..0d9f497b786c --- /dev/null +++ b/drivers/memory/tegra/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | tegra-mc-y := mc.o | ||
| 2 | |||
| 3 | tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o | ||
| 4 | tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o | ||
| 5 | tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_TEGRA_MC) += tegra-mc.o | ||
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c new file mode 100644 index 000000000000..fe3c44e7e1d1 --- /dev/null +++ b/drivers/memory/tegra/mc.c | |||
| @@ -0,0 +1,301 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/clk.h> | ||
| 10 | #include <linux/interrupt.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/of.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/slab.h> | ||
| 16 | |||
| 17 | #include "mc.h" | ||
| 18 | |||
| 19 | #define MC_INTSTATUS 0x000 | ||
| 20 | #define MC_INT_DECERR_MTS (1 << 16) | ||
| 21 | #define MC_INT_SECERR_SEC (1 << 13) | ||
| 22 | #define MC_INT_DECERR_VPR (1 << 12) | ||
| 23 | #define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) | ||
| 24 | #define MC_INT_INVALID_SMMU_PAGE (1 << 10) | ||
| 25 | #define MC_INT_ARBITRATION_EMEM (1 << 9) | ||
| 26 | #define MC_INT_SECURITY_VIOLATION (1 << 8) | ||
| 27 | #define MC_INT_DECERR_EMEM (1 << 6) | ||
| 28 | |||
| 29 | #define MC_INTMASK 0x004 | ||
| 30 | |||
| 31 | #define MC_ERR_STATUS 0x08 | ||
| 32 | #define MC_ERR_STATUS_TYPE_SHIFT 28 | ||
| 33 | #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) | ||
| 34 | #define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) | ||
| 35 | #define MC_ERR_STATUS_READABLE (1 << 27) | ||
| 36 | #define MC_ERR_STATUS_WRITABLE (1 << 26) | ||
| 37 | #define MC_ERR_STATUS_NONSECURE (1 << 25) | ||
| 38 | #define MC_ERR_STATUS_ADR_HI_SHIFT 20 | ||
| 39 | #define MC_ERR_STATUS_ADR_HI_MASK 0x3 | ||
| 40 | #define MC_ERR_STATUS_SECURITY (1 << 17) | ||
| 41 | #define MC_ERR_STATUS_RW (1 << 16) | ||
| 42 | #define MC_ERR_STATUS_CLIENT_MASK 0x7f | ||
| 43 | |||
| 44 | #define MC_ERR_ADR 0x0c | ||
| 45 | |||
| 46 | #define MC_EMEM_ARB_CFG 0x90 | ||
| 47 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0) | ||
| 48 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff | ||
| 49 | #define MC_EMEM_ARB_MISC0 0xd8 | ||
| 50 | |||
| 51 | static const struct of_device_id tegra_mc_of_match[] = { | ||
| 52 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC | ||
| 53 | { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, | ||
| 54 | #endif | ||
| 55 | #ifdef CONFIG_ARCH_TEGRA_114_SOC | ||
| 56 | { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, | ||
| 57 | #endif | ||
| 58 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | ||
| 59 | { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, | ||
| 60 | #endif | ||
| 61 | { } | ||
| 62 | }; | ||
| 63 | MODULE_DEVICE_TABLE(of, tegra_mc_of_match); | ||
| 64 | |||
| 65 | static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) | ||
| 66 | { | ||
| 67 | unsigned long long tick; | ||
| 68 | unsigned int i; | ||
| 69 | u32 value; | ||
| 70 | |||
| 71 | /* compute the number of MC clock cycles per tick */ | ||
| 72 | tick = mc->tick * clk_get_rate(mc->clk); | ||
| 73 | do_div(tick, NSEC_PER_SEC); | ||
| 74 | |||
| 75 | value = readl(mc->regs + MC_EMEM_ARB_CFG); | ||
| 76 | value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; | ||
| 77 | value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); | ||
| 78 | writel(value, mc->regs + MC_EMEM_ARB_CFG); | ||
| 79 | |||
| 80 | /* write latency allowance defaults */ | ||
| 81 | for (i = 0; i < mc->soc->num_clients; i++) { | ||
| 82 | const struct tegra_mc_la *la = &mc->soc->clients[i].la; | ||
| 83 | u32 value; | ||
| 84 | |||
| 85 | value = readl(mc->regs + la->reg); | ||
| 86 | value &= ~(la->mask << la->shift); | ||
| 87 | value |= (la->def & la->mask) << la->shift; | ||
| 88 | writel(value, mc->regs + la->reg); | ||
| 89 | } | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | static const char *const status_names[32] = { | ||
| 95 | [ 1] = "External interrupt", | ||
| 96 | [ 6] = "EMEM address decode error", | ||
| 97 | [ 8] = "Security violation", | ||
| 98 | [ 9] = "EMEM arbitration error", | ||
| 99 | [10] = "Page fault", | ||
| 100 | [11] = "Invalid APB ASID update", | ||
| 101 | [12] = "VPR violation", | ||
| 102 | [13] = "Secure carveout violation", | ||
| 103 | [16] = "MTS carveout violation", | ||
| 104 | }; | ||
| 105 | |||
| 106 | static const char *const error_names[8] = { | ||
| 107 | [2] = "EMEM decode error", | ||
| 108 | [3] = "TrustZone violation", | ||
| 109 | [4] = "Carveout violation", | ||
| 110 | [6] = "SMMU translation error", | ||
| 111 | }; | ||
| 112 | |||
| 113 | static irqreturn_t tegra_mc_irq(int irq, void *data) | ||
| 114 | { | ||
| 115 | struct tegra_mc *mc = data; | ||
| 116 | unsigned long status, mask; | ||
| 117 | unsigned int bit; | ||
| 118 | |||
| 119 | /* mask all interrupts to avoid flooding */ | ||
| 120 | status = mc_readl(mc, MC_INTSTATUS); | ||
| 121 | mask = mc_readl(mc, MC_INTMASK); | ||
| 122 | |||
| 123 | for_each_set_bit(bit, &status, 32) { | ||
| 124 | const char *error = status_names[bit] ?: "unknown"; | ||
| 125 | const char *client = "unknown", *desc; | ||
| 126 | const char *direction, *secure; | ||
| 127 | phys_addr_t addr = 0; | ||
| 128 | unsigned int i; | ||
| 129 | char perm[7]; | ||
| 130 | u8 id, type; | ||
| 131 | u32 value; | ||
| 132 | |||
| 133 | value = mc_readl(mc, MC_ERR_STATUS); | ||
| 134 | |||
| 135 | #ifdef CONFIG_PHYS_ADDR_T_64BIT | ||
| 136 | if (mc->soc->num_address_bits > 32) { | ||
| 137 | addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & | ||
| 138 | MC_ERR_STATUS_ADR_HI_MASK); | ||
| 139 | addr <<= 32; | ||
| 140 | } | ||
| 141 | #endif | ||
| 142 | |||
| 143 | if (value & MC_ERR_STATUS_RW) | ||
| 144 | direction = "write"; | ||
| 145 | else | ||
| 146 | direction = "read"; | ||
| 147 | |||
| 148 | if (value & MC_ERR_STATUS_SECURITY) | ||
| 149 | secure = "secure "; | ||
| 150 | else | ||
| 151 | secure = ""; | ||
| 152 | |||
| 153 | id = value & MC_ERR_STATUS_CLIENT_MASK; | ||
| 154 | |||
| 155 | for (i = 0; i < mc->soc->num_clients; i++) { | ||
| 156 | if (mc->soc->clients[i].id == id) { | ||
| 157 | client = mc->soc->clients[i].name; | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | type = (value & MC_ERR_STATUS_TYPE_MASK) >> | ||
| 163 | MC_ERR_STATUS_TYPE_SHIFT; | ||
| 164 | desc = error_names[type]; | ||
| 165 | |||
| 166 | switch (value & MC_ERR_STATUS_TYPE_MASK) { | ||
| 167 | case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: | ||
| 168 | perm[0] = ' '; | ||
| 169 | perm[1] = '['; | ||
| 170 | |||
| 171 | if (value & MC_ERR_STATUS_READABLE) | ||
| 172 | perm[2] = 'R'; | ||
| 173 | else | ||
| 174 | perm[2] = '-'; | ||
| 175 | |||
| 176 | if (value & MC_ERR_STATUS_WRITABLE) | ||
| 177 | perm[3] = 'W'; | ||
| 178 | else | ||
| 179 | perm[3] = '-'; | ||
| 180 | |||
| 181 | if (value & MC_ERR_STATUS_NONSECURE) | ||
| 182 | perm[4] = '-'; | ||
| 183 | else | ||
| 184 | perm[4] = 'S'; | ||
| 185 | |||
| 186 | perm[5] = ']'; | ||
| 187 | perm[6] = '\0'; | ||
| 188 | break; | ||
| 189 | |||
| 190 | default: | ||
| 191 | perm[0] = '\0'; | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | |||
| 195 | value = mc_readl(mc, MC_ERR_ADR); | ||
| 196 | addr |= value; | ||
| 197 | |||
| 198 | dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", | ||
| 199 | client, secure, direction, &addr, error, | ||
| 200 | desc, perm); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* clear interrupts */ | ||
| 204 | mc_writel(mc, status, MC_INTSTATUS); | ||
| 205 | |||
| 206 | return IRQ_HANDLED; | ||
| 207 | } | ||
| 208 | |||
| 209 | static int tegra_mc_probe(struct platform_device *pdev) | ||
| 210 | { | ||
| 211 | const struct of_device_id *match; | ||
| 212 | struct resource *res; | ||
| 213 | struct tegra_mc *mc; | ||
| 214 | u32 value; | ||
| 215 | int err; | ||
| 216 | |||
| 217 | match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); | ||
| 218 | if (!match) | ||
| 219 | return -ENODEV; | ||
| 220 | |||
| 221 | mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); | ||
| 222 | if (!mc) | ||
| 223 | return -ENOMEM; | ||
| 224 | |||
| 225 | platform_set_drvdata(pdev, mc); | ||
| 226 | mc->soc = match->data; | ||
| 227 | mc->dev = &pdev->dev; | ||
| 228 | |||
| 229 | /* length of MC tick in nanoseconds */ | ||
| 230 | mc->tick = 30; | ||
| 231 | |||
| 232 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 233 | mc->regs = devm_ioremap_resource(&pdev->dev, res); | ||
| 234 | if (IS_ERR(mc->regs)) | ||
| 235 | return PTR_ERR(mc->regs); | ||
| 236 | |||
| 237 | mc->clk = devm_clk_get(&pdev->dev, "mc"); | ||
| 238 | if (IS_ERR(mc->clk)) { | ||
| 239 | dev_err(&pdev->dev, "failed to get MC clock: %ld\n", | ||
| 240 | PTR_ERR(mc->clk)); | ||
| 241 | return PTR_ERR(mc->clk); | ||
| 242 | } | ||
| 243 | |||
| 244 | err = tegra_mc_setup_latency_allowance(mc); | ||
| 245 | if (err < 0) { | ||
| 246 | dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", | ||
| 247 | err); | ||
| 248 | return err; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) { | ||
| 252 | mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); | ||
| 253 | if (IS_ERR(mc->smmu)) { | ||
| 254 | dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", | ||
| 255 | PTR_ERR(mc->smmu)); | ||
| 256 | return PTR_ERR(mc->smmu); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | mc->irq = platform_get_irq(pdev, 0); | ||
| 261 | if (mc->irq < 0) { | ||
| 262 | dev_err(&pdev->dev, "interrupt not specified\n"); | ||
| 263 | return mc->irq; | ||
| 264 | } | ||
| 265 | |||
| 266 | err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED, | ||
| 267 | dev_name(&pdev->dev), mc); | ||
| 268 | if (err < 0) { | ||
| 269 | dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, | ||
| 270 | err); | ||
| 271 | return err; | ||
| 272 | } | ||
| 273 | |||
| 274 | value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | | ||
| 275 | MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | | ||
| 276 | MC_INT_ARBITRATION_EMEM | MC_INT_SECURITY_VIOLATION | | ||
| 277 | MC_INT_DECERR_EMEM; | ||
| 278 | mc_writel(mc, value, MC_INTMASK); | ||
| 279 | |||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 283 | static struct platform_driver tegra_mc_driver = { | ||
| 284 | .driver = { | ||
| 285 | .name = "tegra-mc", | ||
| 286 | .of_match_table = tegra_mc_of_match, | ||
| 287 | .suppress_bind_attrs = true, | ||
| 288 | }, | ||
| 289 | .prevent_deferred_probe = true, | ||
| 290 | .probe = tegra_mc_probe, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static int tegra_mc_init(void) | ||
| 294 | { | ||
| 295 | return platform_driver_register(&tegra_mc_driver); | ||
| 296 | } | ||
| 297 | arch_initcall(tegra_mc_init); | ||
| 298 | |||
| 299 | MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); | ||
| 300 | MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver"); | ||
| 301 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h new file mode 100644 index 000000000000..d5d21147fc77 --- /dev/null +++ b/drivers/memory/tegra/mc.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef MEMORY_TEGRA_MC_H | ||
| 10 | #define MEMORY_TEGRA_MC_H | ||
| 11 | |||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/types.h> | ||
| 14 | |||
| 15 | #include <soc/tegra/mc.h> | ||
| 16 | |||
| 17 | static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) | ||
| 18 | { | ||
| 19 | return readl(mc->regs + offset); | ||
| 20 | } | ||
| 21 | |||
| 22 | static inline void mc_writel(struct tegra_mc *mc, u32 value, | ||
| 23 | unsigned long offset) | ||
| 24 | { | ||
| 25 | writel(value, mc->regs + offset); | ||
| 26 | } | ||
| 27 | |||
| 28 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC | ||
| 29 | extern const struct tegra_mc_soc tegra30_mc_soc; | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #ifdef CONFIG_ARCH_TEGRA_114_SOC | ||
| 33 | extern const struct tegra_mc_soc tegra114_mc_soc; | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | ||
| 37 | extern const struct tegra_mc_soc tegra124_mc_soc; | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #endif /* MEMORY_TEGRA_MC_H */ | ||
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c new file mode 100644 index 000000000000..511e9a25c151 --- /dev/null +++ b/drivers/memory/tegra/tegra114.c | |||
| @@ -0,0 +1,948 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/mm.h> | ||
| 11 | |||
| 12 | #include <asm/cacheflush.h> | ||
| 13 | |||
| 14 | #include <dt-bindings/memory/tegra114-mc.h> | ||
| 15 | |||
| 16 | #include "mc.h" | ||
| 17 | |||
| 18 | static const struct tegra_mc_client tegra114_mc_clients[] = { | ||
| 19 | { | ||
| 20 | .id = 0x00, | ||
| 21 | .name = "ptcr", | ||
| 22 | .swgroup = TEGRA_SWGROUP_PTC, | ||
| 23 | }, { | ||
| 24 | .id = 0x01, | ||
| 25 | .name = "display0a", | ||
| 26 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 27 | .smmu = { | ||
| 28 | .reg = 0x228, | ||
| 29 | .bit = 1, | ||
| 30 | }, | ||
| 31 | .la = { | ||
| 32 | .reg = 0x2e8, | ||
| 33 | .shift = 0, | ||
| 34 | .mask = 0xff, | ||
| 35 | .def = 0x4e, | ||
| 36 | }, | ||
| 37 | }, { | ||
| 38 | .id = 0x02, | ||
| 39 | .name = "display0ab", | ||
| 40 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 41 | .smmu = { | ||
| 42 | .reg = 0x228, | ||
| 43 | .bit = 2, | ||
| 44 | }, | ||
| 45 | .la = { | ||
| 46 | .reg = 0x2f4, | ||
| 47 | .shift = 0, | ||
| 48 | .mask = 0xff, | ||
| 49 | .def = 0x4e, | ||
| 50 | }, | ||
| 51 | }, { | ||
| 52 | .id = 0x03, | ||
| 53 | .name = "display0b", | ||
| 54 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 55 | .smmu = { | ||
| 56 | .reg = 0x228, | ||
| 57 | .bit = 3, | ||
| 58 | }, | ||
| 59 | .la = { | ||
| 60 | .reg = 0x2e8, | ||
| 61 | .shift = 16, | ||
| 62 | .mask = 0xff, | ||
| 63 | .def = 0x4e, | ||
| 64 | }, | ||
| 65 | }, { | ||
| 66 | .id = 0x04, | ||
| 67 | .name = "display0bb", | ||
| 68 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 69 | .smmu = { | ||
| 70 | .reg = 0x228, | ||
| 71 | .bit = 4, | ||
| 72 | }, | ||
| 73 | .la = { | ||
| 74 | .reg = 0x2f4, | ||
| 75 | .shift = 16, | ||
| 76 | .mask = 0xff, | ||
| 77 | .def = 0x4e, | ||
| 78 | }, | ||
| 79 | }, { | ||
| 80 | .id = 0x05, | ||
| 81 | .name = "display0c", | ||
| 82 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 83 | .smmu = { | ||
| 84 | .reg = 0x228, | ||
| 85 | .bit = 5, | ||
| 86 | }, | ||
| 87 | .la = { | ||
| 88 | .reg = 0x2ec, | ||
| 89 | .shift = 0, | ||
| 90 | .mask = 0xff, | ||
| 91 | .def = 0x4e, | ||
| 92 | }, | ||
| 93 | }, { | ||
| 94 | .id = 0x06, | ||
| 95 | .name = "display0cb", | ||
| 96 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 97 | .smmu = { | ||
| 98 | .reg = 0x228, | ||
| 99 | .bit = 6, | ||
| 100 | }, | ||
| 101 | .la = { | ||
| 102 | .reg = 0x2f8, | ||
| 103 | .shift = 0, | ||
| 104 | .mask = 0xff, | ||
| 105 | .def = 0x4e, | ||
| 106 | }, | ||
| 107 | }, { | ||
| 108 | .id = 0x09, | ||
| 109 | .name = "eppup", | ||
| 110 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 111 | .smmu = { | ||
| 112 | .reg = 0x228, | ||
| 113 | .bit = 9, | ||
| 114 | }, | ||
| 115 | .la = { | ||
| 116 | .reg = 0x300, | ||
| 117 | .shift = 0, | ||
| 118 | .mask = 0xff, | ||
| 119 | .def = 0x33, | ||
| 120 | }, | ||
| 121 | }, { | ||
| 122 | .id = 0x0a, | ||
| 123 | .name = "g2pr", | ||
| 124 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 125 | .smmu = { | ||
| 126 | .reg = 0x228, | ||
| 127 | .bit = 10, | ||
| 128 | }, | ||
| 129 | .la = { | ||
| 130 | .reg = 0x308, | ||
| 131 | .shift = 0, | ||
| 132 | .mask = 0xff, | ||
| 133 | .def = 0x09, | ||
| 134 | }, | ||
| 135 | }, { | ||
| 136 | .id = 0x0b, | ||
| 137 | .name = "g2sr", | ||
| 138 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 139 | .smmu = { | ||
| 140 | .reg = 0x228, | ||
| 141 | .bit = 11, | ||
| 142 | }, | ||
| 143 | .la = { | ||
| 144 | .reg = 0x308, | ||
| 145 | .shift = 16, | ||
| 146 | .mask = 0xff, | ||
| 147 | .def = 0x09, | ||
| 148 | }, | ||
| 149 | }, { | ||
| 150 | .id = 0x0f, | ||
| 151 | .name = "avpcarm7r", | ||
| 152 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 153 | .smmu = { | ||
| 154 | .reg = 0x228, | ||
| 155 | .bit = 15, | ||
| 156 | }, | ||
| 157 | .la = { | ||
| 158 | .reg = 0x2e4, | ||
| 159 | .shift = 0, | ||
| 160 | .mask = 0xff, | ||
| 161 | .def = 0x04, | ||
| 162 | }, | ||
| 163 | }, { | ||
| 164 | .id = 0x10, | ||
| 165 | .name = "displayhc", | ||
| 166 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 167 | .smmu = { | ||
| 168 | .reg = 0x228, | ||
| 169 | .bit = 16, | ||
| 170 | }, | ||
| 171 | .la = { | ||
| 172 | .reg = 0x2f0, | ||
| 173 | .shift = 0, | ||
| 174 | .mask = 0xff, | ||
| 175 | .def = 0x68, | ||
| 176 | }, | ||
| 177 | }, { | ||
| 178 | .id = 0x11, | ||
| 179 | .name = "displayhcb", | ||
| 180 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 181 | .smmu = { | ||
| 182 | .reg = 0x228, | ||
| 183 | .bit = 17, | ||
| 184 | }, | ||
| 185 | .la = { | ||
| 186 | .reg = 0x2fc, | ||
| 187 | .shift = 0, | ||
| 188 | .mask = 0xff, | ||
| 189 | .def = 0x68, | ||
| 190 | }, | ||
| 191 | }, { | ||
| 192 | .id = 0x12, | ||
| 193 | .name = "fdcdrd", | ||
| 194 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 195 | .smmu = { | ||
| 196 | .reg = 0x228, | ||
| 197 | .bit = 18, | ||
| 198 | }, | ||
| 199 | .la = { | ||
| 200 | .reg = 0x334, | ||
| 201 | .shift = 0, | ||
| 202 | .mask = 0xff, | ||
| 203 | .def = 0x0c, | ||
| 204 | }, | ||
| 205 | }, { | ||
| 206 | .id = 0x13, | ||
| 207 | .name = "fdcdrd2", | ||
| 208 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 209 | .smmu = { | ||
| 210 | .reg = 0x228, | ||
| 211 | .bit = 19, | ||
| 212 | }, | ||
| 213 | .la = { | ||
| 214 | .reg = 0x33c, | ||
| 215 | .shift = 0, | ||
| 216 | .mask = 0xff, | ||
| 217 | .def = 0x0c, | ||
| 218 | }, | ||
| 219 | }, { | ||
| 220 | .id = 0x14, | ||
| 221 | .name = "g2dr", | ||
| 222 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 223 | .smmu = { | ||
| 224 | .reg = 0x228, | ||
| 225 | .bit = 20, | ||
| 226 | }, | ||
| 227 | .la = { | ||
| 228 | .reg = 0x30c, | ||
| 229 | .shift = 0, | ||
| 230 | .mask = 0xff, | ||
| 231 | .def = 0x0a, | ||
| 232 | }, | ||
| 233 | }, { | ||
| 234 | .id = 0x15, | ||
| 235 | .name = "hdar", | ||
| 236 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 237 | .smmu = { | ||
| 238 | .reg = 0x228, | ||
| 239 | .bit = 21, | ||
| 240 | }, | ||
| 241 | .la = { | ||
| 242 | .reg = 0x318, | ||
| 243 | .shift = 0, | ||
| 244 | .mask = 0xff, | ||
| 245 | .def = 0xff, | ||
| 246 | }, | ||
| 247 | }, { | ||
| 248 | .id = 0x16, | ||
| 249 | .name = "host1xdmar", | ||
| 250 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 251 | .smmu = { | ||
| 252 | .reg = 0x228, | ||
| 253 | .bit = 22, | ||
| 254 | }, | ||
| 255 | .la = { | ||
| 256 | .reg = 0x310, | ||
| 257 | .shift = 0, | ||
| 258 | .mask = 0xff, | ||
| 259 | .def = 0x10, | ||
| 260 | }, | ||
| 261 | }, { | ||
| 262 | .id = 0x17, | ||
| 263 | .name = "host1xr", | ||
| 264 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 265 | .smmu = { | ||
| 266 | .reg = 0x228, | ||
| 267 | .bit = 23, | ||
| 268 | }, | ||
| 269 | .la = { | ||
| 270 | .reg = 0x310, | ||
| 271 | .shift = 16, | ||
| 272 | .mask = 0xff, | ||
| 273 | .def = 0xa5, | ||
| 274 | }, | ||
| 275 | }, { | ||
| 276 | .id = 0x18, | ||
| 277 | .name = "idxsrd", | ||
| 278 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 279 | .smmu = { | ||
| 280 | .reg = 0x228, | ||
| 281 | .bit = 24, | ||
| 282 | }, | ||
| 283 | .la = { | ||
| 284 | .reg = 0x334, | ||
| 285 | .shift = 16, | ||
| 286 | .mask = 0xff, | ||
| 287 | .def = 0x0b, | ||
| 288 | }, | ||
| 289 | }, { | ||
| 290 | .id = 0x1c, | ||
| 291 | .name = "msencsrd", | ||
| 292 | .swgroup = TEGRA_SWGROUP_MSENC, | ||
| 293 | .smmu = { | ||
| 294 | .reg = 0x228, | ||
| 295 | .bit = 28, | ||
| 296 | }, | ||
| 297 | .la = { | ||
| 298 | .reg = 0x328, | ||
| 299 | .shift = 0, | ||
| 300 | .mask = 0xff, | ||
| 301 | .def = 0x80, | ||
| 302 | }, | ||
| 303 | }, { | ||
| 304 | .id = 0x1d, | ||
| 305 | .name = "ppcsahbdmar", | ||
| 306 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 307 | .smmu = { | ||
| 308 | .reg = 0x228, | ||
| 309 | .bit = 29, | ||
| 310 | }, | ||
| 311 | .la = { | ||
| 312 | .reg = 0x344, | ||
| 313 | .shift = 0, | ||
| 314 | .mask = 0xff, | ||
| 315 | .def = 0x50, | ||
| 316 | }, | ||
| 317 | }, { | ||
| 318 | .id = 0x1e, | ||
| 319 | .name = "ppcsahbslvr", | ||
| 320 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 321 | .smmu = { | ||
| 322 | .reg = 0x228, | ||
| 323 | .bit = 30, | ||
| 324 | }, | ||
| 325 | .la = { | ||
| 326 | .reg = 0x344, | ||
| 327 | .shift = 16, | ||
| 328 | .mask = 0xff, | ||
| 329 | .def = 0xe8, | ||
| 330 | }, | ||
| 331 | }, { | ||
| 332 | .id = 0x20, | ||
| 333 | .name = "texl2srd", | ||
| 334 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 335 | .smmu = { | ||
| 336 | .reg = 0x22c, | ||
| 337 | .bit = 0, | ||
| 338 | }, | ||
| 339 | .la = { | ||
| 340 | .reg = 0x338, | ||
| 341 | .shift = 0, | ||
| 342 | .mask = 0xff, | ||
| 343 | .def = 0x0c, | ||
| 344 | }, | ||
| 345 | }, { | ||
| 346 | .id = 0x22, | ||
| 347 | .name = "vdebsevr", | ||
| 348 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 349 | .smmu = { | ||
| 350 | .reg = 0x22c, | ||
| 351 | .bit = 2, | ||
| 352 | }, | ||
| 353 | .la = { | ||
| 354 | .reg = 0x354, | ||
| 355 | .shift = 0, | ||
| 356 | .mask = 0xff, | ||
| 357 | .def = 0xff, | ||
| 358 | }, | ||
| 359 | }, { | ||
| 360 | .id = 0x23, | ||
| 361 | .name = "vdember", | ||
| 362 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 363 | .smmu = { | ||
| 364 | .reg = 0x22c, | ||
| 365 | .bit = 3, | ||
| 366 | }, | ||
| 367 | .la = { | ||
| 368 | .reg = 0x354, | ||
| 369 | .shift = 16, | ||
| 370 | .mask = 0xff, | ||
| 371 | .def = 0xff, | ||
| 372 | }, | ||
| 373 | }, { | ||
| 374 | .id = 0x24, | ||
| 375 | .name = "vdemcer", | ||
| 376 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 377 | .smmu = { | ||
| 378 | .reg = 0x22c, | ||
| 379 | .bit = 4, | ||
| 380 | }, | ||
| 381 | .la = { | ||
| 382 | .reg = 0x358, | ||
| 383 | .shift = 0, | ||
| 384 | .mask = 0xff, | ||
| 385 | .def = 0xb8, | ||
| 386 | }, | ||
| 387 | }, { | ||
| 388 | .id = 0x25, | ||
| 389 | .name = "vdetper", | ||
| 390 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 391 | .smmu = { | ||
| 392 | .reg = 0x22c, | ||
| 393 | .bit = 5, | ||
| 394 | }, | ||
| 395 | .la = { | ||
| 396 | .reg = 0x358, | ||
| 397 | .shift = 16, | ||
| 398 | .mask = 0xff, | ||
| 399 | .def = 0xee, | ||
| 400 | }, | ||
| 401 | }, { | ||
| 402 | .id = 0x26, | ||
| 403 | .name = "mpcorelpr", | ||
| 404 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 405 | .la = { | ||
| 406 | .reg = 0x324, | ||
| 407 | .shift = 0, | ||
| 408 | .mask = 0xff, | ||
| 409 | .def = 0x04, | ||
| 410 | }, | ||
| 411 | }, { | ||
| 412 | .id = 0x27, | ||
| 413 | .name = "mpcorer", | ||
| 414 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 415 | .la = { | ||
| 416 | .reg = 0x320, | ||
| 417 | .shift = 0, | ||
| 418 | .mask = 0xff, | ||
| 419 | .def = 0x04, | ||
| 420 | }, | ||
| 421 | }, { | ||
| 422 | .id = 0x28, | ||
| 423 | .name = "eppu", | ||
| 424 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 425 | .smmu = { | ||
| 426 | .reg = 0x22c, | ||
| 427 | .bit = 8, | ||
| 428 | }, | ||
| 429 | .la = { | ||
| 430 | .reg = 0x300, | ||
| 431 | .shift = 16, | ||
| 432 | .mask = 0xff, | ||
| 433 | .def = 0x33, | ||
| 434 | }, | ||
| 435 | }, { | ||
| 436 | .id = 0x29, | ||
| 437 | .name = "eppv", | ||
| 438 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 439 | .smmu = { | ||
| 440 | .reg = 0x22c, | ||
| 441 | .bit = 9, | ||
| 442 | }, | ||
| 443 | .la = { | ||
| 444 | .reg = 0x304, | ||
| 445 | .shift = 0, | ||
| 446 | .mask = 0xff, | ||
| 447 | .def = 0x6c, | ||
| 448 | }, | ||
| 449 | }, { | ||
| 450 | .id = 0x2a, | ||
| 451 | .name = "eppy", | ||
| 452 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 453 | .smmu = { | ||
| 454 | .reg = 0x22c, | ||
| 455 | .bit = 10, | ||
| 456 | }, | ||
| 457 | .la = { | ||
| 458 | .reg = 0x304, | ||
| 459 | .shift = 16, | ||
| 460 | .mask = 0xff, | ||
| 461 | .def = 0x6c, | ||
| 462 | }, | ||
| 463 | }, { | ||
| 464 | .id = 0x2b, | ||
| 465 | .name = "msencswr", | ||
| 466 | .swgroup = TEGRA_SWGROUP_MSENC, | ||
| 467 | .smmu = { | ||
| 468 | .reg = 0x22c, | ||
| 469 | .bit = 11, | ||
| 470 | }, | ||
| 471 | .la = { | ||
| 472 | .reg = 0x328, | ||
| 473 | .shift = 16, | ||
| 474 | .mask = 0xff, | ||
| 475 | .def = 0x80, | ||
| 476 | }, | ||
| 477 | }, { | ||
| 478 | .id = 0x2c, | ||
| 479 | .name = "viwsb", | ||
| 480 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 481 | .smmu = { | ||
| 482 | .reg = 0x22c, | ||
| 483 | .bit = 12, | ||
| 484 | }, | ||
| 485 | .la = { | ||
| 486 | .reg = 0x364, | ||
| 487 | .shift = 0, | ||
| 488 | .mask = 0xff, | ||
| 489 | .def = 0x47, | ||
| 490 | }, | ||
| 491 | }, { | ||
| 492 | .id = 0x2d, | ||
| 493 | .name = "viwu", | ||
| 494 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 495 | .smmu = { | ||
| 496 | .reg = 0x22c, | ||
| 497 | .bit = 13, | ||
| 498 | }, | ||
| 499 | .la = { | ||
| 500 | .reg = 0x368, | ||
| 501 | .shift = 0, | ||
| 502 | .mask = 0xff, | ||
| 503 | .def = 0xff, | ||
| 504 | }, | ||
| 505 | }, { | ||
| 506 | .id = 0x2e, | ||
| 507 | .name = "viwv", | ||
| 508 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 509 | .smmu = { | ||
| 510 | .reg = 0x22c, | ||
| 511 | .bit = 14, | ||
| 512 | }, | ||
| 513 | .la = { | ||
| 514 | .reg = 0x368, | ||
| 515 | .shift = 16, | ||
| 516 | .mask = 0xff, | ||
| 517 | .def = 0xff, | ||
| 518 | }, | ||
| 519 | }, { | ||
| 520 | .id = 0x2f, | ||
| 521 | .name = "viwy", | ||
| 522 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 523 | .smmu = { | ||
| 524 | .reg = 0x22c, | ||
| 525 | .bit = 15, | ||
| 526 | }, | ||
| 527 | .la = { | ||
| 528 | .reg = 0x36c, | ||
| 529 | .shift = 0, | ||
| 530 | .mask = 0xff, | ||
| 531 | .def = 0x47, | ||
| 532 | }, | ||
| 533 | }, { | ||
| 534 | .id = 0x30, | ||
| 535 | .name = "g2dw", | ||
| 536 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 537 | .smmu = { | ||
| 538 | .reg = 0x22c, | ||
| 539 | .bit = 16, | ||
| 540 | }, | ||
| 541 | .la = { | ||
| 542 | .reg = 0x30c, | ||
| 543 | .shift = 16, | ||
| 544 | .mask = 0xff, | ||
| 545 | .def = 0x9, | ||
| 546 | }, | ||
| 547 | }, { | ||
| 548 | .id = 0x32, | ||
| 549 | .name = "avpcarm7w", | ||
| 550 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 551 | .smmu = { | ||
| 552 | .reg = 0x22c, | ||
| 553 | .bit = 18, | ||
| 554 | }, | ||
| 555 | .la = { | ||
| 556 | .reg = 0x2e4, | ||
| 557 | .shift = 16, | ||
| 558 | .mask = 0xff, | ||
| 559 | .def = 0x0e, | ||
| 560 | }, | ||
| 561 | }, { | ||
| 562 | .id = 0x33, | ||
| 563 | .name = "fdcdwr", | ||
| 564 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 565 | .smmu = { | ||
| 566 | .reg = 0x22c, | ||
| 567 | .bit = 19, | ||
| 568 | }, | ||
| 569 | .la = { | ||
| 570 | .reg = 0x338, | ||
| 571 | .shift = 16, | ||
| 572 | .mask = 0xff, | ||
| 573 | .def = 0x10, | ||
| 574 | }, | ||
| 575 | }, { | ||
| 576 | .id = 0x34, | ||
| 577 | .name = "fdcwr2", | ||
| 578 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 579 | .smmu = { | ||
| 580 | .reg = 0x22c, | ||
| 581 | .bit = 20, | ||
| 582 | }, | ||
| 583 | .la = { | ||
| 584 | .reg = 0x340, | ||
| 585 | .shift = 0, | ||
| 586 | .mask = 0xff, | ||
| 587 | .def = 0x10, | ||
| 588 | }, | ||
| 589 | }, { | ||
| 590 | .id = 0x35, | ||
| 591 | .name = "hdaw", | ||
| 592 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 593 | .smmu = { | ||
| 594 | .reg = 0x22c, | ||
| 595 | .bit = 21, | ||
| 596 | }, | ||
| 597 | .la = { | ||
| 598 | .reg = 0x318, | ||
| 599 | .shift = 16, | ||
| 600 | .mask = 0xff, | ||
| 601 | .def = 0xff, | ||
| 602 | }, | ||
| 603 | }, { | ||
| 604 | .id = 0x36, | ||
| 605 | .name = "host1xw", | ||
| 606 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 607 | .smmu = { | ||
| 608 | .reg = 0x22c, | ||
| 609 | .bit = 22, | ||
| 610 | }, | ||
| 611 | .la = { | ||
| 612 | .reg = 0x314, | ||
| 613 | .shift = 0, | ||
| 614 | .mask = 0xff, | ||
| 615 | .def = 0x25, | ||
| 616 | }, | ||
| 617 | }, { | ||
| 618 | .id = 0x37, | ||
| 619 | .name = "ispw", | ||
| 620 | .swgroup = TEGRA_SWGROUP_ISP, | ||
| 621 | .smmu = { | ||
| 622 | .reg = 0x22c, | ||
| 623 | .bit = 23, | ||
| 624 | }, | ||
| 625 | .la = { | ||
| 626 | .reg = 0x31c, | ||
| 627 | .shift = 0, | ||
| 628 | .mask = 0xff, | ||
| 629 | .def = 0xff, | ||
| 630 | }, | ||
| 631 | }, { | ||
| 632 | .id = 0x38, | ||
| 633 | .name = "mpcorelpw", | ||
| 634 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 635 | .la = { | ||
| 636 | .reg = 0x324, | ||
| 637 | .shift = 16, | ||
| 638 | .mask = 0xff, | ||
| 639 | .def = 0x80, | ||
| 640 | }, | ||
| 641 | }, { | ||
| 642 | .id = 0x39, | ||
| 643 | .name = "mpcorew", | ||
| 644 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 645 | .la = { | ||
| 646 | .reg = 0x320, | ||
| 647 | .shift = 16, | ||
| 648 | .mask = 0xff, | ||
| 649 | .def = 0x0e, | ||
| 650 | }, | ||
| 651 | }, { | ||
| 652 | .id = 0x3b, | ||
| 653 | .name = "ppcsahbdmaw", | ||
| 654 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 655 | .smmu = { | ||
| 656 | .reg = 0x22c, | ||
| 657 | .bit = 27, | ||
| 658 | }, | ||
| 659 | .la = { | ||
| 660 | .reg = 0x348, | ||
| 661 | .shift = 0, | ||
| 662 | .mask = 0xff, | ||
| 663 | .def = 0xa5, | ||
| 664 | }, | ||
| 665 | }, { | ||
| 666 | .id = 0x3c, | ||
| 667 | .name = "ppcsahbslvw", | ||
| 668 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 669 | .smmu = { | ||
| 670 | .reg = 0x22c, | ||
| 671 | .bit = 28, | ||
| 672 | }, | ||
| 673 | .la = { | ||
| 674 | .reg = 0x348, | ||
| 675 | .shift = 16, | ||
| 676 | .mask = 0xff, | ||
| 677 | .def = 0xe8, | ||
| 678 | }, | ||
| 679 | }, { | ||
| 680 | .id = 0x3e, | ||
| 681 | .name = "vdebsevw", | ||
| 682 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 683 | .smmu = { | ||
| 684 | .reg = 0x22c, | ||
| 685 | .bit = 30, | ||
| 686 | }, | ||
| 687 | .la = { | ||
| 688 | .reg = 0x35c, | ||
| 689 | .shift = 0, | ||
| 690 | .mask = 0xff, | ||
| 691 | .def = 0xff, | ||
| 692 | }, | ||
| 693 | }, { | ||
| 694 | .id = 0x3f, | ||
| 695 | .name = "vdedbgw", | ||
| 696 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 697 | .smmu = { | ||
| 698 | .reg = 0x22c, | ||
| 699 | .bit = 31, | ||
| 700 | }, | ||
| 701 | .la = { | ||
| 702 | .reg = 0x35c, | ||
| 703 | .shift = 16, | ||
| 704 | .mask = 0xff, | ||
| 705 | .def = 0xff, | ||
| 706 | }, | ||
| 707 | }, { | ||
| 708 | .id = 0x40, | ||
| 709 | .name = "vdembew", | ||
| 710 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 711 | .smmu = { | ||
| 712 | .reg = 0x230, | ||
| 713 | .bit = 0, | ||
| 714 | }, | ||
| 715 | .la = { | ||
| 716 | .reg = 0x360, | ||
| 717 | .shift = 0, | ||
| 718 | .mask = 0xff, | ||
| 719 | .def = 0x89, | ||
| 720 | }, | ||
| 721 | }, { | ||
| 722 | .id = 0x41, | ||
| 723 | .name = "vdetpmw", | ||
| 724 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 725 | .smmu = { | ||
| 726 | .reg = 0x230, | ||
| 727 | .bit = 1, | ||
| 728 | }, | ||
| 729 | .la = { | ||
| 730 | .reg = 0x360, | ||
| 731 | .shift = 16, | ||
| 732 | .mask = 0xff, | ||
| 733 | .def = 0x59, | ||
| 734 | }, | ||
| 735 | }, { | ||
| 736 | .id = 0x4a, | ||
| 737 | .name = "xusb_hostr", | ||
| 738 | .swgroup = TEGRA_SWGROUP_XUSB_HOST, | ||
| 739 | .smmu = { | ||
| 740 | .reg = 0x230, | ||
| 741 | .bit = 10, | ||
| 742 | }, | ||
| 743 | .la = { | ||
| 744 | .reg = 0x37c, | ||
| 745 | .shift = 0, | ||
| 746 | .mask = 0xff, | ||
| 747 | .def = 0xa5, | ||
| 748 | }, | ||
| 749 | }, { | ||
| 750 | .id = 0x4b, | ||
| 751 | .name = "xusb_hostw", | ||
| 752 | .swgroup = TEGRA_SWGROUP_XUSB_HOST, | ||
| 753 | .smmu = { | ||
| 754 | .reg = 0x230, | ||
| 755 | .bit = 11, | ||
| 756 | }, | ||
| 757 | .la = { | ||
| 758 | .reg = 0x37c, | ||
| 759 | .shift = 16, | ||
| 760 | .mask = 0xff, | ||
| 761 | .def = 0xa5, | ||
| 762 | }, | ||
| 763 | }, { | ||
| 764 | .id = 0x4c, | ||
| 765 | .name = "xusb_devr", | ||
| 766 | .swgroup = TEGRA_SWGROUP_XUSB_DEV, | ||
| 767 | .smmu = { | ||
| 768 | .reg = 0x230, | ||
| 769 | .bit = 12, | ||
| 770 | }, | ||
| 771 | .la = { | ||
| 772 | .reg = 0x380, | ||
| 773 | .shift = 0, | ||
| 774 | .mask = 0xff, | ||
| 775 | .def = 0xa5, | ||
| 776 | }, | ||
| 777 | }, { | ||
| 778 | .id = 0x4d, | ||
| 779 | .name = "xusb_devw", | ||
| 780 | .swgroup = TEGRA_SWGROUP_XUSB_DEV, | ||
| 781 | .smmu = { | ||
| 782 | .reg = 0x230, | ||
| 783 | .bit = 13, | ||
| 784 | }, | ||
| 785 | .la = { | ||
| 786 | .reg = 0x380, | ||
| 787 | .shift = 16, | ||
| 788 | .mask = 0xff, | ||
| 789 | .def = 0xa5, | ||
| 790 | }, | ||
| 791 | }, { | ||
| 792 | .id = 0x4e, | ||
| 793 | .name = "fdcdwr3", | ||
| 794 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 795 | .smmu = { | ||
| 796 | .reg = 0x230, | ||
| 797 | .bit = 14, | ||
| 798 | }, | ||
| 799 | .la = { | ||
| 800 | .reg = 0x388, | ||
| 801 | .shift = 0, | ||
| 802 | .mask = 0xff, | ||
| 803 | .def = 0x10, | ||
| 804 | }, | ||
| 805 | }, { | ||
| 806 | .id = 0x4f, | ||
| 807 | .name = "fdcdrd3", | ||
| 808 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 809 | .smmu = { | ||
| 810 | .reg = 0x230, | ||
| 811 | .bit = 15, | ||
| 812 | }, | ||
| 813 | .la = { | ||
| 814 | .reg = 0x384, | ||
| 815 | .shift = 0, | ||
| 816 | .mask = 0xff, | ||
| 817 | .def = 0x0c, | ||
| 818 | }, | ||
| 819 | }, { | ||
| 820 | .id = 0x50, | ||
| 821 | .name = "fdcwr4", | ||
| 822 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 823 | .smmu = { | ||
| 824 | .reg = 0x230, | ||
| 825 | .bit = 16, | ||
| 826 | }, | ||
| 827 | .la = { | ||
| 828 | .reg = 0x388, | ||
| 829 | .shift = 16, | ||
| 830 | .mask = 0xff, | ||
| 831 | .def = 0x10, | ||
| 832 | }, | ||
| 833 | }, { | ||
| 834 | .id = 0x51, | ||
| 835 | .name = "fdcrd4", | ||
| 836 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 837 | .smmu = { | ||
| 838 | .reg = 0x230, | ||
| 839 | .bit = 17, | ||
| 840 | }, | ||
| 841 | .la = { | ||
| 842 | .reg = 0x384, | ||
| 843 | .shift = 16, | ||
| 844 | .mask = 0xff, | ||
| 845 | .def = 0x0c, | ||
| 846 | }, | ||
| 847 | }, { | ||
| 848 | .id = 0x52, | ||
| 849 | .name = "emucifr", | ||
| 850 | .swgroup = TEGRA_SWGROUP_EMUCIF, | ||
| 851 | .la = { | ||
| 852 | .reg = 0x38c, | ||
| 853 | .shift = 0, | ||
| 854 | .mask = 0xff, | ||
| 855 | .def = 0x04, | ||
| 856 | }, | ||
| 857 | }, { | ||
| 858 | .id = 0x53, | ||
| 859 | .name = "emucifw", | ||
| 860 | .swgroup = TEGRA_SWGROUP_EMUCIF, | ||
| 861 | .la = { | ||
| 862 | .reg = 0x38c, | ||
| 863 | .shift = 16, | ||
| 864 | .mask = 0xff, | ||
| 865 | .def = 0x0e, | ||
| 866 | }, | ||
| 867 | }, { | ||
| 868 | .id = 0x54, | ||
| 869 | .name = "tsecsrd", | ||
| 870 | .swgroup = TEGRA_SWGROUP_TSEC, | ||
| 871 | .smmu = { | ||
| 872 | .reg = 0x230, | ||
| 873 | .bit = 20, | ||
| 874 | }, | ||
| 875 | .la = { | ||
| 876 | .reg = 0x390, | ||
| 877 | .shift = 0, | ||
| 878 | .mask = 0xff, | ||
| 879 | .def = 0x50, | ||
| 880 | }, | ||
| 881 | }, { | ||
| 882 | .id = 0x55, | ||
| 883 | .name = "tsecswr", | ||
| 884 | .swgroup = TEGRA_SWGROUP_TSEC, | ||
| 885 | .smmu = { | ||
| 886 | .reg = 0x230, | ||
| 887 | .bit = 21, | ||
| 888 | }, | ||
| 889 | .la = { | ||
| 890 | .reg = 0x390, | ||
| 891 | .shift = 16, | ||
| 892 | .mask = 0xff, | ||
| 893 | .def = 0x50, | ||
| 894 | }, | ||
| 895 | }, | ||
| 896 | }; | ||
| 897 | |||
| 898 | static const struct tegra_smmu_swgroup tegra114_swgroups[] = { | ||
| 899 | { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, | ||
| 900 | { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, | ||
| 901 | { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, | ||
| 902 | { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, | ||
| 903 | { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, | ||
| 904 | { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, | ||
| 905 | { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, | ||
| 906 | { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, | ||
| 907 | { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, | ||
| 908 | { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, | ||
| 909 | { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, | ||
| 910 | { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, | ||
| 911 | { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, | ||
| 912 | { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, | ||
| 913 | { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, | ||
| 914 | { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, | ||
| 915 | }; | ||
| 916 | |||
| 917 | static void tegra114_flush_dcache(struct page *page, unsigned long offset, | ||
| 918 | size_t size) | ||
| 919 | { | ||
| 920 | phys_addr_t phys = page_to_phys(page) + offset; | ||
| 921 | void *virt = page_address(page) + offset; | ||
| 922 | |||
| 923 | __cpuc_flush_dcache_area(virt, size); | ||
| 924 | outer_flush_range(phys, phys + size); | ||
| 925 | } | ||
| 926 | |||
| 927 | static const struct tegra_smmu_ops tegra114_smmu_ops = { | ||
| 928 | .flush_dcache = tegra114_flush_dcache, | ||
| 929 | }; | ||
| 930 | |||
| 931 | static const struct tegra_smmu_soc tegra114_smmu_soc = { | ||
| 932 | .clients = tegra114_mc_clients, | ||
| 933 | .num_clients = ARRAY_SIZE(tegra114_mc_clients), | ||
| 934 | .swgroups = tegra114_swgroups, | ||
| 935 | .num_swgroups = ARRAY_SIZE(tegra114_swgroups), | ||
| 936 | .supports_round_robin_arbitration = false, | ||
| 937 | .supports_request_limit = false, | ||
| 938 | .num_asids = 4, | ||
| 939 | .ops = &tegra114_smmu_ops, | ||
| 940 | }; | ||
| 941 | |||
| 942 | const struct tegra_mc_soc tegra114_mc_soc = { | ||
| 943 | .clients = tegra114_mc_clients, | ||
| 944 | .num_clients = ARRAY_SIZE(tegra114_mc_clients), | ||
| 945 | .num_address_bits = 32, | ||
| 946 | .atom_size = 32, | ||
| 947 | .smmu = &tegra114_smmu_soc, | ||
| 948 | }; | ||
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c new file mode 100644 index 000000000000..278d40b854c1 --- /dev/null +++ b/drivers/memory/tegra/tegra124.c | |||
| @@ -0,0 +1,995 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/mm.h> | ||
| 11 | |||
| 12 | #include <asm/cacheflush.h> | ||
| 13 | |||
| 14 | #include <dt-bindings/memory/tegra124-mc.h> | ||
| 15 | |||
| 16 | #include "mc.h" | ||
| 17 | |||
| 18 | static const struct tegra_mc_client tegra124_mc_clients[] = { | ||
| 19 | { | ||
| 20 | .id = 0x00, | ||
| 21 | .name = "ptcr", | ||
| 22 | .swgroup = TEGRA_SWGROUP_PTC, | ||
| 23 | }, { | ||
| 24 | .id = 0x01, | ||
| 25 | .name = "display0a", | ||
| 26 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 27 | .smmu = { | ||
| 28 | .reg = 0x228, | ||
| 29 | .bit = 1, | ||
| 30 | }, | ||
| 31 | .la = { | ||
| 32 | .reg = 0x2e8, | ||
| 33 | .shift = 0, | ||
| 34 | .mask = 0xff, | ||
| 35 | .def = 0xc2, | ||
| 36 | }, | ||
| 37 | }, { | ||
| 38 | .id = 0x02, | ||
| 39 | .name = "display0ab", | ||
| 40 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 41 | .smmu = { | ||
| 42 | .reg = 0x228, | ||
| 43 | .bit = 2, | ||
| 44 | }, | ||
| 45 | .la = { | ||
| 46 | .reg = 0x2f4, | ||
| 47 | .shift = 0, | ||
| 48 | .mask = 0xff, | ||
| 49 | .def = 0xc6, | ||
| 50 | }, | ||
| 51 | }, { | ||
| 52 | .id = 0x03, | ||
| 53 | .name = "display0b", | ||
| 54 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 55 | .smmu = { | ||
| 56 | .reg = 0x228, | ||
| 57 | .bit = 3, | ||
| 58 | }, | ||
| 59 | .la = { | ||
| 60 | .reg = 0x2e8, | ||
| 61 | .shift = 16, | ||
| 62 | .mask = 0xff, | ||
| 63 | .def = 0x50, | ||
| 64 | }, | ||
| 65 | }, { | ||
| 66 | .id = 0x04, | ||
| 67 | .name = "display0bb", | ||
| 68 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 69 | .smmu = { | ||
| 70 | .reg = 0x228, | ||
| 71 | .bit = 4, | ||
| 72 | }, | ||
| 73 | .la = { | ||
| 74 | .reg = 0x2f4, | ||
| 75 | .shift = 16, | ||
| 76 | .mask = 0xff, | ||
| 77 | .def = 0x50, | ||
| 78 | }, | ||
| 79 | }, { | ||
| 80 | .id = 0x05, | ||
| 81 | .name = "display0c", | ||
| 82 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 83 | .smmu = { | ||
| 84 | .reg = 0x228, | ||
| 85 | .bit = 5, | ||
| 86 | }, | ||
| 87 | .la = { | ||
| 88 | .reg = 0x2ec, | ||
| 89 | .shift = 0, | ||
| 90 | .mask = 0xff, | ||
| 91 | .def = 0x50, | ||
| 92 | }, | ||
| 93 | }, { | ||
| 94 | .id = 0x06, | ||
| 95 | .name = "display0cb", | ||
| 96 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 97 | .smmu = { | ||
| 98 | .reg = 0x228, | ||
| 99 | .bit = 6, | ||
| 100 | }, | ||
| 101 | .la = { | ||
| 102 | .reg = 0x2f8, | ||
| 103 | .shift = 0, | ||
| 104 | .mask = 0xff, | ||
| 105 | .def = 0x50, | ||
| 106 | }, | ||
| 107 | }, { | ||
| 108 | .id = 0x0e, | ||
| 109 | .name = "afir", | ||
| 110 | .swgroup = TEGRA_SWGROUP_AFI, | ||
| 111 | .smmu = { | ||
| 112 | .reg = 0x228, | ||
| 113 | .bit = 14, | ||
| 114 | }, | ||
| 115 | .la = { | ||
| 116 | .reg = 0x2e0, | ||
| 117 | .shift = 0, | ||
| 118 | .mask = 0xff, | ||
| 119 | .def = 0x13, | ||
| 120 | }, | ||
| 121 | }, { | ||
| 122 | .id = 0x0f, | ||
| 123 | .name = "avpcarm7r", | ||
| 124 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 125 | .smmu = { | ||
| 126 | .reg = 0x228, | ||
| 127 | .bit = 15, | ||
| 128 | }, | ||
| 129 | .la = { | ||
| 130 | .reg = 0x2e4, | ||
| 131 | .shift = 0, | ||
| 132 | .mask = 0xff, | ||
| 133 | .def = 0x04, | ||
| 134 | }, | ||
| 135 | }, { | ||
| 136 | .id = 0x10, | ||
| 137 | .name = "displayhc", | ||
| 138 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 139 | .smmu = { | ||
| 140 | .reg = 0x228, | ||
| 141 | .bit = 16, | ||
| 142 | }, | ||
| 143 | .la = { | ||
| 144 | .reg = 0x2f0, | ||
| 145 | .shift = 0, | ||
| 146 | .mask = 0xff, | ||
| 147 | .def = 0x50, | ||
| 148 | }, | ||
| 149 | }, { | ||
| 150 | .id = 0x11, | ||
| 151 | .name = "displayhcb", | ||
| 152 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 153 | .smmu = { | ||
| 154 | .reg = 0x228, | ||
| 155 | .bit = 17, | ||
| 156 | }, | ||
| 157 | .la = { | ||
| 158 | .reg = 0x2fc, | ||
| 159 | .shift = 0, | ||
| 160 | .mask = 0xff, | ||
| 161 | .def = 0x50, | ||
| 162 | }, | ||
| 163 | }, { | ||
| 164 | .id = 0x15, | ||
| 165 | .name = "hdar", | ||
| 166 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 167 | .smmu = { | ||
| 168 | .reg = 0x228, | ||
| 169 | .bit = 21, | ||
| 170 | }, | ||
| 171 | .la = { | ||
| 172 | .reg = 0x318, | ||
| 173 | .shift = 0, | ||
| 174 | .mask = 0xff, | ||
| 175 | .def = 0x24, | ||
| 176 | }, | ||
| 177 | }, { | ||
| 178 | .id = 0x16, | ||
| 179 | .name = "host1xdmar", | ||
| 180 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 181 | .smmu = { | ||
| 182 | .reg = 0x228, | ||
| 183 | .bit = 22, | ||
| 184 | }, | ||
| 185 | .la = { | ||
| 186 | .reg = 0x310, | ||
| 187 | .shift = 0, | ||
| 188 | .mask = 0xff, | ||
| 189 | .def = 0x1e, | ||
| 190 | }, | ||
| 191 | }, { | ||
| 192 | .id = 0x17, | ||
| 193 | .name = "host1xr", | ||
| 194 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 195 | .smmu = { | ||
| 196 | .reg = 0x228, | ||
| 197 | .bit = 23, | ||
| 198 | }, | ||
| 199 | .la = { | ||
| 200 | .reg = 0x310, | ||
| 201 | .shift = 16, | ||
| 202 | .mask = 0xff, | ||
| 203 | .def = 0x50, | ||
| 204 | }, | ||
| 205 | }, { | ||
| 206 | .id = 0x1c, | ||
| 207 | .name = "msencsrd", | ||
| 208 | .swgroup = TEGRA_SWGROUP_MSENC, | ||
| 209 | .smmu = { | ||
| 210 | .reg = 0x228, | ||
| 211 | .bit = 28, | ||
| 212 | }, | ||
| 213 | .la = { | ||
| 214 | .reg = 0x328, | ||
| 215 | .shift = 0, | ||
| 216 | .mask = 0xff, | ||
| 217 | .def = 0x23, | ||
| 218 | }, | ||
| 219 | }, { | ||
| 220 | .id = 0x1d, | ||
| 221 | .name = "ppcsahbdmar", | ||
| 222 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 223 | .smmu = { | ||
| 224 | .reg = 0x228, | ||
| 225 | .bit = 29, | ||
| 226 | }, | ||
| 227 | .la = { | ||
| 228 | .reg = 0x344, | ||
| 229 | .shift = 0, | ||
| 230 | .mask = 0xff, | ||
| 231 | .def = 0x49, | ||
| 232 | }, | ||
| 233 | }, { | ||
| 234 | .id = 0x1e, | ||
| 235 | .name = "ppcsahbslvr", | ||
| 236 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 237 | .smmu = { | ||
| 238 | .reg = 0x228, | ||
| 239 | .bit = 30, | ||
| 240 | }, | ||
| 241 | .la = { | ||
| 242 | .reg = 0x344, | ||
| 243 | .shift = 16, | ||
| 244 | .mask = 0xff, | ||
| 245 | .def = 0x1a, | ||
| 246 | }, | ||
| 247 | }, { | ||
| 248 | .id = 0x1f, | ||
| 249 | .name = "satar", | ||
| 250 | .swgroup = TEGRA_SWGROUP_SATA, | ||
| 251 | .smmu = { | ||
| 252 | .reg = 0x228, | ||
| 253 | .bit = 31, | ||
| 254 | }, | ||
| 255 | .la = { | ||
| 256 | .reg = 0x350, | ||
| 257 | .shift = 0, | ||
| 258 | .mask = 0xff, | ||
| 259 | .def = 0x65, | ||
| 260 | }, | ||
| 261 | }, { | ||
| 262 | .id = 0x22, | ||
| 263 | .name = "vdebsevr", | ||
| 264 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 265 | .smmu = { | ||
| 266 | .reg = 0x22c, | ||
| 267 | .bit = 2, | ||
| 268 | }, | ||
| 269 | .la = { | ||
| 270 | .reg = 0x354, | ||
| 271 | .shift = 0, | ||
| 272 | .mask = 0xff, | ||
| 273 | .def = 0x4f, | ||
| 274 | }, | ||
| 275 | }, { | ||
| 276 | .id = 0x23, | ||
| 277 | .name = "vdember", | ||
| 278 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 279 | .smmu = { | ||
| 280 | .reg = 0x22c, | ||
| 281 | .bit = 3, | ||
| 282 | }, | ||
| 283 | .la = { | ||
| 284 | .reg = 0x354, | ||
| 285 | .shift = 16, | ||
| 286 | .mask = 0xff, | ||
| 287 | .def = 0x3d, | ||
| 288 | }, | ||
| 289 | }, { | ||
| 290 | .id = 0x24, | ||
| 291 | .name = "vdemcer", | ||
| 292 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 293 | .smmu = { | ||
| 294 | .reg = 0x22c, | ||
| 295 | .bit = 4, | ||
| 296 | }, | ||
| 297 | .la = { | ||
| 298 | .reg = 0x358, | ||
| 299 | .shift = 0, | ||
| 300 | .mask = 0xff, | ||
| 301 | .def = 0x66, | ||
| 302 | }, | ||
| 303 | }, { | ||
| 304 | .id = 0x25, | ||
| 305 | .name = "vdetper", | ||
| 306 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 307 | .smmu = { | ||
| 308 | .reg = 0x22c, | ||
| 309 | .bit = 5, | ||
| 310 | }, | ||
| 311 | .la = { | ||
| 312 | .reg = 0x358, | ||
| 313 | .shift = 16, | ||
| 314 | .mask = 0xff, | ||
| 315 | .def = 0xa5, | ||
| 316 | }, | ||
| 317 | }, { | ||
| 318 | .id = 0x26, | ||
| 319 | .name = "mpcorelpr", | ||
| 320 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 321 | .la = { | ||
| 322 | .reg = 0x324, | ||
| 323 | .shift = 0, | ||
| 324 | .mask = 0xff, | ||
| 325 | .def = 0x04, | ||
| 326 | }, | ||
| 327 | }, { | ||
| 328 | .id = 0x27, | ||
| 329 | .name = "mpcorer", | ||
| 330 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 331 | .la = { | ||
| 332 | .reg = 0x320, | ||
| 333 | .shift = 0, | ||
| 334 | .mask = 0xff, | ||
| 335 | .def = 0x04, | ||
| 336 | }, | ||
| 337 | }, { | ||
| 338 | .id = 0x2b, | ||
| 339 | .name = "msencswr", | ||
| 340 | .swgroup = TEGRA_SWGROUP_MSENC, | ||
| 341 | .smmu = { | ||
| 342 | .reg = 0x22c, | ||
| 343 | .bit = 11, | ||
| 344 | }, | ||
| 345 | .la = { | ||
| 346 | .reg = 0x328, | ||
| 347 | .shift = 16, | ||
| 348 | .mask = 0xff, | ||
| 349 | .def = 0x80, | ||
| 350 | }, | ||
| 351 | }, { | ||
| 352 | .id = 0x31, | ||
| 353 | .name = "afiw", | ||
| 354 | .swgroup = TEGRA_SWGROUP_AFI, | ||
| 355 | .smmu = { | ||
| 356 | .reg = 0x22c, | ||
| 357 | .bit = 17, | ||
| 358 | }, | ||
| 359 | .la = { | ||
| 360 | .reg = 0x2e0, | ||
| 361 | .shift = 16, | ||
| 362 | .mask = 0xff, | ||
| 363 | .def = 0x80, | ||
| 364 | }, | ||
| 365 | }, { | ||
| 366 | .id = 0x32, | ||
| 367 | .name = "avpcarm7w", | ||
| 368 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 369 | .smmu = { | ||
| 370 | .reg = 0x22c, | ||
| 371 | .bit = 18, | ||
| 372 | }, | ||
| 373 | .la = { | ||
| 374 | .reg = 0x2e4, | ||
| 375 | .shift = 16, | ||
| 376 | .mask = 0xff, | ||
| 377 | .def = 0x80, | ||
| 378 | }, | ||
| 379 | }, { | ||
| 380 | .id = 0x35, | ||
| 381 | .name = "hdaw", | ||
| 382 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 383 | .smmu = { | ||
| 384 | .reg = 0x22c, | ||
| 385 | .bit = 21, | ||
| 386 | }, | ||
| 387 | .la = { | ||
| 388 | .reg = 0x318, | ||
| 389 | .shift = 16, | ||
| 390 | .mask = 0xff, | ||
| 391 | .def = 0x80, | ||
| 392 | }, | ||
| 393 | }, { | ||
| 394 | .id = 0x36, | ||
| 395 | .name = "host1xw", | ||
| 396 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 397 | .smmu = { | ||
| 398 | .reg = 0x22c, | ||
| 399 | .bit = 22, | ||
| 400 | }, | ||
| 401 | .la = { | ||
| 402 | .reg = 0x314, | ||
| 403 | .shift = 0, | ||
| 404 | .mask = 0xff, | ||
| 405 | .def = 0x80, | ||
| 406 | }, | ||
| 407 | }, { | ||
| 408 | .id = 0x38, | ||
| 409 | .name = "mpcorelpw", | ||
| 410 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 411 | .la = { | ||
| 412 | .reg = 0x324, | ||
| 413 | .shift = 16, | ||
| 414 | .mask = 0xff, | ||
| 415 | .def = 0x80, | ||
| 416 | }, | ||
| 417 | }, { | ||
| 418 | .id = 0x39, | ||
| 419 | .name = "mpcorew", | ||
| 420 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 421 | .la = { | ||
| 422 | .reg = 0x320, | ||
| 423 | .shift = 16, | ||
| 424 | .mask = 0xff, | ||
| 425 | .def = 0x80, | ||
| 426 | }, | ||
| 427 | }, { | ||
| 428 | .id = 0x3b, | ||
| 429 | .name = "ppcsahbdmaw", | ||
| 430 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 431 | .smmu = { | ||
| 432 | .reg = 0x22c, | ||
| 433 | .bit = 27, | ||
| 434 | }, | ||
| 435 | .la = { | ||
| 436 | .reg = 0x348, | ||
| 437 | .shift = 0, | ||
| 438 | .mask = 0xff, | ||
| 439 | .def = 0x80, | ||
| 440 | }, | ||
| 441 | }, { | ||
| 442 | .id = 0x3c, | ||
| 443 | .name = "ppcsahbslvw", | ||
| 444 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 445 | .smmu = { | ||
| 446 | .reg = 0x22c, | ||
| 447 | .bit = 28, | ||
| 448 | }, | ||
| 449 | .la = { | ||
| 450 | .reg = 0x348, | ||
| 451 | .shift = 16, | ||
| 452 | .mask = 0xff, | ||
| 453 | .def = 0x80, | ||
| 454 | }, | ||
| 455 | }, { | ||
| 456 | .id = 0x3d, | ||
| 457 | .name = "sataw", | ||
| 458 | .swgroup = TEGRA_SWGROUP_SATA, | ||
| 459 | .smmu = { | ||
| 460 | .reg = 0x22c, | ||
| 461 | .bit = 29, | ||
| 462 | }, | ||
| 463 | .la = { | ||
| 464 | .reg = 0x350, | ||
| 465 | .shift = 16, | ||
| 466 | .mask = 0xff, | ||
| 467 | .def = 0x65, | ||
| 468 | }, | ||
| 469 | }, { | ||
| 470 | .id = 0x3e, | ||
| 471 | .name = "vdebsevw", | ||
| 472 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 473 | .smmu = { | ||
| 474 | .reg = 0x22c, | ||
| 475 | .bit = 30, | ||
| 476 | }, | ||
| 477 | .la = { | ||
| 478 | .reg = 0x35c, | ||
| 479 | .shift = 0, | ||
| 480 | .mask = 0xff, | ||
| 481 | .def = 0x80, | ||
| 482 | }, | ||
| 483 | }, { | ||
| 484 | .id = 0x3f, | ||
| 485 | .name = "vdedbgw", | ||
| 486 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 487 | .smmu = { | ||
| 488 | .reg = 0x22c, | ||
| 489 | .bit = 31, | ||
| 490 | }, | ||
| 491 | .la = { | ||
| 492 | .reg = 0x35c, | ||
| 493 | .shift = 16, | ||
| 494 | .mask = 0xff, | ||
| 495 | .def = 0x80, | ||
| 496 | }, | ||
| 497 | }, { | ||
| 498 | .id = 0x40, | ||
| 499 | .name = "vdembew", | ||
| 500 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 501 | .smmu = { | ||
| 502 | .reg = 0x230, | ||
| 503 | .bit = 0, | ||
| 504 | }, | ||
| 505 | .la = { | ||
| 506 | .reg = 0x360, | ||
| 507 | .shift = 0, | ||
| 508 | .mask = 0xff, | ||
| 509 | .def = 0x80, | ||
| 510 | }, | ||
| 511 | }, { | ||
| 512 | .id = 0x41, | ||
| 513 | .name = "vdetpmw", | ||
| 514 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 515 | .smmu = { | ||
| 516 | .reg = 0x230, | ||
| 517 | .bit = 1, | ||
| 518 | }, | ||
| 519 | .la = { | ||
| 520 | .reg = 0x360, | ||
| 521 | .shift = 16, | ||
| 522 | .mask = 0xff, | ||
| 523 | .def = 0x80, | ||
| 524 | }, | ||
| 525 | }, { | ||
| 526 | .id = 0x44, | ||
| 527 | .name = "ispra", | ||
| 528 | .swgroup = TEGRA_SWGROUP_ISP2, | ||
| 529 | .smmu = { | ||
| 530 | .reg = 0x230, | ||
| 531 | .bit = 4, | ||
| 532 | }, | ||
| 533 | .la = { | ||
| 534 | .reg = 0x370, | ||
| 535 | .shift = 0, | ||
| 536 | .mask = 0xff, | ||
| 537 | .def = 0x18, | ||
| 538 | }, | ||
| 539 | }, { | ||
| 540 | .id = 0x46, | ||
| 541 | .name = "ispwa", | ||
| 542 | .swgroup = TEGRA_SWGROUP_ISP2, | ||
| 543 | .smmu = { | ||
| 544 | .reg = 0x230, | ||
| 545 | .bit = 6, | ||
| 546 | }, | ||
| 547 | .la = { | ||
| 548 | .reg = 0x374, | ||
| 549 | .shift = 0, | ||
| 550 | .mask = 0xff, | ||
| 551 | .def = 0x80, | ||
| 552 | }, | ||
| 553 | }, { | ||
| 554 | .id = 0x47, | ||
| 555 | .name = "ispwb", | ||
| 556 | .swgroup = TEGRA_SWGROUP_ISP2, | ||
| 557 | .smmu = { | ||
| 558 | .reg = 0x230, | ||
| 559 | .bit = 7, | ||
| 560 | }, | ||
| 561 | .la = { | ||
| 562 | .reg = 0x374, | ||
| 563 | .shift = 16, | ||
| 564 | .mask = 0xff, | ||
| 565 | .def = 0x80, | ||
| 566 | }, | ||
| 567 | }, { | ||
| 568 | .id = 0x4a, | ||
| 569 | .name = "xusb_hostr", | ||
| 570 | .swgroup = TEGRA_SWGROUP_XUSB_HOST, | ||
| 571 | .smmu = { | ||
| 572 | .reg = 0x230, | ||
| 573 | .bit = 10, | ||
| 574 | }, | ||
| 575 | .la = { | ||
| 576 | .reg = 0x37c, | ||
| 577 | .shift = 0, | ||
| 578 | .mask = 0xff, | ||
| 579 | .def = 0x39, | ||
| 580 | }, | ||
| 581 | }, { | ||
| 582 | .id = 0x4b, | ||
| 583 | .name = "xusb_hostw", | ||
| 584 | .swgroup = TEGRA_SWGROUP_XUSB_HOST, | ||
| 585 | .smmu = { | ||
| 586 | .reg = 0x230, | ||
| 587 | .bit = 11, | ||
| 588 | }, | ||
| 589 | .la = { | ||
| 590 | .reg = 0x37c, | ||
| 591 | .shift = 16, | ||
| 592 | .mask = 0xff, | ||
| 593 | .def = 0x80, | ||
| 594 | }, | ||
| 595 | }, { | ||
| 596 | .id = 0x4c, | ||
| 597 | .name = "xusb_devr", | ||
| 598 | .swgroup = TEGRA_SWGROUP_XUSB_DEV, | ||
| 599 | .smmu = { | ||
| 600 | .reg = 0x230, | ||
| 601 | .bit = 12, | ||
| 602 | }, | ||
| 603 | .la = { | ||
| 604 | .reg = 0x380, | ||
| 605 | .shift = 0, | ||
| 606 | .mask = 0xff, | ||
| 607 | .def = 0x39, | ||
| 608 | }, | ||
| 609 | }, { | ||
| 610 | .id = 0x4d, | ||
| 611 | .name = "xusb_devw", | ||
| 612 | .swgroup = TEGRA_SWGROUP_XUSB_DEV, | ||
| 613 | .smmu = { | ||
| 614 | .reg = 0x230, | ||
| 615 | .bit = 13, | ||
| 616 | }, | ||
| 617 | .la = { | ||
| 618 | .reg = 0x380, | ||
| 619 | .shift = 16, | ||
| 620 | .mask = 0xff, | ||
| 621 | .def = 0x80, | ||
| 622 | }, | ||
| 623 | }, { | ||
| 624 | .id = 0x4e, | ||
| 625 | .name = "isprab", | ||
| 626 | .swgroup = TEGRA_SWGROUP_ISP2B, | ||
| 627 | .smmu = { | ||
| 628 | .reg = 0x230, | ||
| 629 | .bit = 14, | ||
| 630 | }, | ||
| 631 | .la = { | ||
| 632 | .reg = 0x384, | ||
| 633 | .shift = 0, | ||
| 634 | .mask = 0xff, | ||
| 635 | .def = 0x18, | ||
| 636 | }, | ||
| 637 | }, { | ||
| 638 | .id = 0x50, | ||
| 639 | .name = "ispwab", | ||
| 640 | .swgroup = TEGRA_SWGROUP_ISP2B, | ||
| 641 | .smmu = { | ||
| 642 | .reg = 0x230, | ||
| 643 | .bit = 16, | ||
| 644 | }, | ||
| 645 | .la = { | ||
| 646 | .reg = 0x388, | ||
| 647 | .shift = 0, | ||
| 648 | .mask = 0xff, | ||
| 649 | .def = 0x80, | ||
| 650 | }, | ||
| 651 | }, { | ||
| 652 | .id = 0x51, | ||
| 653 | .name = "ispwbb", | ||
| 654 | .swgroup = TEGRA_SWGROUP_ISP2B, | ||
| 655 | .smmu = { | ||
| 656 | .reg = 0x230, | ||
| 657 | .bit = 17, | ||
| 658 | }, | ||
| 659 | .la = { | ||
| 660 | .reg = 0x388, | ||
| 661 | .shift = 16, | ||
| 662 | .mask = 0xff, | ||
| 663 | .def = 0x80, | ||
| 664 | }, | ||
| 665 | }, { | ||
| 666 | .id = 0x54, | ||
| 667 | .name = "tsecsrd", | ||
| 668 | .swgroup = TEGRA_SWGROUP_TSEC, | ||
| 669 | .smmu = { | ||
| 670 | .reg = 0x230, | ||
| 671 | .bit = 20, | ||
| 672 | }, | ||
| 673 | .la = { | ||
| 674 | .reg = 0x390, | ||
| 675 | .shift = 0, | ||
| 676 | .mask = 0xff, | ||
| 677 | .def = 0x9b, | ||
| 678 | }, | ||
| 679 | }, { | ||
| 680 | .id = 0x55, | ||
| 681 | .name = "tsecswr", | ||
| 682 | .swgroup = TEGRA_SWGROUP_TSEC, | ||
| 683 | .smmu = { | ||
| 684 | .reg = 0x230, | ||
| 685 | .bit = 21, | ||
| 686 | }, | ||
| 687 | .la = { | ||
| 688 | .reg = 0x390, | ||
| 689 | .shift = 16, | ||
| 690 | .mask = 0xff, | ||
| 691 | .def = 0x80, | ||
| 692 | }, | ||
| 693 | }, { | ||
| 694 | .id = 0x56, | ||
| 695 | .name = "a9avpscr", | ||
| 696 | .swgroup = TEGRA_SWGROUP_A9AVP, | ||
| 697 | .smmu = { | ||
| 698 | .reg = 0x230, | ||
| 699 | .bit = 22, | ||
| 700 | }, | ||
| 701 | .la = { | ||
| 702 | .reg = 0x3a4, | ||
| 703 | .shift = 0, | ||
| 704 | .mask = 0xff, | ||
| 705 | .def = 0x04, | ||
| 706 | }, | ||
| 707 | }, { | ||
| 708 | .id = 0x57, | ||
| 709 | .name = "a9avpscw", | ||
| 710 | .swgroup = TEGRA_SWGROUP_A9AVP, | ||
| 711 | .smmu = { | ||
| 712 | .reg = 0x230, | ||
| 713 | .bit = 23, | ||
| 714 | }, | ||
| 715 | .la = { | ||
| 716 | .reg = 0x3a4, | ||
| 717 | .shift = 16, | ||
| 718 | .mask = 0xff, | ||
| 719 | .def = 0x80, | ||
| 720 | }, | ||
| 721 | }, { | ||
| 722 | .id = 0x58, | ||
| 723 | .name = "gpusrd", | ||
| 724 | .swgroup = TEGRA_SWGROUP_GPU, | ||
| 725 | .smmu = { | ||
| 726 | /* read-only */ | ||
| 727 | .reg = 0x230, | ||
| 728 | .bit = 24, | ||
| 729 | }, | ||
| 730 | .la = { | ||
| 731 | .reg = 0x3c8, | ||
| 732 | .shift = 0, | ||
| 733 | .mask = 0xff, | ||
| 734 | .def = 0x1a, | ||
| 735 | }, | ||
| 736 | }, { | ||
| 737 | .id = 0x59, | ||
| 738 | .name = "gpuswr", | ||
| 739 | .swgroup = TEGRA_SWGROUP_GPU, | ||
| 740 | .smmu = { | ||
| 741 | /* read-only */ | ||
| 742 | .reg = 0x230, | ||
| 743 | .bit = 25, | ||
| 744 | }, | ||
| 745 | .la = { | ||
| 746 | .reg = 0x3c8, | ||
| 747 | .shift = 16, | ||
| 748 | .mask = 0xff, | ||
| 749 | .def = 0x80, | ||
| 750 | }, | ||
| 751 | }, { | ||
| 752 | .id = 0x5a, | ||
| 753 | .name = "displayt", | ||
| 754 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 755 | .smmu = { | ||
| 756 | .reg = 0x230, | ||
| 757 | .bit = 26, | ||
| 758 | }, | ||
| 759 | .la = { | ||
| 760 | .reg = 0x2f0, | ||
| 761 | .shift = 16, | ||
| 762 | .mask = 0xff, | ||
| 763 | .def = 0x50, | ||
| 764 | }, | ||
| 765 | }, { | ||
| 766 | .id = 0x60, | ||
| 767 | .name = "sdmmcra", | ||
| 768 | .swgroup = TEGRA_SWGROUP_SDMMC1A, | ||
| 769 | .smmu = { | ||
| 770 | .reg = 0x234, | ||
| 771 | .bit = 0, | ||
| 772 | }, | ||
| 773 | .la = { | ||
| 774 | .reg = 0x3b8, | ||
| 775 | .shift = 0, | ||
| 776 | .mask = 0xff, | ||
| 777 | .def = 0x49, | ||
| 778 | }, | ||
| 779 | }, { | ||
| 780 | .id = 0x61, | ||
| 781 | .name = "sdmmcraa", | ||
| 782 | .swgroup = TEGRA_SWGROUP_SDMMC2A, | ||
| 783 | .smmu = { | ||
| 784 | .reg = 0x234, | ||
| 785 | .bit = 1, | ||
| 786 | }, | ||
| 787 | .la = { | ||
| 788 | .reg = 0x3bc, | ||
| 789 | .shift = 0, | ||
| 790 | .mask = 0xff, | ||
| 791 | .def = 0x49, | ||
| 792 | }, | ||
| 793 | }, { | ||
| 794 | .id = 0x62, | ||
| 795 | .name = "sdmmcr", | ||
| 796 | .swgroup = TEGRA_SWGROUP_SDMMC3A, | ||
| 797 | .smmu = { | ||
| 798 | .reg = 0x234, | ||
| 799 | .bit = 2, | ||
| 800 | }, | ||
| 801 | .la = { | ||
| 802 | .reg = 0x3c0, | ||
| 803 | .shift = 0, | ||
| 804 | .mask = 0xff, | ||
| 805 | .def = 0x49, | ||
| 806 | }, | ||
| 807 | }, { | ||
| 808 | .id = 0x63, | ||
| 809 | .swgroup = TEGRA_SWGROUP_SDMMC4A, | ||
| 810 | .name = "sdmmcrab", | ||
| 811 | .smmu = { | ||
| 812 | .reg = 0x234, | ||
| 813 | .bit = 3, | ||
| 814 | }, | ||
| 815 | .la = { | ||
| 816 | .reg = 0x3c4, | ||
| 817 | .shift = 0, | ||
| 818 | .mask = 0xff, | ||
| 819 | .def = 0x49, | ||
| 820 | }, | ||
| 821 | }, { | ||
| 822 | .id = 0x64, | ||
| 823 | .name = "sdmmcwa", | ||
| 824 | .swgroup = TEGRA_SWGROUP_SDMMC1A, | ||
| 825 | .smmu = { | ||
| 826 | .reg = 0x234, | ||
| 827 | .bit = 4, | ||
| 828 | }, | ||
| 829 | .la = { | ||
| 830 | .reg = 0x3b8, | ||
| 831 | .shift = 16, | ||
| 832 | .mask = 0xff, | ||
| 833 | .def = 0x80, | ||
| 834 | }, | ||
| 835 | }, { | ||
| 836 | .id = 0x65, | ||
| 837 | .name = "sdmmcwaa", | ||
| 838 | .swgroup = TEGRA_SWGROUP_SDMMC2A, | ||
| 839 | .smmu = { | ||
| 840 | .reg = 0x234, | ||
| 841 | .bit = 5, | ||
| 842 | }, | ||
| 843 | .la = { | ||
| 844 | .reg = 0x3bc, | ||
| 845 | .shift = 16, | ||
| 846 | .mask = 0xff, | ||
| 847 | .def = 0x80, | ||
| 848 | }, | ||
| 849 | }, { | ||
| 850 | .id = 0x66, | ||
| 851 | .name = "sdmmcw", | ||
| 852 | .swgroup = TEGRA_SWGROUP_SDMMC3A, | ||
| 853 | .smmu = { | ||
| 854 | .reg = 0x234, | ||
| 855 | .bit = 6, | ||
| 856 | }, | ||
| 857 | .la = { | ||
| 858 | .reg = 0x3c0, | ||
| 859 | .shift = 16, | ||
| 860 | .mask = 0xff, | ||
| 861 | .def = 0x80, | ||
| 862 | }, | ||
| 863 | }, { | ||
| 864 | .id = 0x67, | ||
| 865 | .name = "sdmmcwab", | ||
| 866 | .swgroup = TEGRA_SWGROUP_SDMMC4A, | ||
| 867 | .smmu = { | ||
| 868 | .reg = 0x234, | ||
| 869 | .bit = 7, | ||
| 870 | }, | ||
| 871 | .la = { | ||
| 872 | .reg = 0x3c4, | ||
| 873 | .shift = 16, | ||
| 874 | .mask = 0xff, | ||
| 875 | .def = 0x80, | ||
| 876 | }, | ||
| 877 | }, { | ||
| 878 | .id = 0x6c, | ||
| 879 | .name = "vicsrd", | ||
| 880 | .swgroup = TEGRA_SWGROUP_VIC, | ||
| 881 | .smmu = { | ||
| 882 | .reg = 0x234, | ||
| 883 | .bit = 12, | ||
| 884 | }, | ||
| 885 | .la = { | ||
| 886 | .reg = 0x394, | ||
| 887 | .shift = 0, | ||
| 888 | .mask = 0xff, | ||
| 889 | .def = 0x1a, | ||
| 890 | }, | ||
| 891 | }, { | ||
| 892 | .id = 0x6d, | ||
| 893 | .name = "vicswr", | ||
| 894 | .swgroup = TEGRA_SWGROUP_VIC, | ||
| 895 | .smmu = { | ||
| 896 | .reg = 0x234, | ||
| 897 | .bit = 13, | ||
| 898 | }, | ||
| 899 | .la = { | ||
| 900 | .reg = 0x394, | ||
| 901 | .shift = 16, | ||
| 902 | .mask = 0xff, | ||
| 903 | .def = 0x80, | ||
| 904 | }, | ||
| 905 | }, { | ||
| 906 | .id = 0x72, | ||
| 907 | .name = "viw", | ||
| 908 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 909 | .smmu = { | ||
| 910 | .reg = 0x234, | ||
| 911 | .bit = 18, | ||
| 912 | }, | ||
| 913 | .la = { | ||
| 914 | .reg = 0x398, | ||
| 915 | .shift = 0, | ||
| 916 | .mask = 0xff, | ||
| 917 | .def = 0x80, | ||
| 918 | }, | ||
| 919 | }, { | ||
| 920 | .id = 0x73, | ||
| 921 | .name = "displayd", | ||
| 922 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 923 | .smmu = { | ||
| 924 | .reg = 0x234, | ||
| 925 | .bit = 19, | ||
| 926 | }, | ||
| 927 | .la = { | ||
| 928 | .reg = 0x3c8, | ||
| 929 | .shift = 0, | ||
| 930 | .mask = 0xff, | ||
| 931 | .def = 0x50, | ||
| 932 | }, | ||
| 933 | }, | ||
| 934 | }; | ||
| 935 | |||
| 936 | static const struct tegra_smmu_swgroup tegra124_swgroups[] = { | ||
| 937 | { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, | ||
| 938 | { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, | ||
| 939 | { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, | ||
| 940 | { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, | ||
| 941 | { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, | ||
| 942 | { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, | ||
| 943 | { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 }, | ||
| 944 | { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, | ||
| 945 | { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 }, | ||
| 946 | { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, | ||
| 947 | { .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 }, | ||
| 948 | { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 }, | ||
| 949 | { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c }, | ||
| 950 | { .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 }, | ||
| 951 | { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, | ||
| 952 | { .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 }, | ||
| 953 | { .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac }, | ||
| 954 | { .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 }, | ||
| 955 | { .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 }, | ||
| 956 | { .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c }, | ||
| 957 | { .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 }, | ||
| 958 | { .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 }, | ||
| 959 | { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, | ||
| 960 | }; | ||
| 961 | |||
| 962 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | ||
| 963 | static void tegra124_flush_dcache(struct page *page, unsigned long offset, | ||
| 964 | size_t size) | ||
| 965 | { | ||
| 966 | phys_addr_t phys = page_to_phys(page) + offset; | ||
| 967 | void *virt = page_address(page) + offset; | ||
| 968 | |||
| 969 | __cpuc_flush_dcache_area(virt, size); | ||
| 970 | outer_flush_range(phys, phys + size); | ||
| 971 | } | ||
| 972 | |||
| 973 | static const struct tegra_smmu_ops tegra124_smmu_ops = { | ||
| 974 | .flush_dcache = tegra124_flush_dcache, | ||
| 975 | }; | ||
| 976 | |||
| 977 | static const struct tegra_smmu_soc tegra124_smmu_soc = { | ||
| 978 | .clients = tegra124_mc_clients, | ||
| 979 | .num_clients = ARRAY_SIZE(tegra124_mc_clients), | ||
| 980 | .swgroups = tegra124_swgroups, | ||
| 981 | .num_swgroups = ARRAY_SIZE(tegra124_swgroups), | ||
| 982 | .supports_round_robin_arbitration = true, | ||
| 983 | .supports_request_limit = true, | ||
| 984 | .num_asids = 128, | ||
| 985 | .ops = &tegra124_smmu_ops, | ||
| 986 | }; | ||
| 987 | |||
| 988 | const struct tegra_mc_soc tegra124_mc_soc = { | ||
| 989 | .clients = tegra124_mc_clients, | ||
| 990 | .num_clients = ARRAY_SIZE(tegra124_mc_clients), | ||
| 991 | .num_address_bits = 34, | ||
| 992 | .atom_size = 32, | ||
| 993 | .smmu = &tegra124_smmu_soc, | ||
| 994 | }; | ||
| 995 | #endif /* CONFIG_ARCH_TEGRA_124_SOC */ | ||
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c new file mode 100644 index 000000000000..71fe9376fe53 --- /dev/null +++ b/drivers/memory/tegra/tegra30.c | |||
| @@ -0,0 +1,970 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/mm.h> | ||
| 11 | |||
| 12 | #include <asm/cacheflush.h> | ||
| 13 | |||
| 14 | #include <dt-bindings/memory/tegra30-mc.h> | ||
| 15 | |||
| 16 | #include "mc.h" | ||
| 17 | |||
| 18 | static const struct tegra_mc_client tegra30_mc_clients[] = { | ||
| 19 | { | ||
| 20 | .id = 0x00, | ||
| 21 | .name = "ptcr", | ||
| 22 | .swgroup = TEGRA_SWGROUP_PTC, | ||
| 23 | }, { | ||
| 24 | .id = 0x01, | ||
| 25 | .name = "display0a", | ||
| 26 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 27 | .smmu = { | ||
| 28 | .reg = 0x228, | ||
| 29 | .bit = 1, | ||
| 30 | }, | ||
| 31 | .la = { | ||
| 32 | .reg = 0x2e8, | ||
| 33 | .shift = 0, | ||
| 34 | .mask = 0xff, | ||
| 35 | .def = 0x4e, | ||
| 36 | }, | ||
| 37 | }, { | ||
| 38 | .id = 0x02, | ||
| 39 | .name = "display0ab", | ||
| 40 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 41 | .smmu = { | ||
| 42 | .reg = 0x228, | ||
| 43 | .bit = 2, | ||
| 44 | }, | ||
| 45 | .la = { | ||
| 46 | .reg = 0x2f4, | ||
| 47 | .shift = 0, | ||
| 48 | .mask = 0xff, | ||
| 49 | .def = 0x4e, | ||
| 50 | }, | ||
| 51 | }, { | ||
| 52 | .id = 0x03, | ||
| 53 | .name = "display0b", | ||
| 54 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 55 | .smmu = { | ||
| 56 | .reg = 0x228, | ||
| 57 | .bit = 3, | ||
| 58 | }, | ||
| 59 | .la = { | ||
| 60 | .reg = 0x2e8, | ||
| 61 | .shift = 16, | ||
| 62 | .mask = 0xff, | ||
| 63 | .def = 0x4e, | ||
| 64 | }, | ||
| 65 | }, { | ||
| 66 | .id = 0x04, | ||
| 67 | .name = "display0bb", | ||
| 68 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 69 | .smmu = { | ||
| 70 | .reg = 0x228, | ||
| 71 | .bit = 4, | ||
| 72 | }, | ||
| 73 | .la = { | ||
| 74 | .reg = 0x2f4, | ||
| 75 | .shift = 16, | ||
| 76 | .mask = 0xff, | ||
| 77 | .def = 0x4e, | ||
| 78 | }, | ||
| 79 | }, { | ||
| 80 | .id = 0x05, | ||
| 81 | .name = "display0c", | ||
| 82 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 83 | .smmu = { | ||
| 84 | .reg = 0x228, | ||
| 85 | .bit = 5, | ||
| 86 | }, | ||
| 87 | .la = { | ||
| 88 | .reg = 0x2ec, | ||
| 89 | .shift = 0, | ||
| 90 | .mask = 0xff, | ||
| 91 | .def = 0x4e, | ||
| 92 | }, | ||
| 93 | }, { | ||
| 94 | .id = 0x06, | ||
| 95 | .name = "display0cb", | ||
| 96 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 97 | .smmu = { | ||
| 98 | .reg = 0x228, | ||
| 99 | .bit = 6, | ||
| 100 | }, | ||
| 101 | .la = { | ||
| 102 | .reg = 0x2f8, | ||
| 103 | .shift = 0, | ||
| 104 | .mask = 0xff, | ||
| 105 | .def = 0x4e, | ||
| 106 | }, | ||
| 107 | }, { | ||
| 108 | .id = 0x07, | ||
| 109 | .name = "display1b", | ||
| 110 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 111 | .smmu = { | ||
| 112 | .reg = 0x228, | ||
| 113 | .bit = 7, | ||
| 114 | }, | ||
| 115 | .la = { | ||
| 116 | .reg = 0x2ec, | ||
| 117 | .shift = 16, | ||
| 118 | .mask = 0xff, | ||
| 119 | .def = 0x4e, | ||
| 120 | }, | ||
| 121 | }, { | ||
| 122 | .id = 0x08, | ||
| 123 | .name = "display1bb", | ||
| 124 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 125 | .smmu = { | ||
| 126 | .reg = 0x228, | ||
| 127 | .bit = 8, | ||
| 128 | }, | ||
| 129 | .la = { | ||
| 130 | .reg = 0x2f8, | ||
| 131 | .shift = 16, | ||
| 132 | .mask = 0xff, | ||
| 133 | .def = 0x4e, | ||
| 134 | }, | ||
| 135 | }, { | ||
| 136 | .id = 0x09, | ||
| 137 | .name = "eppup", | ||
| 138 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 139 | .smmu = { | ||
| 140 | .reg = 0x228, | ||
| 141 | .bit = 9, | ||
| 142 | }, | ||
| 143 | .la = { | ||
| 144 | .reg = 0x300, | ||
| 145 | .shift = 0, | ||
| 146 | .mask = 0xff, | ||
| 147 | .def = 0x17, | ||
| 148 | }, | ||
| 149 | }, { | ||
| 150 | .id = 0x0a, | ||
| 151 | .name = "g2pr", | ||
| 152 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 153 | .smmu = { | ||
| 154 | .reg = 0x228, | ||
| 155 | .bit = 10, | ||
| 156 | }, | ||
| 157 | .la = { | ||
| 158 | .reg = 0x308, | ||
| 159 | .shift = 0, | ||
| 160 | .mask = 0xff, | ||
| 161 | .def = 0x09, | ||
| 162 | }, | ||
| 163 | }, { | ||
| 164 | .id = 0x0b, | ||
| 165 | .name = "g2sr", | ||
| 166 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 167 | .smmu = { | ||
| 168 | .reg = 0x228, | ||
| 169 | .bit = 11, | ||
| 170 | }, | ||
| 171 | .la = { | ||
| 172 | .reg = 0x308, | ||
| 173 | .shift = 16, | ||
| 174 | .mask = 0xff, | ||
| 175 | .def = 0x09, | ||
| 176 | }, | ||
| 177 | }, { | ||
| 178 | .id = 0x0c, | ||
| 179 | .name = "mpeunifbr", | ||
| 180 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 181 | .smmu = { | ||
| 182 | .reg = 0x228, | ||
| 183 | .bit = 12, | ||
| 184 | }, | ||
| 185 | .la = { | ||
| 186 | .reg = 0x328, | ||
| 187 | .shift = 0, | ||
| 188 | .mask = 0xff, | ||
| 189 | .def = 0x50, | ||
| 190 | }, | ||
| 191 | }, { | ||
| 192 | .id = 0x0d, | ||
| 193 | .name = "viruv", | ||
| 194 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 195 | .smmu = { | ||
| 196 | .reg = 0x228, | ||
| 197 | .bit = 13, | ||
| 198 | }, | ||
| 199 | .la = { | ||
| 200 | .reg = 0x364, | ||
| 201 | .shift = 0, | ||
| 202 | .mask = 0xff, | ||
| 203 | .def = 0x2c, | ||
| 204 | }, | ||
| 205 | }, { | ||
| 206 | .id = 0x0e, | ||
| 207 | .name = "afir", | ||
| 208 | .swgroup = TEGRA_SWGROUP_AFI, | ||
| 209 | .smmu = { | ||
| 210 | .reg = 0x228, | ||
| 211 | .bit = 14, | ||
| 212 | }, | ||
| 213 | .la = { | ||
| 214 | .reg = 0x2e0, | ||
| 215 | .shift = 0, | ||
| 216 | .mask = 0xff, | ||
| 217 | .def = 0x10, | ||
| 218 | }, | ||
| 219 | }, { | ||
| 220 | .id = 0x0f, | ||
| 221 | .name = "avpcarm7r", | ||
| 222 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 223 | .smmu = { | ||
| 224 | .reg = 0x228, | ||
| 225 | .bit = 15, | ||
| 226 | }, | ||
| 227 | .la = { | ||
| 228 | .reg = 0x2e4, | ||
| 229 | .shift = 0, | ||
| 230 | .mask = 0xff, | ||
| 231 | .def = 0x04, | ||
| 232 | }, | ||
| 233 | }, { | ||
| 234 | .id = 0x10, | ||
| 235 | .name = "displayhc", | ||
| 236 | .swgroup = TEGRA_SWGROUP_DC, | ||
| 237 | .smmu = { | ||
| 238 | .reg = 0x228, | ||
| 239 | .bit = 16, | ||
| 240 | }, | ||
| 241 | .la = { | ||
| 242 | .reg = 0x2f0, | ||
| 243 | .shift = 0, | ||
| 244 | .mask = 0xff, | ||
| 245 | .def = 0xff, | ||
| 246 | }, | ||
| 247 | }, { | ||
| 248 | .id = 0x11, | ||
| 249 | .name = "displayhcb", | ||
| 250 | .swgroup = TEGRA_SWGROUP_DCB, | ||
| 251 | .smmu = { | ||
| 252 | .reg = 0x228, | ||
| 253 | .bit = 17, | ||
| 254 | }, | ||
| 255 | .la = { | ||
| 256 | .reg = 0x2fc, | ||
| 257 | .shift = 0, | ||
| 258 | .mask = 0xff, | ||
| 259 | .def = 0xff, | ||
| 260 | }, | ||
| 261 | }, { | ||
| 262 | .id = 0x12, | ||
| 263 | .name = "fdcdrd", | ||
| 264 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 265 | .smmu = { | ||
| 266 | .reg = 0x228, | ||
| 267 | .bit = 18, | ||
| 268 | }, | ||
| 269 | .la = { | ||
| 270 | .reg = 0x334, | ||
| 271 | .shift = 0, | ||
| 272 | .mask = 0xff, | ||
| 273 | .def = 0x0a, | ||
| 274 | }, | ||
| 275 | }, { | ||
| 276 | .id = 0x13, | ||
| 277 | .name = "fdcdrd2", | ||
| 278 | .swgroup = TEGRA_SWGROUP_NV2, | ||
| 279 | .smmu = { | ||
| 280 | .reg = 0x228, | ||
| 281 | .bit = 19, | ||
| 282 | }, | ||
| 283 | .la = { | ||
| 284 | .reg = 0x33c, | ||
| 285 | .shift = 0, | ||
| 286 | .mask = 0xff, | ||
| 287 | .def = 0x0a, | ||
| 288 | }, | ||
| 289 | }, { | ||
| 290 | .id = 0x14, | ||
| 291 | .name = "g2dr", | ||
| 292 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 293 | .smmu = { | ||
| 294 | .reg = 0x228, | ||
| 295 | .bit = 20, | ||
| 296 | }, | ||
| 297 | .la = { | ||
| 298 | .reg = 0x30c, | ||
| 299 | .shift = 0, | ||
| 300 | .mask = 0xff, | ||
| 301 | .def = 0x0a, | ||
| 302 | }, | ||
| 303 | }, { | ||
| 304 | .id = 0x15, | ||
| 305 | .name = "hdar", | ||
| 306 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 307 | .smmu = { | ||
| 308 | .reg = 0x228, | ||
| 309 | .bit = 21, | ||
| 310 | }, | ||
| 311 | .la = { | ||
| 312 | .reg = 0x318, | ||
| 313 | .shift = 0, | ||
| 314 | .mask = 0xff, | ||
| 315 | .def = 0xff, | ||
| 316 | }, | ||
| 317 | }, { | ||
| 318 | .id = 0x16, | ||
| 319 | .name = "host1xdmar", | ||
| 320 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 321 | .smmu = { | ||
| 322 | .reg = 0x228, | ||
| 323 | .bit = 22, | ||
| 324 | }, | ||
| 325 | .la = { | ||
| 326 | .reg = 0x310, | ||
| 327 | .shift = 0, | ||
| 328 | .mask = 0xff, | ||
| 329 | .def = 0x05, | ||
| 330 | }, | ||
| 331 | }, { | ||
| 332 | .id = 0x17, | ||
| 333 | .name = "host1xr", | ||
| 334 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 335 | .smmu = { | ||
| 336 | .reg = 0x228, | ||
| 337 | .bit = 23, | ||
| 338 | }, | ||
| 339 | .la = { | ||
| 340 | .reg = 0x310, | ||
| 341 | .shift = 16, | ||
| 342 | .mask = 0xff, | ||
| 343 | .def = 0x50, | ||
| 344 | }, | ||
| 345 | }, { | ||
| 346 | .id = 0x18, | ||
| 347 | .name = "idxsrd", | ||
| 348 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 349 | .smmu = { | ||
| 350 | .reg = 0x228, | ||
| 351 | .bit = 24, | ||
| 352 | }, | ||
| 353 | .la = { | ||
| 354 | .reg = 0x334, | ||
| 355 | .shift = 16, | ||
| 356 | .mask = 0xff, | ||
| 357 | .def = 0x13, | ||
| 358 | }, | ||
| 359 | }, { | ||
| 360 | .id = 0x19, | ||
| 361 | .name = "idxsrd2", | ||
| 362 | .swgroup = TEGRA_SWGROUP_NV2, | ||
| 363 | .smmu = { | ||
| 364 | .reg = 0x228, | ||
| 365 | .bit = 25, | ||
| 366 | }, | ||
| 367 | .la = { | ||
| 368 | .reg = 0x33c, | ||
| 369 | .shift = 16, | ||
| 370 | .mask = 0xff, | ||
| 371 | .def = 0x13, | ||
| 372 | }, | ||
| 373 | }, { | ||
| 374 | .id = 0x1a, | ||
| 375 | .name = "mpe_ipred", | ||
| 376 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 377 | .smmu = { | ||
| 378 | .reg = 0x228, | ||
| 379 | .bit = 26, | ||
| 380 | }, | ||
| 381 | .la = { | ||
| 382 | .reg = 0x328, | ||
| 383 | .shift = 16, | ||
| 384 | .mask = 0xff, | ||
| 385 | .def = 0x80, | ||
| 386 | }, | ||
| 387 | }, { | ||
| 388 | .id = 0x1b, | ||
| 389 | .name = "mpeamemrd", | ||
| 390 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 391 | .smmu = { | ||
| 392 | .reg = 0x228, | ||
| 393 | .bit = 27, | ||
| 394 | }, | ||
| 395 | .la = { | ||
| 396 | .reg = 0x32c, | ||
| 397 | .shift = 0, | ||
| 398 | .mask = 0xff, | ||
| 399 | .def = 0x42, | ||
| 400 | }, | ||
| 401 | }, { | ||
| 402 | .id = 0x1c, | ||
| 403 | .name = "mpecsrd", | ||
| 404 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 405 | .smmu = { | ||
| 406 | .reg = 0x228, | ||
| 407 | .bit = 28, | ||
| 408 | }, | ||
| 409 | .la = { | ||
| 410 | .reg = 0x32c, | ||
| 411 | .shift = 16, | ||
| 412 | .mask = 0xff, | ||
| 413 | .def = 0xff, | ||
| 414 | }, | ||
| 415 | }, { | ||
| 416 | .id = 0x1d, | ||
| 417 | .name = "ppcsahbdmar", | ||
| 418 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 419 | .smmu = { | ||
| 420 | .reg = 0x228, | ||
| 421 | .bit = 29, | ||
| 422 | }, | ||
| 423 | .la = { | ||
| 424 | .reg = 0x344, | ||
| 425 | .shift = 0, | ||
| 426 | .mask = 0xff, | ||
| 427 | .def = 0x10, | ||
| 428 | }, | ||
| 429 | }, { | ||
| 430 | .id = 0x1e, | ||
| 431 | .name = "ppcsahbslvr", | ||
| 432 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 433 | .smmu = { | ||
| 434 | .reg = 0x228, | ||
| 435 | .bit = 30, | ||
| 436 | }, | ||
| 437 | .la = { | ||
| 438 | .reg = 0x344, | ||
| 439 | .shift = 16, | ||
| 440 | .mask = 0xff, | ||
| 441 | .def = 0x12, | ||
| 442 | }, | ||
| 443 | }, { | ||
| 444 | .id = 0x1f, | ||
| 445 | .name = "satar", | ||
| 446 | .swgroup = TEGRA_SWGROUP_SATA, | ||
| 447 | .smmu = { | ||
| 448 | .reg = 0x228, | ||
| 449 | .bit = 31, | ||
| 450 | }, | ||
| 451 | .la = { | ||
| 452 | .reg = 0x350, | ||
| 453 | .shift = 0, | ||
| 454 | .mask = 0xff, | ||
| 455 | .def = 0x33, | ||
| 456 | }, | ||
| 457 | }, { | ||
| 458 | .id = 0x20, | ||
| 459 | .name = "texsrd", | ||
| 460 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 461 | .smmu = { | ||
| 462 | .reg = 0x22c, | ||
| 463 | .bit = 0, | ||
| 464 | }, | ||
| 465 | .la = { | ||
| 466 | .reg = 0x338, | ||
| 467 | .shift = 0, | ||
| 468 | .mask = 0xff, | ||
| 469 | .def = 0x13, | ||
| 470 | }, | ||
| 471 | }, { | ||
| 472 | .id = 0x21, | ||
| 473 | .name = "texsrd2", | ||
| 474 | .swgroup = TEGRA_SWGROUP_NV2, | ||
| 475 | .smmu = { | ||
| 476 | .reg = 0x22c, | ||
| 477 | .bit = 1, | ||
| 478 | }, | ||
| 479 | .la = { | ||
| 480 | .reg = 0x340, | ||
| 481 | .shift = 0, | ||
| 482 | .mask = 0xff, | ||
| 483 | .def = 0x13, | ||
| 484 | }, | ||
| 485 | }, { | ||
| 486 | .id = 0x22, | ||
| 487 | .name = "vdebsevr", | ||
| 488 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 489 | .smmu = { | ||
| 490 | .reg = 0x22c, | ||
| 491 | .bit = 2, | ||
| 492 | }, | ||
| 493 | .la = { | ||
| 494 | .reg = 0x354, | ||
| 495 | .shift = 0, | ||
| 496 | .mask = 0xff, | ||
| 497 | .def = 0xff, | ||
| 498 | }, | ||
| 499 | }, { | ||
| 500 | .id = 0x23, | ||
| 501 | .name = "vdember", | ||
| 502 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 503 | .smmu = { | ||
| 504 | .reg = 0x22c, | ||
| 505 | .bit = 3, | ||
| 506 | }, | ||
| 507 | .la = { | ||
| 508 | .reg = 0x354, | ||
| 509 | .shift = 16, | ||
| 510 | .mask = 0xff, | ||
| 511 | .def = 0xd0, | ||
| 512 | }, | ||
| 513 | }, { | ||
| 514 | .id = 0x24, | ||
| 515 | .name = "vdemcer", | ||
| 516 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 517 | .smmu = { | ||
| 518 | .reg = 0x22c, | ||
| 519 | .bit = 4, | ||
| 520 | }, | ||
| 521 | .la = { | ||
| 522 | .reg = 0x358, | ||
| 523 | .shift = 0, | ||
| 524 | .mask = 0xff, | ||
| 525 | .def = 0x2a, | ||
| 526 | }, | ||
| 527 | }, { | ||
| 528 | .id = 0x25, | ||
| 529 | .name = "vdetper", | ||
| 530 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 531 | .smmu = { | ||
| 532 | .reg = 0x22c, | ||
| 533 | .bit = 5, | ||
| 534 | }, | ||
| 535 | .la = { | ||
| 536 | .reg = 0x358, | ||
| 537 | .shift = 16, | ||
| 538 | .mask = 0xff, | ||
| 539 | .def = 0x74, | ||
| 540 | }, | ||
| 541 | }, { | ||
| 542 | .id = 0x26, | ||
| 543 | .name = "mpcorelpr", | ||
| 544 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 545 | .la = { | ||
| 546 | .reg = 0x324, | ||
| 547 | .shift = 0, | ||
| 548 | .mask = 0xff, | ||
| 549 | .def = 0x04, | ||
| 550 | }, | ||
| 551 | }, { | ||
| 552 | .id = 0x27, | ||
| 553 | .name = "mpcorer", | ||
| 554 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 555 | .la = { | ||
| 556 | .reg = 0x320, | ||
| 557 | .shift = 0, | ||
| 558 | .mask = 0xff, | ||
| 559 | .def = 0x04, | ||
| 560 | }, | ||
| 561 | }, { | ||
| 562 | .id = 0x28, | ||
| 563 | .name = "eppu", | ||
| 564 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 565 | .smmu = { | ||
| 566 | .reg = 0x22c, | ||
| 567 | .bit = 8, | ||
| 568 | }, | ||
| 569 | .la = { | ||
| 570 | .reg = 0x300, | ||
| 571 | .shift = 16, | ||
| 572 | .mask = 0xff, | ||
| 573 | .def = 0x6c, | ||
| 574 | }, | ||
| 575 | }, { | ||
| 576 | .id = 0x29, | ||
| 577 | .name = "eppv", | ||
| 578 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 579 | .smmu = { | ||
| 580 | .reg = 0x22c, | ||
| 581 | .bit = 9, | ||
| 582 | }, | ||
| 583 | .la = { | ||
| 584 | .reg = 0x304, | ||
| 585 | .shift = 0, | ||
| 586 | .mask = 0xff, | ||
| 587 | .def = 0x6c, | ||
| 588 | }, | ||
| 589 | }, { | ||
| 590 | .id = 0x2a, | ||
| 591 | .name = "eppy", | ||
| 592 | .swgroup = TEGRA_SWGROUP_EPP, | ||
| 593 | .smmu = { | ||
| 594 | .reg = 0x22c, | ||
| 595 | .bit = 10, | ||
| 596 | }, | ||
| 597 | .la = { | ||
| 598 | .reg = 0x304, | ||
| 599 | .shift = 16, | ||
| 600 | .mask = 0xff, | ||
| 601 | .def = 0x6c, | ||
| 602 | }, | ||
| 603 | }, { | ||
| 604 | .id = 0x2b, | ||
| 605 | .name = "mpeunifbw", | ||
| 606 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 607 | .smmu = { | ||
| 608 | .reg = 0x22c, | ||
| 609 | .bit = 11, | ||
| 610 | }, | ||
| 611 | .la = { | ||
| 612 | .reg = 0x330, | ||
| 613 | .shift = 0, | ||
| 614 | .mask = 0xff, | ||
| 615 | .def = 0x13, | ||
| 616 | }, | ||
| 617 | }, { | ||
| 618 | .id = 0x2c, | ||
| 619 | .name = "viwsb", | ||
| 620 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 621 | .smmu = { | ||
| 622 | .reg = 0x22c, | ||
| 623 | .bit = 12, | ||
| 624 | }, | ||
| 625 | .la = { | ||
| 626 | .reg = 0x364, | ||
| 627 | .shift = 16, | ||
| 628 | .mask = 0xff, | ||
| 629 | .def = 0x12, | ||
| 630 | }, | ||
| 631 | }, { | ||
| 632 | .id = 0x2d, | ||
| 633 | .name = "viwu", | ||
| 634 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 635 | .smmu = { | ||
| 636 | .reg = 0x22c, | ||
| 637 | .bit = 13, | ||
| 638 | }, | ||
| 639 | .la = { | ||
| 640 | .reg = 0x368, | ||
| 641 | .shift = 0, | ||
| 642 | .mask = 0xff, | ||
| 643 | .def = 0xb2, | ||
| 644 | }, | ||
| 645 | }, { | ||
| 646 | .id = 0x2e, | ||
| 647 | .name = "viwv", | ||
| 648 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 649 | .smmu = { | ||
| 650 | .reg = 0x22c, | ||
| 651 | .bit = 14, | ||
| 652 | }, | ||
| 653 | .la = { | ||
| 654 | .reg = 0x368, | ||
| 655 | .shift = 16, | ||
| 656 | .mask = 0xff, | ||
| 657 | .def = 0xb2, | ||
| 658 | }, | ||
| 659 | }, { | ||
| 660 | .id = 0x2f, | ||
| 661 | .name = "viwy", | ||
| 662 | .swgroup = TEGRA_SWGROUP_VI, | ||
| 663 | .smmu = { | ||
| 664 | .reg = 0x22c, | ||
| 665 | .bit = 15, | ||
| 666 | }, | ||
| 667 | .la = { | ||
| 668 | .reg = 0x36c, | ||
| 669 | .shift = 0, | ||
| 670 | .mask = 0xff, | ||
| 671 | .def = 0x12, | ||
| 672 | }, | ||
| 673 | }, { | ||
| 674 | .id = 0x30, | ||
| 675 | .name = "g2dw", | ||
| 676 | .swgroup = TEGRA_SWGROUP_G2, | ||
| 677 | .smmu = { | ||
| 678 | .reg = 0x22c, | ||
| 679 | .bit = 16, | ||
| 680 | }, | ||
| 681 | .la = { | ||
| 682 | .reg = 0x30c, | ||
| 683 | .shift = 16, | ||
| 684 | .mask = 0xff, | ||
| 685 | .def = 0x9, | ||
| 686 | }, | ||
| 687 | }, { | ||
| 688 | .id = 0x31, | ||
| 689 | .name = "afiw", | ||
| 690 | .swgroup = TEGRA_SWGROUP_AFI, | ||
| 691 | .smmu = { | ||
| 692 | .reg = 0x22c, | ||
| 693 | .bit = 17, | ||
| 694 | }, | ||
| 695 | .la = { | ||
| 696 | .reg = 0x2e0, | ||
| 697 | .shift = 16, | ||
| 698 | .mask = 0xff, | ||
| 699 | .def = 0x0c, | ||
| 700 | }, | ||
| 701 | }, { | ||
| 702 | .id = 0x32, | ||
| 703 | .name = "avpcarm7w", | ||
| 704 | .swgroup = TEGRA_SWGROUP_AVPC, | ||
| 705 | .smmu = { | ||
| 706 | .reg = 0x22c, | ||
| 707 | .bit = 18, | ||
| 708 | }, | ||
| 709 | .la = { | ||
| 710 | .reg = 0x2e4, | ||
| 711 | .shift = 16, | ||
| 712 | .mask = 0xff, | ||
| 713 | .def = 0x0e, | ||
| 714 | }, | ||
| 715 | }, { | ||
| 716 | .id = 0x33, | ||
| 717 | .name = "fdcdwr", | ||
| 718 | .swgroup = TEGRA_SWGROUP_NV, | ||
| 719 | .smmu = { | ||
| 720 | .reg = 0x22c, | ||
| 721 | .bit = 19, | ||
| 722 | }, | ||
| 723 | .la = { | ||
| 724 | .reg = 0x338, | ||
| 725 | .shift = 16, | ||
| 726 | .mask = 0xff, | ||
| 727 | .def = 0x0a, | ||
| 728 | }, | ||
| 729 | }, { | ||
| 730 | .id = 0x34, | ||
| 731 | .name = "fdcwr2", | ||
| 732 | .swgroup = TEGRA_SWGROUP_NV2, | ||
| 733 | .smmu = { | ||
| 734 | .reg = 0x22c, | ||
| 735 | .bit = 20, | ||
| 736 | }, | ||
| 737 | .la = { | ||
| 738 | .reg = 0x340, | ||
| 739 | .shift = 16, | ||
| 740 | .mask = 0xff, | ||
| 741 | .def = 0x0a, | ||
| 742 | }, | ||
| 743 | }, { | ||
| 744 | .id = 0x35, | ||
| 745 | .name = "hdaw", | ||
| 746 | .swgroup = TEGRA_SWGROUP_HDA, | ||
| 747 | .smmu = { | ||
| 748 | .reg = 0x22c, | ||
| 749 | .bit = 21, | ||
| 750 | }, | ||
| 751 | .la = { | ||
| 752 | .reg = 0x318, | ||
| 753 | .shift = 16, | ||
| 754 | .mask = 0xff, | ||
| 755 | .def = 0xff, | ||
| 756 | }, | ||
| 757 | }, { | ||
| 758 | .id = 0x36, | ||
| 759 | .name = "host1xw", | ||
| 760 | .swgroup = TEGRA_SWGROUP_HC, | ||
| 761 | .smmu = { | ||
| 762 | .reg = 0x22c, | ||
| 763 | .bit = 22, | ||
| 764 | }, | ||
| 765 | .la = { | ||
| 766 | .reg = 0x314, | ||
| 767 | .shift = 0, | ||
| 768 | .mask = 0xff, | ||
| 769 | .def = 0x10, | ||
| 770 | }, | ||
| 771 | }, { | ||
| 772 | .id = 0x37, | ||
| 773 | .name = "ispw", | ||
| 774 | .swgroup = TEGRA_SWGROUP_ISP, | ||
| 775 | .smmu = { | ||
| 776 | .reg = 0x22c, | ||
| 777 | .bit = 23, | ||
| 778 | }, | ||
| 779 | .la = { | ||
| 780 | .reg = 0x31c, | ||
| 781 | .shift = 0, | ||
| 782 | .mask = 0xff, | ||
| 783 | .def = 0xff, | ||
| 784 | }, | ||
| 785 | }, { | ||
| 786 | .id = 0x38, | ||
| 787 | .name = "mpcorelpw", | ||
| 788 | .swgroup = TEGRA_SWGROUP_MPCORELP, | ||
| 789 | .la = { | ||
| 790 | .reg = 0x324, | ||
| 791 | .shift = 16, | ||
| 792 | .mask = 0xff, | ||
| 793 | .def = 0x0e, | ||
| 794 | }, | ||
| 795 | }, { | ||
| 796 | .id = 0x39, | ||
| 797 | .name = "mpcorew", | ||
| 798 | .swgroup = TEGRA_SWGROUP_MPCORE, | ||
| 799 | .la = { | ||
| 800 | .reg = 0x320, | ||
| 801 | .shift = 16, | ||
| 802 | .mask = 0xff, | ||
| 803 | .def = 0x0e, | ||
| 804 | }, | ||
| 805 | }, { | ||
| 806 | .id = 0x3a, | ||
| 807 | .name = "mpecswr", | ||
| 808 | .swgroup = TEGRA_SWGROUP_MPE, | ||
| 809 | .smmu = { | ||
| 810 | .reg = 0x22c, | ||
| 811 | .bit = 26, | ||
| 812 | }, | ||
| 813 | .la = { | ||
| 814 | .reg = 0x330, | ||
| 815 | .shift = 16, | ||
| 816 | .mask = 0xff, | ||
| 817 | .def = 0xff, | ||
| 818 | }, | ||
| 819 | }, { | ||
| 820 | .id = 0x3b, | ||
| 821 | .name = "ppcsahbdmaw", | ||
| 822 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 823 | .smmu = { | ||
| 824 | .reg = 0x22c, | ||
| 825 | .bit = 27, | ||
| 826 | }, | ||
| 827 | .la = { | ||
| 828 | .reg = 0x348, | ||
| 829 | .shift = 0, | ||
| 830 | .mask = 0xff, | ||
| 831 | .def = 0x10, | ||
| 832 | }, | ||
| 833 | }, { | ||
| 834 | .id = 0x3c, | ||
| 835 | .name = "ppcsahbslvw", | ||
| 836 | .swgroup = TEGRA_SWGROUP_PPCS, | ||
| 837 | .smmu = { | ||
| 838 | .reg = 0x22c, | ||
| 839 | .bit = 28, | ||
| 840 | }, | ||
| 841 | .la = { | ||
| 842 | .reg = 0x348, | ||
| 843 | .shift = 16, | ||
| 844 | .mask = 0xff, | ||
| 845 | .def = 0x06, | ||
| 846 | }, | ||
| 847 | }, { | ||
| 848 | .id = 0x3d, | ||
| 849 | .name = "sataw", | ||
| 850 | .swgroup = TEGRA_SWGROUP_SATA, | ||
| 851 | .smmu = { | ||
| 852 | .reg = 0x22c, | ||
| 853 | .bit = 29, | ||
| 854 | }, | ||
| 855 | .la = { | ||
| 856 | .reg = 0x350, | ||
| 857 | .shift = 16, | ||
| 858 | .mask = 0xff, | ||
| 859 | .def = 0x33, | ||
| 860 | }, | ||
| 861 | }, { | ||
| 862 | .id = 0x3e, | ||
| 863 | .name = "vdebsevw", | ||
| 864 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 865 | .smmu = { | ||
| 866 | .reg = 0x22c, | ||
| 867 | .bit = 30, | ||
| 868 | }, | ||
| 869 | .la = { | ||
| 870 | .reg = 0x35c, | ||
| 871 | .shift = 0, | ||
| 872 | .mask = 0xff, | ||
| 873 | .def = 0xff, | ||
| 874 | }, | ||
| 875 | }, { | ||
| 876 | .id = 0x3f, | ||
| 877 | .name = "vdedbgw", | ||
| 878 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 879 | .smmu = { | ||
| 880 | .reg = 0x22c, | ||
| 881 | .bit = 31, | ||
| 882 | }, | ||
| 883 | .la = { | ||
| 884 | .reg = 0x35c, | ||
| 885 | .shift = 16, | ||
| 886 | .mask = 0xff, | ||
| 887 | .def = 0xff, | ||
| 888 | }, | ||
| 889 | }, { | ||
| 890 | .id = 0x40, | ||
| 891 | .name = "vdembew", | ||
| 892 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 893 | .smmu = { | ||
| 894 | .reg = 0x230, | ||
| 895 | .bit = 0, | ||
| 896 | }, | ||
| 897 | .la = { | ||
| 898 | .reg = 0x360, | ||
| 899 | .shift = 0, | ||
| 900 | .mask = 0xff, | ||
| 901 | .def = 0x42, | ||
| 902 | }, | ||
| 903 | }, { | ||
| 904 | .id = 0x41, | ||
| 905 | .name = "vdetpmw", | ||
| 906 | .swgroup = TEGRA_SWGROUP_VDE, | ||
| 907 | .smmu = { | ||
| 908 | .reg = 0x230, | ||
| 909 | .bit = 1, | ||
| 910 | }, | ||
| 911 | .la = { | ||
| 912 | .reg = 0x360, | ||
| 913 | .shift = 16, | ||
| 914 | .mask = 0xff, | ||
| 915 | .def = 0x2a, | ||
| 916 | }, | ||
| 917 | }, | ||
| 918 | }; | ||
| 919 | |||
| 920 | static const struct tegra_smmu_swgroup tegra30_swgroups[] = { | ||
| 921 | { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 }, | ||
| 922 | { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 }, | ||
| 923 | { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 }, | ||
| 924 | { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c }, | ||
| 925 | { .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 }, | ||
| 926 | { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, | ||
| 927 | { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 }, | ||
| 928 | { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c }, | ||
| 929 | { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 }, | ||
| 930 | { .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c }, | ||
| 931 | { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 }, | ||
| 932 | { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 }, | ||
| 933 | { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 }, | ||
| 934 | { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 }, | ||
| 935 | { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c }, | ||
| 936 | { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, | ||
| 937 | }; | ||
| 938 | |||
| 939 | static void tegra30_flush_dcache(struct page *page, unsigned long offset, | ||
| 940 | size_t size) | ||
| 941 | { | ||
| 942 | phys_addr_t phys = page_to_phys(page) + offset; | ||
| 943 | void *virt = page_address(page) + offset; | ||
| 944 | |||
| 945 | __cpuc_flush_dcache_area(virt, size); | ||
| 946 | outer_flush_range(phys, phys + size); | ||
| 947 | } | ||
| 948 | |||
| 949 | static const struct tegra_smmu_ops tegra30_smmu_ops = { | ||
| 950 | .flush_dcache = tegra30_flush_dcache, | ||
| 951 | }; | ||
| 952 | |||
| 953 | static const struct tegra_smmu_soc tegra30_smmu_soc = { | ||
| 954 | .clients = tegra30_mc_clients, | ||
| 955 | .num_clients = ARRAY_SIZE(tegra30_mc_clients), | ||
| 956 | .swgroups = tegra30_swgroups, | ||
| 957 | .num_swgroups = ARRAY_SIZE(tegra30_swgroups), | ||
| 958 | .supports_round_robin_arbitration = false, | ||
| 959 | .supports_request_limit = false, | ||
| 960 | .num_asids = 4, | ||
| 961 | .ops = &tegra30_smmu_ops, | ||
| 962 | }; | ||
| 963 | |||
| 964 | const struct tegra_mc_soc tegra30_mc_soc = { | ||
| 965 | .clients = tegra30_mc_clients, | ||
| 966 | .num_clients = ARRAY_SIZE(tegra30_mc_clients), | ||
| 967 | .num_address_bits = 32, | ||
| 968 | .atom_size = 16, | ||
| 969 | .smmu = &tegra30_smmu_soc, | ||
| 970 | }; | ||
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c deleted file mode 100644 index ef7934535fd1..000000000000 --- a/drivers/memory/tegra30-mc.c +++ /dev/null | |||
| @@ -1,378 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Tegra30 Memory Controller | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/err.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/ratelimit.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 26 | #include <linux/io.h> | ||
| 27 | |||
| 28 | #define DRV_NAME "tegra30-mc" | ||
| 29 | |||
| 30 | #define MC_INTSTATUS 0x0 | ||
| 31 | #define MC_INTMASK 0x4 | ||
| 32 | |||
| 33 | #define MC_INT_ERR_SHIFT 6 | ||
| 34 | #define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT) | ||
| 35 | #define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT) | ||
| 36 | #define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2) | ||
| 37 | #define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3) | ||
| 38 | #define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4) | ||
| 39 | |||
| 40 | #define MC_ERR_STATUS 0x8 | ||
| 41 | #define MC_ERR_ADR 0xc | ||
| 42 | |||
| 43 | #define MC_ERR_TYPE_SHIFT 28 | ||
| 44 | #define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT) | ||
| 45 | #define MC_ERR_TYPE_DECERR_EMEM 2 | ||
| 46 | #define MC_ERR_TYPE_SECURITY_TRUSTZONE 3 | ||
| 47 | #define MC_ERR_TYPE_SECURITY_CARVEOUT 4 | ||
| 48 | #define MC_ERR_TYPE_INVALID_SMMU_PAGE 6 | ||
| 49 | |||
| 50 | #define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25 | ||
| 51 | #define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT) | ||
| 52 | #define MC_ERR_RW_SHIFT 16 | ||
| 53 | #define MC_ERR_RW BIT(MC_ERR_RW_SHIFT) | ||
| 54 | #define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1) | ||
| 55 | |||
| 56 | #define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */ | ||
| 57 | |||
| 58 | #define MC_EMEM_ARB_CFG 0x90 | ||
| 59 | #define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 | ||
| 60 | #define MC_EMEM_ARB_TIMING_RCD 0x98 | ||
| 61 | #define MC_EMEM_ARB_TIMING_RP 0x9c | ||
| 62 | #define MC_EMEM_ARB_TIMING_RC 0xa0 | ||
| 63 | #define MC_EMEM_ARB_TIMING_RAS 0xa4 | ||
| 64 | #define MC_EMEM_ARB_TIMING_FAW 0xa8 | ||
| 65 | #define MC_EMEM_ARB_TIMING_RRD 0xac | ||
| 66 | #define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 | ||
| 67 | #define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 | ||
| 68 | #define MC_EMEM_ARB_TIMING_R2R 0xb8 | ||
| 69 | #define MC_EMEM_ARB_TIMING_W2W 0xbc | ||
| 70 | #define MC_EMEM_ARB_TIMING_R2W 0xc0 | ||
| 71 | #define MC_EMEM_ARB_TIMING_W2R 0xc4 | ||
| 72 | |||
| 73 | #define MC_EMEM_ARB_DA_TURNS 0xd0 | ||
| 74 | #define MC_EMEM_ARB_DA_COVERS 0xd4 | ||
| 75 | #define MC_EMEM_ARB_MISC0 0xd8 | ||
| 76 | #define MC_EMEM_ARB_MISC1 0xdc | ||
| 77 | |||
| 78 | #define MC_EMEM_ARB_RING3_THROTTLE 0xe4 | ||
| 79 | #define MC_EMEM_ARB_OVERRIDE 0xe8 | ||
| 80 | |||
| 81 | #define MC_TIMING_CONTROL 0xfc | ||
| 82 | |||
| 83 | #define MC_CLIENT_ID_MASK 0x7f | ||
| 84 | |||
| 85 | #define NUM_MC_REG_BANKS 4 | ||
| 86 | |||
| 87 | struct tegra30_mc { | ||
| 88 | void __iomem *regs[NUM_MC_REG_BANKS]; | ||
| 89 | struct device *dev; | ||
| 90 | u32 ctx[0]; | ||
| 91 | }; | ||
| 92 | |||
| 93 | static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs) | ||
| 94 | { | ||
| 95 | u32 val = 0; | ||
| 96 | |||
| 97 | if (offs < 0x10) | ||
| 98 | val = readl(mc->regs[0] + offs); | ||
| 99 | else if (offs < 0x1f0) | ||
| 100 | val = readl(mc->regs[1] + offs - 0x3c); | ||
| 101 | else if (offs < 0x228) | ||
| 102 | val = readl(mc->regs[2] + offs - 0x200); | ||
| 103 | else if (offs < 0x400) | ||
| 104 | val = readl(mc->regs[3] + offs - 0x284); | ||
| 105 | |||
| 106 | return val; | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs) | ||
| 110 | { | ||
| 111 | if (offs < 0x10) | ||
| 112 | writel(val, mc->regs[0] + offs); | ||
| 113 | else if (offs < 0x1f0) | ||
| 114 | writel(val, mc->regs[1] + offs - 0x3c); | ||
| 115 | else if (offs < 0x228) | ||
| 116 | writel(val, mc->regs[2] + offs - 0x200); | ||
| 117 | else if (offs < 0x400) | ||
| 118 | writel(val, mc->regs[3] + offs - 0x284); | ||
| 119 | } | ||
| 120 | |||
| 121 | static const char * const tegra30_mc_client[] = { | ||
| 122 | "csr_ptcr", | ||
| 123 | "cbr_display0a", | ||
| 124 | "cbr_display0ab", | ||
| 125 | "cbr_display0b", | ||
| 126 | "cbr_display0bb", | ||
| 127 | "cbr_display0c", | ||
| 128 | "cbr_display0cb", | ||
| 129 | "cbr_display1b", | ||
| 130 | "cbr_display1bb", | ||
| 131 | "cbr_eppup", | ||
| 132 | "cbr_g2pr", | ||
| 133 | "cbr_g2sr", | ||
| 134 | "cbr_mpeunifbr", | ||
| 135 | "cbr_viruv", | ||
| 136 | "csr_afir", | ||
| 137 | "csr_avpcarm7r", | ||
| 138 | "csr_displayhc", | ||
| 139 | "csr_displayhcb", | ||
| 140 | "csr_fdcdrd", | ||
| 141 | "csr_fdcdrd2", | ||
| 142 | "csr_g2dr", | ||
| 143 | "csr_hdar", | ||
| 144 | "csr_host1xdmar", | ||
| 145 | "csr_host1xr", | ||
| 146 | "csr_idxsrd", | ||
| 147 | "csr_idxsrd2", | ||
| 148 | "csr_mpe_ipred", | ||
| 149 | "csr_mpeamemrd", | ||
| 150 | "csr_mpecsrd", | ||
| 151 | "csr_ppcsahbdmar", | ||
| 152 | "csr_ppcsahbslvr", | ||
| 153 | "csr_satar", | ||
| 154 | "csr_texsrd", | ||
| 155 | "csr_texsrd2", | ||
| 156 | "csr_vdebsevr", | ||
| 157 | "csr_vdember", | ||
| 158 | "csr_vdemcer", | ||
| 159 | "csr_vdetper", | ||
| 160 | "csr_mpcorelpr", | ||
| 161 | "csr_mpcorer", | ||
| 162 | "cbw_eppu", | ||
| 163 | "cbw_eppv", | ||
| 164 | "cbw_eppy", | ||
| 165 | "cbw_mpeunifbw", | ||
| 166 | "cbw_viwsb", | ||
| 167 | "cbw_viwu", | ||
| 168 | "cbw_viwv", | ||
| 169 | "cbw_viwy", | ||
| 170 | "ccw_g2dw", | ||
| 171 | "csw_afiw", | ||
| 172 | "csw_avpcarm7w", | ||
| 173 | "csw_fdcdwr", | ||
| 174 | "csw_fdcdwr2", | ||
| 175 | "csw_hdaw", | ||
| 176 | "csw_host1xw", | ||
| 177 | "csw_ispw", | ||
| 178 | "csw_mpcorelpw", | ||
| 179 | "csw_mpcorew", | ||
| 180 | "csw_mpecswr", | ||
| 181 | "csw_ppcsahbdmaw", | ||
| 182 | "csw_ppcsahbslvw", | ||
| 183 | "csw_sataw", | ||
| 184 | "csw_vdebsevw", | ||
| 185 | "csw_vdedbgw", | ||
| 186 | "csw_vdembew", | ||
| 187 | "csw_vdetpmw", | ||
| 188 | }; | ||
| 189 | |||
| 190 | static void tegra30_mc_decode(struct tegra30_mc *mc, int n) | ||
| 191 | { | ||
| 192 | u32 err, addr; | ||
| 193 | const char * const mc_int_err[] = { | ||
| 194 | "MC_DECERR", | ||
| 195 | "Unknown", | ||
| 196 | "MC_SECURITY_ERR", | ||
| 197 | "MC_ARBITRATION_EMEM", | ||
| 198 | "MC_SMMU_ERR", | ||
| 199 | }; | ||
| 200 | const char * const err_type[] = { | ||
| 201 | "Unknown", | ||
| 202 | "Unknown", | ||
| 203 | "DECERR_EMEM", | ||
| 204 | "SECURITY_TRUSTZONE", | ||
| 205 | "SECURITY_CARVEOUT", | ||
| 206 | "Unknown", | ||
| 207 | "INVALID_SMMU_PAGE", | ||
| 208 | "Unknown", | ||
| 209 | }; | ||
| 210 | char attr[6]; | ||
| 211 | int cid, perm, type, idx; | ||
| 212 | const char *client = "Unknown"; | ||
| 213 | |||
| 214 | idx = n - MC_INT_ERR_SHIFT; | ||
| 215 | if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) { | ||
| 216 | dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", | ||
| 217 | BIT(n)); | ||
| 218 | return; | ||
| 219 | } | ||
| 220 | |||
| 221 | err = mc_readl(mc, MC_ERR_STATUS); | ||
| 222 | |||
| 223 | type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT; | ||
| 224 | perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >> | ||
| 225 | MC_ERR_INVALID_SMMU_PAGE_SHIFT; | ||
| 226 | if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE) | ||
| 227 | sprintf(attr, "%c-%c-%c", | ||
| 228 | (perm & BIT(2)) ? 'R' : '-', | ||
| 229 | (perm & BIT(1)) ? 'W' : '-', | ||
| 230 | (perm & BIT(0)) ? 'S' : '-'); | ||
| 231 | else | ||
| 232 | attr[0] = '\0'; | ||
| 233 | |||
| 234 | cid = err & MC_CLIENT_ID_MASK; | ||
| 235 | if (cid < ARRAY_SIZE(tegra30_mc_client)) | ||
| 236 | client = tegra30_mc_client[cid]; | ||
| 237 | |||
| 238 | addr = mc_readl(mc, MC_ERR_ADR); | ||
| 239 | |||
| 240 | dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n", | ||
| 241 | mc_int_err[idx], err, addr, client, | ||
| 242 | (err & MC_ERR_SECURITY) ? "secure" : "non-secure", | ||
| 243 | (err & MC_ERR_RW) ? "write" : "read", | ||
| 244 | err_type[type], attr); | ||
| 245 | } | ||
| 246 | |||
| 247 | static const u32 tegra30_mc_ctx[] = { | ||
| 248 | MC_EMEM_ARB_CFG, | ||
| 249 | MC_EMEM_ARB_OUTSTANDING_REQ, | ||
| 250 | MC_EMEM_ARB_TIMING_RCD, | ||
| 251 | MC_EMEM_ARB_TIMING_RP, | ||
| 252 | MC_EMEM_ARB_TIMING_RC, | ||
| 253 | MC_EMEM_ARB_TIMING_RAS, | ||
| 254 | MC_EMEM_ARB_TIMING_FAW, | ||
| 255 | MC_EMEM_ARB_TIMING_RRD, | ||
| 256 | MC_EMEM_ARB_TIMING_RAP2PRE, | ||
| 257 | MC_EMEM_ARB_TIMING_WAP2PRE, | ||
| 258 | MC_EMEM_ARB_TIMING_R2R, | ||
| 259 | MC_EMEM_ARB_TIMING_W2W, | ||
| 260 | MC_EMEM_ARB_TIMING_R2W, | ||
| 261 | MC_EMEM_ARB_TIMING_W2R, | ||
| 262 | MC_EMEM_ARB_DA_TURNS, | ||
| 263 | MC_EMEM_ARB_DA_COVERS, | ||
| 264 | MC_EMEM_ARB_MISC0, | ||
| 265 | MC_EMEM_ARB_MISC1, | ||
| 266 | MC_EMEM_ARB_RING3_THROTTLE, | ||
| 267 | MC_EMEM_ARB_OVERRIDE, | ||
| 268 | MC_INTMASK, | ||
| 269 | }; | ||
| 270 | |||
| 271 | #ifdef CONFIG_PM | ||
| 272 | static int tegra30_mc_suspend(struct device *dev) | ||
| 273 | { | ||
| 274 | int i; | ||
| 275 | struct tegra30_mc *mc = dev_get_drvdata(dev); | ||
| 276 | |||
| 277 | for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) | ||
| 278 | mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]); | ||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int tegra30_mc_resume(struct device *dev) | ||
| 283 | { | ||
| 284 | int i; | ||
| 285 | struct tegra30_mc *mc = dev_get_drvdata(dev); | ||
| 286 | |||
| 287 | for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) | ||
| 288 | mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]); | ||
| 289 | |||
| 290 | mc_writel(mc, 1, MC_TIMING_CONTROL); | ||
| 291 | /* Read-back to ensure that write reached */ | ||
| 292 | mc_readl(mc, MC_TIMING_CONTROL); | ||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | #endif | ||
| 296 | |||
| 297 | static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm, | ||
| 298 | tegra30_mc_suspend, | ||
| 299 | tegra30_mc_resume, NULL); | ||
| 300 | |||
| 301 | static const struct of_device_id tegra30_mc_of_match[] = { | ||
| 302 | { .compatible = "nvidia,tegra30-mc", }, | ||
| 303 | {}, | ||
| 304 | }; | ||
| 305 | |||
| 306 | static irqreturn_t tegra30_mc_isr(int irq, void *data) | ||
| 307 | { | ||
| 308 | u32 stat, mask, bit; | ||
| 309 | struct tegra30_mc *mc = data; | ||
| 310 | |||
| 311 | stat = mc_readl(mc, MC_INTSTATUS); | ||
| 312 | mask = mc_readl(mc, MC_INTMASK); | ||
| 313 | mask &= stat; | ||
| 314 | if (!mask) | ||
| 315 | return IRQ_NONE; | ||
| 316 | while ((bit = ffs(mask)) != 0) { | ||
| 317 | tegra30_mc_decode(mc, bit - 1); | ||
| 318 | mask &= ~BIT(bit - 1); | ||
| 319 | } | ||
| 320 | |||
| 321 | mc_writel(mc, stat, MC_INTSTATUS); | ||
| 322 | return IRQ_HANDLED; | ||
| 323 | } | ||
| 324 | |||
| 325 | static int tegra30_mc_probe(struct platform_device *pdev) | ||
| 326 | { | ||
| 327 | struct resource *irq; | ||
| 328 | struct tegra30_mc *mc; | ||
| 329 | size_t bytes; | ||
| 330 | int err, i; | ||
| 331 | u32 intmask; | ||
| 332 | |||
| 333 | bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx); | ||
| 334 | mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL); | ||
| 335 | if (!mc) | ||
| 336 | return -ENOMEM; | ||
| 337 | mc->dev = &pdev->dev; | ||
| 338 | |||
| 339 | for (i = 0; i < ARRAY_SIZE(mc->regs); i++) { | ||
| 340 | struct resource *res; | ||
| 341 | |||
| 342 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
| 343 | mc->regs[i] = devm_ioremap_resource(&pdev->dev, res); | ||
| 344 | if (IS_ERR(mc->regs[i])) | ||
| 345 | return PTR_ERR(mc->regs[i]); | ||
| 346 | } | ||
| 347 | |||
| 348 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 349 | if (!irq) | ||
| 350 | return -ENODEV; | ||
| 351 | err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr, | ||
| 352 | IRQF_SHARED, dev_name(&pdev->dev), mc); | ||
| 353 | if (err) | ||
| 354 | return -ENODEV; | ||
| 355 | |||
| 356 | platform_set_drvdata(pdev, mc); | ||
| 357 | |||
| 358 | intmask = MC_INT_INVALID_SMMU_PAGE | | ||
| 359 | MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION; | ||
| 360 | mc_writel(mc, intmask, MC_INTMASK); | ||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | |||
| 364 | static struct platform_driver tegra30_mc_driver = { | ||
| 365 | .probe = tegra30_mc_probe, | ||
| 366 | .driver = { | ||
| 367 | .name = DRV_NAME, | ||
| 368 | .owner = THIS_MODULE, | ||
| 369 | .of_match_table = tegra30_mc_of_match, | ||
| 370 | .pm = &tegra30_mc_pm, | ||
| 371 | }, | ||
| 372 | }; | ||
| 373 | module_platform_driver(tegra30_mc_driver); | ||
| 374 | |||
| 375 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); | ||
| 376 | MODULE_DESCRIPTION("Tegra30 MC driver"); | ||
| 377 | MODULE_LICENSE("GPL v2"); | ||
| 378 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 60fed3d7820b..157d421f755b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | obj-$(CONFIG_RESET_CONTROLLER) += core.o | 1 | obj-$(CONFIG_RESET_CONTROLLER) += core.o |
| 2 | obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o | 2 | obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o |
| 3 | obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o | ||
| 3 | obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o | 4 | obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o |
| 4 | obj-$(CONFIG_ARCH_STI) += sti/ | 5 | obj-$(CONFIG_ARCH_STI) += sti/ |
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index baeaf82d40d9..7955e00d04d4 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c | |||
| @@ -126,6 +126,21 @@ int reset_control_deassert(struct reset_control *rstc) | |||
| 126 | EXPORT_SYMBOL_GPL(reset_control_deassert); | 126 | EXPORT_SYMBOL_GPL(reset_control_deassert); |
| 127 | 127 | ||
| 128 | /** | 128 | /** |
| 129 | * reset_control_status - returns a negative errno if not supported, a | ||
| 130 | * positive value if the reset line is asserted, or zero if the reset | ||
| 131 | * line is not asserted. | ||
| 132 | * @rstc: reset controller | ||
| 133 | */ | ||
| 134 | int reset_control_status(struct reset_control *rstc) | ||
| 135 | { | ||
| 136 | if (rstc->rcdev->ops->status) | ||
| 137 | return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); | ||
| 138 | |||
| 139 | return -ENOSYS; | ||
| 140 | } | ||
| 141 | EXPORT_SYMBOL_GPL(reset_control_status); | ||
| 142 | |||
| 143 | /** | ||
| 129 | * of_reset_control_get - Lookup and obtain a reference to a reset controller. | 144 | * of_reset_control_get - Lookup and obtain a reference to a reset controller. |
| 130 | * @node: device to be reset by the controller | 145 | * @node: device to be reset by the controller |
| 131 | * @id: reset line name | 146 | * @id: reset line name |
diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c new file mode 100644 index 000000000000..f8b48a13cf0b --- /dev/null +++ b/drivers/reset/reset-berlin.c | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 Marvell Technology Group Ltd. | ||
| 3 | * | ||
| 4 | * Antoine Tenart <antoine.tenart@free-electrons.com> | ||
| 5 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> | ||
| 6 | * | ||
| 7 | * This file is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2. This program is licensed "as is" without any | ||
| 9 | * warranty of any kind, whether express or implied. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/delay.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/of.h> | ||
| 16 | #include <linux/of_address.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/reset-controller.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/types.h> | ||
| 21 | |||
| 22 | #define BERLIN_MAX_RESETS 32 | ||
| 23 | |||
| 24 | #define to_berlin_reset_priv(p) \ | ||
| 25 | container_of((p), struct berlin_reset_priv, rcdev) | ||
| 26 | |||
| 27 | struct berlin_reset_priv { | ||
| 28 | void __iomem *base; | ||
| 29 | unsigned int size; | ||
| 30 | struct reset_controller_dev rcdev; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static int berlin_reset_reset(struct reset_controller_dev *rcdev, | ||
| 34 | unsigned long id) | ||
| 35 | { | ||
| 36 | struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev); | ||
| 37 | int offset = id >> 8; | ||
| 38 | int mask = BIT(id & 0x1f); | ||
| 39 | |||
| 40 | writel(mask, priv->base + offset); | ||
| 41 | |||
| 42 | /* let the reset be effective */ | ||
| 43 | udelay(10); | ||
| 44 | |||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static struct reset_control_ops berlin_reset_ops = { | ||
| 49 | .reset = berlin_reset_reset, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static int berlin_reset_xlate(struct reset_controller_dev *rcdev, | ||
| 53 | const struct of_phandle_args *reset_spec) | ||
| 54 | { | ||
| 55 | struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev); | ||
| 56 | unsigned offset, bit; | ||
| 57 | |||
| 58 | if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) | ||
| 59 | return -EINVAL; | ||
| 60 | |||
| 61 | offset = reset_spec->args[0]; | ||
| 62 | bit = reset_spec->args[1]; | ||
| 63 | |||
| 64 | if (offset >= priv->size) | ||
| 65 | return -EINVAL; | ||
| 66 | |||
| 67 | if (bit >= BERLIN_MAX_RESETS) | ||
| 68 | return -EINVAL; | ||
| 69 | |||
| 70 | return (offset << 8) | bit; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int __berlin_reset_init(struct device_node *np) | ||
| 74 | { | ||
| 75 | struct berlin_reset_priv *priv; | ||
| 76 | struct resource res; | ||
| 77 | resource_size_t size; | ||
| 78 | int ret; | ||
| 79 | |||
| 80 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
| 81 | if (!priv) | ||
| 82 | return -ENOMEM; | ||
| 83 | |||
| 84 | ret = of_address_to_resource(np, 0, &res); | ||
| 85 | if (ret) | ||
| 86 | goto err; | ||
| 87 | |||
| 88 | size = resource_size(&res); | ||
| 89 | priv->base = ioremap(res.start, size); | ||
| 90 | if (!priv->base) { | ||
| 91 | ret = -ENOMEM; | ||
| 92 | goto err; | ||
| 93 | } | ||
| 94 | priv->size = size; | ||
| 95 | |||
| 96 | priv->rcdev.owner = THIS_MODULE; | ||
| 97 | priv->rcdev.ops = &berlin_reset_ops; | ||
| 98 | priv->rcdev.of_node = np; | ||
| 99 | priv->rcdev.of_reset_n_cells = 2; | ||
| 100 | priv->rcdev.of_xlate = berlin_reset_xlate; | ||
| 101 | |||
| 102 | reset_controller_register(&priv->rcdev); | ||
| 103 | |||
| 104 | return 0; | ||
| 105 | |||
| 106 | err: | ||
| 107 | kfree(priv); | ||
| 108 | return ret; | ||
| 109 | } | ||
| 110 | |||
| 111 | static const struct of_device_id berlin_reset_of_match[] __initconst = { | ||
| 112 | { .compatible = "marvell,berlin2-chip-ctrl" }, | ||
| 113 | { .compatible = "marvell,berlin2cd-chip-ctrl" }, | ||
| 114 | { .compatible = "marvell,berlin2q-chip-ctrl" }, | ||
| 115 | { }, | ||
| 116 | }; | ||
| 117 | |||
| 118 | static int __init berlin_reset_init(void) | ||
| 119 | { | ||
| 120 | struct device_node *np; | ||
| 121 | int ret; | ||
| 122 | |||
| 123 | for_each_matching_node(np, berlin_reset_of_match) { | ||
| 124 | ret = __berlin_reset_init(np); | ||
| 125 | if (ret) | ||
| 126 | return ret; | ||
| 127 | } | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | arch_initcall(berlin_reset_init); | ||
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 79c32ca84ef1..40582089474a 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c | |||
| @@ -76,9 +76,24 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, | |||
| 76 | return 0; | 76 | return 0; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static int socfpga_reset_status(struct reset_controller_dev *rcdev, | ||
| 80 | unsigned long id) | ||
| 81 | { | ||
| 82 | struct socfpga_reset_data *data = container_of(rcdev, | ||
| 83 | struct socfpga_reset_data, rcdev); | ||
| 84 | int bank = id / BITS_PER_LONG; | ||
| 85 | int offset = id % BITS_PER_LONG; | ||
| 86 | u32 reg; | ||
| 87 | |||
| 88 | reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); | ||
| 89 | |||
| 90 | return !(reg & BIT(offset)); | ||
| 91 | } | ||
| 92 | |||
| 79 | static struct reset_control_ops socfpga_reset_ops = { | 93 | static struct reset_control_ops socfpga_reset_ops = { |
| 80 | .assert = socfpga_reset_assert, | 94 | .assert = socfpga_reset_assert, |
| 81 | .deassert = socfpga_reset_deassert, | 95 | .deassert = socfpga_reset_deassert, |
| 96 | .status = socfpga_reset_status, | ||
| 82 | }; | 97 | }; |
| 83 | 98 | ||
| 84 | static int socfpga_reset_probe(struct platform_device *pdev) | 99 | static int socfpga_reset_probe(struct platform_device *pdev) |
diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig index 88d2d0316613..f8c15a37fb35 100644 --- a/drivers/reset/sti/Kconfig +++ b/drivers/reset/sti/Kconfig | |||
| @@ -12,4 +12,8 @@ config STIH416_RESET | |||
| 12 | bool | 12 | bool |
| 13 | select STI_RESET_SYSCFG | 13 | select STI_RESET_SYSCFG |
| 14 | 14 | ||
| 15 | config STIH407_RESET | ||
| 16 | bool | ||
| 17 | select STI_RESET_SYSCFG | ||
| 18 | |||
| 15 | endif | 19 | endif |
diff --git a/drivers/reset/sti/Makefile b/drivers/reset/sti/Makefile index be1c97647871..dc85dfbe56a9 100644 --- a/drivers/reset/sti/Makefile +++ b/drivers/reset/sti/Makefile | |||
| @@ -2,3 +2,4 @@ obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o | |||
| 2 | 2 | ||
| 3 | obj-$(CONFIG_STIH415_RESET) += reset-stih415.o | 3 | obj-$(CONFIG_STIH415_RESET) += reset-stih415.o |
| 4 | obj-$(CONFIG_STIH416_RESET) += reset-stih416.o | 4 | obj-$(CONFIG_STIH416_RESET) += reset-stih416.o |
| 5 | obj-$(CONFIG_STIH407_RESET) += reset-stih407.o | ||
diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c new file mode 100644 index 000000000000..d83db5d72d08 --- /dev/null +++ b/drivers/reset/sti/reset-stih407.c | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 STMicroelectronics (R&D) Limited | ||
| 3 | * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | */ | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/of.h> | ||
| 12 | #include <linux/of_platform.h> | ||
| 13 | #include <linux/platform_device.h> | ||
| 14 | #include <dt-bindings/reset-controller/stih407-resets.h> | ||
| 15 | #include "reset-syscfg.h" | ||
| 16 | |||
| 17 | /* STiH407 Peripheral powerdown definitions. */ | ||
| 18 | static const char stih407_core[] = "st,stih407-core-syscfg"; | ||
| 19 | static const char stih407_sbc_reg[] = "st,stih407-sbc-reg-syscfg"; | ||
| 20 | static const char stih407_lpm[] = "st,stih407-lpm-syscfg"; | ||
| 21 | |||
| 22 | #define STIH407_PDN_0(_bit) \ | ||
| 23 | _SYSCFG_RST_CH(stih407_core, SYSCFG_5000, _bit, SYSSTAT_5500, _bit) | ||
| 24 | #define STIH407_PDN_1(_bit) \ | ||
| 25 | _SYSCFG_RST_CH(stih407_core, SYSCFG_5001, _bit, SYSSTAT_5501, _bit) | ||
| 26 | #define STIH407_PDN_ETH(_bit, _stat) \ | ||
| 27 | _SYSCFG_RST_CH(stih407_sbc_reg, SYSCFG_4032, _bit, SYSSTAT_4520, _stat) | ||
| 28 | |||
| 29 | /* Powerdown requests control 0 */ | ||
| 30 | #define SYSCFG_5000 0x0 | ||
| 31 | #define SYSSTAT_5500 0x7d0 | ||
| 32 | /* Powerdown requests control 1 (High Speed Links) */ | ||
| 33 | #define SYSCFG_5001 0x4 | ||
| 34 | #define SYSSTAT_5501 0x7d4 | ||
| 35 | |||
| 36 | /* Ethernet powerdown/status/reset */ | ||
| 37 | #define SYSCFG_4032 0x80 | ||
| 38 | #define SYSSTAT_4520 0x820 | ||
| 39 | #define SYSCFG_4002 0x8 | ||
| 40 | |||
| 41 | static const struct syscfg_reset_channel_data stih407_powerdowns[] = { | ||
| 42 | [STIH407_EMISS_POWERDOWN] = STIH407_PDN_0(1), | ||
| 43 | [STIH407_NAND_POWERDOWN] = STIH407_PDN_0(0), | ||
| 44 | [STIH407_USB3_POWERDOWN] = STIH407_PDN_1(6), | ||
| 45 | [STIH407_USB2_PORT1_POWERDOWN] = STIH407_PDN_1(5), | ||
| 46 | [STIH407_USB2_PORT0_POWERDOWN] = STIH407_PDN_1(4), | ||
| 47 | [STIH407_PCIE1_POWERDOWN] = STIH407_PDN_1(3), | ||
| 48 | [STIH407_PCIE0_POWERDOWN] = STIH407_PDN_1(2), | ||
| 49 | [STIH407_SATA1_POWERDOWN] = STIH407_PDN_1(1), | ||
| 50 | [STIH407_SATA0_POWERDOWN] = STIH407_PDN_1(0), | ||
| 51 | [STIH407_ETH1_POWERDOWN] = STIH407_PDN_ETH(0, 2), | ||
| 52 | }; | ||
| 53 | |||
| 54 | /* Reset Generator control 0/1 */ | ||
| 55 | #define SYSCFG_5131 0x20c | ||
| 56 | #define SYSCFG_5132 0x210 | ||
| 57 | |||
| 58 | #define LPM_SYSCFG_1 0x4 /* Softreset IRB & SBC UART */ | ||
| 59 | |||
| 60 | #define STIH407_SRST_CORE(_reg, _bit) \ | ||
| 61 | _SYSCFG_RST_CH_NO_ACK(stih407_core, _reg, _bit) | ||
| 62 | |||
| 63 | #define STIH407_SRST_SBC(_reg, _bit) \ | ||
| 64 | _SYSCFG_RST_CH_NO_ACK(stih407_sbc_reg, _reg, _bit) | ||
| 65 | |||
| 66 | #define STIH407_SRST_LPM(_reg, _bit) \ | ||
| 67 | _SYSCFG_RST_CH_NO_ACK(stih407_lpm, _reg, _bit) | ||
| 68 | |||
| 69 | static const struct syscfg_reset_channel_data stih407_softresets[] = { | ||
| 70 | [STIH407_ETH1_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 4), | ||
| 71 | [STIH407_MMC1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 3), | ||
| 72 | [STIH407_USB2_PORT0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 28), | ||
| 73 | [STIH407_USB2_PORT1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 29), | ||
| 74 | [STIH407_PICOPHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 30), | ||
| 75 | [STIH407_IRB_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 6), | ||
| 76 | [STIH407_PCIE0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 6), | ||
| 77 | [STIH407_PCIE1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 15), | ||
| 78 | [STIH407_SATA0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 7), | ||
| 79 | [STIH407_SATA1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 16), | ||
| 80 | [STIH407_MIPHY0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 4), | ||
| 81 | [STIH407_MIPHY1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 13), | ||
| 82 | [STIH407_MIPHY2_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 22), | ||
| 83 | [STIH407_SATA0_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 5), | ||
| 84 | [STIH407_SATA1_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 14), | ||
| 85 | [STIH407_DELTA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 3), | ||
| 86 | [STIH407_BLITTER_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 10), | ||
| 87 | [STIH407_HDTVOUT_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 11), | ||
| 88 | [STIH407_HDQVDP_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 12), | ||
| 89 | [STIH407_VDP_AUX_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 14), | ||
| 90 | [STIH407_COMPO_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 15), | ||
| 91 | [STIH407_HDMI_TX_PHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 21), | ||
| 92 | [STIH407_JPEG_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 23), | ||
| 93 | [STIH407_VP8_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 24), | ||
| 94 | [STIH407_GPU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 30), | ||
| 95 | [STIH407_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 0), | ||
| 96 | [STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1), | ||
| 97 | [STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2), | ||
| 98 | [STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8), | ||
| 99 | }; | ||
| 100 | |||
| 101 | /* PicoPHY reset/control */ | ||
| 102 | #define SYSCFG_5061 0x0f4 | ||
| 103 | |||
| 104 | static const struct syscfg_reset_channel_data stih407_picophyresets[] = { | ||
| 105 | [STIH407_PICOPHY0_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 5), | ||
| 106 | [STIH407_PICOPHY1_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 6), | ||
| 107 | [STIH407_PICOPHY2_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 7), | ||
| 108 | }; | ||
| 109 | |||
| 110 | static const struct syscfg_reset_controller_data stih407_powerdown_controller = { | ||
| 111 | .wait_for_ack = true, | ||
| 112 | .nr_channels = ARRAY_SIZE(stih407_powerdowns), | ||
| 113 | .channels = stih407_powerdowns, | ||
| 114 | }; | ||
| 115 | |||
| 116 | static const struct syscfg_reset_controller_data stih407_softreset_controller = { | ||
| 117 | .wait_for_ack = false, | ||
| 118 | .active_low = true, | ||
| 119 | .nr_channels = ARRAY_SIZE(stih407_softresets), | ||
| 120 | .channels = stih407_softresets, | ||
| 121 | }; | ||
| 122 | |||
| 123 | static const struct syscfg_reset_controller_data stih407_picophyreset_controller = { | ||
| 124 | .wait_for_ack = false, | ||
| 125 | .nr_channels = ARRAY_SIZE(stih407_picophyresets), | ||
| 126 | .channels = stih407_picophyresets, | ||
| 127 | }; | ||
| 128 | |||
| 129 | static struct of_device_id stih407_reset_match[] = { | ||
| 130 | { | ||
| 131 | .compatible = "st,stih407-powerdown", | ||
| 132 | .data = &stih407_powerdown_controller, | ||
| 133 | }, | ||
| 134 | { | ||
| 135 | .compatible = "st,stih407-softreset", | ||
| 136 | .data = &stih407_softreset_controller, | ||
| 137 | }, | ||
| 138 | { | ||
| 139 | .compatible = "st,stih407-picophyreset", | ||
| 140 | .data = &stih407_picophyreset_controller, | ||
| 141 | }, | ||
| 142 | { /* sentinel */ }, | ||
| 143 | }; | ||
| 144 | |||
| 145 | static struct platform_driver stih407_reset_driver = { | ||
| 146 | .probe = syscfg_reset_probe, | ||
| 147 | .driver = { | ||
| 148 | .name = "reset-stih407", | ||
| 149 | .of_match_table = stih407_reset_match, | ||
| 150 | }, | ||
| 151 | }; | ||
| 152 | |||
| 153 | static int __init stih407_reset_init(void) | ||
| 154 | { | ||
| 155 | return platform_driver_register(&stih407_reset_driver); | ||
| 156 | } | ||
| 157 | |||
| 158 | arch_initcall(stih407_reset_init); | ||
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index da5a1c789f36..b682651b5307 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -1109,16 +1109,18 @@ config RTC_DRV_AT91RM9200 | |||
| 1109 | this is powered by the backup power supply. | 1109 | this is powered by the backup power supply. |
| 1110 | 1110 | ||
| 1111 | config RTC_DRV_AT91SAM9 | 1111 | config RTC_DRV_AT91SAM9 |
| 1112 | tristate "AT91SAM9x/AT91CAP9 RTT as RTC" | 1112 | tristate "AT91SAM9 RTT as RTC" |
| 1113 | depends on ARCH_AT91 | 1113 | depends on ARCH_AT91 |
| 1114 | select MFD_SYSCON | ||
| 1114 | help | 1115 | help |
| 1115 | RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT | 1116 | Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which |
| 1116 | (Real Time Timer). These timers are powered by the backup power | 1117 | can be used as an RTC thanks to the backup power supply (e.g. a |
| 1117 | supply (such as a small coin cell battery), but do not need to | 1118 | small coin cell battery) which keeps this block and the GPBR |
| 1118 | be used as RTCs. | 1119 | (General Purpose Backup Registers) block powered when the device |
| 1119 | 1120 | is shutdown. | |
| 1120 | (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the | 1121 | Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd |
| 1121 | dedicated RTC module and leave the RTT available for other uses.) | 1122 | probably want to use the real RTC block instead of the "RTT as an |
| 1123 | RTC" driver. | ||
| 1122 | 1124 | ||
| 1123 | config RTC_DRV_AT91SAM9_RTT | 1125 | config RTC_DRV_AT91SAM9_RTT |
| 1124 | int | 1126 | int |
| @@ -1126,6 +1128,9 @@ config RTC_DRV_AT91SAM9_RTT | |||
| 1126 | default 0 | 1128 | default 0 |
| 1127 | depends on RTC_DRV_AT91SAM9 | 1129 | depends on RTC_DRV_AT91SAM9 |
| 1128 | help | 1130 | help |
| 1131 | This option is only relevant for legacy board support and | ||
| 1132 | won't be used when booting a DT board. | ||
| 1133 | |||
| 1129 | More than one RTT module is available. You can choose which | 1134 | More than one RTT module is available. You can choose which |
| 1130 | one will be used as an RTC. The default of zero is normally | 1135 | one will be used as an RTC. The default of zero is normally |
| 1131 | OK to use, though some systems use that for non-RTC purposes. | 1136 | OK to use, though some systems use that for non-RTC purposes. |
| @@ -1137,6 +1142,9 @@ config RTC_DRV_AT91SAM9_GPBR | |||
| 1137 | prompt "Backup Register Number" | 1142 | prompt "Backup Register Number" |
| 1138 | depends on RTC_DRV_AT91SAM9 | 1143 | depends on RTC_DRV_AT91SAM9 |
| 1139 | help | 1144 | help |
| 1145 | This option is only relevant for legacy board support and | ||
| 1146 | won't be used when booting a DT board. | ||
| 1147 | |||
| 1140 | The RTC driver needs to use one of the General Purpose Backup | 1148 | The RTC driver needs to use one of the General Purpose Backup |
| 1141 | Registers (GPBRs) as well as the RTT. You can choose which one | 1149 | Registers (GPBRs) as well as the RTT. You can choose which one |
| 1142 | will be used. The default of zero is normally OK to use, but | 1150 | will be used. The default of zero is normally OK to use, but |
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 596374304532..abac38abd38e 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c | |||
| @@ -21,10 +21,9 @@ | |||
| 21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 22 | #include <linux/platform_data/atmel.h> | 22 | #include <linux/platform_data/atmel.h> |
| 23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| 24 | 24 | #include <linux/mfd/syscon.h> | |
| 25 | #include <mach/at91_rtt.h> | 25 | #include <linux/regmap.h> |
| 26 | #include <mach/cpu.h> | 26 | #include <linux/clk.h> |
| 27 | #include <mach/hardware.h> | ||
| 28 | 27 | ||
| 29 | /* | 28 | /* |
| 30 | * This driver uses two configurable hardware resources that live in the | 29 | * This driver uses two configurable hardware resources that live in the |
| @@ -47,6 +46,22 @@ | |||
| 47 | * registers available, likewise usable for more than "RTC" support. | 46 | * registers available, likewise usable for more than "RTC" support. |
| 48 | */ | 47 | */ |
| 49 | 48 | ||
| 49 | #define AT91_RTT_MR 0x00 /* Real-time Mode Register */ | ||
| 50 | #define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */ | ||
| 51 | #define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */ | ||
| 52 | #define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */ | ||
| 53 | #define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */ | ||
| 54 | |||
| 55 | #define AT91_RTT_AR 0x04 /* Real-time Alarm Register */ | ||
| 56 | #define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */ | ||
| 57 | |||
| 58 | #define AT91_RTT_VR 0x08 /* Real-time Value Register */ | ||
| 59 | #define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */ | ||
| 60 | |||
| 61 | #define AT91_RTT_SR 0x0c /* Real-time Status Register */ | ||
| 62 | #define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */ | ||
| 63 | #define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */ | ||
| 64 | |||
| 50 | /* | 65 | /* |
| 51 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. | 66 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. |
| 52 | * It's also the reset value for that field. | 67 | * It's also the reset value for that field. |
| @@ -58,19 +73,30 @@ struct sam9_rtc { | |||
| 58 | void __iomem *rtt; | 73 | void __iomem *rtt; |
| 59 | struct rtc_device *rtcdev; | 74 | struct rtc_device *rtcdev; |
| 60 | u32 imr; | 75 | u32 imr; |
| 61 | void __iomem *gpbr; | 76 | struct regmap *gpbr; |
| 77 | unsigned int gpbr_offset; | ||
| 62 | int irq; | 78 | int irq; |
| 79 | struct clk *sclk; | ||
| 63 | }; | 80 | }; |
| 64 | 81 | ||
| 65 | #define rtt_readl(rtc, field) \ | 82 | #define rtt_readl(rtc, field) \ |
| 66 | __raw_readl((rtc)->rtt + AT91_RTT_ ## field) | 83 | readl((rtc)->rtt + AT91_RTT_ ## field) |
| 67 | #define rtt_writel(rtc, field, val) \ | 84 | #define rtt_writel(rtc, field, val) \ |
| 68 | __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) | 85 | writel((val), (rtc)->rtt + AT91_RTT_ ## field) |
| 86 | |||
| 87 | static inline unsigned int gpbr_readl(struct sam9_rtc *rtc) | ||
| 88 | { | ||
| 89 | unsigned int val; | ||
| 90 | |||
| 91 | regmap_read(rtc->gpbr, rtc->gpbr_offset, &val); | ||
| 69 | 92 | ||
| 70 | #define gpbr_readl(rtc) \ | 93 | return val; |
| 71 | __raw_readl((rtc)->gpbr) | 94 | } |
| 72 | #define gpbr_writel(rtc, val) \ | 95 | |
| 73 | __raw_writel((val), (rtc)->gpbr) | 96 | static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val) |
| 97 | { | ||
| 98 | regmap_write(rtc->gpbr, rtc->gpbr_offset, val); | ||
| 99 | } | ||
| 74 | 100 | ||
| 75 | /* | 101 | /* |
| 76 | * Read current time and date in RTC | 102 | * Read current time and date in RTC |
| @@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = { | |||
| 287 | .alarm_irq_enable = at91_rtc_alarm_irq_enable, | 313 | .alarm_irq_enable = at91_rtc_alarm_irq_enable, |
| 288 | }; | 314 | }; |
| 289 | 315 | ||
| 316 | static struct regmap_config gpbr_regmap_config = { | ||
| 317 | .reg_bits = 32, | ||
| 318 | .val_bits = 32, | ||
| 319 | .reg_stride = 4, | ||
| 320 | }; | ||
| 321 | |||
| 290 | /* | 322 | /* |
| 291 | * Initialize and install RTC driver | 323 | * Initialize and install RTC driver |
| 292 | */ | 324 | */ |
| 293 | static int at91_rtc_probe(struct platform_device *pdev) | 325 | static int at91_rtc_probe(struct platform_device *pdev) |
| 294 | { | 326 | { |
| 295 | struct resource *r, *r_gpbr; | 327 | struct resource *r; |
| 296 | struct sam9_rtc *rtc; | 328 | struct sam9_rtc *rtc; |
| 297 | int ret, irq; | 329 | int ret, irq; |
| 298 | u32 mr; | 330 | u32 mr; |
| 299 | 331 | unsigned int sclk_rate; | |
| 300 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 301 | r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 302 | if (!r || !r_gpbr) { | ||
| 303 | dev_err(&pdev->dev, "need 2 ressources\n"); | ||
| 304 | return -ENODEV; | ||
| 305 | } | ||
| 306 | 332 | ||
| 307 | irq = platform_get_irq(pdev, 0); | 333 | irq = platform_get_irq(pdev, 0); |
| 308 | if (irq < 0) { | 334 | if (irq < 0) { |
| @@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev) | |||
| 321 | device_init_wakeup(&pdev->dev, 1); | 347 | device_init_wakeup(&pdev->dev, 1); |
| 322 | 348 | ||
| 323 | platform_set_drvdata(pdev, rtc); | 349 | platform_set_drvdata(pdev, rtc); |
| 324 | rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r)); | 350 | |
| 325 | if (!rtc->rtt) { | 351 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 326 | dev_err(&pdev->dev, "failed to map registers, aborting.\n"); | 352 | rtc->rtt = devm_ioremap_resource(&pdev->dev, r); |
| 327 | return -ENOMEM; | 353 | if (IS_ERR(rtc->rtt)) |
| 354 | return PTR_ERR(rtc->rtt); | ||
| 355 | |||
| 356 | if (!pdev->dev.of_node) { | ||
| 357 | /* | ||
| 358 | * TODO: Remove this code chunk when removing non DT board | ||
| 359 | * support. Remember to remove the gpbr_regmap_config | ||
| 360 | * variable too. | ||
| 361 | */ | ||
| 362 | void __iomem *gpbr; | ||
| 363 | |||
| 364 | r = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 365 | gpbr = devm_ioremap_resource(&pdev->dev, r); | ||
| 366 | if (IS_ERR(gpbr)) | ||
| 367 | return PTR_ERR(gpbr); | ||
| 368 | |||
| 369 | rtc->gpbr = regmap_init_mmio(NULL, gpbr, | ||
| 370 | &gpbr_regmap_config); | ||
| 371 | } else { | ||
| 372 | struct of_phandle_args args; | ||
| 373 | |||
| 374 | ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, | ||
| 375 | "atmel,rtt-rtc-time-reg", 1, 0, | ||
| 376 | &args); | ||
| 377 | if (ret) | ||
| 378 | return ret; | ||
| 379 | |||
| 380 | rtc->gpbr = syscon_node_to_regmap(args.np); | ||
| 381 | rtc->gpbr_offset = args.args[0]; | ||
| 328 | } | 382 | } |
| 329 | 383 | ||
| 330 | rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, | 384 | if (IS_ERR(rtc->gpbr)) { |
| 331 | resource_size(r_gpbr)); | 385 | dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); |
| 332 | if (!rtc->gpbr) { | ||
| 333 | dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); | ||
| 334 | return -ENOMEM; | 386 | return -ENOMEM; |
| 335 | } | 387 | } |
| 336 | 388 | ||
| 389 | rtc->sclk = devm_clk_get(&pdev->dev, NULL); | ||
| 390 | if (IS_ERR(rtc->sclk)) | ||
| 391 | return PTR_ERR(rtc->sclk); | ||
| 392 | |||
| 393 | sclk_rate = clk_get_rate(rtc->sclk); | ||
| 394 | if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { | ||
| 395 | dev_err(&pdev->dev, "Invalid slow clock rate\n"); | ||
| 396 | return -EINVAL; | ||
| 397 | } | ||
| 398 | |||
| 399 | ret = clk_prepare_enable(rtc->sclk); | ||
| 400 | if (ret) { | ||
| 401 | dev_err(&pdev->dev, "Could not enable slow clock\n"); | ||
| 402 | return ret; | ||
| 403 | } | ||
| 404 | |||
| 337 | mr = rtt_readl(rtc, MR); | 405 | mr = rtt_readl(rtc, MR); |
| 338 | 406 | ||
| 339 | /* unless RTT is counting at 1 Hz, re-initialize it */ | 407 | /* unless RTT is counting at 1 Hz, re-initialize it */ |
| 340 | if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { | 408 | if ((mr & AT91_RTT_RTPRES) != sclk_rate) { |
| 341 | mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); | 409 | mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES); |
| 342 | gpbr_writel(rtc, 0); | 410 | gpbr_writel(rtc, 0); |
| 343 | } | 411 | } |
| 344 | 412 | ||
| @@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev) | |||
| 383 | /* disable all interrupts */ | 451 | /* disable all interrupts */ |
| 384 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | 452 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); |
| 385 | 453 | ||
| 454 | if (!IS_ERR(rtc->sclk)) | ||
| 455 | clk_disable_unprepare(rtc->sclk); | ||
| 456 | |||
| 386 | return 0; | 457 | return 0; |
| 387 | } | 458 | } |
| 388 | 459 | ||
| @@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev) | |||
| 440 | 511 | ||
| 441 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); | 512 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); |
| 442 | 513 | ||
| 514 | #ifdef CONFIG_OF | ||
| 515 | static const struct of_device_id at91_rtc_dt_ids[] = { | ||
| 516 | { .compatible = "atmel,at91sam9260-rtt" }, | ||
| 517 | { /* sentinel */ } | ||
| 518 | }; | ||
| 519 | MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); | ||
| 520 | #endif | ||
| 521 | |||
| 443 | static struct platform_driver at91_rtc_driver = { | 522 | static struct platform_driver at91_rtc_driver = { |
| 444 | .probe = at91_rtc_probe, | 523 | .probe = at91_rtc_probe, |
| 445 | .remove = at91_rtc_remove, | 524 | .remove = at91_rtc_remove, |
| @@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = { | |||
| 448 | .name = "rtc-at91sam9", | 527 | .name = "rtc-at91sam9", |
| 449 | .owner = THIS_MODULE, | 528 | .owner = THIS_MODULE, |
| 450 | .pm = &at91_rtc_pm_ops, | 529 | .pm = &at91_rtc_pm_ops, |
| 530 | .of_match_table = of_match_ptr(at91_rtc_dt_ids), | ||
| 451 | }, | 531 | }, |
| 452 | }; | 532 | }; |
| 453 | 533 | ||
diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h index bc9dcc8cc3ce..51da2341280d 100644 --- a/drivers/soc/ti/knav_qmss.h +++ b/drivers/soc/ti/knav_qmss.h | |||
| @@ -348,15 +348,15 @@ struct knav_range_info { | |||
| 348 | list_for_each_entry(region, &kdev->regions, list) | 348 | list_for_each_entry(region, &kdev->regions, list) |
| 349 | 349 | ||
| 350 | #define first_region(kdev) \ | 350 | #define first_region(kdev) \ |
| 351 | list_first_entry(&kdev->regions, \ | 351 | list_first_entry_or_null(&kdev->regions, \ |
| 352 | struct knav_region, list) | 352 | struct knav_region, list) |
| 353 | 353 | ||
| 354 | #define for_each_queue_range(kdev, range) \ | 354 | #define for_each_queue_range(kdev, range) \ |
| 355 | list_for_each_entry(range, &kdev->queue_ranges, list) | 355 | list_for_each_entry(range, &kdev->queue_ranges, list) |
| 356 | 356 | ||
| 357 | #define first_queue_range(kdev) \ | 357 | #define first_queue_range(kdev) \ |
| 358 | list_first_entry(&kdev->queue_ranges, \ | 358 | list_first_entry_or_null(&kdev->queue_ranges, \ |
| 359 | struct knav_range_info, list) | 359 | struct knav_range_info, list) |
| 360 | 360 | ||
| 361 | #define for_each_pool(kdev, pool) \ | 361 | #define for_each_pool(kdev, pool) \ |
| 362 | list_for_each_entry(pool, &kdev->pools, list) | 362 | list_for_each_entry(pool, &kdev->pools, list) |
diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 0a2c8634c48b..9b8dd6732681 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c | |||
| @@ -785,7 +785,7 @@ void *knav_pool_create(const char *name, | |||
| 785 | dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n", | 785 | dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n", |
| 786 | region_id, name); | 786 | region_id, name); |
| 787 | ret = -ENOMEM; | 787 | ret = -ENOMEM; |
| 788 | goto err; | 788 | goto err_unlock; |
| 789 | } | 789 | } |
| 790 | 790 | ||
| 791 | /* Region maintains a sorted (by region offset) list of pools | 791 | /* Region maintains a sorted (by region offset) list of pools |
| @@ -815,15 +815,16 @@ void *knav_pool_create(const char *name, | |||
| 815 | dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n", | 815 | dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n", |
| 816 | name, region_id); | 816 | name, region_id); |
| 817 | ret = -ENOMEM; | 817 | ret = -ENOMEM; |
| 818 | goto err; | 818 | goto err_unlock; |
| 819 | } | 819 | } |
| 820 | 820 | ||
| 821 | mutex_unlock(&knav_dev_lock); | 821 | mutex_unlock(&knav_dev_lock); |
| 822 | kdesc_fill_pool(pool); | 822 | kdesc_fill_pool(pool); |
| 823 | return pool; | 823 | return pool; |
| 824 | 824 | ||
| 825 | err: | 825 | err_unlock: |
| 826 | mutex_unlock(&knav_dev_lock); | 826 | mutex_unlock(&knav_dev_lock); |
| 827 | err: | ||
| 827 | kfree(pool->name); | 828 | kfree(pool->name); |
| 828 | devm_kfree(kdev->dev, pool); | 829 | devm_kfree(kdev->dev, pool); |
| 829 | return ERR_PTR(ret); | 830 | return ERR_PTR(ret); |
| @@ -1305,14 +1306,14 @@ static void knav_free_queue_ranges(struct knav_device *kdev) | |||
| 1305 | static void knav_queue_free_regions(struct knav_device *kdev) | 1306 | static void knav_queue_free_regions(struct knav_device *kdev) |
| 1306 | { | 1307 | { |
| 1307 | struct knav_region *region; | 1308 | struct knav_region *region; |
| 1308 | struct knav_pool *pool; | 1309 | struct knav_pool *pool, *tmp; |
| 1309 | unsigned size; | 1310 | unsigned size; |
| 1310 | 1311 | ||
| 1311 | for (;;) { | 1312 | for (;;) { |
| 1312 | region = first_region(kdev); | 1313 | region = first_region(kdev); |
| 1313 | if (!region) | 1314 | if (!region) |
| 1314 | break; | 1315 | break; |
| 1315 | list_for_each_entry(pool, ®ion->pools, region_inst) | 1316 | list_for_each_entry_safe(pool, tmp, ®ion->pools, region_inst) |
| 1316 | knav_pool_destroy(pool); | 1317 | knav_pool_destroy(pool); |
| 1317 | 1318 | ||
| 1318 | size = region->virt_end - region->virt_start; | 1319 | size = region->virt_end - region->virt_start; |
| @@ -1639,7 +1640,7 @@ static int knav_queue_init_queues(struct knav_device *kdev) | |||
| 1639 | size = (1 << kdev->inst_shift) * kdev->num_queues_in_use; | 1640 | size = (1 << kdev->inst_shift) * kdev->num_queues_in_use; |
| 1640 | kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL); | 1641 | kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL); |
| 1641 | if (!kdev->instances) | 1642 | if (!kdev->instances) |
| 1642 | return -1; | 1643 | return -ENOMEM; |
| 1643 | 1644 | ||
| 1644 | for_each_queue_range(kdev, range) { | 1645 | for_each_queue_range(kdev, range) { |
| 1645 | if (range->ops && range->ops->init_range) | 1646 | if (range->ops && range->ops->init_range) |
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 649b784081c7..98f8bcaf3e7e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig | |||
| @@ -249,14 +249,14 @@ config SERIAL_SAMSUNG | |||
| 249 | 249 | ||
| 250 | config SERIAL_SAMSUNG_UARTS_4 | 250 | config SERIAL_SAMSUNG_UARTS_4 |
| 251 | bool | 251 | bool |
| 252 | depends on PLAT_SAMSUNG | 252 | depends on SERIAL_SAMSUNG |
| 253 | default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442) | 253 | default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442) |
| 254 | help | 254 | help |
| 255 | Internal node for the common case of 4 Samsung compatible UARTs | 255 | Internal node for the common case of 4 Samsung compatible UARTs |
| 256 | 256 | ||
| 257 | config SERIAL_SAMSUNG_UARTS | 257 | config SERIAL_SAMSUNG_UARTS |
| 258 | int | 258 | int |
| 259 | depends on PLAT_SAMSUNG | 259 | depends on SERIAL_SAMSUNG |
| 260 | default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 | 260 | default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 |
| 261 | default 3 | 261 | default 3 |
| 262 | help | 262 | help |
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 9900e8ec7393..03321d6a2684 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c | |||
| @@ -72,11 +72,18 @@ struct hdq_data { | |||
| 72 | static int omap_hdq_probe(struct platform_device *pdev); | 72 | static int omap_hdq_probe(struct platform_device *pdev); |
| 73 | static int omap_hdq_remove(struct platform_device *pdev); | 73 | static int omap_hdq_remove(struct platform_device *pdev); |
| 74 | 74 | ||
| 75 | static struct of_device_id omap_hdq_dt_ids[] = { | ||
| 76 | { .compatible = "ti,omap3-1w" }, | ||
| 77 | {} | ||
| 78 | }; | ||
| 79 | MODULE_DEVICE_TABLE(of, omap_hdq_dt_ids); | ||
| 80 | |||
| 75 | static struct platform_driver omap_hdq_driver = { | 81 | static struct platform_driver omap_hdq_driver = { |
| 76 | .probe = omap_hdq_probe, | 82 | .probe = omap_hdq_probe, |
| 77 | .remove = omap_hdq_remove, | 83 | .remove = omap_hdq_remove, |
| 78 | .driver = { | 84 | .driver = { |
| 79 | .name = "omap_hdq", | 85 | .name = "omap_hdq", |
| 86 | .of_match_table = omap_hdq_dt_ids, | ||
| 80 | }, | 87 | }, |
| 81 | }; | 88 | }; |
| 82 | 89 | ||
diff --git a/include/dt-bindings/clock/tegra114-car.h b/include/dt-bindings/clock/tegra114-car.h index fc12621fb432..534c03f8ad72 100644 --- a/include/dt-bindings/clock/tegra114-car.h +++ b/include/dt-bindings/clock/tegra114-car.h | |||
| @@ -49,7 +49,7 @@ | |||
| 49 | #define TEGRA114_CLK_I2S0 30 | 49 | #define TEGRA114_CLK_I2S0 30 |
| 50 | /* 31 */ | 50 | /* 31 */ |
| 51 | 51 | ||
| 52 | /* 32 */ | 52 | #define TEGRA114_CLK_MC 32 |
| 53 | /* 33 */ | 53 | /* 33 */ |
| 54 | #define TEGRA114_CLK_APBDMA 34 | 54 | #define TEGRA114_CLK_APBDMA 34 |
| 55 | /* 35 */ | 55 | /* 35 */ |
diff --git a/include/dt-bindings/clock/tegra124-car.h b/include/dt-bindings/clock/tegra124-car.h index 6bac637fd635..af9bc9a3ddbc 100644 --- a/include/dt-bindings/clock/tegra124-car.h +++ b/include/dt-bindings/clock/tegra124-car.h | |||
| @@ -48,7 +48,7 @@ | |||
| 48 | #define TEGRA124_CLK_I2S0 30 | 48 | #define TEGRA124_CLK_I2S0 30 |
| 49 | /* 31 */ | 49 | /* 31 */ |
| 50 | 50 | ||
| 51 | /* 32 */ | 51 | #define TEGRA124_CLK_MC 32 |
| 52 | /* 33 */ | 52 | /* 33 */ |
| 53 | #define TEGRA124_CLK_APBDMA 34 | 53 | #define TEGRA124_CLK_APBDMA 34 |
| 54 | /* 35 */ | 54 | /* 35 */ |
diff --git a/include/dt-bindings/clock/tegra20-car.h b/include/dt-bindings/clock/tegra20-car.h index 9406207cfac8..04500b243a4d 100644 --- a/include/dt-bindings/clock/tegra20-car.h +++ b/include/dt-bindings/clock/tegra20-car.h | |||
| @@ -49,7 +49,7 @@ | |||
| 49 | /* 30 */ | 49 | /* 30 */ |
| 50 | #define TEGRA20_CLK_CACHE2 31 | 50 | #define TEGRA20_CLK_CACHE2 31 |
| 51 | 51 | ||
| 52 | #define TEGRA20_CLK_MEM 32 | 52 | #define TEGRA20_CLK_MC 32 |
| 53 | #define TEGRA20_CLK_AHBDMA 33 | 53 | #define TEGRA20_CLK_AHBDMA 33 |
| 54 | #define TEGRA20_CLK_APBDMA 34 | 54 | #define TEGRA20_CLK_APBDMA 34 |
| 55 | /* 35 */ | 55 | /* 35 */ |
diff --git a/include/dt-bindings/memory/tegra114-mc.h b/include/dt-bindings/memory/tegra114-mc.h new file mode 100644 index 000000000000..8f48985a3139 --- /dev/null +++ b/include/dt-bindings/memory/tegra114-mc.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #ifndef DT_BINDINGS_MEMORY_TEGRA114_MC_H | ||
| 2 | #define DT_BINDINGS_MEMORY_TEGRA114_MC_H | ||
| 3 | |||
| 4 | #define TEGRA_SWGROUP_PTC 0 | ||
| 5 | #define TEGRA_SWGROUP_DC 1 | ||
| 6 | #define TEGRA_SWGROUP_DCB 2 | ||
| 7 | #define TEGRA_SWGROUP_EPP 3 | ||
| 8 | #define TEGRA_SWGROUP_G2 4 | ||
| 9 | #define TEGRA_SWGROUP_AVPC 5 | ||
| 10 | #define TEGRA_SWGROUP_NV 6 | ||
| 11 | #define TEGRA_SWGROUP_HDA 7 | ||
| 12 | #define TEGRA_SWGROUP_HC 8 | ||
| 13 | #define TEGRA_SWGROUP_MSENC 9 | ||
| 14 | #define TEGRA_SWGROUP_PPCS 10 | ||
| 15 | #define TEGRA_SWGROUP_VDE 11 | ||
| 16 | #define TEGRA_SWGROUP_MPCORELP 12 | ||
| 17 | #define TEGRA_SWGROUP_MPCORE 13 | ||
| 18 | #define TEGRA_SWGROUP_VI 14 | ||
| 19 | #define TEGRA_SWGROUP_ISP 15 | ||
| 20 | #define TEGRA_SWGROUP_XUSB_HOST 16 | ||
| 21 | #define TEGRA_SWGROUP_XUSB_DEV 17 | ||
| 22 | #define TEGRA_SWGROUP_EMUCIF 18 | ||
| 23 | #define TEGRA_SWGROUP_TSEC 19 | ||
| 24 | |||
| 25 | #endif | ||
diff --git a/include/dt-bindings/memory/tegra124-mc.h b/include/dt-bindings/memory/tegra124-mc.h new file mode 100644 index 000000000000..7d8ee798f34e --- /dev/null +++ b/include/dt-bindings/memory/tegra124-mc.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #ifndef DT_BINDINGS_MEMORY_TEGRA124_MC_H | ||
| 2 | #define DT_BINDINGS_MEMORY_TEGRA124_MC_H | ||
| 3 | |||
| 4 | #define TEGRA_SWGROUP_PTC 0 | ||
| 5 | #define TEGRA_SWGROUP_DC 1 | ||
| 6 | #define TEGRA_SWGROUP_DCB 2 | ||
| 7 | #define TEGRA_SWGROUP_AFI 3 | ||
| 8 | #define TEGRA_SWGROUP_AVPC 4 | ||
| 9 | #define TEGRA_SWGROUP_HDA 5 | ||
| 10 | #define TEGRA_SWGROUP_HC 6 | ||
| 11 | #define TEGRA_SWGROUP_MSENC 7 | ||
| 12 | #define TEGRA_SWGROUP_PPCS 8 | ||
| 13 | #define TEGRA_SWGROUP_SATA 9 | ||
| 14 | #define TEGRA_SWGROUP_VDE 10 | ||
| 15 | #define TEGRA_SWGROUP_MPCORELP 11 | ||
| 16 | #define TEGRA_SWGROUP_MPCORE 12 | ||
| 17 | #define TEGRA_SWGROUP_ISP2 13 | ||
| 18 | #define TEGRA_SWGROUP_XUSB_HOST 14 | ||
| 19 | #define TEGRA_SWGROUP_XUSB_DEV 15 | ||
| 20 | #define TEGRA_SWGROUP_ISP2B 16 | ||
| 21 | #define TEGRA_SWGROUP_TSEC 17 | ||
| 22 | #define TEGRA_SWGROUP_A9AVP 18 | ||
| 23 | #define TEGRA_SWGROUP_GPU 19 | ||
| 24 | #define TEGRA_SWGROUP_SDMMC1A 20 | ||
| 25 | #define TEGRA_SWGROUP_SDMMC2A 21 | ||
| 26 | #define TEGRA_SWGROUP_SDMMC3A 22 | ||
| 27 | #define TEGRA_SWGROUP_SDMMC4A 23 | ||
| 28 | #define TEGRA_SWGROUP_VIC 24 | ||
| 29 | #define TEGRA_SWGROUP_VI 25 | ||
| 30 | |||
| 31 | #endif | ||
diff --git a/include/dt-bindings/memory/tegra30-mc.h b/include/dt-bindings/memory/tegra30-mc.h new file mode 100644 index 000000000000..502beb03d777 --- /dev/null +++ b/include/dt-bindings/memory/tegra30-mc.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #ifndef DT_BINDINGS_MEMORY_TEGRA30_MC_H | ||
| 2 | #define DT_BINDINGS_MEMORY_TEGRA30_MC_H | ||
| 3 | |||
| 4 | #define TEGRA_SWGROUP_PTC 0 | ||
| 5 | #define TEGRA_SWGROUP_DC 1 | ||
| 6 | #define TEGRA_SWGROUP_DCB 2 | ||
| 7 | #define TEGRA_SWGROUP_EPP 3 | ||
| 8 | #define TEGRA_SWGROUP_G2 4 | ||
| 9 | #define TEGRA_SWGROUP_MPE 5 | ||
| 10 | #define TEGRA_SWGROUP_VI 6 | ||
| 11 | #define TEGRA_SWGROUP_AFI 7 | ||
| 12 | #define TEGRA_SWGROUP_AVPC 8 | ||
| 13 | #define TEGRA_SWGROUP_NV 9 | ||
| 14 | #define TEGRA_SWGROUP_NV2 10 | ||
| 15 | #define TEGRA_SWGROUP_HDA 11 | ||
| 16 | #define TEGRA_SWGROUP_HC 12 | ||
| 17 | #define TEGRA_SWGROUP_PPCS 13 | ||
| 18 | #define TEGRA_SWGROUP_SATA 14 | ||
| 19 | #define TEGRA_SWGROUP_VDE 15 | ||
| 20 | #define TEGRA_SWGROUP_MPCORELP 16 | ||
| 21 | #define TEGRA_SWGROUP_MPCORE 17 | ||
| 22 | #define TEGRA_SWGROUP_ISP 18 | ||
| 23 | |||
| 24 | #endif | ||
diff --git a/include/dt-bindings/reset-controller/stih407-resets.h b/include/dt-bindings/reset-controller/stih407-resets.h new file mode 100644 index 000000000000..02d4328fe479 --- /dev/null +++ b/include/dt-bindings/reset-controller/stih407-resets.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | /* | ||
| 2 | * This header provides constants for the reset controller | ||
| 3 | * based peripheral powerdown requests on the STMicroelectronics | ||
| 4 | * STiH407 SoC. | ||
| 5 | */ | ||
| 6 | #ifndef _DT_BINDINGS_RESET_CONTROLLER_STIH407 | ||
| 7 | #define _DT_BINDINGS_RESET_CONTROLLER_STIH407 | ||
| 8 | |||
| 9 | /* Powerdown requests control 0 */ | ||
| 10 | #define STIH407_EMISS_POWERDOWN 0 | ||
| 11 | #define STIH407_NAND_POWERDOWN 1 | ||
| 12 | |||
| 13 | /* Synp GMAC PowerDown */ | ||
| 14 | #define STIH407_ETH1_POWERDOWN 2 | ||
| 15 | |||
| 16 | /* Powerdown requests control 1 */ | ||
| 17 | #define STIH407_USB3_POWERDOWN 3 | ||
| 18 | #define STIH407_USB2_PORT1_POWERDOWN 4 | ||
| 19 | #define STIH407_USB2_PORT0_POWERDOWN 5 | ||
| 20 | #define STIH407_PCIE1_POWERDOWN 6 | ||
| 21 | #define STIH407_PCIE0_POWERDOWN 7 | ||
| 22 | #define STIH407_SATA1_POWERDOWN 8 | ||
| 23 | #define STIH407_SATA0_POWERDOWN 9 | ||
| 24 | |||
| 25 | /* Reset defines */ | ||
| 26 | #define STIH407_ETH1_SOFTRESET 0 | ||
| 27 | #define STIH407_MMC1_SOFTRESET 1 | ||
| 28 | #define STIH407_PICOPHY_SOFTRESET 2 | ||
| 29 | #define STIH407_IRB_SOFTRESET 3 | ||
| 30 | #define STIH407_PCIE0_SOFTRESET 4 | ||
| 31 | #define STIH407_PCIE1_SOFTRESET 5 | ||
| 32 | #define STIH407_SATA0_SOFTRESET 6 | ||
| 33 | #define STIH407_SATA1_SOFTRESET 7 | ||
| 34 | #define STIH407_MIPHY0_SOFTRESET 8 | ||
| 35 | #define STIH407_MIPHY1_SOFTRESET 9 | ||
| 36 | #define STIH407_MIPHY2_SOFTRESET 10 | ||
| 37 | #define STIH407_SATA0_PWR_SOFTRESET 11 | ||
| 38 | #define STIH407_SATA1_PWR_SOFTRESET 12 | ||
| 39 | #define STIH407_DELTA_SOFTRESET 13 | ||
| 40 | #define STIH407_BLITTER_SOFTRESET 14 | ||
| 41 | #define STIH407_HDTVOUT_SOFTRESET 15 | ||
| 42 | #define STIH407_HDQVDP_SOFTRESET 16 | ||
| 43 | #define STIH407_VDP_AUX_SOFTRESET 17 | ||
| 44 | #define STIH407_COMPO_SOFTRESET 18 | ||
| 45 | #define STIH407_HDMI_TX_PHY_SOFTRESET 19 | ||
| 46 | #define STIH407_JPEG_DEC_SOFTRESET 20 | ||
| 47 | #define STIH407_VP8_DEC_SOFTRESET 21 | ||
| 48 | #define STIH407_GPU_SOFTRESET 22 | ||
| 49 | #define STIH407_HVA_SOFTRESET 23 | ||
| 50 | #define STIH407_ERAM_HVA_SOFTRESET 24 | ||
| 51 | #define STIH407_LPM_SOFTRESET 25 | ||
| 52 | #define STIH407_KEYSCAN_SOFTRESET 26 | ||
| 53 | #define STIH407_USB2_PORT0_SOFTRESET 27 | ||
| 54 | #define STIH407_USB2_PORT1_SOFTRESET 28 | ||
| 55 | |||
| 56 | /* Picophy reset defines */ | ||
| 57 | #define STIH407_PICOPHY0_RESET 0 | ||
| 58 | #define STIH407_PICOPHY1_RESET 1 | ||
| 59 | #define STIH407_PICOPHY2_RESET 2 | ||
| 60 | |||
| 61 | #endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH407 */ | ||
diff --git a/include/linux/iommu.h b/include/linux/iommu.h index e6a7c9ff72f2..b29a5982e1c3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/errno.h> | 22 | #include <linux/errno.h> |
| 23 | #include <linux/err.h> | 23 | #include <linux/err.h> |
| 24 | #include <linux/types.h> | 24 | #include <linux/types.h> |
| 25 | #include <linux/scatterlist.h> | ||
| 25 | #include <trace/events/iommu.h> | 26 | #include <trace/events/iommu.h> |
| 26 | 27 | ||
| 27 | #define IOMMU_READ (1 << 0) | 28 | #define IOMMU_READ (1 << 0) |
| @@ -97,6 +98,8 @@ enum iommu_attr { | |||
| 97 | * @detach_dev: detach device from an iommu domain | 98 | * @detach_dev: detach device from an iommu domain |
| 98 | * @map: map a physically contiguous memory region to an iommu domain | 99 | * @map: map a physically contiguous memory region to an iommu domain |
| 99 | * @unmap: unmap a physically contiguous memory region from an iommu domain | 100 | * @unmap: unmap a physically contiguous memory region from an iommu domain |
| 101 | * @map_sg: map a scatter-gather list of physically contiguous memory chunks | ||
| 102 | * to an iommu domain | ||
| 100 | * @iova_to_phys: translate iova to physical address | 103 | * @iova_to_phys: translate iova to physical address |
| 101 | * @add_device: add device to iommu grouping | 104 | * @add_device: add device to iommu grouping |
| 102 | * @remove_device: remove device from iommu grouping | 105 | * @remove_device: remove device from iommu grouping |
| @@ -114,6 +117,8 @@ struct iommu_ops { | |||
| 114 | phys_addr_t paddr, size_t size, int prot); | 117 | phys_addr_t paddr, size_t size, int prot); |
| 115 | size_t (*unmap)(struct iommu_domain *domain, unsigned long iova, | 118 | size_t (*unmap)(struct iommu_domain *domain, unsigned long iova, |
| 116 | size_t size); | 119 | size_t size); |
| 120 | size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova, | ||
| 121 | struct scatterlist *sg, unsigned int nents, int prot); | ||
| 117 | phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); | 122 | phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); |
| 118 | int (*add_device)(struct device *dev); | 123 | int (*add_device)(struct device *dev); |
| 119 | void (*remove_device)(struct device *dev); | 124 | void (*remove_device)(struct device *dev); |
| @@ -156,6 +161,9 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova, | |||
| 156 | phys_addr_t paddr, size_t size, int prot); | 161 | phys_addr_t paddr, size_t size, int prot); |
| 157 | extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, | 162 | extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, |
| 158 | size_t size); | 163 | size_t size); |
| 164 | extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, | ||
| 165 | struct scatterlist *sg,unsigned int nents, | ||
| 166 | int prot); | ||
| 159 | extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova); | 167 | extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova); |
| 160 | extern void iommu_set_fault_handler(struct iommu_domain *domain, | 168 | extern void iommu_set_fault_handler(struct iommu_domain *domain, |
| 161 | iommu_fault_handler_t handler, void *token); | 169 | iommu_fault_handler_t handler, void *token); |
| @@ -241,6 +249,13 @@ static inline int report_iommu_fault(struct iommu_domain *domain, | |||
| 241 | return ret; | 249 | return ret; |
| 242 | } | 250 | } |
| 243 | 251 | ||
| 252 | static inline size_t iommu_map_sg(struct iommu_domain *domain, | ||
| 253 | unsigned long iova, struct scatterlist *sg, | ||
| 254 | unsigned int nents, int prot) | ||
| 255 | { | ||
| 256 | return domain->ops->map_sg(domain, iova, sg, nents, prot); | ||
| 257 | } | ||
| 258 | |||
| 244 | #else /* CONFIG_IOMMU_API */ | 259 | #else /* CONFIG_IOMMU_API */ |
| 245 | 260 | ||
| 246 | struct iommu_ops {}; | 261 | struct iommu_ops {}; |
| @@ -293,6 +308,13 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, | |||
| 293 | return -ENODEV; | 308 | return -ENODEV; |
| 294 | } | 309 | } |
| 295 | 310 | ||
| 311 | static inline size_t iommu_map_sg(struct iommu_domain *domain, | ||
| 312 | unsigned long iova, struct scatterlist *sg, | ||
| 313 | unsigned int nents, int prot) | ||
| 314 | { | ||
| 315 | return -ENODEV; | ||
| 316 | } | ||
| 317 | |||
| 296 | static inline int iommu_domain_window_enable(struct iommu_domain *domain, | 318 | static inline int iommu_domain_window_enable(struct iommu_domain *domain, |
| 297 | u32 wnd_nr, phys_addr_t paddr, | 319 | u32 wnd_nr, phys_addr_t paddr, |
| 298 | u64 size, int prot) | 320 | u64 size, int prot) |
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index 41a4695fde08..ce6b962ffed4 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h | |||
| @@ -12,11 +12,13 @@ struct reset_controller_dev; | |||
| 12 | * things to reset the device | 12 | * things to reset the device |
| 13 | * @assert: manually assert the reset line, if supported | 13 | * @assert: manually assert the reset line, if supported |
| 14 | * @deassert: manually deassert the reset line, if supported | 14 | * @deassert: manually deassert the reset line, if supported |
| 15 | * @status: return the status of the reset line, if supported | ||
| 15 | */ | 16 | */ |
| 16 | struct reset_control_ops { | 17 | struct reset_control_ops { |
| 17 | int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); | 18 | int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); |
| 18 | int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); | 19 | int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); |
| 19 | int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); | 20 | int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); |
| 21 | int (*status)(struct reset_controller_dev *rcdev, unsigned long id); | ||
| 20 | }; | 22 | }; |
| 21 | 23 | ||
| 22 | struct module; | 24 | struct module; |
diff --git a/include/linux/reset.h b/include/linux/reset.h index 349f150ae12c..da5602bd77d7 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h | |||
| @@ -10,6 +10,7 @@ struct reset_control; | |||
| 10 | int reset_control_reset(struct reset_control *rstc); | 10 | int reset_control_reset(struct reset_control *rstc); |
| 11 | int reset_control_assert(struct reset_control *rstc); | 11 | int reset_control_assert(struct reset_control *rstc); |
| 12 | int reset_control_deassert(struct reset_control *rstc); | 12 | int reset_control_deassert(struct reset_control *rstc); |
| 13 | int reset_control_status(struct reset_control *rstc); | ||
| 13 | 14 | ||
| 14 | struct reset_control *reset_control_get(struct device *dev, const char *id); | 15 | struct reset_control *reset_control_get(struct device *dev, const char *id); |
| 15 | void reset_control_put(struct reset_control *rstc); | 16 | void reset_control_put(struct reset_control *rstc); |
| @@ -57,6 +58,12 @@ static inline int reset_control_deassert(struct reset_control *rstc) | |||
| 57 | return 0; | 58 | return 0; |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | static inline int reset_control_status(struct reset_control *rstc) | ||
| 62 | { | ||
| 63 | WARN_ON(1); | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 60 | static inline void reset_control_put(struct reset_control *rstc) | 67 | static inline void reset_control_put(struct reset_control *rstc) |
| 61 | { | 68 | { |
| 62 | WARN_ON(1); | 69 | WARN_ON(1); |
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h new file mode 100644 index 000000000000..63deb8d9f82a --- /dev/null +++ b/include/soc/tegra/mc.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 NVIDIA Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef __SOC_TEGRA_MC_H__ | ||
| 10 | #define __SOC_TEGRA_MC_H__ | ||
| 11 | |||
| 12 | #include <linux/types.h> | ||
| 13 | |||
| 14 | struct clk; | ||
| 15 | struct device; | ||
| 16 | struct page; | ||
| 17 | |||
| 18 | struct tegra_smmu_enable { | ||
| 19 | unsigned int reg; | ||
| 20 | unsigned int bit; | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* latency allowance */ | ||
| 24 | struct tegra_mc_la { | ||
| 25 | unsigned int reg; | ||
| 26 | unsigned int shift; | ||
| 27 | unsigned int mask; | ||
| 28 | unsigned int def; | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct tegra_mc_client { | ||
| 32 | unsigned int id; | ||
| 33 | const char *name; | ||
| 34 | unsigned int swgroup; | ||
| 35 | |||
| 36 | unsigned int fifo_size; | ||
| 37 | |||
| 38 | struct tegra_smmu_enable smmu; | ||
| 39 | struct tegra_mc_la la; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct tegra_smmu_swgroup { | ||
| 43 | unsigned int swgroup; | ||
| 44 | unsigned int reg; | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct tegra_smmu_ops { | ||
| 48 | void (*flush_dcache)(struct page *page, unsigned long offset, | ||
| 49 | size_t size); | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct tegra_smmu_soc { | ||
| 53 | const struct tegra_mc_client *clients; | ||
| 54 | unsigned int num_clients; | ||
| 55 | |||
| 56 | const struct tegra_smmu_swgroup *swgroups; | ||
| 57 | unsigned int num_swgroups; | ||
| 58 | |||
| 59 | bool supports_round_robin_arbitration; | ||
| 60 | bool supports_request_limit; | ||
| 61 | |||
| 62 | unsigned int num_asids; | ||
| 63 | |||
| 64 | const struct tegra_smmu_ops *ops; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct tegra_mc; | ||
| 68 | struct tegra_smmu; | ||
| 69 | |||
| 70 | #ifdef CONFIG_TEGRA_IOMMU_SMMU | ||
| 71 | struct tegra_smmu *tegra_smmu_probe(struct device *dev, | ||
| 72 | const struct tegra_smmu_soc *soc, | ||
| 73 | struct tegra_mc *mc); | ||
| 74 | #else | ||
| 75 | static inline struct tegra_smmu * | ||
| 76 | tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc, | ||
| 77 | struct tegra_mc *mc) | ||
| 78 | { | ||
| 79 | return NULL; | ||
| 80 | } | ||
| 81 | #endif | ||
| 82 | |||
| 83 | struct tegra_mc_soc { | ||
| 84 | const struct tegra_mc_client *clients; | ||
| 85 | unsigned int num_clients; | ||
| 86 | |||
| 87 | const unsigned int *emem_regs; | ||
| 88 | unsigned int num_emem_regs; | ||
| 89 | |||
| 90 | unsigned int num_address_bits; | ||
| 91 | unsigned int atom_size; | ||
| 92 | |||
| 93 | const struct tegra_smmu_soc *smmu; | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct tegra_mc { | ||
| 97 | struct device *dev; | ||
| 98 | struct tegra_smmu *smmu; | ||
| 99 | void __iomem *regs; | ||
| 100 | struct clk *clk; | ||
| 101 | int irq; | ||
| 102 | |||
| 103 | const struct tegra_mc_soc *soc; | ||
| 104 | unsigned long tick; | ||
| 105 | }; | ||
| 106 | |||
| 107 | #endif /* __SOC_TEGRA_MC_H__ */ | ||
