mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-11 15:38:07 +09:00
Merge tag 'lsk-v3.10-android-15.01'
LSK Android 15.01 v3.10 Conflicts: arch/arm/Kconfig.debug arch/arm64/mm/init.c drivers/Makefile drivers/cpufreq/cpufreq_interactive.c
This commit is contained in:
204
Documentation/devicetree/bindings/arm/coresight.txt
Normal file
204
Documentation/devicetree/bindings/arm/coresight.txt
Normal file
@@ -0,0 +1,204 @@
|
||||
* CoreSight Components:
|
||||
|
||||
CoreSight components are compliant with the ARM CoreSight architecture
|
||||
specification and can be connected in various topologies to suit a particular
|
||||
SoCs tracing needs. These trace components can generally be classified as
|
||||
sinks, links and sources. Trace data produced by one or more sources flows
|
||||
through the intermediate links connecting the source to the currently selected
|
||||
sink. Each CoreSight component device should use these properties to describe
|
||||
its hardware characteristcs.
|
||||
|
||||
* Required properties for all components *except* non-configurable replicators:
|
||||
|
||||
* compatible: These have to be supplemented with "arm,primecell" as
|
||||
drivers are using the AMBA bus interface. Possible values include:
|
||||
- "arm,coresight-etb10", "arm,primecell";
|
||||
- "arm,coresight-tpiu", "arm,primecell";
|
||||
- "arm,coresight-tmc", "arm,primecell";
|
||||
- "arm,coresight-funnel", "arm,primecell";
|
||||
- "arm,coresight-etm3x", "arm,primecell";
|
||||
|
||||
* reg: physical base address and length of the register
|
||||
set(s) of the component.
|
||||
|
||||
* clocks: the clock associated to this component.
|
||||
|
||||
* clock-names: the name of the clock as referenced by the code.
|
||||
Since we are using the AMBA framework, the name should be
|
||||
"apb_pclk".
|
||||
|
||||
* port or ports: The representation of the component's port
|
||||
layout using the generic DT graph presentation found in
|
||||
"bindings/graph.txt".
|
||||
|
||||
* Required properties for devices that don't show up on the AMBA bus, such as
|
||||
non-configurable replicators:
|
||||
|
||||
* compatible: Currently supported value is (note the absence of the
|
||||
AMBA markee):
|
||||
- "arm,coresight-replicator"
|
||||
|
||||
* id: a unique number that will identify this replicator.
|
||||
|
||||
* port or ports: same as above.
|
||||
|
||||
* Optional properties for ETM/PTMs:
|
||||
|
||||
* arm,cp14: must be present if the system accesses ETM/PTM management
|
||||
registers via co-processor 14.
|
||||
|
||||
* cpu: the cpu phandle this ETM/PTM is affined to. When omitted the
|
||||
source is considered to belong to CPU0.
|
||||
|
||||
* Optional property for TMC:
|
||||
|
||||
* arm,buffer-size: size of contiguous buffer space for TMC ETR
|
||||
(embedded trace router)
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
1. Sinks
|
||||
etb@20010000 {
|
||||
compatible = "arm,coresight-etb10", "arm,primecell";
|
||||
reg = <0 0x20010000 0 0x1000>;
|
||||
|
||||
coresight-default-sink;
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
etb_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tpiu@20030000 {
|
||||
compatible = "arm,coresight-tpiu", "arm,primecell";
|
||||
reg = <0 0x20030000 0 0x1000>;
|
||||
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
tpiu_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator_out_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
2. Links
|
||||
replicator {
|
||||
/* non-configurable replicators don't show up on the
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
/* this will show up in debugfs as "0.replicator" */
|
||||
id = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* replicator output ports */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
replicator_out_port0: endpoint {
|
||||
remote-endpoint = <&etb_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
replicator_out_port1: endpoint {
|
||||
remote-endpoint = <&tpiu_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
/* replicator input port */
|
||||
port@2 {
|
||||
reg = <0>;
|
||||
replicator_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@20040000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0x20040000 0 0x1000>;
|
||||
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel_out_port0: endpoint {
|
||||
remote-endpoint =
|
||||
<&replicator_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm0_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm1_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&etm0_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
3. Sources
|
||||
ptm@2201c000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0x2201c000 0 0x1000>;
|
||||
|
||||
cpu = <&cpu0>;
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
ptm0_out_port: endpoint {
|
||||
remote-endpoint = <&funnel_in_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@2201d000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0x2201d000 0 0x1000>;
|
||||
|
||||
cpu = <&cpu1>;
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
ptm1_out_port: endpoint {
|
||||
remote-endpoint = <&funnel_in_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -14,11 +14,19 @@ survive after a restart.
|
||||
|
||||
1. Ramoops concepts
|
||||
|
||||
Ramoops uses a predefined memory area to store the dump. The start and size of
|
||||
the memory area are set using two variables:
|
||||
Ramoops uses a predefined memory area to store the dump. The start and size
|
||||
and type of the memory area are set using three variables:
|
||||
* "mem_address" for the start
|
||||
* "mem_size" for the size. The memory size will be rounded down to a
|
||||
power of two.
|
||||
* "mem_type" to specifiy if the memory type (default is pgprot_writecombine).
|
||||
|
||||
Typically the default value of mem_type=0 should be used as that sets the pstore
|
||||
mapping to pgprot_writecombine. Setting mem_type=1 attempts to use
|
||||
pgprot_noncached, which only works on some platforms. This is because pstore
|
||||
depends on atomic operations. At least on ARM, pgprot_noncached causes the
|
||||
memory to be mapped strongly ordered, and atomic operations on strongly ordered
|
||||
memory are implementation defined, and won't work on many ARMs such as omaps.
|
||||
|
||||
The memory area is divided into "record_size" chunks (also rounded down to
|
||||
power of two) and each oops/panic writes a "record_size" chunk of
|
||||
@@ -55,6 +63,7 @@ Setting the ramoops parameters can be done in 2 different manners:
|
||||
static struct ramoops_platform_data ramoops_data = {
|
||||
.mem_size = <...>,
|
||||
.mem_address = <...>,
|
||||
.mem_type = <...>,
|
||||
.record_size = <...>,
|
||||
.dump_oops = <...>,
|
||||
.ecc = <...>,
|
||||
|
||||
299
Documentation/trace/coresight.txt
Normal file
299
Documentation/trace/coresight.txt
Normal file
@@ -0,0 +1,299 @@
|
||||
Coresight - HW Assisted Tracing on ARM
|
||||
======================================
|
||||
|
||||
Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Date: September 11th, 2014
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Coresight is an umbrella of technologies allowing for the debugging of ARM
|
||||
based SoC. It includes solutions for JTAG and HW assisted tracing. This
|
||||
document is concerned with the latter.
|
||||
|
||||
HW assisted tracing is becoming increasingly useful when dealing with systems
|
||||
that have many SoCs and other components like GPU and DMA engines. ARM has
|
||||
developed a HW assisted tracing solution by means of different components, each
|
||||
being added to a design at systhesis time to cater to specific tracing needs.
|
||||
Compoments are generally categorised as source, link and sinks and are
|
||||
(usually) discovered using the AMBA bus.
|
||||
|
||||
"Sources" generate a compressed stream representing the processor instruction
|
||||
path based on tracing scenarios as configured by users. From there the stream
|
||||
flows through the coresight system (via ATB bus) using links that are connecting
|
||||
the emanating source to a sink(s). Sinks serve as endpoints to the coresight
|
||||
implementation, either storing the compressed stream in a memory buffer or
|
||||
creating an interface to the outside world where data can be transferred to a
|
||||
host without fear of filling up the onboard coresight memory buffer.
|
||||
|
||||
At typical coresight system would look like this:
|
||||
|
||||
*****************************************************************
|
||||
**************************** AMBA AXI ****************************===||
|
||||
***************************************************************** ||
|
||||
^ ^ | ||
|
||||
| | * **
|
||||
0000000 ::::: 0000000 ::::: ::::: @@@@@@@ ||||||||||||
|
||||
0 CPU 0<-->: C : 0 CPU 0<-->: C : : C : @ STM @ || System ||
|
||||
|->0000000 : T : |->0000000 : T : : T :<--->@@@@@ || Memory ||
|
||||
| #######<-->: I : | #######<-->: I : : I : @@@<-| ||||||||||||
|
||||
| # ETM # ::::: | # PTM # ::::: ::::: @ |
|
||||
| ##### ^ ^ | ##### ^ ! ^ ! . | |||||||||
|
||||
| |->### | ! | |->### | ! | ! . | || DAP ||
|
||||
| | # | ! | | # | ! | ! . | |||||||||
|
||||
| | . | ! | | . | ! | ! . | | |
|
||||
| | . | ! | | . | ! | ! . | | *
|
||||
| | . | ! | | . | ! | ! . | | SWD/
|
||||
| | . | ! | | . | ! | ! . | | JTAG
|
||||
*****************************************************************<-|
|
||||
*************************** AMBA Debug ABP ************************
|
||||
*****************************************************************
|
||||
| . ! . ! ! . |
|
||||
| . * . * * . |
|
||||
*****************************************************************
|
||||
******************** Cross Trigger Matrix (CTM) *******************
|
||||
*****************************************************************
|
||||
| . ^ . . |
|
||||
| * ! * * |
|
||||
*****************************************************************
|
||||
****************** AMBA Advanced Trace Bus (ATB) ******************
|
||||
*****************************************************************
|
||||
| ! =============== |
|
||||
| * ===== F =====<---------|
|
||||
| ::::::::: ==== U ====
|
||||
|-->:: CTI ::<!! === N ===
|
||||
| ::::::::: ! == N ==
|
||||
| ^ * == E ==
|
||||
| ! &&&&&&&&& IIIIIII == L ==
|
||||
|------>&& ETB &&<......II I =======
|
||||
| ! &&&&&&&&& II I .
|
||||
| ! I I .
|
||||
| ! I REP I<..........
|
||||
| ! I I
|
||||
| !!>&&&&&&&&& II I *Source: ARM ltd.
|
||||
|------>& TPIU &<......II I DAP = Debug Access Port
|
||||
&&&&&&&&& IIIIIII ETM = Embedded Trace Macrocell
|
||||
; PTM = Program Trace Macrocell
|
||||
; CTI = Cross Trigger Interface
|
||||
* ETB = Embedded Trace Buffer
|
||||
To trace port TPIU= Trace Port Interface Unit
|
||||
SWD = Serial Wire Debug
|
||||
|
||||
While on target configuration of the components is done via the ABP bus,
|
||||
all trace data are carried out-of-band on the ATB bus. The CTM provides
|
||||
a way to aggregate and distribute signals between CoreSight components.
|
||||
|
||||
The coresight framework provides a central point to represent, configure and
|
||||
manage coresight devices on a platform. This first implementation centers on
|
||||
the basic tracing functionality, enabling components such ETM/PTM, funnel,
|
||||
replicator, TMC, TPIU and ETB. Future work will enable more
|
||||
intricate IP blocks such as STM and CTI.
|
||||
|
||||
|
||||
Acronyms and Classification
|
||||
---------------------------
|
||||
|
||||
Acronyms:
|
||||
|
||||
PTM: Program Trace Macrocell
|
||||
ETM: Embedded Trace Macrocell
|
||||
STM: System trace Macrocell
|
||||
ETB: Embedded Trace Buffer
|
||||
ITM: Instrumentation Trace Macrocell
|
||||
TPIU: Trace Port Interface Unit
|
||||
TMC-ETR: Trace Memory Controller, configured as Embedded Trace Router
|
||||
TMC-ETF: Trace Memory Controller, configured as Embedded Trace FIFO
|
||||
CTI: Cross Trigger Interface
|
||||
|
||||
Classification:
|
||||
|
||||
Source:
|
||||
ETMv3.x ETMv4, PTMv1.0, PTMv1.1, STM, STM500, ITM
|
||||
Link:
|
||||
Funnel, replicator (intelligent or not), TMC-ETR
|
||||
Sinks:
|
||||
ETBv1.0, ETB1.1, TPIU, TMC-ETF
|
||||
Misc:
|
||||
CTI
|
||||
|
||||
|
||||
Device Tree Bindings
|
||||
----------------------
|
||||
|
||||
See Documentation/devicetree/bindings/arm/coresight.txt for details.
|
||||
|
||||
As of this writing drivers for ITM, STMs and CTIs are not provided but are
|
||||
expected to be added as the solution matures.
|
||||
|
||||
|
||||
Framework and implementation
|
||||
----------------------------
|
||||
|
||||
The coresight framework provides a central point to represent, configure and
|
||||
manage coresight devices on a platform. Any coresight compliant device can
|
||||
register with the framework for as long as they use the right APIs:
|
||||
|
||||
struct coresight_device *coresight_register(struct coresight_desc *desc);
|
||||
void coresight_unregister(struct coresight_device *csdev);
|
||||
|
||||
The registering function is taking a "struct coresight_device *csdev" and
|
||||
register the device with the core framework. The unregister function takes
|
||||
a reference to a "strut coresight_device", obtained at registration time.
|
||||
|
||||
If everything goes well during the registration process the new devices will
|
||||
show up under /sys/bus/coresight/devices, as showns here for a TC2 platform:
|
||||
|
||||
root:~# ls /sys/bus/coresight/devices/
|
||||
replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm
|
||||
20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm
|
||||
root:~#
|
||||
|
||||
The functions take a "struct coresight_device", which looks like this:
|
||||
|
||||
struct coresight_desc {
|
||||
enum coresight_dev_type type;
|
||||
struct coresight_dev_subtype subtype;
|
||||
const struct coresight_ops *ops;
|
||||
struct coresight_platform_data *pdata;
|
||||
struct device *dev;
|
||||
const struct attribute_group **groups;
|
||||
};
|
||||
|
||||
|
||||
The "coresight_dev_type" identifies what the device is, i.e, source link or
|
||||
sink while the "coresight_dev_subtype" will characterise that type further.
|
||||
|
||||
The "struct coresight_ops" is mandatory and will tell the framework how to
|
||||
perform base operations related to the components, each component having
|
||||
a different set of requirement. For that "struct coresight_ops_sink",
|
||||
"struct coresight_ops_link" and "struct coresight_ops_source" have been
|
||||
provided.
|
||||
|
||||
The next field, "struct coresight_platform_data *pdata" is acquired by calling
|
||||
"of_get_coresight_platform_data()", as part of the driver's _probe routine and
|
||||
"struct device *dev" gets the device reference embedded in the "amba_device":
|
||||
|
||||
static int etm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
...
|
||||
...
|
||||
drvdata->dev = &adev->dev;
|
||||
...
|
||||
}
|
||||
|
||||
Specific class of device (source, link, or sink) have generic operations
|
||||
that can be performed on them (see "struct coresight_ops"). The
|
||||
"**groups" is a list of sysfs entries pertaining to operations
|
||||
specific to that component only. "Implementation defined" customisations are
|
||||
expected to be accessed and controlled using those entries.
|
||||
|
||||
Last but not least, "struct module *owner" is expected to be set to reflect
|
||||
the information carried in "THIS_MODULE".
|
||||
|
||||
How to use
|
||||
----------
|
||||
|
||||
Before trace collection can start, a coresight sink needs to be identify.
|
||||
There is no limit on the amount of sinks (nor sources) that can be enabled at
|
||||
any given moment. As a generic operation, all device pertaining to the sink
|
||||
class will have an "active" entry in sysfs:
|
||||
|
||||
root:/sys/bus/coresight/devices# ls
|
||||
replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm
|
||||
20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm
|
||||
root:/sys/bus/coresight/devices# ls 20010000.etb
|
||||
enable_sink status trigger_cntr
|
||||
root:/sys/bus/coresight/devices# echo 1 > 20010000.etb/enable_sink
|
||||
root:/sys/bus/coresight/devices# cat 20010000.etb/enable_sink
|
||||
1
|
||||
root:/sys/bus/coresight/devices#
|
||||
|
||||
At boot time the current etm3x driver will configure the first address
|
||||
comparator with "_stext" and "_etext", essentially tracing any instruction
|
||||
that falls within that range. As such "enabling" a source will immediately
|
||||
trigger a trace capture:
|
||||
|
||||
root:/sys/bus/coresight/devices# echo 1 > 2201c000.ptm/enable_source
|
||||
root:/sys/bus/coresight/devices# cat 2201c000.ptm/enable_source
|
||||
1
|
||||
root:/sys/bus/coresight/devices# cat 20010000.etb/status
|
||||
Depth: 0x2000
|
||||
Status: 0x1
|
||||
RAM read ptr: 0x0
|
||||
RAM wrt ptr: 0x19d3 <----- The write pointer is moving
|
||||
Trigger cnt: 0x0
|
||||
Control: 0x1
|
||||
Flush status: 0x0
|
||||
Flush ctrl: 0x2001
|
||||
root:/sys/bus/coresight/devices#
|
||||
|
||||
Trace collection is stopped the same way:
|
||||
|
||||
root:/sys/bus/coresight/devices# echo 0 > 2201c000.ptm/enable_source
|
||||
root:/sys/bus/coresight/devices#
|
||||
|
||||
The content of the ETB buffer can be harvested directly from /dev:
|
||||
|
||||
root:/sys/bus/coresight/devices# dd if=/dev/20010000.etb \
|
||||
of=~/cstrace.bin
|
||||
|
||||
64+0 records in
|
||||
64+0 records out
|
||||
32768 bytes (33 kB) copied, 0.00125258 s, 26.2 MB/s
|
||||
root:/sys/bus/coresight/devices#
|
||||
|
||||
The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
|
||||
|
||||
Following is a DS-5 output of an experimental loop that increments a variable up
|
||||
to a certain value. The example is simple and yet provides a glimpse of the
|
||||
wealth of possibilities that coresight provides.
|
||||
|
||||
Info Tracing enabled
|
||||
Instruction 106378866 0x8026B53C E52DE004 false PUSH {lr}
|
||||
Instruction 0 0x8026B540 E24DD00C false SUB sp,sp,#0xc
|
||||
Instruction 0 0x8026B544 E3A03000 false MOV r3,#0
|
||||
Instruction 0 0x8026B548 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Timestamp Timestamp: 17106715833
|
||||
Instruction 319 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Instruction 9 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Instruction 10 0x8026B54C E59D3004 false LDR r3,[sp,#4]
|
||||
Instruction 0 0x8026B550 E3530004 false CMP r3,#4
|
||||
Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1
|
||||
Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4]
|
||||
Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c
|
||||
Instruction 6 0x8026B560 EE1D3F30 false MRC p15,#0x0,r3,c13,c0,#1
|
||||
Instruction 0 0x8026B564 E1A0100D false MOV r1,sp
|
||||
Instruction 0 0x8026B568 E3C12D7F false BIC r2,r1,#0x1fc0
|
||||
Instruction 0 0x8026B56C E3C2203F false BIC r2,r2,#0x3f
|
||||
Instruction 0 0x8026B570 E59D1004 false LDR r1,[sp,#4]
|
||||
Instruction 0 0x8026B574 E59F0010 false LDR r0,[pc,#16] ; [0x8026B58C] = 0x80550368
|
||||
Instruction 0 0x8026B578 E592200C false LDR r2,[r2,#0xc]
|
||||
Instruction 0 0x8026B57C E59221D0 false LDR r2,[r2,#0x1d0]
|
||||
Instruction 0 0x8026B580 EB07A4CF true BL {pc}+0x1e9344 ; 0x804548c4
|
||||
Info Tracing enabled
|
||||
Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc
|
||||
Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc}
|
||||
Timestamp Timestamp: 17107041535
|
||||
@@ -783,6 +783,14 @@ M: Hubert Feurstein <hubert.feurstein@contec.at>
|
||||
S: Maintained
|
||||
F: arch/arm/mach-ep93xx/micro9.c
|
||||
|
||||
ARM/CORESIGHT FRAMEWORK AND DRIVERS
|
||||
M: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/coresight/*
|
||||
F: Documentation/trace/coresight.txt
|
||||
F: Documentation/devicetree/bindings/arm/coresight.txt
|
||||
|
||||
ARM/CORGI MACHINE SUPPORT
|
||||
M: Richard Purdie <rpurdie@rpsys.net>
|
||||
S: Maintained
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
VERSION = 3
|
||||
PATCHLEVEL = 10
|
||||
SUBLEVEL = 62
|
||||
SUBLEVEL = 65
|
||||
EXTRAVERSION =
|
||||
NAME = TOSSUG Baby Fish
|
||||
|
||||
|
||||
@@ -706,14 +706,6 @@ config EARLY_PRINTK_DIRECT
|
||||
kernel low-level debugging functions and EARLY_PRINTK is
|
||||
not early enough.
|
||||
|
||||
config OC_ETM
|
||||
bool "On-chip ETM and ETB"
|
||||
depends on ARM_AMBA
|
||||
help
|
||||
Enables the on-chip embedded trace macrocell and embedded trace
|
||||
buffer driver that will allow you to collect traces of the
|
||||
kernel code.
|
||||
|
||||
config ARM_KPROBES_TEST
|
||||
tristate "Kprobes test module"
|
||||
depends on KPROBES && MODULES
|
||||
@@ -729,4 +721,70 @@ config PID_IN_CONTEXTIDR
|
||||
additional instructions during context switch. Say Y here only if you
|
||||
are planning to use hardware trace tools with this kernel.
|
||||
|
||||
config DEBUG_SET_MODULE_RONX
|
||||
bool "Set loadable kernel module data as NX and text as RO"
|
||||
depends on MODULES
|
||||
---help---
|
||||
This option helps catch unintended modifications to loadable
|
||||
kernel module's text and read-only data. It also prevents execution
|
||||
of module data. Such protection may interfere with run-time code
|
||||
patching and dynamic kernel tracing - and they might also protect
|
||||
against certain classes of kernel exploits.
|
||||
If in doubt, say "N".
|
||||
|
||||
menuconfig CORESIGHT
|
||||
bool "CoreSight Tracing Support"
|
||||
select ARM_AMBA
|
||||
help
|
||||
This framework provides a kernel interface for the CoreSight debug
|
||||
and trace drivers to register themselves with. It's intended to build
|
||||
a topological view of the CoreSight components based on a DT
|
||||
specification and configure the right serie of components when a
|
||||
trace source gets enabled.
|
||||
|
||||
if CORESIGHT
|
||||
config CORESIGHT_LINKS_AND_SINKS
|
||||
bool "CoreSight Link and Sink drivers"
|
||||
help
|
||||
This enables support for CoreSight link and sink drivers that are
|
||||
responsible for transporting and collecting the trace data
|
||||
respectively. Link and sinks are dynamically aggregated with a trace
|
||||
entity at run time to form a complete trace path.
|
||||
|
||||
config CORESIGHT_LINK_AND_SINK_TMC
|
||||
bool "Coresight generic TMC driver"
|
||||
depends on CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This enables support for the Trace Memory Controller driver. Depending
|
||||
on its configuration the device can act as a link (embedded trace router
|
||||
- ETR) or sink (embedded trace FIFO). The driver complies with the
|
||||
generic implementation of the component without special enhancement or
|
||||
added features.
|
||||
|
||||
config CORESIGHT_SINK_TPIU
|
||||
bool "Coresight generic TPIU driver"
|
||||
depends on CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This enables support for the Trace Port Interface Unit driver, responsible
|
||||
for bridging the gap between the on-chip coresight components and a trace
|
||||
port collection engine, typically connected to an external host for use
|
||||
case capturing more traces than the on-board coresight memory can handle.
|
||||
|
||||
config CORESIGHT_SINK_ETBV10
|
||||
bool "Coresight ETBv1.0 driver"
|
||||
depends on CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This enables support for the Embedded Trace Buffer version 1.0 driver
|
||||
that complies with the generic implementation of the component without
|
||||
special enhancement or added features.
|
||||
|
||||
config CORESIGHT_SOURCE_ETM3X
|
||||
bool "CoreSight Embedded Trace Macrocell 3.x driver"
|
||||
select CORESIGHT_LINKS_AND_SINKS
|
||||
help
|
||||
This driver provides support for processor ETM3.x and PTM1.x modules,
|
||||
which allows tracing the instructions that a processor is executing
|
||||
This is primarily useful for instruction level tracing. Depending
|
||||
the ETM version data tracing may also be available.
|
||||
endif
|
||||
endmenu
|
||||
|
||||
984
arch/arm/boot/dts/hip04.dtsi
Normal file
984
arch/arm/boot/dts/hip04.dtsi
Normal file
@@ -0,0 +1,984 @@
|
||||
/*
|
||||
* Hisilicon Ltd. HiP04 SoC
|
||||
*
|
||||
* Copyright (C) 2013-2014 Hisilicon Ltd.
|
||||
* Copyright (C) 2013-2014 Linaro Ltd.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/ {
|
||||
/* memory bus is 64-bit */
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
};
|
||||
|
||||
bootwrapper {
|
||||
compatible = "hisilicon,hip04-bootwrapper";
|
||||
boot-method = <0x10c00000 0x10000>, <0xe0000100 0x1000>;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu-map {
|
||||
cluster0 {
|
||||
core0 {
|
||||
cpu = <&CPU0>;
|
||||
};
|
||||
core1 {
|
||||
cpu = <&CPU1>;
|
||||
};
|
||||
core2 {
|
||||
cpu = <&CPU2>;
|
||||
};
|
||||
core3 {
|
||||
cpu = <&CPU3>;
|
||||
};
|
||||
};
|
||||
cluster1 {
|
||||
core0 {
|
||||
cpu = <&CPU4>;
|
||||
};
|
||||
core1 {
|
||||
cpu = <&CPU5>;
|
||||
};
|
||||
core2 {
|
||||
cpu = <&CPU6>;
|
||||
};
|
||||
core3 {
|
||||
cpu = <&CPU7>;
|
||||
};
|
||||
};
|
||||
cluster2 {
|
||||
core0 {
|
||||
cpu = <&CPU8>;
|
||||
};
|
||||
core1 {
|
||||
cpu = <&CPU9>;
|
||||
};
|
||||
core2 {
|
||||
cpu = <&CPU10>;
|
||||
};
|
||||
core3 {
|
||||
cpu = <&CPU11>;
|
||||
};
|
||||
};
|
||||
cluster3 {
|
||||
core0 {
|
||||
cpu = <&CPU12>;
|
||||
};
|
||||
core1 {
|
||||
cpu = <&CPU13>;
|
||||
};
|
||||
core2 {
|
||||
cpu = <&CPU14>;
|
||||
};
|
||||
core3 {
|
||||
cpu = <&CPU15>;
|
||||
};
|
||||
};
|
||||
};
|
||||
CPU0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
};
|
||||
CPU1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
};
|
||||
CPU2: cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <2>;
|
||||
};
|
||||
CPU3: cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <3>;
|
||||
};
|
||||
CPU4: cpu@100 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x100>;
|
||||
};
|
||||
CPU5: cpu@101 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x101>;
|
||||
};
|
||||
CPU6: cpu@102 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x102>;
|
||||
};
|
||||
CPU7: cpu@103 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x103>;
|
||||
};
|
||||
CPU8: cpu@200 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x200>;
|
||||
};
|
||||
CPU9: cpu@201 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x201>;
|
||||
};
|
||||
CPU10: cpu@202 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x202>;
|
||||
};
|
||||
CPU11: cpu@203 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x203>;
|
||||
};
|
||||
CPU12: cpu@300 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x300>;
|
||||
};
|
||||
CPU13: cpu@301 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x301>;
|
||||
};
|
||||
CPU14: cpu@302 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x302>;
|
||||
};
|
||||
CPU15: cpu@303 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x303>;
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
clk_50m: clk_50m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <50000000>;
|
||||
};
|
||||
|
||||
clk_168m: clk_168m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <168000000>;
|
||||
};
|
||||
|
||||
clk_375m: clk_375m {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <375000000>;
|
||||
};
|
||||
|
||||
soc {
|
||||
/* It's a 32-bit SoC. */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "simple-bus";
|
||||
interrupt-parent = <&gic>;
|
||||
ranges = <0 0 0xe0000000 0x10000000>;
|
||||
|
||||
gic: interrupt-controller@c01000 {
|
||||
compatible = "hisilicon,hip04-intc";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
interrupts = <1 9 0xf04>;
|
||||
|
||||
reg = <0xc01000 0x1000>, <0xc02000 0x1000>,
|
||||
<0xc04000 0x2000>, <0xc06000 0x2000>;
|
||||
};
|
||||
|
||||
sysctrl: sysctrl {
|
||||
compatible = "hisilicon,sysctrl";
|
||||
reg = <0x3e00000 0x00100000>;
|
||||
};
|
||||
|
||||
fabric: fabric {
|
||||
compatible = "hisilicon,hip04-fabric";
|
||||
reg = <0x302a000 0x1000>;
|
||||
};
|
||||
|
||||
dual_timer0: dual_timer@3000000 {
|
||||
compatible = "arm,sp804", "arm,primecell";
|
||||
reg = <0x3000000 0x1000>;
|
||||
interrupts = <0 224 4>;
|
||||
clocks = <&clk_50m>, <&clk_50m>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
arm-pmu {
|
||||
compatible = "arm,cortex-a15-pmu";
|
||||
interrupts = <0 64 4>,
|
||||
<0 65 4>,
|
||||
<0 66 4>,
|
||||
<0 67 4>,
|
||||
<0 68 4>,
|
||||
<0 69 4>,
|
||||
<0 70 4>,
|
||||
<0 71 4>,
|
||||
<0 72 4>,
|
||||
<0 73 4>,
|
||||
<0 74 4>,
|
||||
<0 75 4>,
|
||||
<0 76 4>,
|
||||
<0 77 4>,
|
||||
<0 78 4>,
|
||||
<0 79 4>;
|
||||
};
|
||||
|
||||
uart0: uart@4007000 {
|
||||
compatible = "snps,dw-apb-uart";
|
||||
reg = <0x4007000 0x1000>;
|
||||
interrupts = <0 381 4>;
|
||||
clocks = <&clk_168m>;
|
||||
clock-names = "uartclk";
|
||||
reg-shift = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
sata0: sata@a000000 {
|
||||
compatible = "hisilicon,hisi-ahci";
|
||||
reg = <0xa000000 0x1000000>;
|
||||
interrupts = <0 372 4>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
etb@0,e3c42000 {
|
||||
compatible = "arm,coresight-etb10", "arm,primecell";
|
||||
reg = <0 0xe3c42000 0 0x1000>;
|
||||
|
||||
coresight-default-sink;
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
etb0_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator0_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
etb@0,e3c82000 {
|
||||
compatible = "arm,coresight-etb10", "arm,primecell";
|
||||
reg = <0 0xe3c82000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
etb1_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator1_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
etb@0,e3cc2000 {
|
||||
compatible = "arm,coresight-etb10", "arm,primecell";
|
||||
reg = <0 0xe3cc2000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
etb2_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator2_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
etb@0,e3d02000 {
|
||||
compatible = "arm,coresight-etb10", "arm,primecell";
|
||||
reg = <0 0xe3d02000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
etb3_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&replicator3_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tpiu@0,e3c05000 {
|
||||
compatible = "arm,coresight-tpiu", "arm,primecell";
|
||||
reg = <0 0xe3c05000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
port {
|
||||
tpiu_in_port: endpoint@0 {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel4_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
replicator0 {
|
||||
/* non-configurable replicators don't show up on the
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* replicator output ports */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
replicator0_out_port0: endpoint {
|
||||
remote-endpoint = <&etb0_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
replicator0_out_port1: endpoint {
|
||||
remote-endpoint = <&funnel4_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* replicator input port */
|
||||
port@2 {
|
||||
reg = <0>;
|
||||
replicator0_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel0_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
replicator1 {
|
||||
/* non-configurable replicators don't show up on the
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* replicator output ports */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
replicator1_out_port0: endpoint {
|
||||
remote-endpoint = <&etb1_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
replicator1_out_port1: endpoint {
|
||||
remote-endpoint = <&funnel4_in_port1>;
|
||||
};
|
||||
};
|
||||
|
||||
/* replicator input port */
|
||||
port@2 {
|
||||
reg = <0>;
|
||||
replicator1_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel1_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
replicator2 {
|
||||
/* non-configurable replicators don't show up on the
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* replicator output ports */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
replicator2_out_port0: endpoint {
|
||||
remote-endpoint = <&etb2_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
replicator2_out_port1: endpoint {
|
||||
remote-endpoint = <&funnel4_in_port2>;
|
||||
};
|
||||
};
|
||||
|
||||
/* replicator input port */
|
||||
port@2 {
|
||||
reg = <0>;
|
||||
replicator2_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel2_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
replicator3 {
|
||||
/* non-configurable replicators don't show up on the
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* replicator output ports */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
replicator3_out_port0: endpoint {
|
||||
remote-endpoint = <&etb3_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
replicator3_out_port1: endpoint {
|
||||
remote-endpoint = <&funnel4_in_port3>;
|
||||
};
|
||||
};
|
||||
|
||||
/* replicator input port */
|
||||
port@2 {
|
||||
reg = <0>;
|
||||
replicator3_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&funnel3_out_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@0,e3c41000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0xe3c41000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel0_out_port0: endpoint {
|
||||
remote-endpoint =
|
||||
<&replicator0_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel0_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm0_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel0_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm1_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel0_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm2_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <3>;
|
||||
funnel0_in_port3: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm3_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@0,e3c81000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0xe3c81000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel1_out_port0: endpoint {
|
||||
remote-endpoint =
|
||||
<&replicator1_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel1_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm4_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel1_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm5_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel1_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm6_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <3>;
|
||||
funnel1_in_port3: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm7_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@0,e3cc1000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0xe3cc1000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel2_out_port0: endpoint {
|
||||
remote-endpoint =
|
||||
<&replicator2_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel2_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm8_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel2_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm9_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel2_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm10_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <3>;
|
||||
funnel2_in_port3: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm11_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@0,e3d01000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0xe3d01000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel3_out_port0: endpoint {
|
||||
remote-endpoint =
|
||||
<&replicator3_in_port0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel3_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm12_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel3_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm13_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel3_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm14_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <3>;
|
||||
funnel3_in_port3: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint = <&ptm15_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
funnel@0,e3c04000 {
|
||||
compatible = "arm,coresight-funnel", "arm,primecell";
|
||||
reg = <0 0xe3c04000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* funnel output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
funnel4_out_port0: endpoint {
|
||||
remote-endpoint = <&tpiu_in_port>;
|
||||
};
|
||||
};
|
||||
|
||||
/* funnel input ports */
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
funnel4_in_port0: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint =
|
||||
<&replicator0_out_port1>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <1>;
|
||||
funnel4_in_port1: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint =
|
||||
<&replicator1_out_port1>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <2>;
|
||||
funnel4_in_port2: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint =
|
||||
<&replicator2_out_port1>;
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <3>;
|
||||
funnel4_in_port3: endpoint {
|
||||
slave-mode;
|
||||
remote-endpoint =
|
||||
<&replicator3_out_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3c7c000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3c7c000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU0>;
|
||||
port {
|
||||
ptm0_out_port: endpoint {
|
||||
remote-endpoint = <&funnel0_in_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3c7d000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3c7d000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU1>;
|
||||
port {
|
||||
ptm1_out_port: endpoint {
|
||||
remote-endpoint = <&funnel0_in_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3c7e000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3c7e000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU2>;
|
||||
port {
|
||||
ptm2_out_port: endpoint {
|
||||
remote-endpoint = <&funnel0_in_port2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3c7f000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3c7f000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU3>;
|
||||
port {
|
||||
ptm3_out_port: endpoint {
|
||||
remote-endpoint = <&funnel0_in_port3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cbc000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cbc000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU4>;
|
||||
port {
|
||||
ptm4_out_port: endpoint {
|
||||
remote-endpoint = <&funnel1_in_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cbd000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cbd000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU5>;
|
||||
port {
|
||||
ptm5_out_port: endpoint {
|
||||
remote-endpoint = <&funnel1_in_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cbe000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cbe000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU6>;
|
||||
port {
|
||||
ptm6_out_port: endpoint {
|
||||
remote-endpoint = <&funnel1_in_port2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cbf000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cbf000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU7>;
|
||||
port {
|
||||
ptm7_out_port: endpoint {
|
||||
remote-endpoint = <&funnel1_in_port3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cfc000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cfc000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU8>;
|
||||
port {
|
||||
ptm8_out_port: endpoint {
|
||||
remote-endpoint = <&funnel2_in_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cfd000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cfd000 0 0x1000>;
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU9>;
|
||||
port {
|
||||
ptm9_out_port: endpoint {
|
||||
remote-endpoint = <&funnel2_in_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cfe000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cfe000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU10>;
|
||||
port {
|
||||
ptm10_out_port: endpoint {
|
||||
remote-endpoint = <&funnel2_in_port2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3cff000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3cff000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU11>;
|
||||
port {
|
||||
ptm11_out_port: endpoint {
|
||||
remote-endpoint = <&funnel2_in_port3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3d3c000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3d3c000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU12>;
|
||||
port {
|
||||
ptm12_out_port: endpoint {
|
||||
remote-endpoint = <&funnel3_in_port0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3d3d000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3d3d000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU13>;
|
||||
port {
|
||||
ptm13_out_port: endpoint {
|
||||
remote-endpoint = <&funnel3_in_port1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3d3e000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3d3e000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU14>;
|
||||
port {
|
||||
ptm14_out_port: endpoint {
|
||||
remote-endpoint = <&funnel3_in_port2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ptm@0,e3d3f000 {
|
||||
compatible = "arm,coresight-etm3x", "arm,primecell";
|
||||
reg = <0 0xe3d3f000 0 0x1000>;
|
||||
|
||||
clocks = <&clk_375m>;
|
||||
clock-names = "apb_pclk";
|
||||
cpu = <&CPU15>;
|
||||
port {
|
||||
ptm15_out_port: endpoint {
|
||||
remote-endpoint = <&funnel3_in_port3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* linux/arch/arm/include/asm/hardware/coresight.h
|
||||
*
|
||||
* CoreSight components' registers
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation.
|
||||
* Alexander Shishkin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_HARDWARE_CORESIGHT_H
|
||||
#define __ASM_HARDWARE_CORESIGHT_H
|
||||
|
||||
#define TRACER_ACCESSED_BIT 0
|
||||
#define TRACER_RUNNING_BIT 1
|
||||
#define TRACER_CYCLE_ACC_BIT 2
|
||||
#define TRACER_TRACE_DATA_BIT 3
|
||||
#define TRACER_TIMESTAMP_BIT 4
|
||||
#define TRACER_BRANCHOUTPUT_BIT 5
|
||||
#define TRACER_RETURN_STACK_BIT 6
|
||||
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
|
||||
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
|
||||
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
|
||||
#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
|
||||
#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT)
|
||||
#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT)
|
||||
#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT)
|
||||
|
||||
#define TRACER_TIMEOUT 10000
|
||||
|
||||
#define etm_writel(t, v, x) \
|
||||
(writel_relaxed((v), (t)->etm_regs + (x)))
|
||||
#define etm_readl(t, x) (readl_relaxed((t)->etm_regs + (x)))
|
||||
|
||||
/* CoreSight Management Registers */
|
||||
#define CSMR_LOCKACCESS 0xfb0
|
||||
#define CSMR_LOCKSTATUS 0xfb4
|
||||
#define CSMR_AUTHSTATUS 0xfb8
|
||||
#define CSMR_DEVID 0xfc8
|
||||
#define CSMR_DEVTYPE 0xfcc
|
||||
/* CoreSight Component Registers */
|
||||
#define CSCR_CLASS 0xff4
|
||||
|
||||
#define CS_LAR_KEY 0xc5acce55
|
||||
|
||||
/* ETM control register, "ETM Architecture", 3.3.1 */
|
||||
#define ETMR_CTRL 0
|
||||
#define ETMCTRL_POWERDOWN 1
|
||||
#define ETMCTRL_PROGRAM (1 << 10)
|
||||
#define ETMCTRL_PORTSEL (1 << 11)
|
||||
#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14)
|
||||
#define ETMCTRL_PORTMASK1 (7 << 4)
|
||||
#define ETMCTRL_PORTMASK2 (1 << 21)
|
||||
#define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2)
|
||||
#define ETMCTRL_PORTSIZE(x) ((((x) & 7) << 4) | (!!((x) & 8)) << 21)
|
||||
#define ETMCTRL_DO_CPRT (1 << 1)
|
||||
#define ETMCTRL_DATAMASK (3 << 2)
|
||||
#define ETMCTRL_DATA_DO_DATA (1 << 2)
|
||||
#define ETMCTRL_DATA_DO_ADDR (1 << 3)
|
||||
#define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR)
|
||||
#define ETMCTRL_BRANCH_OUTPUT (1 << 8)
|
||||
#define ETMCTRL_CYCLEACCURATE (1 << 12)
|
||||
#define ETMCTRL_TIMESTAMP_EN (1 << 28)
|
||||
#define ETMCTRL_RETURN_STACK_EN (1 << 29)
|
||||
|
||||
/* ETM configuration code register */
|
||||
#define ETMR_CONFCODE (0x04)
|
||||
#define ETMCCR_ETMIDR_PRESENT BIT(31)
|
||||
|
||||
/* ETM trace start/stop resource control register */
|
||||
#define ETMR_TRACESSCTRL (0x18)
|
||||
|
||||
/* ETM trigger event register */
|
||||
#define ETMR_TRIGEVT (0x08)
|
||||
|
||||
/* address access type register bits, "ETM architecture",
|
||||
* table 3-27 */
|
||||
/* - access type */
|
||||
#define ETMAAT_IFETCH 0
|
||||
#define ETMAAT_IEXEC 1
|
||||
#define ETMAAT_IEXECPASS 2
|
||||
#define ETMAAT_IEXECFAIL 3
|
||||
#define ETMAAT_DLOADSTORE 4
|
||||
#define ETMAAT_DLOAD 5
|
||||
#define ETMAAT_DSTORE 6
|
||||
/* - comparison access size */
|
||||
#define ETMAAT_JAVA (0 << 3)
|
||||
#define ETMAAT_THUMB (1 << 3)
|
||||
#define ETMAAT_ARM (3 << 3)
|
||||
/* - data value comparison control */
|
||||
#define ETMAAT_NOVALCMP (0 << 5)
|
||||
#define ETMAAT_VALMATCH (1 << 5)
|
||||
#define ETMAAT_VALNOMATCH (3 << 5)
|
||||
/* - exact match */
|
||||
#define ETMAAT_EXACTMATCH (1 << 7)
|
||||
/* - context id comparator control */
|
||||
#define ETMAAT_IGNCONTEXTID (0 << 8)
|
||||
#define ETMAAT_VALUE1 (1 << 8)
|
||||
#define ETMAAT_VALUE2 (2 << 8)
|
||||
#define ETMAAT_VALUE3 (3 << 8)
|
||||
/* - security level control */
|
||||
#define ETMAAT_IGNSECURITY (0 << 10)
|
||||
#define ETMAAT_NSONLY (1 << 10)
|
||||
#define ETMAAT_SONLY (2 << 10)
|
||||
|
||||
#define ETMR_COMP_VAL(x) (0x40 + (x) * 4)
|
||||
#define ETMR_COMP_ACC_TYPE(x) (0x80 + (x) * 4)
|
||||
|
||||
/* ETM status register, "ETM Architecture", 3.3.2 */
|
||||
#define ETMR_STATUS (0x10)
|
||||
#define ETMST_OVERFLOW BIT(0)
|
||||
#define ETMST_PROGBIT BIT(1)
|
||||
#define ETMST_STARTSTOP BIT(2)
|
||||
#define ETMST_TRIGGER BIT(3)
|
||||
|
||||
#define etm_progbit(t) (etm_readl((t), ETMR_STATUS) & ETMST_PROGBIT)
|
||||
#define etm_started(t) (etm_readl((t), ETMR_STATUS) & ETMST_STARTSTOP)
|
||||
#define etm_triggered(t) (etm_readl((t), ETMR_STATUS) & ETMST_TRIGGER)
|
||||
|
||||
#define ETMR_TRACEENCTRL2 0x1c
|
||||
#define ETMR_TRACEENCTRL 0x24
|
||||
#define ETMTE_INCLEXCL BIT(24)
|
||||
#define ETMR_TRACEENEVT 0x20
|
||||
|
||||
#define ETMR_VIEWDATAEVT 0x30
|
||||
#define ETMR_VIEWDATACTRL1 0x34
|
||||
#define ETMR_VIEWDATACTRL2 0x38
|
||||
#define ETMR_VIEWDATACTRL3 0x3c
|
||||
#define ETMVDC3_EXCLONLY BIT(16)
|
||||
|
||||
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT)
|
||||
|
||||
#define ETMR_ID 0x1e4
|
||||
#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff)
|
||||
#define ETMIDR_VERSION_3_1 0x21
|
||||
#define ETMIDR_VERSION_PFT_1_0 0x30
|
||||
|
||||
#define ETMR_CCE 0x1e8
|
||||
#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23)
|
||||
#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22)
|
||||
|
||||
#define ETMR_TRACEIDR 0x200
|
||||
|
||||
/* ETM management registers, "ETM Architecture", 3.5.24 */
|
||||
#define ETMMR_OSLAR 0x300
|
||||
#define ETMMR_OSLSR 0x304
|
||||
#define ETMMR_OSSRR 0x308
|
||||
#define ETMMR_PDSR 0x314
|
||||
|
||||
/* ETB registers, "CoreSight Components TRM", 9.3 */
|
||||
#define ETBR_DEPTH 0x04
|
||||
#define ETBR_STATUS 0x0c
|
||||
#define ETBR_READMEM 0x10
|
||||
#define ETBR_READADDR 0x14
|
||||
#define ETBR_WRITEADDR 0x18
|
||||
#define ETBR_TRIGGERCOUNT 0x1c
|
||||
#define ETBR_CTRL 0x20
|
||||
#define ETBR_FORMATTERCTRL 0x304
|
||||
#define ETBFF_ENFTC 1
|
||||
#define ETBFF_ENFCONT BIT(1)
|
||||
#define ETBFF_FONFLIN BIT(4)
|
||||
#define ETBFF_MANUAL_FLUSH BIT(6)
|
||||
#define ETBFF_TRIGIN BIT(8)
|
||||
#define ETBFF_TRIGEVT BIT(9)
|
||||
#define ETBFF_TRIGFL BIT(10)
|
||||
#define ETBFF_STOPFL BIT(12)
|
||||
|
||||
#define etb_writel(t, v, x) \
|
||||
(writel_relaxed((v), (t)->etb_regs + (x)))
|
||||
#define etb_readl(t, x) (readl_relaxed((t)->etb_regs + (x)))
|
||||
|
||||
#define etm_lock(t, id) \
|
||||
do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
|
||||
#define etm_unlock(t, id) \
|
||||
do { etm_writel((t), (id), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
|
||||
|
||||
#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
|
||||
#define etb_unlock(t) \
|
||||
do { etb_writel((t), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
|
||||
|
||||
#endif /* __ASM_HARDWARE_CORESIGHT_H */
|
||||
|
||||
542
arch/arm/include/asm/hardware/cp14.h
Normal file
542
arch/arm/include/asm/hardware/cp14.h
Normal file
@@ -0,0 +1,542 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_HARDWARE_CP14_H
|
||||
#define __ASM_HARDWARE_CP14_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Accessors for CP14 registers */
|
||||
#define dbg_read(reg) RCP14_##reg()
|
||||
#define dbg_write(val, reg) WCP14_##reg(val)
|
||||
#define etm_read(reg) RCP14_##reg()
|
||||
#define etm_write(val, reg) WCP14_##reg(val)
|
||||
|
||||
/* MRC14 and MCR14 */
|
||||
#define MRC14(op1, crn, crm, op2) \
|
||||
({ \
|
||||
u32 val; \
|
||||
asm volatile("mrc p14, "#op1", %0, "#crn", "#crm", "#op2 : "=r" (val)); \
|
||||
val; \
|
||||
})
|
||||
|
||||
#define MCR14(val, op1, crn, crm, op2) \
|
||||
({ \
|
||||
asm volatile("mcr p14, "#op1", %0, "#crn", "#crm", "#op2 : : "r" (val));\
|
||||
})
|
||||
|
||||
/*
|
||||
* Debug Registers
|
||||
*
|
||||
* Available only in DBGv7
|
||||
* DBGECR, DBGDSCCR, DBGDSMCR, DBGDRCR
|
||||
*
|
||||
* Available only in DBGv7.1
|
||||
* DBGBXVRm, DBGOSDLR, DBGDEVID2, DBGDEVID1
|
||||
*
|
||||
* Read only
|
||||
* DBGDIDR, DBGDSCRint, DBGDTRRXint, DBGDRAR, DBGOSLSR, DBGOSSRR, DBGPRSR,
|
||||
* DBGPRSR, DBGDSAR, DBGAUTHSTATUS, DBGDEVID2, DBGDEVID1, DBGDEVID
|
||||
*
|
||||
* Write only
|
||||
* DBGDTRTXint, DBGOSLAR
|
||||
*/
|
||||
#define RCP14_DBGDIDR() MRC14(0, c0, c0, 0)
|
||||
#define RCP14_DBGDSCRint() MRC14(0, c0, c1, 0)
|
||||
#define RCP14_DBGDTRRXint() MRC14(0, c0, c5, 0)
|
||||
#define RCP14_DBGWFAR() MRC14(0, c0, c6, 0)
|
||||
#define RCP14_DBGVCR() MRC14(0, c0, c7, 0)
|
||||
#define RCP14_DBGECR() MRC14(0, c0, c9, 0)
|
||||
#define RCP14_DBGDSCCR() MRC14(0, c0, c10, 0)
|
||||
#define RCP14_DBGDSMCR() MRC14(0, c0, c11, 0)
|
||||
#define RCP14_DBGDTRRXext() MRC14(0, c0, c0, 2)
|
||||
#define RCP14_DBGDSCRext() MRC14(0, c0, c2, 2)
|
||||
#define RCP14_DBGDTRTXext() MRC14(0, c0, c3, 2)
|
||||
#define RCP14_DBGDRCR() MRC14(0, c0, c4, 2)
|
||||
#define RCP14_DBGBVR0() MRC14(0, c0, c0, 4)
|
||||
#define RCP14_DBGBVR1() MRC14(0, c0, c1, 4)
|
||||
#define RCP14_DBGBVR2() MRC14(0, c0, c2, 4)
|
||||
#define RCP14_DBGBVR3() MRC14(0, c0, c3, 4)
|
||||
#define RCP14_DBGBVR4() MRC14(0, c0, c4, 4)
|
||||
#define RCP14_DBGBVR5() MRC14(0, c0, c5, 4)
|
||||
#define RCP14_DBGBVR6() MRC14(0, c0, c6, 4)
|
||||
#define RCP14_DBGBVR7() MRC14(0, c0, c7, 4)
|
||||
#define RCP14_DBGBVR8() MRC14(0, c0, c8, 4)
|
||||
#define RCP14_DBGBVR9() MRC14(0, c0, c9, 4)
|
||||
#define RCP14_DBGBVR10() MRC14(0, c0, c10, 4)
|
||||
#define RCP14_DBGBVR11() MRC14(0, c0, c11, 4)
|
||||
#define RCP14_DBGBVR12() MRC14(0, c0, c12, 4)
|
||||
#define RCP14_DBGBVR13() MRC14(0, c0, c13, 4)
|
||||
#define RCP14_DBGBVR14() MRC14(0, c0, c14, 4)
|
||||
#define RCP14_DBGBVR15() MRC14(0, c0, c15, 4)
|
||||
#define RCP14_DBGBCR0() MRC14(0, c0, c0, 5)
|
||||
#define RCP14_DBGBCR1() MRC14(0, c0, c1, 5)
|
||||
#define RCP14_DBGBCR2() MRC14(0, c0, c2, 5)
|
||||
#define RCP14_DBGBCR3() MRC14(0, c0, c3, 5)
|
||||
#define RCP14_DBGBCR4() MRC14(0, c0, c4, 5)
|
||||
#define RCP14_DBGBCR5() MRC14(0, c0, c5, 5)
|
||||
#define RCP14_DBGBCR6() MRC14(0, c0, c6, 5)
|
||||
#define RCP14_DBGBCR7() MRC14(0, c0, c7, 5)
|
||||
#define RCP14_DBGBCR8() MRC14(0, c0, c8, 5)
|
||||
#define RCP14_DBGBCR9() MRC14(0, c0, c9, 5)
|
||||
#define RCP14_DBGBCR10() MRC14(0, c0, c10, 5)
|
||||
#define RCP14_DBGBCR11() MRC14(0, c0, c11, 5)
|
||||
#define RCP14_DBGBCR12() MRC14(0, c0, c12, 5)
|
||||
#define RCP14_DBGBCR13() MRC14(0, c0, c13, 5)
|
||||
#define RCP14_DBGBCR14() MRC14(0, c0, c14, 5)
|
||||
#define RCP14_DBGBCR15() MRC14(0, c0, c15, 5)
|
||||
#define RCP14_DBGWVR0() MRC14(0, c0, c0, 6)
|
||||
#define RCP14_DBGWVR1() MRC14(0, c0, c1, 6)
|
||||
#define RCP14_DBGWVR2() MRC14(0, c0, c2, 6)
|
||||
#define RCP14_DBGWVR3() MRC14(0, c0, c3, 6)
|
||||
#define RCP14_DBGWVR4() MRC14(0, c0, c4, 6)
|
||||
#define RCP14_DBGWVR5() MRC14(0, c0, c5, 6)
|
||||
#define RCP14_DBGWVR6() MRC14(0, c0, c6, 6)
|
||||
#define RCP14_DBGWVR7() MRC14(0, c0, c7, 6)
|
||||
#define RCP14_DBGWVR8() MRC14(0, c0, c8, 6)
|
||||
#define RCP14_DBGWVR9() MRC14(0, c0, c9, 6)
|
||||
#define RCP14_DBGWVR10() MRC14(0, c0, c10, 6)
|
||||
#define RCP14_DBGWVR11() MRC14(0, c0, c11, 6)
|
||||
#define RCP14_DBGWVR12() MRC14(0, c0, c12, 6)
|
||||
#define RCP14_DBGWVR13() MRC14(0, c0, c13, 6)
|
||||
#define RCP14_DBGWVR14() MRC14(0, c0, c14, 6)
|
||||
#define RCP14_DBGWVR15() MRC14(0, c0, c15, 6)
|
||||
#define RCP14_DBGWCR0() MRC14(0, c0, c0, 7)
|
||||
#define RCP14_DBGWCR1() MRC14(0, c0, c1, 7)
|
||||
#define RCP14_DBGWCR2() MRC14(0, c0, c2, 7)
|
||||
#define RCP14_DBGWCR3() MRC14(0, c0, c3, 7)
|
||||
#define RCP14_DBGWCR4() MRC14(0, c0, c4, 7)
|
||||
#define RCP14_DBGWCR5() MRC14(0, c0, c5, 7)
|
||||
#define RCP14_DBGWCR6() MRC14(0, c0, c6, 7)
|
||||
#define RCP14_DBGWCR7() MRC14(0, c0, c7, 7)
|
||||
#define RCP14_DBGWCR8() MRC14(0, c0, c8, 7)
|
||||
#define RCP14_DBGWCR9() MRC14(0, c0, c9, 7)
|
||||
#define RCP14_DBGWCR10() MRC14(0, c0, c10, 7)
|
||||
#define RCP14_DBGWCR11() MRC14(0, c0, c11, 7)
|
||||
#define RCP14_DBGWCR12() MRC14(0, c0, c12, 7)
|
||||
#define RCP14_DBGWCR13() MRC14(0, c0, c13, 7)
|
||||
#define RCP14_DBGWCR14() MRC14(0, c0, c14, 7)
|
||||
#define RCP14_DBGWCR15() MRC14(0, c0, c15, 7)
|
||||
#define RCP14_DBGDRAR() MRC14(0, c1, c0, 0)
|
||||
#define RCP14_DBGBXVR0() MRC14(0, c1, c0, 1)
|
||||
#define RCP14_DBGBXVR1() MRC14(0, c1, c1, 1)
|
||||
#define RCP14_DBGBXVR2() MRC14(0, c1, c2, 1)
|
||||
#define RCP14_DBGBXVR3() MRC14(0, c1, c3, 1)
|
||||
#define RCP14_DBGBXVR4() MRC14(0, c1, c4, 1)
|
||||
#define RCP14_DBGBXVR5() MRC14(0, c1, c5, 1)
|
||||
#define RCP14_DBGBXVR6() MRC14(0, c1, c6, 1)
|
||||
#define RCP14_DBGBXVR7() MRC14(0, c1, c7, 1)
|
||||
#define RCP14_DBGBXVR8() MRC14(0, c1, c8, 1)
|
||||
#define RCP14_DBGBXVR9() MRC14(0, c1, c9, 1)
|
||||
#define RCP14_DBGBXVR10() MRC14(0, c1, c10, 1)
|
||||
#define RCP14_DBGBXVR11() MRC14(0, c1, c11, 1)
|
||||
#define RCP14_DBGBXVR12() MRC14(0, c1, c12, 1)
|
||||
#define RCP14_DBGBXVR13() MRC14(0, c1, c13, 1)
|
||||
#define RCP14_DBGBXVR14() MRC14(0, c1, c14, 1)
|
||||
#define RCP14_DBGBXVR15() MRC14(0, c1, c15, 1)
|
||||
#define RCP14_DBGOSLSR() MRC14(0, c1, c1, 4)
|
||||
#define RCP14_DBGOSSRR() MRC14(0, c1, c2, 4)
|
||||
#define RCP14_DBGOSDLR() MRC14(0, c1, c3, 4)
|
||||
#define RCP14_DBGPRCR() MRC14(0, c1, c4, 4)
|
||||
#define RCP14_DBGPRSR() MRC14(0, c1, c5, 4)
|
||||
#define RCP14_DBGDSAR() MRC14(0, c2, c0, 0)
|
||||
#define RCP14_DBGITCTRL() MRC14(0, c7, c0, 4)
|
||||
#define RCP14_DBGCLAIMSET() MRC14(0, c7, c8, 6)
|
||||
#define RCP14_DBGCLAIMCLR() MRC14(0, c7, c9, 6)
|
||||
#define RCP14_DBGAUTHSTATUS() MRC14(0, c7, c14, 6)
|
||||
#define RCP14_DBGDEVID2() MRC14(0, c7, c0, 7)
|
||||
#define RCP14_DBGDEVID1() MRC14(0, c7, c1, 7)
|
||||
#define RCP14_DBGDEVID() MRC14(0, c7, c2, 7)
|
||||
|
||||
#define WCP14_DBGDTRTXint(val) MCR14(val, 0, c0, c5, 0)
|
||||
#define WCP14_DBGWFAR(val) MCR14(val, 0, c0, c6, 0)
|
||||
#define WCP14_DBGVCR(val) MCR14(val, 0, c0, c7, 0)
|
||||
#define WCP14_DBGECR(val) MCR14(val, 0, c0, c9, 0)
|
||||
#define WCP14_DBGDSCCR(val) MCR14(val, 0, c0, c10, 0)
|
||||
#define WCP14_DBGDSMCR(val) MCR14(val, 0, c0, c11, 0)
|
||||
#define WCP14_DBGDTRRXext(val) MCR14(val, 0, c0, c0, 2)
|
||||
#define WCP14_DBGDSCRext(val) MCR14(val, 0, c0, c2, 2)
|
||||
#define WCP14_DBGDTRTXext(val) MCR14(val, 0, c0, c3, 2)
|
||||
#define WCP14_DBGDRCR(val) MCR14(val, 0, c0, c4, 2)
|
||||
#define WCP14_DBGBVR0(val) MCR14(val, 0, c0, c0, 4)
|
||||
#define WCP14_DBGBVR1(val) MCR14(val, 0, c0, c1, 4)
|
||||
#define WCP14_DBGBVR2(val) MCR14(val, 0, c0, c2, 4)
|
||||
#define WCP14_DBGBVR3(val) MCR14(val, 0, c0, c3, 4)
|
||||
#define WCP14_DBGBVR4(val) MCR14(val, 0, c0, c4, 4)
|
||||
#define WCP14_DBGBVR5(val) MCR14(val, 0, c0, c5, 4)
|
||||
#define WCP14_DBGBVR6(val) MCR14(val, 0, c0, c6, 4)
|
||||
#define WCP14_DBGBVR7(val) MCR14(val, 0, c0, c7, 4)
|
||||
#define WCP14_DBGBVR8(val) MCR14(val, 0, c0, c8, 4)
|
||||
#define WCP14_DBGBVR9(val) MCR14(val, 0, c0, c9, 4)
|
||||
#define WCP14_DBGBVR10(val) MCR14(val, 0, c0, c10, 4)
|
||||
#define WCP14_DBGBVR11(val) MCR14(val, 0, c0, c11, 4)
|
||||
#define WCP14_DBGBVR12(val) MCR14(val, 0, c0, c12, 4)
|
||||
#define WCP14_DBGBVR13(val) MCR14(val, 0, c0, c13, 4)
|
||||
#define WCP14_DBGBVR14(val) MCR14(val, 0, c0, c14, 4)
|
||||
#define WCP14_DBGBVR15(val) MCR14(val, 0, c0, c15, 4)
|
||||
#define WCP14_DBGBCR0(val) MCR14(val, 0, c0, c0, 5)
|
||||
#define WCP14_DBGBCR1(val) MCR14(val, 0, c0, c1, 5)
|
||||
#define WCP14_DBGBCR2(val) MCR14(val, 0, c0, c2, 5)
|
||||
#define WCP14_DBGBCR3(val) MCR14(val, 0, c0, c3, 5)
|
||||
#define WCP14_DBGBCR4(val) MCR14(val, 0, c0, c4, 5)
|
||||
#define WCP14_DBGBCR5(val) MCR14(val, 0, c0, c5, 5)
|
||||
#define WCP14_DBGBCR6(val) MCR14(val, 0, c0, c6, 5)
|
||||
#define WCP14_DBGBCR7(val) MCR14(val, 0, c0, c7, 5)
|
||||
#define WCP14_DBGBCR8(val) MCR14(val, 0, c0, c8, 5)
|
||||
#define WCP14_DBGBCR9(val) MCR14(val, 0, c0, c9, 5)
|
||||
#define WCP14_DBGBCR10(val) MCR14(val, 0, c0, c10, 5)
|
||||
#define WCP14_DBGBCR11(val) MCR14(val, 0, c0, c11, 5)
|
||||
#define WCP14_DBGBCR12(val) MCR14(val, 0, c0, c12, 5)
|
||||
#define WCP14_DBGBCR13(val) MCR14(val, 0, c0, c13, 5)
|
||||
#define WCP14_DBGBCR14(val) MCR14(val, 0, c0, c14, 5)
|
||||
#define WCP14_DBGBCR15(val) MCR14(val, 0, c0, c15, 5)
|
||||
#define WCP14_DBGWVR0(val) MCR14(val, 0, c0, c0, 6)
|
||||
#define WCP14_DBGWVR1(val) MCR14(val, 0, c0, c1, 6)
|
||||
#define WCP14_DBGWVR2(val) MCR14(val, 0, c0, c2, 6)
|
||||
#define WCP14_DBGWVR3(val) MCR14(val, 0, c0, c3, 6)
|
||||
#define WCP14_DBGWVR4(val) MCR14(val, 0, c0, c4, 6)
|
||||
#define WCP14_DBGWVR5(val) MCR14(val, 0, c0, c5, 6)
|
||||
#define WCP14_DBGWVR6(val) MCR14(val, 0, c0, c6, 6)
|
||||
#define WCP14_DBGWVR7(val) MCR14(val, 0, c0, c7, 6)
|
||||
#define WCP14_DBGWVR8(val) MCR14(val, 0, c0, c8, 6)
|
||||
#define WCP14_DBGWVR9(val) MCR14(val, 0, c0, c9, 6)
|
||||
#define WCP14_DBGWVR10(val) MCR14(val, 0, c0, c10, 6)
|
||||
#define WCP14_DBGWVR11(val) MCR14(val, 0, c0, c11, 6)
|
||||
#define WCP14_DBGWVR12(val) MCR14(val, 0, c0, c12, 6)
|
||||
#define WCP14_DBGWVR13(val) MCR14(val, 0, c0, c13, 6)
|
||||
#define WCP14_DBGWVR14(val) MCR14(val, 0, c0, c14, 6)
|
||||
#define WCP14_DBGWVR15(val) MCR14(val, 0, c0, c15, 6)
|
||||
#define WCP14_DBGWCR0(val) MCR14(val, 0, c0, c0, 7)
|
||||
#define WCP14_DBGWCR1(val) MCR14(val, 0, c0, c1, 7)
|
||||
#define WCP14_DBGWCR2(val) MCR14(val, 0, c0, c2, 7)
|
||||
#define WCP14_DBGWCR3(val) MCR14(val, 0, c0, c3, 7)
|
||||
#define WCP14_DBGWCR4(val) MCR14(val, 0, c0, c4, 7)
|
||||
#define WCP14_DBGWCR5(val) MCR14(val, 0, c0, c5, 7)
|
||||
#define WCP14_DBGWCR6(val) MCR14(val, 0, c0, c6, 7)
|
||||
#define WCP14_DBGWCR7(val) MCR14(val, 0, c0, c7, 7)
|
||||
#define WCP14_DBGWCR8(val) MCR14(val, 0, c0, c8, 7)
|
||||
#define WCP14_DBGWCR9(val) MCR14(val, 0, c0, c9, 7)
|
||||
#define WCP14_DBGWCR10(val) MCR14(val, 0, c0, c10, 7)
|
||||
#define WCP14_DBGWCR11(val) MCR14(val, 0, c0, c11, 7)
|
||||
#define WCP14_DBGWCR12(val) MCR14(val, 0, c0, c12, 7)
|
||||
#define WCP14_DBGWCR13(val) MCR14(val, 0, c0, c13, 7)
|
||||
#define WCP14_DBGWCR14(val) MCR14(val, 0, c0, c14, 7)
|
||||
#define WCP14_DBGWCR15(val) MCR14(val, 0, c0, c15, 7)
|
||||
#define WCP14_DBGBXVR0(val) MCR14(val, 0, c1, c0, 1)
|
||||
#define WCP14_DBGBXVR1(val) MCR14(val, 0, c1, c1, 1)
|
||||
#define WCP14_DBGBXVR2(val) MCR14(val, 0, c1, c2, 1)
|
||||
#define WCP14_DBGBXVR3(val) MCR14(val, 0, c1, c3, 1)
|
||||
#define WCP14_DBGBXVR4(val) MCR14(val, 0, c1, c4, 1)
|
||||
#define WCP14_DBGBXVR5(val) MCR14(val, 0, c1, c5, 1)
|
||||
#define WCP14_DBGBXVR6(val) MCR14(val, 0, c1, c6, 1)
|
||||
#define WCP14_DBGBXVR7(val) MCR14(val, 0, c1, c7, 1)
|
||||
#define WCP14_DBGBXVR8(val) MCR14(val, 0, c1, c8, 1)
|
||||
#define WCP14_DBGBXVR9(val) MCR14(val, 0, c1, c9, 1)
|
||||
#define WCP14_DBGBXVR10(val) MCR14(val, 0, c1, c10, 1)
|
||||
#define WCP14_DBGBXVR11(val) MCR14(val, 0, c1, c11, 1)
|
||||
#define WCP14_DBGBXVR12(val) MCR14(val, 0, c1, c12, 1)
|
||||
#define WCP14_DBGBXVR13(val) MCR14(val, 0, c1, c13, 1)
|
||||
#define WCP14_DBGBXVR14(val) MCR14(val, 0, c1, c14, 1)
|
||||
#define WCP14_DBGBXVR15(val) MCR14(val, 0, c1, c15, 1)
|
||||
#define WCP14_DBGOSLAR(val) MCR14(val, 0, c1, c0, 4)
|
||||
#define WCP14_DBGOSSRR(val) MCR14(val, 0, c1, c2, 4)
|
||||
#define WCP14_DBGOSDLR(val) MCR14(val, 0, c1, c3, 4)
|
||||
#define WCP14_DBGPRCR(val) MCR14(val, 0, c1, c4, 4)
|
||||
#define WCP14_DBGITCTRL(val) MCR14(val, 0, c7, c0, 4)
|
||||
#define WCP14_DBGCLAIMSET(val) MCR14(val, 0, c7, c8, 6)
|
||||
#define WCP14_DBGCLAIMCLR(val) MCR14(val, 0, c7, c9, 6)
|
||||
|
||||
/*
|
||||
* ETM Registers
|
||||
*
|
||||
* Available only in ETMv3.3, 3.4, 3.5
|
||||
* ETMASICCR, ETMTECR2, ETMFFRR, ETMVDEVR, ETMVDCR1, ETMVDCR2, ETMVDCR3,
|
||||
* ETMDCVRn, ETMDCMRn
|
||||
*
|
||||
* Available only in ETMv3.5 as read only
|
||||
* ETMIDR2
|
||||
*
|
||||
* Available only in ETMv3.5, PFTv1.0, 1.1
|
||||
* ETMTSEVR, ETMVMIDCVR, ETMPDCR
|
||||
*
|
||||
* Read only
|
||||
* ETMCCR, ETMSCR, ETMIDR, ETMCCER, ETMOSLSR
|
||||
* ETMLSR, ETMAUTHSTATUS, ETMDEVID, ETMDEVTYPE, ETMPIDR4, ETMPIDR5, ETMPIDR6,
|
||||
* ETMPIDR7, ETMPIDR0, ETMPIDR1, ETMPIDR2, ETMPIDR2, ETMPIDR3, ETMCIDR0,
|
||||
* ETMCIDR1, ETMCIDR2, ETMCIDR3
|
||||
*
|
||||
* Write only
|
||||
* ETMOSLAR, ETMLAR
|
||||
* Note: ETMCCER[11] controls WO nature of certain regs. Refer ETM arch spec.
|
||||
*/
|
||||
#define RCP14_ETMCR() MRC14(1, c0, c0, 0)
|
||||
#define RCP14_ETMCCR() MRC14(1, c0, c1, 0)
|
||||
#define RCP14_ETMTRIGGER() MRC14(1, c0, c2, 0)
|
||||
#define RCP14_ETMASICCR() MRC14(1, c0, c3, 0)
|
||||
#define RCP14_ETMSR() MRC14(1, c0, c4, 0)
|
||||
#define RCP14_ETMSCR() MRC14(1, c0, c5, 0)
|
||||
#define RCP14_ETMTSSCR() MRC14(1, c0, c6, 0)
|
||||
#define RCP14_ETMTECR2() MRC14(1, c0, c7, 0)
|
||||
#define RCP14_ETMTEEVR() MRC14(1, c0, c8, 0)
|
||||
#define RCP14_ETMTECR1() MRC14(1, c0, c9, 0)
|
||||
#define RCP14_ETMFFRR() MRC14(1, c0, c10, 0)
|
||||
#define RCP14_ETMFFLR() MRC14(1, c0, c11, 0)
|
||||
#define RCP14_ETMVDEVR() MRC14(1, c0, c12, 0)
|
||||
#define RCP14_ETMVDCR1() MRC14(1, c0, c13, 0)
|
||||
#define RCP14_ETMVDCR2() MRC14(1, c0, c14, 0)
|
||||
#define RCP14_ETMVDCR3() MRC14(1, c0, c15, 0)
|
||||
#define RCP14_ETMACVR0() MRC14(1, c0, c0, 1)
|
||||
#define RCP14_ETMACVR1() MRC14(1, c0, c1, 1)
|
||||
#define RCP14_ETMACVR2() MRC14(1, c0, c2, 1)
|
||||
#define RCP14_ETMACVR3() MRC14(1, c0, c3, 1)
|
||||
#define RCP14_ETMACVR4() MRC14(1, c0, c4, 1)
|
||||
#define RCP14_ETMACVR5() MRC14(1, c0, c5, 1)
|
||||
#define RCP14_ETMACVR6() MRC14(1, c0, c6, 1)
|
||||
#define RCP14_ETMACVR7() MRC14(1, c0, c7, 1)
|
||||
#define RCP14_ETMACVR8() MRC14(1, c0, c8, 1)
|
||||
#define RCP14_ETMACVR9() MRC14(1, c0, c9, 1)
|
||||
#define RCP14_ETMACVR10() MRC14(1, c0, c10, 1)
|
||||
#define RCP14_ETMACVR11() MRC14(1, c0, c11, 1)
|
||||
#define RCP14_ETMACVR12() MRC14(1, c0, c12, 1)
|
||||
#define RCP14_ETMACVR13() MRC14(1, c0, c13, 1)
|
||||
#define RCP14_ETMACVR14() MRC14(1, c0, c14, 1)
|
||||
#define RCP14_ETMACVR15() MRC14(1, c0, c15, 1)
|
||||
#define RCP14_ETMACTR0() MRC14(1, c0, c0, 2)
|
||||
#define RCP14_ETMACTR1() MRC14(1, c0, c1, 2)
|
||||
#define RCP14_ETMACTR2() MRC14(1, c0, c2, 2)
|
||||
#define RCP14_ETMACTR3() MRC14(1, c0, c3, 2)
|
||||
#define RCP14_ETMACTR4() MRC14(1, c0, c4, 2)
|
||||
#define RCP14_ETMACTR5() MRC14(1, c0, c5, 2)
|
||||
#define RCP14_ETMACTR6() MRC14(1, c0, c6, 2)
|
||||
#define RCP14_ETMACTR7() MRC14(1, c0, c7, 2)
|
||||
#define RCP14_ETMACTR8() MRC14(1, c0, c8, 2)
|
||||
#define RCP14_ETMACTR9() MRC14(1, c0, c9, 2)
|
||||
#define RCP14_ETMACTR10() MRC14(1, c0, c10, 2)
|
||||
#define RCP14_ETMACTR11() MRC14(1, c0, c11, 2)
|
||||
#define RCP14_ETMACTR12() MRC14(1, c0, c12, 2)
|
||||
#define RCP14_ETMACTR13() MRC14(1, c0, c13, 2)
|
||||
#define RCP14_ETMACTR14() MRC14(1, c0, c14, 2)
|
||||
#define RCP14_ETMACTR15() MRC14(1, c0, c15, 2)
|
||||
#define RCP14_ETMDCVR0() MRC14(1, c0, c0, 3)
|
||||
#define RCP14_ETMDCVR2() MRC14(1, c0, c2, 3)
|
||||
#define RCP14_ETMDCVR4() MRC14(1, c0, c4, 3)
|
||||
#define RCP14_ETMDCVR6() MRC14(1, c0, c6, 3)
|
||||
#define RCP14_ETMDCVR8() MRC14(1, c0, c8, 3)
|
||||
#define RCP14_ETMDCVR10() MRC14(1, c0, c10, 3)
|
||||
#define RCP14_ETMDCVR12() MRC14(1, c0, c12, 3)
|
||||
#define RCP14_ETMDCVR14() MRC14(1, c0, c14, 3)
|
||||
#define RCP14_ETMDCMR0() MRC14(1, c0, c0, 4)
|
||||
#define RCP14_ETMDCMR2() MRC14(1, c0, c2, 4)
|
||||
#define RCP14_ETMDCMR4() MRC14(1, c0, c4, 4)
|
||||
#define RCP14_ETMDCMR6() MRC14(1, c0, c6, 4)
|
||||
#define RCP14_ETMDCMR8() MRC14(1, c0, c8, 4)
|
||||
#define RCP14_ETMDCMR10() MRC14(1, c0, c10, 4)
|
||||
#define RCP14_ETMDCMR12() MRC14(1, c0, c12, 4)
|
||||
#define RCP14_ETMDCMR14() MRC14(1, c0, c14, 4)
|
||||
#define RCP14_ETMCNTRLDVR0() MRC14(1, c0, c0, 5)
|
||||
#define RCP14_ETMCNTRLDVR1() MRC14(1, c0, c1, 5)
|
||||
#define RCP14_ETMCNTRLDVR2() MRC14(1, c0, c2, 5)
|
||||
#define RCP14_ETMCNTRLDVR3() MRC14(1, c0, c3, 5)
|
||||
#define RCP14_ETMCNTENR0() MRC14(1, c0, c4, 5)
|
||||
#define RCP14_ETMCNTENR1() MRC14(1, c0, c5, 5)
|
||||
#define RCP14_ETMCNTENR2() MRC14(1, c0, c6, 5)
|
||||
#define RCP14_ETMCNTENR3() MRC14(1, c0, c7, 5)
|
||||
#define RCP14_ETMCNTRLDEVR0() MRC14(1, c0, c8, 5)
|
||||
#define RCP14_ETMCNTRLDEVR1() MRC14(1, c0, c9, 5)
|
||||
#define RCP14_ETMCNTRLDEVR2() MRC14(1, c0, c10, 5)
|
||||
#define RCP14_ETMCNTRLDEVR3() MRC14(1, c0, c11, 5)
|
||||
#define RCP14_ETMCNTVR0() MRC14(1, c0, c12, 5)
|
||||
#define RCP14_ETMCNTVR1() MRC14(1, c0, c13, 5)
|
||||
#define RCP14_ETMCNTVR2() MRC14(1, c0, c14, 5)
|
||||
#define RCP14_ETMCNTVR3() MRC14(1, c0, c15, 5)
|
||||
#define RCP14_ETMSQ12EVR() MRC14(1, c0, c0, 6)
|
||||
#define RCP14_ETMSQ21EVR() MRC14(1, c0, c1, 6)
|
||||
#define RCP14_ETMSQ23EVR() MRC14(1, c0, c2, 6)
|
||||
#define RCP14_ETMSQ31EVR() MRC14(1, c0, c3, 6)
|
||||
#define RCP14_ETMSQ32EVR() MRC14(1, c0, c4, 6)
|
||||
#define RCP14_ETMSQ13EVR() MRC14(1, c0, c5, 6)
|
||||
#define RCP14_ETMSQR() MRC14(1, c0, c7, 6)
|
||||
#define RCP14_ETMEXTOUTEVR0() MRC14(1, c0, c8, 6)
|
||||
#define RCP14_ETMEXTOUTEVR1() MRC14(1, c0, c9, 6)
|
||||
#define RCP14_ETMEXTOUTEVR2() MRC14(1, c0, c10, 6)
|
||||
#define RCP14_ETMEXTOUTEVR3() MRC14(1, c0, c11, 6)
|
||||
#define RCP14_ETMCIDCVR0() MRC14(1, c0, c12, 6)
|
||||
#define RCP14_ETMCIDCVR1() MRC14(1, c0, c13, 6)
|
||||
#define RCP14_ETMCIDCVR2() MRC14(1, c0, c14, 6)
|
||||
#define RCP14_ETMCIDCMR() MRC14(1, c0, c15, 6)
|
||||
#define RCP14_ETMIMPSPEC0() MRC14(1, c0, c0, 7)
|
||||
#define RCP14_ETMIMPSPEC1() MRC14(1, c0, c1, 7)
|
||||
#define RCP14_ETMIMPSPEC2() MRC14(1, c0, c2, 7)
|
||||
#define RCP14_ETMIMPSPEC3() MRC14(1, c0, c3, 7)
|
||||
#define RCP14_ETMIMPSPEC4() MRC14(1, c0, c4, 7)
|
||||
#define RCP14_ETMIMPSPEC5() MRC14(1, c0, c5, 7)
|
||||
#define RCP14_ETMIMPSPEC6() MRC14(1, c0, c6, 7)
|
||||
#define RCP14_ETMIMPSPEC7() MRC14(1, c0, c7, 7)
|
||||
#define RCP14_ETMSYNCFR() MRC14(1, c0, c8, 7)
|
||||
#define RCP14_ETMIDR() MRC14(1, c0, c9, 7)
|
||||
#define RCP14_ETMCCER() MRC14(1, c0, c10, 7)
|
||||
#define RCP14_ETMEXTINSELR() MRC14(1, c0, c11, 7)
|
||||
#define RCP14_ETMTESSEICR() MRC14(1, c0, c12, 7)
|
||||
#define RCP14_ETMEIBCR() MRC14(1, c0, c13, 7)
|
||||
#define RCP14_ETMTSEVR() MRC14(1, c0, c14, 7)
|
||||
#define RCP14_ETMAUXCR() MRC14(1, c0, c15, 7)
|
||||
#define RCP14_ETMTRACEIDR() MRC14(1, c1, c0, 0)
|
||||
#define RCP14_ETMIDR2() MRC14(1, c1, c2, 0)
|
||||
#define RCP14_ETMVMIDCVR() MRC14(1, c1, c0, 1)
|
||||
#define RCP14_ETMOSLSR() MRC14(1, c1, c1, 4)
|
||||
/* Not available in PFTv1.1 */
|
||||
#define RCP14_ETMOSSRR() MRC14(1, c1, c2, 4)
|
||||
#define RCP14_ETMPDCR() MRC14(1, c1, c4, 4)
|
||||
#define RCP14_ETMPDSR() MRC14(1, c1, c5, 4)
|
||||
#define RCP14_ETMITCTRL() MRC14(1, c7, c0, 4)
|
||||
#define RCP14_ETMCLAIMSET() MRC14(1, c7, c8, 6)
|
||||
#define RCP14_ETMCLAIMCLR() MRC14(1, c7, c9, 6)
|
||||
#define RCP14_ETMLSR() MRC14(1, c7, c13, 6)
|
||||
#define RCP14_ETMAUTHSTATUS() MRC14(1, c7, c14, 6)
|
||||
#define RCP14_ETMDEVID() MRC14(1, c7, c2, 7)
|
||||
#define RCP14_ETMDEVTYPE() MRC14(1, c7, c3, 7)
|
||||
#define RCP14_ETMPIDR4() MRC14(1, c7, c4, 7)
|
||||
#define RCP14_ETMPIDR5() MRC14(1, c7, c5, 7)
|
||||
#define RCP14_ETMPIDR6() MRC14(1, c7, c6, 7)
|
||||
#define RCP14_ETMPIDR7() MRC14(1, c7, c7, 7)
|
||||
#define RCP14_ETMPIDR0() MRC14(1, c7, c8, 7)
|
||||
#define RCP14_ETMPIDR1() MRC14(1, c7, c9, 7)
|
||||
#define RCP14_ETMPIDR2() MRC14(1, c7, c10, 7)
|
||||
#define RCP14_ETMPIDR3() MRC14(1, c7, c11, 7)
|
||||
#define RCP14_ETMCIDR0() MRC14(1, c7, c12, 7)
|
||||
#define RCP14_ETMCIDR1() MRC14(1, c7, c13, 7)
|
||||
#define RCP14_ETMCIDR2() MRC14(1, c7, c14, 7)
|
||||
#define RCP14_ETMCIDR3() MRC14(1, c7, c15, 7)
|
||||
|
||||
#define WCP14_ETMCR(val) MCR14(val, 1, c0, c0, 0)
|
||||
#define WCP14_ETMTRIGGER(val) MCR14(val, 1, c0, c2, 0)
|
||||
#define WCP14_ETMASICCR(val) MCR14(val, 1, c0, c3, 0)
|
||||
#define WCP14_ETMSR(val) MCR14(val, 1, c0, c4, 0)
|
||||
#define WCP14_ETMTSSCR(val) MCR14(val, 1, c0, c6, 0)
|
||||
#define WCP14_ETMTECR2(val) MCR14(val, 1, c0, c7, 0)
|
||||
#define WCP14_ETMTEEVR(val) MCR14(val, 1, c0, c8, 0)
|
||||
#define WCP14_ETMTECR1(val) MCR14(val, 1, c0, c9, 0)
|
||||
#define WCP14_ETMFFRR(val) MCR14(val, 1, c0, c10, 0)
|
||||
#define WCP14_ETMFFLR(val) MCR14(val, 1, c0, c11, 0)
|
||||
#define WCP14_ETMVDEVR(val) MCR14(val, 1, c0, c12, 0)
|
||||
#define WCP14_ETMVDCR1(val) MCR14(val, 1, c0, c13, 0)
|
||||
#define WCP14_ETMVDCR2(val) MCR14(val, 1, c0, c14, 0)
|
||||
#define WCP14_ETMVDCR3(val) MCR14(val, 1, c0, c15, 0)
|
||||
#define WCP14_ETMACVR0(val) MCR14(val, 1, c0, c0, 1)
|
||||
#define WCP14_ETMACVR1(val) MCR14(val, 1, c0, c1, 1)
|
||||
#define WCP14_ETMACVR2(val) MCR14(val, 1, c0, c2, 1)
|
||||
#define WCP14_ETMACVR3(val) MCR14(val, 1, c0, c3, 1)
|
||||
#define WCP14_ETMACVR4(val) MCR14(val, 1, c0, c4, 1)
|
||||
#define WCP14_ETMACVR5(val) MCR14(val, 1, c0, c5, 1)
|
||||
#define WCP14_ETMACVR6(val) MCR14(val, 1, c0, c6, 1)
|
||||
#define WCP14_ETMACVR7(val) MCR14(val, 1, c0, c7, 1)
|
||||
#define WCP14_ETMACVR8(val) MCR14(val, 1, c0, c8, 1)
|
||||
#define WCP14_ETMACVR9(val) MCR14(val, 1, c0, c9, 1)
|
||||
#define WCP14_ETMACVR10(val) MCR14(val, 1, c0, c10, 1)
|
||||
#define WCP14_ETMACVR11(val) MCR14(val, 1, c0, c11, 1)
|
||||
#define WCP14_ETMACVR12(val) MCR14(val, 1, c0, c12, 1)
|
||||
#define WCP14_ETMACVR13(val) MCR14(val, 1, c0, c13, 1)
|
||||
#define WCP14_ETMACVR14(val) MCR14(val, 1, c0, c14, 1)
|
||||
#define WCP14_ETMACVR15(val) MCR14(val, 1, c0, c15, 1)
|
||||
#define WCP14_ETMACTR0(val) MCR14(val, 1, c0, c0, 2)
|
||||
#define WCP14_ETMACTR1(val) MCR14(val, 1, c0, c1, 2)
|
||||
#define WCP14_ETMACTR2(val) MCR14(val, 1, c0, c2, 2)
|
||||
#define WCP14_ETMACTR3(val) MCR14(val, 1, c0, c3, 2)
|
||||
#define WCP14_ETMACTR4(val) MCR14(val, 1, c0, c4, 2)
|
||||
#define WCP14_ETMACTR5(val) MCR14(val, 1, c0, c5, 2)
|
||||
#define WCP14_ETMACTR6(val) MCR14(val, 1, c0, c6, 2)
|
||||
#define WCP14_ETMACTR7(val) MCR14(val, 1, c0, c7, 2)
|
||||
#define WCP14_ETMACTR8(val) MCR14(val, 1, c0, c8, 2)
|
||||
#define WCP14_ETMACTR9(val) MCR14(val, 1, c0, c9, 2)
|
||||
#define WCP14_ETMACTR10(val) MCR14(val, 1, c0, c10, 2)
|
||||
#define WCP14_ETMACTR11(val) MCR14(val, 1, c0, c11, 2)
|
||||
#define WCP14_ETMACTR12(val) MCR14(val, 1, c0, c12, 2)
|
||||
#define WCP14_ETMACTR13(val) MCR14(val, 1, c0, c13, 2)
|
||||
#define WCP14_ETMACTR14(val) MCR14(val, 1, c0, c14, 2)
|
||||
#define WCP14_ETMACTR15(val) MCR14(val, 1, c0, c15, 2)
|
||||
#define WCP14_ETMDCVR0(val) MCR14(val, 1, c0, c0, 3)
|
||||
#define WCP14_ETMDCVR2(val) MCR14(val, 1, c0, c2, 3)
|
||||
#define WCP14_ETMDCVR4(val) MCR14(val, 1, c0, c4, 3)
|
||||
#define WCP14_ETMDCVR6(val) MCR14(val, 1, c0, c6, 3)
|
||||
#define WCP14_ETMDCVR8(val) MCR14(val, 1, c0, c8, 3)
|
||||
#define WCP14_ETMDCVR10(val) MCR14(val, 1, c0, c10, 3)
|
||||
#define WCP14_ETMDCVR12(val) MCR14(val, 1, c0, c12, 3)
|
||||
#define WCP14_ETMDCVR14(val) MCR14(val, 1, c0, c14, 3)
|
||||
#define WCP14_ETMDCMR0(val) MCR14(val, 1, c0, c0, 4)
|
||||
#define WCP14_ETMDCMR2(val) MCR14(val, 1, c0, c2, 4)
|
||||
#define WCP14_ETMDCMR4(val) MCR14(val, 1, c0, c4, 4)
|
||||
#define WCP14_ETMDCMR6(val) MCR14(val, 1, c0, c6, 4)
|
||||
#define WCP14_ETMDCMR8(val) MCR14(val, 1, c0, c8, 4)
|
||||
#define WCP14_ETMDCMR10(val) MCR14(val, 1, c0, c10, 4)
|
||||
#define WCP14_ETMDCMR12(val) MCR14(val, 1, c0, c12, 4)
|
||||
#define WCP14_ETMDCMR14(val) MCR14(val, 1, c0, c14, 4)
|
||||
#define WCP14_ETMCNTRLDVR0(val) MCR14(val, 1, c0, c0, 5)
|
||||
#define WCP14_ETMCNTRLDVR1(val) MCR14(val, 1, c0, c1, 5)
|
||||
#define WCP14_ETMCNTRLDVR2(val) MCR14(val, 1, c0, c2, 5)
|
||||
#define WCP14_ETMCNTRLDVR3(val) MCR14(val, 1, c0, c3, 5)
|
||||
#define WCP14_ETMCNTENR0(val) MCR14(val, 1, c0, c4, 5)
|
||||
#define WCP14_ETMCNTENR1(val) MCR14(val, 1, c0, c5, 5)
|
||||
#define WCP14_ETMCNTENR2(val) MCR14(val, 1, c0, c6, 5)
|
||||
#define WCP14_ETMCNTENR3(val) MCR14(val, 1, c0, c7, 5)
|
||||
#define WCP14_ETMCNTRLDEVR0(val) MCR14(val, 1, c0, c8, 5)
|
||||
#define WCP14_ETMCNTRLDEVR1(val) MCR14(val, 1, c0, c9, 5)
|
||||
#define WCP14_ETMCNTRLDEVR2(val) MCR14(val, 1, c0, c10, 5)
|
||||
#define WCP14_ETMCNTRLDEVR3(val) MCR14(val, 1, c0, c11, 5)
|
||||
#define WCP14_ETMCNTVR0(val) MCR14(val, 1, c0, c12, 5)
|
||||
#define WCP14_ETMCNTVR1(val) MCR14(val, 1, c0, c13, 5)
|
||||
#define WCP14_ETMCNTVR2(val) MCR14(val, 1, c0, c14, 5)
|
||||
#define WCP14_ETMCNTVR3(val) MCR14(val, 1, c0, c15, 5)
|
||||
#define WCP14_ETMSQ12EVR(val) MCR14(val, 1, c0, c0, 6)
|
||||
#define WCP14_ETMSQ21EVR(val) MCR14(val, 1, c0, c1, 6)
|
||||
#define WCP14_ETMSQ23EVR(val) MCR14(val, 1, c0, c2, 6)
|
||||
#define WCP14_ETMSQ31EVR(val) MCR14(val, 1, c0, c3, 6)
|
||||
#define WCP14_ETMSQ32EVR(val) MCR14(val, 1, c0, c4, 6)
|
||||
#define WCP14_ETMSQ13EVR(val) MCR14(val, 1, c0, c5, 6)
|
||||
#define WCP14_ETMSQR(val) MCR14(val, 1, c0, c7, 6)
|
||||
#define WCP14_ETMEXTOUTEVR0(val) MCR14(val, 1, c0, c8, 6)
|
||||
#define WCP14_ETMEXTOUTEVR1(val) MCR14(val, 1, c0, c9, 6)
|
||||
#define WCP14_ETMEXTOUTEVR2(val) MCR14(val, 1, c0, c10, 6)
|
||||
#define WCP14_ETMEXTOUTEVR3(val) MCR14(val, 1, c0, c11, 6)
|
||||
#define WCP14_ETMCIDCVR0(val) MCR14(val, 1, c0, c12, 6)
|
||||
#define WCP14_ETMCIDCVR1(val) MCR14(val, 1, c0, c13, 6)
|
||||
#define WCP14_ETMCIDCVR2(val) MCR14(val, 1, c0, c14, 6)
|
||||
#define WCP14_ETMCIDCMR(val) MCR14(val, 1, c0, c15, 6)
|
||||
#define WCP14_ETMIMPSPEC0(val) MCR14(val, 1, c0, c0, 7)
|
||||
#define WCP14_ETMIMPSPEC1(val) MCR14(val, 1, c0, c1, 7)
|
||||
#define WCP14_ETMIMPSPEC2(val) MCR14(val, 1, c0, c2, 7)
|
||||
#define WCP14_ETMIMPSPEC3(val) MCR14(val, 1, c0, c3, 7)
|
||||
#define WCP14_ETMIMPSPEC4(val) MCR14(val, 1, c0, c4, 7)
|
||||
#define WCP14_ETMIMPSPEC5(val) MCR14(val, 1, c0, c5, 7)
|
||||
#define WCP14_ETMIMPSPEC6(val) MCR14(val, 1, c0, c6, 7)
|
||||
#define WCP14_ETMIMPSPEC7(val) MCR14(val, 1, c0, c7, 7)
|
||||
/* Can be read only in ETMv3.4, ETMv3.5 */
|
||||
#define WCP14_ETMSYNCFR(val) MCR14(val, 1, c0, c8, 7)
|
||||
#define WCP14_ETMEXTINSELR(val) MCR14(val, 1, c0, c11, 7)
|
||||
#define WCP14_ETMTESSEICR(val) MCR14(val, 1, c0, c12, 7)
|
||||
#define WCP14_ETMEIBCR(val) MCR14(val, 1, c0, c13, 7)
|
||||
#define WCP14_ETMTSEVR(val) MCR14(val, 1, c0, c14, 7)
|
||||
#define WCP14_ETMAUXCR(val) MCR14(val, 1, c0, c15, 7)
|
||||
#define WCP14_ETMTRACEIDR(val) MCR14(val, 1, c1, c0, 0)
|
||||
#define WCP14_ETMIDR2(val) MCR14(val, 1, c1, c2, 0)
|
||||
#define WCP14_ETMVMIDCVR(val) MCR14(val, 1, c1, c0, 1)
|
||||
#define WCP14_ETMOSLAR(val) MCR14(val, 1, c1, c0, 4)
|
||||
/* Not available in PFTv1.1 */
|
||||
#define WCP14_ETMOSSRR(val) MCR14(val, 1, c1, c2, 4)
|
||||
#define WCP14_ETMPDCR(val) MCR14(val, 1, c1, c4, 4)
|
||||
#define WCP14_ETMPDSR(val) MCR14(val, 1, c1, c5, 4)
|
||||
#define WCP14_ETMITCTRL(val) MCR14(val, 1, c7, c0, 4)
|
||||
#define WCP14_ETMCLAIMSET(val) MCR14(val, 1, c7, c8, 6)
|
||||
#define WCP14_ETMCLAIMCLR(val) MCR14(val, 1, c7, c9, 6)
|
||||
/* Writes to this from CP14 interface are ignored */
|
||||
#define WCP14_ETMLAR(val) MCR14(val, 1, c7, c12, 6)
|
||||
|
||||
#endif
|
||||
@@ -24,7 +24,6 @@ obj-$(CONFIG_ATAGS) += atags_parse.o
|
||||
obj-$(CONFIG_ATAGS_PROC) += atags_proc.o
|
||||
obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += atags_compat.o
|
||||
|
||||
obj-$(CONFIG_OC_ETM) += etm.o
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
||||
obj-$(CONFIG_ISA_DMA_API) += dma.o
|
||||
obj-$(CONFIG_FIQ) += fiq.o fiqasm.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/coresight.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
@@ -36,7 +37,6 @@
|
||||
#include <asm/hw_breakpoint.h>
|
||||
#include <asm/kdebug.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/hardware/coresight.h>
|
||||
|
||||
/* Breakpoint currently in use for each BRP. */
|
||||
static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]);
|
||||
@@ -975,7 +975,7 @@ static void reset_ctrl_regs(void *unused)
|
||||
* Unconditionally clear the OS lock by writing a value
|
||||
* other than CS_LAR_KEY to the access register.
|
||||
*/
|
||||
ARM_DBG_WRITE(c1, c0, 4, ~CS_LAR_KEY);
|
||||
ARM_DBG_WRITE(c1, c0, 4, ~CORESIGHT_UNLOCK);
|
||||
isb();
|
||||
|
||||
/*
|
||||
|
||||
@@ -51,10 +51,11 @@ static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
|
||||
return (cyc * mult) >> shift;
|
||||
}
|
||||
|
||||
static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask)
|
||||
static unsigned long long notrace sched_clock_32(void)
|
||||
{
|
||||
u64 epoch_ns;
|
||||
u32 epoch_cyc;
|
||||
u32 cyc;
|
||||
|
||||
if (cd.suspended)
|
||||
return cd.epoch_ns;
|
||||
@@ -73,7 +74,9 @@ static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask)
|
||||
smp_rmb();
|
||||
} while (epoch_cyc != cd.epoch_cyc_copy);
|
||||
|
||||
return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, cd.mult, cd.shift);
|
||||
cyc = read_sched_clock();
|
||||
cyc = (cyc - epoch_cyc) & sched_clock_mask;
|
||||
return epoch_ns + cyc_to_ns(cyc, cd.mult, cd.shift);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -165,12 +168,6 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
|
||||
pr_debug("Registered %pF as sched_clock source\n", read);
|
||||
}
|
||||
|
||||
static unsigned long long notrace sched_clock_32(void)
|
||||
{
|
||||
u32 cyc = read_sched_clock();
|
||||
return cyc_to_sched_clock(cyc, sched_clock_mask);
|
||||
}
|
||||
|
||||
unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32;
|
||||
|
||||
unsigned long long notrace sched_clock(void)
|
||||
|
||||
@@ -141,6 +141,29 @@ int __init coherency_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/*
|
||||
* The coherency fabric is needed:
|
||||
* - For coherency between processors on Armada XP, so only
|
||||
* when SMP is enabled.
|
||||
* - For coherency between the processor and I/O devices, but
|
||||
* this coherency requires many pre-requisites (write
|
||||
* allocate cache policy, shareable pages, SMP bit set) that
|
||||
* are only meant in SMP situations.
|
||||
*
|
||||
* Note that this means that on Armada 370, there is currently
|
||||
* no way to use hardware I/O coherency, because even when
|
||||
* CONFIG_SMP is enabled, is_smp() returns false due to the
|
||||
* Armada 370 being a single-core processor. To lift this
|
||||
* limitation, we would have to find a way to make the cache
|
||||
* policy set to write-allocate (on all Armada SoCs), and to
|
||||
* set the shareable attribute in page tables (on all Armada
|
||||
* SoCs except the Armada 370). Unfortunately, such decisions
|
||||
* are taken very early in the kernel boot process, at a point
|
||||
* where we don't know yet on which SoC we are running.
|
||||
*/
|
||||
if (!is_smp())
|
||||
return 0;
|
||||
|
||||
np = of_find_matching_node(NULL, of_coherency_table);
|
||||
if (np) {
|
||||
pr_info("Initializing Coherency fabric\n");
|
||||
|
||||
@@ -394,14 +394,6 @@ config MACH_OMAP4_PANDA
|
||||
select OMAP_PACKAGE_CBS
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
|
||||
config OMAP3_EMU
|
||||
bool "OMAP3 debugging peripherals"
|
||||
depends on ARCH_OMAP3
|
||||
select ARM_AMBA
|
||||
select OC_ETM
|
||||
help
|
||||
Say Y here to enable debugging hardware of omap3
|
||||
|
||||
config OMAP3_SDRC_AC_TIMING
|
||||
bool "Enable SDRC AC timing register changes"
|
||||
depends on ARCH_OMAP3
|
||||
|
||||
@@ -200,7 +200,6 @@ obj-$(CONFIG_SOC_AM33XX) += omap_hwmod_33xx_data.o
|
||||
obj-$(CONFIG_ARCH_OMAP4) += omap_hwmod_44xx_data.o
|
||||
|
||||
# EMU peripherals
|
||||
obj-$(CONFIG_OMAP3_EMU) += emu.o
|
||||
obj-$(CONFIG_HW_PERF_EVENTS) += pmu.o
|
||||
|
||||
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* emu.c
|
||||
*
|
||||
* ETM and ETB CoreSight components' resources as found in OMAP3xxx.
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation.
|
||||
* Alexander Shishkin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "soc.h"
|
||||
#include "iomap.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alexander Shishkin");
|
||||
|
||||
/* Cortex CoreSight components within omap3xxx EMU */
|
||||
#define ETM_BASE (L4_EMU_34XX_PHYS + 0x10000)
|
||||
#define DBG_BASE (L4_EMU_34XX_PHYS + 0x11000)
|
||||
#define ETB_BASE (L4_EMU_34XX_PHYS + 0x1b000)
|
||||
#define DAPCTL (L4_EMU_34XX_PHYS + 0x1d000)
|
||||
|
||||
static AMBA_APB_DEVICE(omap3_etb, "etb", 0x000bb907, ETB_BASE, { }, NULL);
|
||||
static AMBA_APB_DEVICE(omap3_etm, "etm", 0x102bb921, ETM_BASE, { }, NULL);
|
||||
|
||||
static int __init emu_init(void)
|
||||
{
|
||||
if (!cpu_is_omap34xx())
|
||||
return -ENODEV;
|
||||
|
||||
amba_device_register(&omap3_etb_device, &iomem_resource);
|
||||
amba_device_register(&omap3_etm_device, &iomem_resource);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
omap_subsys_initcall(emu_init);
|
||||
@@ -268,37 +268,19 @@ static void *
|
||||
__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
unsigned long addr;
|
||||
|
||||
/*
|
||||
* DMA allocation can be mapped to user space, so lets
|
||||
* set VM_USERMAP flags too.
|
||||
*/
|
||||
area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
addr = (unsigned long)area->addr;
|
||||
area->phys_addr = __pfn_to_phys(page_to_pfn(page));
|
||||
|
||||
if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) {
|
||||
vunmap((void *)addr);
|
||||
return NULL;
|
||||
}
|
||||
return (void *)addr;
|
||||
return dma_common_contiguous_remap(page, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
prot, caller);
|
||||
}
|
||||
|
||||
static void __dma_free_remap(void *cpu_addr, size_t size)
|
||||
{
|
||||
unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP;
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
if (!area || (area->flags & flags) != flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
dma_common_free_remap(cpu_addr, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP);
|
||||
}
|
||||
|
||||
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
|
||||
@@ -1223,29 +1205,8 @@ static void *
|
||||
__iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
struct vm_struct *area;
|
||||
unsigned long p;
|
||||
|
||||
area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
area->nr_pages = nr_pages;
|
||||
p = (unsigned long)area->addr;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i]));
|
||||
if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot))
|
||||
goto err;
|
||||
p += PAGE_SIZE;
|
||||
}
|
||||
return area->addr;
|
||||
err:
|
||||
unmap_kernel_range((unsigned long)area->addr, size);
|
||||
vunmap(area->addr);
|
||||
return dma_common_pages_remap(pages, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP, prot, caller);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1442,8 +1403,8 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
||||
}
|
||||
|
||||
if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
dma_common_free_remap(cpu_addr, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP);
|
||||
}
|
||||
|
||||
__iommu_remove_mapping(dev, handle, size);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
config ARM64
|
||||
def_bool y
|
||||
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
|
||||
select ARCH_HAS_OPP
|
||||
select ARCH_USE_CMPXCHG_LOCKREF
|
||||
select ARCH_HAS_OPP
|
||||
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
|
||||
@@ -18,6 +19,7 @@ config ARM64
|
||||
select COMMON_CLK
|
||||
select CPU_PM if (SUSPEND || CPU_IDLE)
|
||||
select DCACHE_WORD_ACCESS
|
||||
select GENERIC_ALLOCATOR
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select GENERIC_CPU_AUTOPROBE
|
||||
@@ -36,6 +38,7 @@ config ARM64
|
||||
select HAVE_ARCH_SECCOMP_FILTER
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
select HAVE_C_RECORDMCOUNT
|
||||
select HAVE_CC_STACKPROTECTOR
|
||||
select HAVE_DEBUG_BUGVERBOSE
|
||||
select HAVE_DEBUG_KMEMLEAK
|
||||
select HAVE_DMA_API_DEBUG
|
||||
|
||||
@@ -44,4 +44,15 @@ config PID_IN_CONTEXTIDR
|
||||
instructions during context switch. Say Y here only if you are
|
||||
planning to use hardware trace tools with this kernel.
|
||||
|
||||
config DEBUG_SET_MODULE_RONX
|
||||
bool "Set loadable kernel module data as NX and text as RO"
|
||||
depends on MODULES
|
||||
help
|
||||
This option helps catch unintended modifications to loadable
|
||||
kernel module's text and read-only data. It also prevents execution
|
||||
of module data. Such protection may interfere with run-time code
|
||||
patching and dynamic kernel tracing - and they might also protect
|
||||
against certain classes of kernel exploits.
|
||||
If in doubt, say "N".
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -39,7 +39,11 @@ CHECKFLAGS += -D__aarch64__
|
||||
head-y := arch/arm64/kernel/head.o
|
||||
|
||||
# The byte offset of the kernel image in RAM from the start of RAM.
|
||||
ifeq ($(CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET), y)
|
||||
TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%03x000\n", int(512 * rand())}')
|
||||
else
|
||||
TEXT_OFFSET := 0x00080000
|
||||
endif
|
||||
|
||||
export TEXT_OFFSET GZFLAGS
|
||||
|
||||
|
||||
@@ -148,4 +148,8 @@ static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
|
||||
{
|
||||
}
|
||||
|
||||
int set_memory_ro(unsigned long addr, int numpages);
|
||||
int set_memory_rw(unsigned long addr, int numpages);
|
||||
int set_memory_x(unsigned long addr, int numpages);
|
||||
int set_memory_nx(unsigned long addr, int numpages);
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,8 @@ struct device_node;
|
||||
* enable-method property.
|
||||
* @cpu_init: Reads any data necessary for a specific enable-method from the
|
||||
* devicetree, for a given cpu node and proposed logical id.
|
||||
* @cpu_init_idle: Reads any data necessary to initialize CPU idle states from
|
||||
* devicetree, for a given cpu node and proposed logical id.
|
||||
* @cpu_prepare: Early one-time preparation step for a cpu. If there is a
|
||||
* mechanism for doing so, tests whether it is possible to boot
|
||||
* the given CPU.
|
||||
@@ -39,6 +41,7 @@ struct device_node;
|
||||
* from the cpu to be killed.
|
||||
* @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the
|
||||
* cpu being killed.
|
||||
* @cpu_kill: Ensures a cpu has left the kernel. Called from another cpu.
|
||||
* @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
|
||||
* to wrong parameters or error conditions. Called from the
|
||||
* CPU being suspended. Must be called with IRQs disabled.
|
||||
@@ -46,12 +49,14 @@ struct device_node;
|
||||
struct cpu_operations {
|
||||
const char *name;
|
||||
int (*cpu_init)(struct device_node *, unsigned int);
|
||||
int (*cpu_init_idle)(struct device_node *, unsigned int);
|
||||
int (*cpu_prepare)(unsigned int);
|
||||
int (*cpu_boot)(unsigned int);
|
||||
void (*cpu_postboot)(void);
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
int (*cpu_disable)(unsigned int cpu);
|
||||
void (*cpu_die)(unsigned int cpu);
|
||||
int (*cpu_kill)(unsigned int cpu);
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_CPU_SUSPEND
|
||||
int (*cpu_suspend)(unsigned long);
|
||||
|
||||
13
arch/arm64/include/asm/cpuidle.h
Normal file
13
arch/arm64/include/asm/cpuidle.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __ASM_CPUIDLE_H
|
||||
#define __ASM_CPUIDLE_H
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE
|
||||
extern int cpu_init_idle(unsigned int cpu);
|
||||
#else
|
||||
static inline int cpu_init_idle(unsigned int cpu)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -36,15 +36,34 @@
|
||||
__val; \
|
||||
})
|
||||
|
||||
#define MIDR_REVISION_MASK 0xf
|
||||
#define MIDR_REVISION(midr) ((midr) & MIDR_REVISION_MASK)
|
||||
#define MIDR_PARTNUM_SHIFT 4
|
||||
#define MIDR_PARTNUM_MASK (0xfff << MIDR_PARTNUM_SHIFT)
|
||||
#define MIDR_PARTNUM(midr) \
|
||||
(((midr) & MIDR_PARTNUM_MASK) >> MIDR_PARTNUM_SHIFT)
|
||||
#define MIDR_ARCHITECTURE_SHIFT 16
|
||||
#define MIDR_ARCHITECTURE_MASK (0xf << MIDR_ARCHITECTURE_SHIFT)
|
||||
#define MIDR_ARCHITECTURE(midr) \
|
||||
(((midr) & MIDR_ARCHITECTURE_MASK) >> MIDR_ARCHITECTURE_SHIFT)
|
||||
#define MIDR_VARIANT_SHIFT 20
|
||||
#define MIDR_VARIANT_MASK (0xf << MIDR_VARIANT_SHIFT)
|
||||
#define MIDR_VARIANT(midr) \
|
||||
(((midr) & MIDR_VARIANT_MASK) >> MIDR_VARIANT_SHIFT)
|
||||
#define MIDR_IMPLEMENTOR_SHIFT 24
|
||||
#define MIDR_IMPLEMENTOR_MASK (0xff << MIDR_IMPLEMENTOR_SHIFT)
|
||||
#define MIDR_IMPLEMENTOR(midr) \
|
||||
(((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT)
|
||||
|
||||
#define ARM_CPU_IMP_ARM 0x41
|
||||
#define ARM_CPU_IMP_APM 0x50
|
||||
|
||||
#define ARM_CPU_PART_AEM_V8 0xD0F0
|
||||
#define ARM_CPU_PART_FOUNDATION 0xD000
|
||||
#define ARM_CPU_PART_CORTEX_A53 0xD030
|
||||
#define ARM_CPU_PART_CORTEX_A57 0xD070
|
||||
#define ARM_CPU_PART_AEM_V8 0xD0F
|
||||
#define ARM_CPU_PART_FOUNDATION 0xD00
|
||||
#define ARM_CPU_PART_CORTEX_A57 0xD07
|
||||
#define ARM_CPU_PART_CORTEX_A53 0xD03
|
||||
|
||||
#define APM_CPU_PART_POTENZA 0x0000
|
||||
#define APM_CPU_PART_POTENZA 0x000
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@@ -65,12 +84,12 @@ static inline u64 __attribute_const__ read_cpuid_mpidr(void)
|
||||
|
||||
static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
|
||||
{
|
||||
return (read_cpuid_id() & 0xFF000000) >> 24;
|
||||
return MIDR_IMPLEMENTOR(read_cpuid_id());
|
||||
}
|
||||
|
||||
static inline unsigned int __attribute_const__ read_cpuid_part_number(void)
|
||||
{
|
||||
return (read_cpuid_id() & 0xFFF0);
|
||||
return MIDR_PARTNUM(read_cpuid_id());
|
||||
}
|
||||
|
||||
static inline u32 __attribute_const__ read_cpuid_cachetype(void)
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
#include <asm-generic/dma-coherent.h>
|
||||
|
||||
#define ARCH_HAS_DMA_GET_REQUIRED_MASK
|
||||
|
||||
#define DMA_ERROR_CODE (~(dma_addr_t)0)
|
||||
extern struct dma_map_ops *dma_ops;
|
||||
extern struct dma_map_ops coherent_swiotlb_dma_ops;
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
|
||||
/*
|
||||
* The idmap and swapper page tables need some space reserved in the kernel
|
||||
* image. The idmap only requires a pgd and a next level table to (section) map
|
||||
* the kernel, while the swapper also maps the FDT and requires an additional
|
||||
* table to map an early UART. See __create_page_tables for more information.
|
||||
* image. Both require a pgd and a next level table to (section) map the
|
||||
* kernel. The the swapper also maps the FDT (see __create_page_tables for
|
||||
* more information).
|
||||
*/
|
||||
#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
|
||||
#define SWAPPER_DIR_SIZE (2 * PAGE_SIZE)
|
||||
#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@@ -26,13 +26,13 @@ static inline void set_my_cpu_offset(unsigned long off)
|
||||
static inline unsigned long __my_cpu_offset(void)
|
||||
{
|
||||
unsigned long off;
|
||||
register unsigned long *sp asm ("sp");
|
||||
|
||||
/*
|
||||
* We want to allow caching the value, so avoid using volatile and
|
||||
* instead use a fake stack read to hazard against barrier().
|
||||
*/
|
||||
asm("mrs %0, tpidr_el1" : "=r" (off) : "Q" (*sp));
|
||||
asm("mrs %0, tpidr_el1" : "=r" (off) :
|
||||
"Q" (*(const unsigned long *)current_stack_pointer));
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
@@ -141,46 +141,51 @@ extern struct page *empty_zero_page;
|
||||
#define pte_valid_not_user(pte) \
|
||||
((pte_val(pte) & (PTE_VALID | PTE_USER)) == PTE_VALID)
|
||||
|
||||
static inline pte_t clear_pte_bit(pte_t pte, pgprot_t prot)
|
||||
{
|
||||
pte_val(pte) &= ~pgprot_val(prot);
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline pte_t set_pte_bit(pte_t pte, pgprot_t prot)
|
||||
{
|
||||
pte_val(pte) |= pgprot_val(prot);
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline pte_t pte_wrprotect(pte_t pte)
|
||||
{
|
||||
pte_val(pte) &= ~PTE_WRITE;
|
||||
return pte;
|
||||
return clear_pte_bit(pte, __pgprot(PTE_WRITE));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkwrite(pte_t pte)
|
||||
{
|
||||
pte_val(pte) |= PTE_WRITE;
|
||||
return pte;
|
||||
return set_pte_bit(pte, __pgprot(PTE_WRITE));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkclean(pte_t pte)
|
||||
{
|
||||
pte_val(pte) &= ~PTE_DIRTY;
|
||||
return pte;
|
||||
return clear_pte_bit(pte, __pgprot(PTE_DIRTY));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkdirty(pte_t pte)
|
||||
{
|
||||
pte_val(pte) |= PTE_DIRTY;
|
||||
return pte;
|
||||
return set_pte_bit(pte, __pgprot(PTE_DIRTY));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkold(pte_t pte)
|
||||
{
|
||||
pte_val(pte) &= ~PTE_AF;
|
||||
return pte;
|
||||
return clear_pte_bit(pte, __pgprot(PTE_AF));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkyoung(pte_t pte)
|
||||
{
|
||||
pte_val(pte) |= PTE_AF;
|
||||
return pte;
|
||||
return set_pte_bit(pte, __pgprot(PTE_AF));
|
||||
}
|
||||
|
||||
static inline pte_t pte_mkspecial(pte_t pte)
|
||||
{
|
||||
pte_val(pte) |= PTE_SPECIAL;
|
||||
return pte;
|
||||
return set_pte_bit(pte, __pgprot(PTE_SPECIAL));
|
||||
}
|
||||
|
||||
static inline void set_pte(pte_t *ptep, pte_t pte)
|
||||
@@ -257,7 +262,7 @@ static inline pmd_t pte_pmd(pte_t pte)
|
||||
#define pmd_mkwrite(pmd) pte_pmd(pte_mkwrite(pmd_pte(pmd)))
|
||||
#define pmd_mkdirty(pmd) pte_pmd(pte_mkdirty(pmd_pte(pmd)))
|
||||
#define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd)))
|
||||
#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) &= ~PMD_TYPE_MASK))
|
||||
#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_TYPE_MASK))
|
||||
|
||||
#define __HAVE_ARCH_PMD_WRITE
|
||||
#define pmd_write(pmd) pte_write(pmd_pte(pmd))
|
||||
|
||||
@@ -34,6 +34,8 @@ extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
|
||||
extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
|
||||
extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
|
||||
extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
|
||||
void cpu_soft_restart(phys_addr_t cpu_reset,
|
||||
unsigned long addr) __attribute__((noreturn));
|
||||
|
||||
#include <asm/memory.h>
|
||||
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
#ifndef __ASM_PSCI_H
|
||||
#define __ASM_PSCI_H
|
||||
|
||||
struct cpuidle_driver;
|
||||
void psci_init(void);
|
||||
|
||||
int __init psci_dt_register_idle_states(struct cpuidle_driver *,
|
||||
struct device_node *[]);
|
||||
int psci_init(void);
|
||||
|
||||
#endif /* __ASM_PSCI_H */
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#define __ASM_SPARSEMEM_H
|
||||
|
||||
#ifdef CONFIG_SPARSEMEM
|
||||
#define MAX_PHYSMEM_BITS 40
|
||||
#define MAX_PHYSMEM_BITS 48
|
||||
#define SECTION_SIZE_BITS 30
|
||||
#endif
|
||||
|
||||
|
||||
38
arch/arm64/include/asm/stackprotector.h
Normal file
38
arch/arm64/include/asm/stackprotector.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* GCC stack protector support.
|
||||
*
|
||||
* Stack protector works by putting predefined pattern at the start of
|
||||
* the stack frame and verifying that it hasn't been overwritten when
|
||||
* returning from the function. The pattern is called stack canary
|
||||
* and gcc expects it to be defined by a global variable called
|
||||
* "__stack_chk_guard" on ARM. This unfortunately means that on SMP
|
||||
* we cannot have a different canary value per task.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_STACKPROTECTOR_H
|
||||
#define __ASM_STACKPROTECTOR_H
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
extern unsigned long __stack_chk_guard;
|
||||
|
||||
/*
|
||||
* Initialize the stackprotector canary value.
|
||||
*
|
||||
* NOTE: this must only be called from functions that never return,
|
||||
* and it must always be inlined.
|
||||
*/
|
||||
static __always_inline void boot_init_stack_canary(void)
|
||||
{
|
||||
unsigned long canary;
|
||||
|
||||
/* Try to get a semi random initial value. */
|
||||
get_random_bytes(&canary, sizeof(canary));
|
||||
canary ^= LINUX_VERSION_CODE;
|
||||
|
||||
current->stack_canary = canary;
|
||||
__stack_chk_guard = current->stack_canary;
|
||||
}
|
||||
|
||||
#endif /* _ASM_STACKPROTECTOR_H */
|
||||
@@ -22,6 +22,18 @@ extern char *strrchr(const char *, int c);
|
||||
#define __HAVE_ARCH_STRCHR
|
||||
extern char *strchr(const char *, int c);
|
||||
|
||||
#define __HAVE_ARCH_STRCMP
|
||||
extern int strcmp(const char *, const char *);
|
||||
|
||||
#define __HAVE_ARCH_STRNCMP
|
||||
extern int strncmp(const char *, const char *, __kernel_size_t);
|
||||
|
||||
#define __HAVE_ARCH_STRLEN
|
||||
extern __kernel_size_t strlen(const char *);
|
||||
|
||||
#define __HAVE_ARCH_STRNLEN
|
||||
extern __kernel_size_t strnlen(const char *, __kernel_size_t);
|
||||
|
||||
#define __HAVE_ARCH_MEMCPY
|
||||
extern void *memcpy(void *, const void *, __kernel_size_t);
|
||||
|
||||
@@ -34,4 +46,7 @@ extern void *memchr(const void *, int, __kernel_size_t);
|
||||
#define __HAVE_ARCH_MEMSET
|
||||
extern void *memset(void *, int, __kernel_size_t);
|
||||
|
||||
#define __HAVE_ARCH_MEMCMP
|
||||
extern int memcmp(const void *, const void *, size_t);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,6 +68,11 @@ struct thread_info {
|
||||
#define init_thread_info (init_thread_union.thread_info)
|
||||
#define init_stack (init_thread_union.stack)
|
||||
|
||||
/*
|
||||
* how to get the current stack pointer from C
|
||||
*/
|
||||
register unsigned long current_stack_pointer asm ("sp");
|
||||
|
||||
/*
|
||||
* how to get the thread information struct from C
|
||||
*/
|
||||
@@ -75,8 +80,8 @@ static inline struct thread_info *current_thread_info(void) __attribute_const__;
|
||||
|
||||
static inline struct thread_info *current_thread_info(void)
|
||||
{
|
||||
register unsigned long sp asm ("sp");
|
||||
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
|
||||
return (struct thread_info *)
|
||||
(current_stack_pointer & ~(THREAD_SIZE - 1));
|
||||
}
|
||||
|
||||
#define thread_saved_pc(tsk) \
|
||||
|
||||
@@ -98,8 +98,8 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
static inline void flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long asid = (unsigned long)ASID(vma->vm_mm) << 48;
|
||||
unsigned long addr;
|
||||
@@ -112,7 +112,7 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
static inline void __flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long addr;
|
||||
start >>= 12;
|
||||
@@ -125,6 +125,29 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
|
||||
isb();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is meant to avoid soft lock-ups on large TLB flushing ranges and not
|
||||
* necessarily a performance improvement.
|
||||
*/
|
||||
#define MAX_TLB_RANGE (1024UL << PAGE_SHIFT)
|
||||
|
||||
static inline void flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
if ((end - start) <= MAX_TLB_RANGE)
|
||||
__flush_tlb_range(vma, start, end);
|
||||
else
|
||||
flush_tlb_mm(vma->vm_mm);
|
||||
}
|
||||
|
||||
static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
if ((end - start) <= MAX_TLB_RANGE)
|
||||
__flush_tlb_kernel_range(start, end);
|
||||
else
|
||||
flush_tlb_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* On AArch64, the cache coherency is handled via the set_pte_at() function.
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
||||
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
|
||||
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||
arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
||||
arm64-obj-$(CONFIG_KGDB) += kgdb.o
|
||||
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
|
||||
|
||||
|
||||
@@ -44,10 +44,15 @@ EXPORT_SYMBOL(memstart_addr);
|
||||
/* string / mem functions */
|
||||
EXPORT_SYMBOL(strchr);
|
||||
EXPORT_SYMBOL(strrchr);
|
||||
EXPORT_SYMBOL(strcmp);
|
||||
EXPORT_SYMBOL(strncmp);
|
||||
EXPORT_SYMBOL(strlen);
|
||||
EXPORT_SYMBOL(strnlen);
|
||||
EXPORT_SYMBOL(memset);
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
EXPORT_SYMBOL(memmove);
|
||||
EXPORT_SYMBOL(memchr);
|
||||
EXPORT_SYMBOL(memcmp);
|
||||
|
||||
/* atomic bitops */
|
||||
EXPORT_SYMBOL(set_bit);
|
||||
|
||||
@@ -30,8 +30,8 @@ const struct cpu_operations *cpu_ops[NR_CPUS];
|
||||
static const struct cpu_operations *supported_cpu_ops[] __initconst = {
|
||||
#ifdef CONFIG_SMP
|
||||
&smp_spin_table_ops,
|
||||
&cpu_psci_ops,
|
||||
#endif
|
||||
&cpu_psci_ops,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
31
arch/arm64/kernel/cpuidle.c
Normal file
31
arch/arm64/kernel/cpuidle.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ARM64 CPU idle arch support
|
||||
*
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/cpu_ops.h>
|
||||
|
||||
int cpu_init_idle(unsigned int cpu)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
struct device_node *cpu_node = of_cpu_device_node_get(cpu);
|
||||
|
||||
if (!cpu_node)
|
||||
return -ENODEV;
|
||||
|
||||
if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
|
||||
ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
|
||||
|
||||
of_node_put(cpu_node);
|
||||
return ret;
|
||||
}
|
||||
@@ -303,20 +303,20 @@ static int brk_handler(unsigned long addr, unsigned int esr,
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED)
|
||||
return 0;
|
||||
if (user_mode(regs)) {
|
||||
info = (siginfo_t) {
|
||||
.si_signo = SIGTRAP,
|
||||
.si_errno = 0,
|
||||
.si_code = TRAP_BRKPT,
|
||||
.si_addr = (void __user *)instruction_pointer(regs),
|
||||
};
|
||||
|
||||
if (!user_mode(regs))
|
||||
force_sig_info(SIGTRAP, &info, current);
|
||||
} else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
|
||||
pr_warning("Unexpected kernel BRK exception at EL1\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
info = (siginfo_t) {
|
||||
.si_signo = SIGTRAP,
|
||||
.si_errno = 0,
|
||||
.si_code = TRAP_BRKPT,
|
||||
.si_addr = (void __user *)instruction_pointer(regs),
|
||||
};
|
||||
|
||||
force_sig_info(SIGTRAP, &info, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,12 @@
|
||||
|
||||
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
|
||||
|
||||
#if (KERNEL_RAM_VADDR & 0xfffff) != 0x80000
|
||||
#error KERNEL_RAM_VADDR must start at 0xXXX80000
|
||||
#if (TEXT_OFFSET & 0xfff) != 0
|
||||
#error TEXT_OFFSET must be at least 4KB aligned
|
||||
#elif (PAGE_OFFSET & 0x1fffff) != 0
|
||||
#error PAGE_OFFSET must be at least 2MB aligned
|
||||
#elif TEXT_OFFSET > 0x1fffff
|
||||
#error TEXT_OFFSET must be less than 2MB
|
||||
#endif
|
||||
|
||||
.macro pgtbl, ttb0, ttb1, virt_to_phys
|
||||
@@ -349,6 +353,8 @@ ENTRY(set_cpu_boot_mode_flag)
|
||||
b.ne 1f
|
||||
add x1, x1, #4
|
||||
1: str w20, [x1] // This CPU has booted in EL1
|
||||
dmb sy
|
||||
dc ivac, x1 // Invalidate potentially stale cache line
|
||||
ret
|
||||
ENDPROC(set_cpu_boot_mode_flag)
|
||||
|
||||
@@ -366,10 +372,6 @@ ENTRY(__boot_cpu_mode)
|
||||
.long 0
|
||||
.popsection
|
||||
|
||||
.align 3
|
||||
2: .quad .
|
||||
.quad PAGE_OFFSET
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.align 3
|
||||
1: .quad .
|
||||
@@ -594,13 +596,6 @@ __create_page_tables:
|
||||
sub x6, x6, #1 // inclusive range
|
||||
create_block_map x0, x7, x3, x5, x6
|
||||
1:
|
||||
/*
|
||||
* Create the pgd entry for the fixed mappings.
|
||||
*/
|
||||
ldr x5, =FIXADDR_TOP // Fixed mapping virtual address
|
||||
add x0, x26, #2 * PAGE_SIZE // section table address
|
||||
create_pgd_entry x26, x0, x5, x6, x7
|
||||
|
||||
/*
|
||||
* Since the page tables have been populated with non-cacheable
|
||||
* accesses (MMU disabled), invalidate the idmap and swapper page
|
||||
|
||||
@@ -105,7 +105,7 @@ static bool migrate_one_irq(struct irq_desc *desc)
|
||||
c = irq_data_get_irq_chip(d);
|
||||
if (!c->irq_set_affinity)
|
||||
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
|
||||
else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret)
|
||||
else if (c->irq_set_affinity(d, affinity, false) == IRQ_SET_MASK_OK && ret)
|
||||
cpumask_copy(d->affinity, affinity);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -52,36 +52,16 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
static void setup_restart(void)
|
||||
{
|
||||
/*
|
||||
* Tell the mm system that we are going to reboot -
|
||||
* we may need it to insert some 1:1 mappings so that
|
||||
* soft boot works.
|
||||
*/
|
||||
setup_mm_for_reboot();
|
||||
|
||||
/* Clean and invalidate caches */
|
||||
flush_cache_all();
|
||||
|
||||
/* Turn D-cache off */
|
||||
cpu_cache_off();
|
||||
|
||||
/* Push out any further dirty data, and ensure cache is empty */
|
||||
flush_cache_all();
|
||||
}
|
||||
#ifdef CONFIG_CC_STACKPROTECTOR
|
||||
#include <linux/stackprotector.h>
|
||||
unsigned long __stack_chk_guard __read_mostly;
|
||||
EXPORT_SYMBOL(__stack_chk_guard);
|
||||
#endif
|
||||
|
||||
void soft_restart(unsigned long addr)
|
||||
{
|
||||
typedef void (*phys_reset_t)(unsigned long);
|
||||
phys_reset_t phys_reset;
|
||||
|
||||
setup_restart();
|
||||
|
||||
/* Switch to the identity mapping */
|
||||
phys_reset = (phys_reset_t)virt_to_phys(cpu_reset);
|
||||
phys_reset(addr);
|
||||
|
||||
setup_mm_for_reboot();
|
||||
cpu_soft_restart(virt_to_phys(cpu_reset), addr);
|
||||
/* Should never get here */
|
||||
BUG();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include <asm/compiler.h>
|
||||
#include <asm/cpu_ops.h>
|
||||
@@ -27,6 +31,7 @@
|
||||
#include <asm/psci.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
#define PSCI_POWER_STATE_TYPE_STANDBY 0
|
||||
#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
|
||||
@@ -43,17 +48,23 @@ struct psci_operations {
|
||||
int (*cpu_off)(struct psci_power_state state);
|
||||
int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
|
||||
int (*migrate)(unsigned long cpuid);
|
||||
int (*affinity_info)(unsigned long target_affinity,
|
||||
unsigned long lowest_affinity_level);
|
||||
int (*migrate_info_type)(void);
|
||||
};
|
||||
|
||||
static struct psci_operations psci_ops;
|
||||
|
||||
static int (*invoke_psci_fn)(u64, u64, u64, u64);
|
||||
typedef int (*psci_initcall_t)(const struct device_node *);
|
||||
|
||||
enum psci_function {
|
||||
PSCI_FN_CPU_SUSPEND,
|
||||
PSCI_FN_CPU_ON,
|
||||
PSCI_FN_CPU_OFF,
|
||||
PSCI_FN_MIGRATE,
|
||||
PSCI_FN_AFFINITY_INFO,
|
||||
PSCI_FN_MIGRATE_INFO_TYPE,
|
||||
PSCI_FN_MAX,
|
||||
};
|
||||
|
||||
@@ -61,53 +72,41 @@ static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
|
||||
|
||||
static u32 psci_function_id[PSCI_FN_MAX];
|
||||
|
||||
#define PSCI_RET_SUCCESS 0
|
||||
#define PSCI_RET_EOPNOTSUPP -1
|
||||
#define PSCI_RET_EINVAL -2
|
||||
#define PSCI_RET_EPERM -3
|
||||
|
||||
static int psci_to_linux_errno(int errno)
|
||||
{
|
||||
switch (errno) {
|
||||
case PSCI_RET_SUCCESS:
|
||||
return 0;
|
||||
case PSCI_RET_EOPNOTSUPP:
|
||||
case PSCI_RET_NOT_SUPPORTED:
|
||||
return -EOPNOTSUPP;
|
||||
case PSCI_RET_EINVAL:
|
||||
case PSCI_RET_INVALID_PARAMS:
|
||||
return -EINVAL;
|
||||
case PSCI_RET_EPERM:
|
||||
case PSCI_RET_DENIED:
|
||||
return -EPERM;
|
||||
};
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define PSCI_POWER_STATE_ID_MASK 0xffff
|
||||
#define PSCI_POWER_STATE_ID_SHIFT 0
|
||||
#define PSCI_POWER_STATE_TYPE_MASK 0x1
|
||||
#define PSCI_POWER_STATE_TYPE_SHIFT 16
|
||||
#define PSCI_POWER_STATE_AFFL_MASK 0x3
|
||||
#define PSCI_POWER_STATE_AFFL_SHIFT 24
|
||||
|
||||
static u32 psci_power_state_pack(struct psci_power_state state)
|
||||
{
|
||||
return ((state.id & PSCI_POWER_STATE_ID_MASK)
|
||||
<< PSCI_POWER_STATE_ID_SHIFT) |
|
||||
((state.type & PSCI_POWER_STATE_TYPE_MASK)
|
||||
<< PSCI_POWER_STATE_TYPE_SHIFT) |
|
||||
((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
|
||||
<< PSCI_POWER_STATE_AFFL_SHIFT);
|
||||
return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_ID_MASK) |
|
||||
((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_TYPE_MASK) |
|
||||
((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_AFFL_MASK);
|
||||
}
|
||||
|
||||
static void psci_power_state_unpack(u32 power_state,
|
||||
struct psci_power_state *state)
|
||||
{
|
||||
state->id = (power_state >> PSCI_POWER_STATE_ID_SHIFT)
|
||||
& PSCI_POWER_STATE_ID_MASK;
|
||||
state->type = (power_state >> PSCI_POWER_STATE_TYPE_SHIFT)
|
||||
& PSCI_POWER_STATE_TYPE_MASK;
|
||||
state->affinity_level = (power_state >> PSCI_POWER_STATE_AFFL_SHIFT)
|
||||
& PSCI_POWER_STATE_AFFL_MASK;
|
||||
state->id = (power_state >> PSCI_0_2_POWER_STATE_ID_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_ID_MASK;
|
||||
state->type = (power_state >> PSCI_0_2_POWER_STATE_TYPE_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_TYPE_MASK;
|
||||
state->affinity_level = (power_state >> PSCI_0_2_POWER_STATE_AFFL_SHIFT)
|
||||
& PSCI_0_2_POWER_STATE_AFFL_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,6 +143,14 @@ static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
|
||||
return function_id;
|
||||
}
|
||||
|
||||
static int psci_get_version(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int psci_cpu_suspend(struct psci_power_state state,
|
||||
unsigned long entry_point)
|
||||
{
|
||||
@@ -187,97 +194,36 @@ static int psci_migrate(unsigned long cpuid)
|
||||
return psci_to_linux_errno(err);
|
||||
}
|
||||
|
||||
static const struct of_device_id psci_of_match[] __initconst = {
|
||||
{ .compatible = "arm,psci", },
|
||||
{},
|
||||
};
|
||||
|
||||
int __init psci_dt_register_idle_states(struct cpuidle_driver *drv,
|
||||
struct device_node *state_nodes[])
|
||||
static int psci_affinity_info(unsigned long target_affinity,
|
||||
unsigned long lowest_affinity_level)
|
||||
{
|
||||
int cpu, i;
|
||||
struct psci_power_state *psci_states;
|
||||
const struct cpu_operations *cpu_ops_ptr;
|
||||
int err;
|
||||
u32 fn;
|
||||
|
||||
if (!state_nodes)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* This is belt-and-braces: make sure that if the idle
|
||||
* specified protocol is psci, the cpu_ops have been
|
||||
* initialized to psci operations. Anything else is
|
||||
* a recipe for mayhem.
|
||||
*/
|
||||
for_each_cpu(cpu, drv->cpumask) {
|
||||
cpu_ops_ptr = cpu_ops[cpu];
|
||||
if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci")))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
psci_states = kcalloc(drv->state_count, sizeof(*psci_states),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!psci_states) {
|
||||
pr_warn("psci idle state allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_cpu(cpu, drv->cpumask) {
|
||||
if (per_cpu(psci_power_state, cpu)) {
|
||||
pr_warn("idle states already initialized on cpu %u\n",
|
||||
cpu);
|
||||
continue;
|
||||
}
|
||||
per_cpu(psci_power_state, cpu) = psci_states;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < drv->state_count; i++) {
|
||||
u32 psci_power_state;
|
||||
|
||||
if (!state_nodes[i]) {
|
||||
/*
|
||||
* An index with a missing node pointer falls back to
|
||||
* simple STANDBYWFI
|
||||
*/
|
||||
psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(state_nodes[i], "entry-method-param",
|
||||
&psci_power_state)) {
|
||||
pr_warn(" * %s missing entry-method-param property\n",
|
||||
state_nodes[i]->full_name);
|
||||
/*
|
||||
* If entry-method-param property is missing, fall
|
||||
* back to STANDBYWFI state
|
||||
*/
|
||||
psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY;
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_debug("psci-power-state %#x index %u\n",
|
||||
psci_power_state, i);
|
||||
psci_power_state_unpack(psci_power_state, &psci_states[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
|
||||
err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
void __init psci_init(void)
|
||||
static int psci_migrate_info_type(void)
|
||||
{
|
||||
int err;
|
||||
u32 fn;
|
||||
|
||||
fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
|
||||
err = invoke_psci_fn(fn, 0, 0, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_set_conduit_method(struct device_node *np)
|
||||
{
|
||||
struct device_node *np;
|
||||
const char *method;
|
||||
u32 id;
|
||||
|
||||
np = of_find_matching_node(NULL, psci_of_match);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
pr_info("probing function IDs from device-tree\n");
|
||||
pr_info("probing for conduit method from DT.\n");
|
||||
|
||||
if (of_property_read_string(np, "method", &method)) {
|
||||
pr_warning("missing \"method\" property\n");
|
||||
goto out_put_node;
|
||||
pr_warn("missing \"method\" property\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (!strcmp("hvc", method)) {
|
||||
@@ -285,9 +231,98 @@ void __init psci_init(void)
|
||||
} else if (!strcmp("smc", method)) {
|
||||
invoke_psci_fn = __invoke_psci_fn_smc;
|
||||
} else {
|
||||
pr_warning("invalid \"method\" property: %s\n", method);
|
||||
goto out_put_node;
|
||||
pr_warn("invalid \"method\" property: %s\n", method);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psci_sys_reset(char str, const char *cmd)
|
||||
{
|
||||
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void psci_sys_poweroff(void)
|
||||
{
|
||||
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* PSCI Function IDs for v0.2+ are well defined so use
|
||||
* standard values.
|
||||
*/
|
||||
static int __init psci_0_2_init(struct device_node *np)
|
||||
{
|
||||
int err, ver;
|
||||
|
||||
err = get_set_conduit_method(np);
|
||||
|
||||
if (err)
|
||||
goto out_put_node;
|
||||
|
||||
ver = psci_get_version();
|
||||
|
||||
if (ver == PSCI_RET_NOT_SUPPORTED) {
|
||||
/* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
|
||||
pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_put_node;
|
||||
} else {
|
||||
pr_info("PSCIv%d.%d detected in firmware.\n",
|
||||
PSCI_VERSION_MAJOR(ver),
|
||||
PSCI_VERSION_MINOR(ver));
|
||||
|
||||
if (PSCI_VERSION_MAJOR(ver) == 0 &&
|
||||
PSCI_VERSION_MINOR(ver) < 2) {
|
||||
err = -EINVAL;
|
||||
pr_err("Conflicting PSCI version detected.\n");
|
||||
goto out_put_node;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("Using standard PSCI v0.2 function IDs\n");
|
||||
psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
|
||||
psci_ops.cpu_suspend = psci_cpu_suspend;
|
||||
|
||||
psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
|
||||
psci_ops.cpu_off = psci_cpu_off;
|
||||
|
||||
psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
|
||||
psci_ops.cpu_on = psci_cpu_on;
|
||||
|
||||
psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
|
||||
psci_ops.migrate = psci_migrate;
|
||||
|
||||
psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
|
||||
psci_ops.affinity_info = psci_affinity_info;
|
||||
|
||||
psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
|
||||
PSCI_0_2_FN_MIGRATE_INFO_TYPE;
|
||||
psci_ops.migrate_info_type = psci_migrate_info_type;
|
||||
|
||||
arm_pm_restart = psci_sys_reset;
|
||||
|
||||
pm_power_off = psci_sys_poweroff;
|
||||
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* PSCI < v0.2 get PSCI Function IDs via DT.
|
||||
*/
|
||||
static int __init psci_0_1_init(struct device_node *np)
|
||||
{
|
||||
u32 id;
|
||||
int err;
|
||||
|
||||
err = get_set_conduit_method(np);
|
||||
|
||||
if (err)
|
||||
goto out_put_node;
|
||||
|
||||
pr_info("Using PSCI v0.1 Function IDs from DT\n");
|
||||
|
||||
if (!of_property_read_u32(np, "cpu_suspend", &id)) {
|
||||
psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
|
||||
@@ -311,7 +346,28 @@ void __init psci_init(void)
|
||||
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
return;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id psci_of_match[] __initconst = {
|
||||
{ .compatible = "arm,psci", .data = psci_0_1_init},
|
||||
{ .compatible = "arm,psci-0.2", .data = psci_0_2_init},
|
||||
{},
|
||||
};
|
||||
|
||||
int __init psci_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
const struct of_device_id *matched_np;
|
||||
psci_initcall_t init_fn;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
init_fn = (psci_initcall_t)matched_np->data;
|
||||
return init_fn(np);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@@ -364,6 +420,36 @@ static void cpu_psci_cpu_die(unsigned int cpu)
|
||||
|
||||
pr_crit("unable to power off CPU%u (%d)\n", cpu, ret);
|
||||
}
|
||||
|
||||
static int cpu_psci_cpu_kill(unsigned int cpu)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
if (!psci_ops.affinity_info)
|
||||
return 1;
|
||||
/*
|
||||
* cpu_kill could race with cpu_die and we can
|
||||
* potentially end up declaring this cpu undead
|
||||
* while it is dying. So, try again a few times.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
|
||||
if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
|
||||
pr_info("CPU%d killed.\n", cpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
pr_info("Retrying again to check for CPU kill\n");
|
||||
}
|
||||
|
||||
pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
|
||||
cpu, err);
|
||||
/* Make op_cpu_kill() fail. */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM64_CPU_SUSPEND
|
||||
@@ -380,16 +466,18 @@ static int cpu_psci_cpu_suspend(unsigned long index)
|
||||
|
||||
const struct cpu_operations cpu_psci_ops = {
|
||||
.name = "psci",
|
||||
#ifdef CONFIG_SMP
|
||||
.cpu_init = cpu_psci_cpu_init,
|
||||
.cpu_prepare = cpu_psci_cpu_prepare,
|
||||
.cpu_boot = cpu_psci_cpu_boot,
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
.cpu_disable = cpu_psci_cpu_disable,
|
||||
.cpu_die = cpu_psci_cpu_die,
|
||||
.cpu_kill = cpu_psci_cpu_kill,
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_CPU_SUSPEND
|
||||
.cpu_suspend = cpu_psci_cpu_suspend,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -659,11 +659,18 @@ static int compat_gpr_get(struct task_struct *target,
|
||||
reg = task_pt_regs(target)->regs[idx];
|
||||
}
|
||||
|
||||
ret = copy_to_user(ubuf, ®, sizeof(reg));
|
||||
if (ret)
|
||||
break;
|
||||
if (kbuf) {
|
||||
memcpy(kbuf, ®, sizeof(reg));
|
||||
kbuf += sizeof(reg);
|
||||
} else {
|
||||
ret = copy_to_user(ubuf, ®, sizeof(reg));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ubuf += sizeof(reg);
|
||||
ubuf += sizeof(reg);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -693,11 +700,18 @@ static int compat_gpr_set(struct task_struct *target,
|
||||
unsigned int idx = start + i;
|
||||
compat_ulong_t reg;
|
||||
|
||||
ret = copy_from_user(®, ubuf, sizeof(reg));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (kbuf) {
|
||||
memcpy(®, kbuf, sizeof(reg));
|
||||
kbuf += sizeof(reg);
|
||||
} else {
|
||||
ret = copy_from_user(®, ubuf, sizeof(reg));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ubuf += sizeof(reg);
|
||||
ubuf += sizeof(reg);
|
||||
}
|
||||
|
||||
switch (idx) {
|
||||
case 15:
|
||||
|
||||
@@ -231,6 +231,19 @@ int __cpu_disable(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int op_cpu_kill(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* If we have no means of synchronising with the dying CPU, then assume
|
||||
* that it is really dead. We can only wait for an arbitrary length of
|
||||
* time and hope that it's dead, so let's skip the wait and just hope.
|
||||
*/
|
||||
if (!cpu_ops[cpu]->cpu_kill)
|
||||
return 1;
|
||||
|
||||
return cpu_ops[cpu]->cpu_kill(cpu);
|
||||
}
|
||||
|
||||
static DECLARE_COMPLETION(cpu_died);
|
||||
|
||||
/*
|
||||
@@ -244,6 +257,15 @@ void __cpu_die(unsigned int cpu)
|
||||
return;
|
||||
}
|
||||
pr_notice("CPU%u: shutdown\n", cpu);
|
||||
|
||||
/*
|
||||
* Now that the dying CPU is beyond the point of no return w.r.t.
|
||||
* in-kernel synchronisation, try to get the firwmare to help us to
|
||||
* verify that it has really left the kernel before we consider
|
||||
* clobbering anything it might still be using.
|
||||
*/
|
||||
if (!op_cpu_kill(cpu))
|
||||
pr_warn("CPU%d may not have shut down cleanly\n", cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -111,10 +111,9 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||
frame.sp = thread_saved_sp(tsk);
|
||||
frame.pc = thread_saved_pc(tsk);
|
||||
} else {
|
||||
register unsigned long current_sp asm("sp");
|
||||
data.no_sched_functions = 0;
|
||||
frame.fp = (unsigned long)__builtin_frame_address(0);
|
||||
frame.sp = current_sp;
|
||||
frame.sp = current_stack_pointer;
|
||||
frame.pc = (unsigned long)save_stack_trace_tsk;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -74,6 +75,8 @@ void __init time_init(void)
|
||||
|
||||
clocksource_of_init();
|
||||
|
||||
tick_setup_hrtimer_broadcast();
|
||||
|
||||
arch_timer_rate = arch_timer_get_rate();
|
||||
if (!arch_timer_rate)
|
||||
panic("Unable to initialise architected timer.\n");
|
||||
|
||||
@@ -133,7 +133,6 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
|
||||
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
|
||||
{
|
||||
struct stackframe frame;
|
||||
const register unsigned long current_sp asm ("sp");
|
||||
|
||||
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
|
||||
|
||||
@@ -146,7 +145,7 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
|
||||
frame.pc = regs->pc;
|
||||
} else if (tsk == current) {
|
||||
frame.fp = (unsigned long)__builtin_frame_address(0);
|
||||
frame.sp = current_sp;
|
||||
frame.sp = current_stack_pointer;
|
||||
frame.pc = (unsigned long)dump_backtrace;
|
||||
} else {
|
||||
/*
|
||||
@@ -157,7 +156,7 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
|
||||
frame.pc = thread_saved_pc(tsk);
|
||||
}
|
||||
|
||||
printk("Call trace:\n");
|
||||
pr_emerg("Call trace:\n");
|
||||
while (1) {
|
||||
unsigned long where = frame.pc;
|
||||
int ret;
|
||||
@@ -372,17 +371,17 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
|
||||
|
||||
void __pte_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pte %016lx.\n", file, line, val);
|
||||
pr_crit("%s:%d: bad pte %016lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
void __pmd_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pmd %016lx.\n", file, line, val);
|
||||
pr_crit("%s:%d: bad pmd %016lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
void __pgd_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pgd %016lx.\n", file, line, val);
|
||||
pr_crit("%s:%d: bad pgd %016lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
void __init trap_init(void)
|
||||
|
||||
@@ -43,7 +43,7 @@ $(obj)/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
|
||||
$(call if_changed,vdsosym)
|
||||
|
||||
# Assembly rules for the .S files
|
||||
$(obj-vdso): %.o: %.S
|
||||
$(obj-vdso): %.o: %.S FORCE
|
||||
$(call if_changed_dep,vdsoas)
|
||||
|
||||
# Actual build commands
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define ARM_EXIT_DISCARD(x) x
|
||||
|
||||
OUTPUT_ARCH(aarch64)
|
||||
ENTRY(stext)
|
||||
ENTRY(_text)
|
||||
|
||||
jiffies = jiffies_64;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
lib-y := bitops.o clear_user.o delay.o copy_from_user.o \
|
||||
copy_to_user.o copy_in_user.o copy_page.o \
|
||||
clear_page.o memchr.o memcpy.o memmove.o memset.o \
|
||||
memcmp.o strcmp.o strncmp.o strlen.o strnlen.o \
|
||||
strchr.o strrchr.o
|
||||
|
||||
258
arch/arm64/lib/memcmp.S
Normal file
258
arch/arm64/lib/memcmp.S
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* compare memory areas(when two memory areas' offset are different,
|
||||
* alignment handled by the hardware)
|
||||
*
|
||||
* Parameters:
|
||||
* x0 - const memory area 1 pointer
|
||||
* x1 - const memory area 2 pointer
|
||||
* x2 - the maximal compare byte length
|
||||
* Returns:
|
||||
* x0 - a compare result, maybe less than, equal to, or greater than ZERO
|
||||
*/
|
||||
|
||||
/* Parameters and result. */
|
||||
src1 .req x0
|
||||
src2 .req x1
|
||||
limit .req x2
|
||||
result .req x0
|
||||
|
||||
/* Internal variables. */
|
||||
data1 .req x3
|
||||
data1w .req w3
|
||||
data2 .req x4
|
||||
data2w .req w4
|
||||
has_nul .req x5
|
||||
diff .req x6
|
||||
endloop .req x7
|
||||
tmp1 .req x8
|
||||
tmp2 .req x9
|
||||
tmp3 .req x10
|
||||
pos .req x11
|
||||
limit_wd .req x12
|
||||
mask .req x13
|
||||
|
||||
ENTRY(memcmp)
|
||||
cbz limit, .Lret0
|
||||
eor tmp1, src1, src2
|
||||
tst tmp1, #7
|
||||
b.ne .Lmisaligned8
|
||||
ands tmp1, src1, #7
|
||||
b.ne .Lmutual_align
|
||||
sub limit_wd, limit, #1 /* limit != 0, so no underflow. */
|
||||
lsr limit_wd, limit_wd, #3 /* Convert to Dwords. */
|
||||
/*
|
||||
* The input source addresses are at alignment boundary.
|
||||
* Directly compare eight bytes each time.
|
||||
*/
|
||||
.Lloop_aligned:
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
.Lstart_realigned:
|
||||
subs limit_wd, limit_wd, #1
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
csinv endloop, diff, xzr, cs /* Last Dword or differences. */
|
||||
cbz endloop, .Lloop_aligned
|
||||
|
||||
/* Not reached the limit, must have found a diff. */
|
||||
tbz limit_wd, #63, .Lnot_limit
|
||||
|
||||
/* Limit % 8 == 0 => the diff is in the last 8 bytes. */
|
||||
ands limit, limit, #7
|
||||
b.eq .Lnot_limit
|
||||
/*
|
||||
* The remained bytes less than 8. It is needed to extract valid data
|
||||
* from last eight bytes of the intended memory range.
|
||||
*/
|
||||
lsl limit, limit, #3 /* bytes-> bits. */
|
||||
mov mask, #~0
|
||||
CPU_BE( lsr mask, mask, limit )
|
||||
CPU_LE( lsl mask, mask, limit )
|
||||
bic data1, data1, mask
|
||||
bic data2, data2, mask
|
||||
|
||||
orr diff, diff, mask
|
||||
b .Lnot_limit
|
||||
|
||||
.Lmutual_align:
|
||||
/*
|
||||
* Sources are mutually aligned, but are not currently at an
|
||||
* alignment boundary. Round down the addresses and then mask off
|
||||
* the bytes that precede the start point.
|
||||
*/
|
||||
bic src1, src1, #7
|
||||
bic src2, src2, #7
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
/*
|
||||
* We can not add limit with alignment offset(tmp1) here. Since the
|
||||
* addition probably make the limit overflown.
|
||||
*/
|
||||
sub limit_wd, limit, #1/*limit != 0, so no underflow.*/
|
||||
and tmp3, limit_wd, #7
|
||||
lsr limit_wd, limit_wd, #3
|
||||
add tmp3, tmp3, tmp1
|
||||
add limit_wd, limit_wd, tmp3, lsr #3
|
||||
add limit, limit, tmp1/* Adjust the limit for the extra. */
|
||||
|
||||
lsl tmp1, tmp1, #3/* Bytes beyond alignment -> bits.*/
|
||||
neg tmp1, tmp1/* Bits to alignment -64. */
|
||||
mov tmp2, #~0
|
||||
/*mask off the non-intended bytes before the start address.*/
|
||||
CPU_BE( lsl tmp2, tmp2, tmp1 )/*Big-endian.Early bytes are at MSB*/
|
||||
/* Little-endian. Early bytes are at LSB. */
|
||||
CPU_LE( lsr tmp2, tmp2, tmp1 )
|
||||
|
||||
orr data1, data1, tmp2
|
||||
orr data2, data2, tmp2
|
||||
b .Lstart_realigned
|
||||
|
||||
/*src1 and src2 have different alignment offset.*/
|
||||
.Lmisaligned8:
|
||||
cmp limit, #8
|
||||
b.lo .Ltiny8proc /*limit < 8: compare byte by byte*/
|
||||
|
||||
and tmp1, src1, #7
|
||||
neg tmp1, tmp1
|
||||
add tmp1, tmp1, #8/*valid length in the first 8 bytes of src1*/
|
||||
and tmp2, src2, #7
|
||||
neg tmp2, tmp2
|
||||
add tmp2, tmp2, #8/*valid length in the first 8 bytes of src2*/
|
||||
subs tmp3, tmp1, tmp2
|
||||
csel pos, tmp1, tmp2, hi /*Choose the maximum.*/
|
||||
|
||||
sub limit, limit, pos
|
||||
/*compare the proceeding bytes in the first 8 byte segment.*/
|
||||
.Ltinycmp:
|
||||
ldrb data1w, [src1], #1
|
||||
ldrb data2w, [src2], #1
|
||||
subs pos, pos, #1
|
||||
ccmp data1w, data2w, #0, ne /* NZCV = 0b0000. */
|
||||
b.eq .Ltinycmp
|
||||
cbnz pos, 1f /*diff occurred before the last byte.*/
|
||||
cmp data1w, data2w
|
||||
b.eq .Lstart_align
|
||||
1:
|
||||
sub result, data1, data2
|
||||
ret
|
||||
|
||||
.Lstart_align:
|
||||
lsr limit_wd, limit, #3
|
||||
cbz limit_wd, .Lremain8
|
||||
|
||||
ands xzr, src1, #7
|
||||
b.eq .Lrecal_offset
|
||||
/*process more leading bytes to make src1 aligned...*/
|
||||
add src1, src1, tmp3 /*backwards src1 to alignment boundary*/
|
||||
add src2, src2, tmp3
|
||||
sub limit, limit, tmp3
|
||||
lsr limit_wd, limit, #3
|
||||
cbz limit_wd, .Lremain8
|
||||
/*load 8 bytes from aligned SRC1..*/
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
|
||||
subs limit_wd, limit_wd, #1
|
||||
eor diff, data1, data2 /*Non-zero if differences found.*/
|
||||
csinv endloop, diff, xzr, ne
|
||||
cbnz endloop, .Lunequal_proc
|
||||
/*How far is the current SRC2 from the alignment boundary...*/
|
||||
and tmp3, tmp3, #7
|
||||
|
||||
.Lrecal_offset:/*src1 is aligned now..*/
|
||||
neg pos, tmp3
|
||||
.Lloopcmp_proc:
|
||||
/*
|
||||
* Divide the eight bytes into two parts. First,backwards the src2
|
||||
* to an alignment boundary,load eight bytes and compare from
|
||||
* the SRC2 alignment boundary. If all 8 bytes are equal,then start
|
||||
* the second part's comparison. Otherwise finish the comparison.
|
||||
* This special handle can garantee all the accesses are in the
|
||||
* thread/task space in avoid to overrange access.
|
||||
*/
|
||||
ldr data1, [src1,pos]
|
||||
ldr data2, [src2,pos]
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
cbnz diff, .Lnot_limit
|
||||
|
||||
/*The second part process*/
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
subs limit_wd, limit_wd, #1
|
||||
csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/
|
||||
cbz endloop, .Lloopcmp_proc
|
||||
.Lunequal_proc:
|
||||
cbz diff, .Lremain8
|
||||
|
||||
/*There is differnence occured in the latest comparison.*/
|
||||
.Lnot_limit:
|
||||
/*
|
||||
* For little endian,reverse the low significant equal bits into MSB,then
|
||||
* following CLZ can find how many equal bits exist.
|
||||
*/
|
||||
CPU_LE( rev diff, diff )
|
||||
CPU_LE( rev data1, data1 )
|
||||
CPU_LE( rev data2, data2 )
|
||||
|
||||
/*
|
||||
* The MS-non-zero bit of DIFF marks either the first bit
|
||||
* that is different, or the end of the significant data.
|
||||
* Shifting left now will bring the critical information into the
|
||||
* top bits.
|
||||
*/
|
||||
clz pos, diff
|
||||
lsl data1, data1, pos
|
||||
lsl data2, data2, pos
|
||||
/*
|
||||
* We need to zero-extend (char is unsigned) the value and then
|
||||
* perform a signed subtraction.
|
||||
*/
|
||||
lsr data1, data1, #56
|
||||
sub result, data1, data2, lsr #56
|
||||
ret
|
||||
|
||||
.Lremain8:
|
||||
/* Limit % 8 == 0 =>. all data are equal.*/
|
||||
ands limit, limit, #7
|
||||
b.eq .Lret0
|
||||
|
||||
.Ltiny8proc:
|
||||
ldrb data1w, [src1], #1
|
||||
ldrb data2w, [src2], #1
|
||||
subs limit, limit, #1
|
||||
|
||||
ccmp data1w, data2w, #0, ne /* NZCV = 0b0000. */
|
||||
b.eq .Ltiny8proc
|
||||
sub result, data1, data2
|
||||
ret
|
||||
.Lret0:
|
||||
mov result, #0
|
||||
ret
|
||||
ENDPROC(memcmp)
|
||||
@@ -1,5 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -16,6 +24,7 @@
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/cache.h>
|
||||
|
||||
/*
|
||||
* Copy a buffer from src to dest (alignment handled by the hardware)
|
||||
@@ -27,27 +36,166 @@
|
||||
* Returns:
|
||||
* x0 - dest
|
||||
*/
|
||||
dstin .req x0
|
||||
src .req x1
|
||||
count .req x2
|
||||
tmp1 .req x3
|
||||
tmp1w .req w3
|
||||
tmp2 .req x4
|
||||
tmp2w .req w4
|
||||
tmp3 .req x5
|
||||
tmp3w .req w5
|
||||
dst .req x6
|
||||
|
||||
A_l .req x7
|
||||
A_h .req x8
|
||||
B_l .req x9
|
||||
B_h .req x10
|
||||
C_l .req x11
|
||||
C_h .req x12
|
||||
D_l .req x13
|
||||
D_h .req x14
|
||||
|
||||
ENTRY(memcpy)
|
||||
mov x4, x0
|
||||
subs x2, x2, #8
|
||||
b.mi 2f
|
||||
1: ldr x3, [x1], #8
|
||||
subs x2, x2, #8
|
||||
str x3, [x4], #8
|
||||
b.pl 1b
|
||||
2: adds x2, x2, #4
|
||||
b.mi 3f
|
||||
ldr w3, [x1], #4
|
||||
sub x2, x2, #4
|
||||
str w3, [x4], #4
|
||||
3: adds x2, x2, #2
|
||||
b.mi 4f
|
||||
ldrh w3, [x1], #2
|
||||
sub x2, x2, #2
|
||||
strh w3, [x4], #2
|
||||
4: adds x2, x2, #1
|
||||
b.mi 5f
|
||||
ldrb w3, [x1]
|
||||
strb w3, [x4]
|
||||
5: ret
|
||||
mov dst, dstin
|
||||
cmp count, #16
|
||||
/*When memory length is less than 16, the accessed are not aligned.*/
|
||||
b.lo .Ltiny15
|
||||
|
||||
neg tmp2, src
|
||||
ands tmp2, tmp2, #15/* Bytes to reach alignment. */
|
||||
b.eq .LSrcAligned
|
||||
sub count, count, tmp2
|
||||
/*
|
||||
* Copy the leading memory data from src to dst in an increasing
|
||||
* address order.By this way,the risk of overwritting the source
|
||||
* memory data is eliminated when the distance between src and
|
||||
* dst is less than 16. The memory accesses here are alignment.
|
||||
*/
|
||||
tbz tmp2, #0, 1f
|
||||
ldrb tmp1w, [src], #1
|
||||
strb tmp1w, [dst], #1
|
||||
1:
|
||||
tbz tmp2, #1, 2f
|
||||
ldrh tmp1w, [src], #2
|
||||
strh tmp1w, [dst], #2
|
||||
2:
|
||||
tbz tmp2, #2, 3f
|
||||
ldr tmp1w, [src], #4
|
||||
str tmp1w, [dst], #4
|
||||
3:
|
||||
tbz tmp2, #3, .LSrcAligned
|
||||
ldr tmp1, [src],#8
|
||||
str tmp1, [dst],#8
|
||||
|
||||
.LSrcAligned:
|
||||
cmp count, #64
|
||||
b.ge .Lcpy_over64
|
||||
/*
|
||||
* Deal with small copies quickly by dropping straight into the
|
||||
* exit block.
|
||||
*/
|
||||
.Ltail63:
|
||||
/*
|
||||
* Copy up to 48 bytes of data. At this point we only need the
|
||||
* bottom 6 bits of count to be accurate.
|
||||
*/
|
||||
ands tmp1, count, #0x30
|
||||
b.eq .Ltiny15
|
||||
cmp tmp1w, #0x20
|
||||
b.eq 1f
|
||||
b.lt 2f
|
||||
ldp A_l, A_h, [src], #16
|
||||
stp A_l, A_h, [dst], #16
|
||||
1:
|
||||
ldp A_l, A_h, [src], #16
|
||||
stp A_l, A_h, [dst], #16
|
||||
2:
|
||||
ldp A_l, A_h, [src], #16
|
||||
stp A_l, A_h, [dst], #16
|
||||
.Ltiny15:
|
||||
/*
|
||||
* Prefer to break one ldp/stp into several load/store to access
|
||||
* memory in an increasing address order,rather than to load/store 16
|
||||
* bytes from (src-16) to (dst-16) and to backward the src to aligned
|
||||
* address,which way is used in original cortex memcpy. If keeping
|
||||
* the original memcpy process here, memmove need to satisfy the
|
||||
* precondition that src address is at least 16 bytes bigger than dst
|
||||
* address,otherwise some source data will be overwritten when memove
|
||||
* call memcpy directly. To make memmove simpler and decouple the
|
||||
* memcpy's dependency on memmove, withdrew the original process.
|
||||
*/
|
||||
tbz count, #3, 1f
|
||||
ldr tmp1, [src], #8
|
||||
str tmp1, [dst], #8
|
||||
1:
|
||||
tbz count, #2, 2f
|
||||
ldr tmp1w, [src], #4
|
||||
str tmp1w, [dst], #4
|
||||
2:
|
||||
tbz count, #1, 3f
|
||||
ldrh tmp1w, [src], #2
|
||||
strh tmp1w, [dst], #2
|
||||
3:
|
||||
tbz count, #0, .Lexitfunc
|
||||
ldrb tmp1w, [src]
|
||||
strb tmp1w, [dst]
|
||||
|
||||
.Lexitfunc:
|
||||
ret
|
||||
|
||||
.Lcpy_over64:
|
||||
subs count, count, #128
|
||||
b.ge .Lcpy_body_large
|
||||
/*
|
||||
* Less than 128 bytes to copy, so handle 64 here and then jump
|
||||
* to the tail.
|
||||
*/
|
||||
ldp A_l, A_h, [src],#16
|
||||
stp A_l, A_h, [dst],#16
|
||||
ldp B_l, B_h, [src],#16
|
||||
ldp C_l, C_h, [src],#16
|
||||
stp B_l, B_h, [dst],#16
|
||||
stp C_l, C_h, [dst],#16
|
||||
ldp D_l, D_h, [src],#16
|
||||
stp D_l, D_h, [dst],#16
|
||||
|
||||
tst count, #0x3f
|
||||
b.ne .Ltail63
|
||||
ret
|
||||
|
||||
/*
|
||||
* Critical loop. Start at a new cache line boundary. Assuming
|
||||
* 64 bytes per line this ensures the entire loop is in one line.
|
||||
*/
|
||||
.p2align L1_CACHE_SHIFT
|
||||
.Lcpy_body_large:
|
||||
/* pre-get 64 bytes data. */
|
||||
ldp A_l, A_h, [src],#16
|
||||
ldp B_l, B_h, [src],#16
|
||||
ldp C_l, C_h, [src],#16
|
||||
ldp D_l, D_h, [src],#16
|
||||
1:
|
||||
/*
|
||||
* interlace the load of next 64 bytes data block with store of the last
|
||||
* loaded 64 bytes data.
|
||||
*/
|
||||
stp A_l, A_h, [dst],#16
|
||||
ldp A_l, A_h, [src],#16
|
||||
stp B_l, B_h, [dst],#16
|
||||
ldp B_l, B_h, [src],#16
|
||||
stp C_l, C_h, [dst],#16
|
||||
ldp C_l, C_h, [src],#16
|
||||
stp D_l, D_h, [dst],#16
|
||||
ldp D_l, D_h, [src],#16
|
||||
subs count, count, #64
|
||||
b.ge 1b
|
||||
stp A_l, A_h, [dst],#16
|
||||
stp B_l, B_h, [dst],#16
|
||||
stp C_l, C_h, [dst],#16
|
||||
stp D_l, D_h, [dst],#16
|
||||
|
||||
tst count, #0x3f
|
||||
b.ne .Ltail63
|
||||
ret
|
||||
ENDPROC(memcpy)
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -16,6 +24,7 @@
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/cache.h>
|
||||
|
||||
/*
|
||||
* Move a buffer from src to test (alignment handled by the hardware).
|
||||
@@ -28,30 +37,161 @@
|
||||
* Returns:
|
||||
* x0 - dest
|
||||
*/
|
||||
dstin .req x0
|
||||
src .req x1
|
||||
count .req x2
|
||||
tmp1 .req x3
|
||||
tmp1w .req w3
|
||||
tmp2 .req x4
|
||||
tmp2w .req w4
|
||||
tmp3 .req x5
|
||||
tmp3w .req w5
|
||||
dst .req x6
|
||||
|
||||
A_l .req x7
|
||||
A_h .req x8
|
||||
B_l .req x9
|
||||
B_h .req x10
|
||||
C_l .req x11
|
||||
C_h .req x12
|
||||
D_l .req x13
|
||||
D_h .req x14
|
||||
|
||||
ENTRY(memmove)
|
||||
cmp x0, x1
|
||||
b.ls memcpy
|
||||
add x4, x0, x2
|
||||
add x1, x1, x2
|
||||
subs x2, x2, #8
|
||||
b.mi 2f
|
||||
1: ldr x3, [x1, #-8]!
|
||||
subs x2, x2, #8
|
||||
str x3, [x4, #-8]!
|
||||
b.pl 1b
|
||||
2: adds x2, x2, #4
|
||||
b.mi 3f
|
||||
ldr w3, [x1, #-4]!
|
||||
sub x2, x2, #4
|
||||
str w3, [x4, #-4]!
|
||||
3: adds x2, x2, #2
|
||||
b.mi 4f
|
||||
ldrh w3, [x1, #-2]!
|
||||
sub x2, x2, #2
|
||||
strh w3, [x4, #-2]!
|
||||
4: adds x2, x2, #1
|
||||
b.mi 5f
|
||||
ldrb w3, [x1, #-1]
|
||||
strb w3, [x4, #-1]
|
||||
5: ret
|
||||
cmp dstin, src
|
||||
b.lo memcpy
|
||||
add tmp1, src, count
|
||||
cmp dstin, tmp1
|
||||
b.hs memcpy /* No overlap. */
|
||||
|
||||
add dst, dstin, count
|
||||
add src, src, count
|
||||
cmp count, #16
|
||||
b.lo .Ltail15 /*probably non-alignment accesses.*/
|
||||
|
||||
ands tmp2, src, #15 /* Bytes to reach alignment. */
|
||||
b.eq .LSrcAligned
|
||||
sub count, count, tmp2
|
||||
/*
|
||||
* process the aligned offset length to make the src aligned firstly.
|
||||
* those extra instructions' cost is acceptable. It also make the
|
||||
* coming accesses are based on aligned address.
|
||||
*/
|
||||
tbz tmp2, #0, 1f
|
||||
ldrb tmp1w, [src, #-1]!
|
||||
strb tmp1w, [dst, #-1]!
|
||||
1:
|
||||
tbz tmp2, #1, 2f
|
||||
ldrh tmp1w, [src, #-2]!
|
||||
strh tmp1w, [dst, #-2]!
|
||||
2:
|
||||
tbz tmp2, #2, 3f
|
||||
ldr tmp1w, [src, #-4]!
|
||||
str tmp1w, [dst, #-4]!
|
||||
3:
|
||||
tbz tmp2, #3, .LSrcAligned
|
||||
ldr tmp1, [src, #-8]!
|
||||
str tmp1, [dst, #-8]!
|
||||
|
||||
.LSrcAligned:
|
||||
cmp count, #64
|
||||
b.ge .Lcpy_over64
|
||||
|
||||
/*
|
||||
* Deal with small copies quickly by dropping straight into the
|
||||
* exit block.
|
||||
*/
|
||||
.Ltail63:
|
||||
/*
|
||||
* Copy up to 48 bytes of data. At this point we only need the
|
||||
* bottom 6 bits of count to be accurate.
|
||||
*/
|
||||
ands tmp1, count, #0x30
|
||||
b.eq .Ltail15
|
||||
cmp tmp1w, #0x20
|
||||
b.eq 1f
|
||||
b.lt 2f
|
||||
ldp A_l, A_h, [src, #-16]!
|
||||
stp A_l, A_h, [dst, #-16]!
|
||||
1:
|
||||
ldp A_l, A_h, [src, #-16]!
|
||||
stp A_l, A_h, [dst, #-16]!
|
||||
2:
|
||||
ldp A_l, A_h, [src, #-16]!
|
||||
stp A_l, A_h, [dst, #-16]!
|
||||
|
||||
.Ltail15:
|
||||
tbz count, #3, 1f
|
||||
ldr tmp1, [src, #-8]!
|
||||
str tmp1, [dst, #-8]!
|
||||
1:
|
||||
tbz count, #2, 2f
|
||||
ldr tmp1w, [src, #-4]!
|
||||
str tmp1w, [dst, #-4]!
|
||||
2:
|
||||
tbz count, #1, 3f
|
||||
ldrh tmp1w, [src, #-2]!
|
||||
strh tmp1w, [dst, #-2]!
|
||||
3:
|
||||
tbz count, #0, .Lexitfunc
|
||||
ldrb tmp1w, [src, #-1]
|
||||
strb tmp1w, [dst, #-1]
|
||||
|
||||
.Lexitfunc:
|
||||
ret
|
||||
|
||||
.Lcpy_over64:
|
||||
subs count, count, #128
|
||||
b.ge .Lcpy_body_large
|
||||
/*
|
||||
* Less than 128 bytes to copy, so handle 64 bytes here and then jump
|
||||
* to the tail.
|
||||
*/
|
||||
ldp A_l, A_h, [src, #-16]
|
||||
stp A_l, A_h, [dst, #-16]
|
||||
ldp B_l, B_h, [src, #-32]
|
||||
ldp C_l, C_h, [src, #-48]
|
||||
stp B_l, B_h, [dst, #-32]
|
||||
stp C_l, C_h, [dst, #-48]
|
||||
ldp D_l, D_h, [src, #-64]!
|
||||
stp D_l, D_h, [dst, #-64]!
|
||||
|
||||
tst count, #0x3f
|
||||
b.ne .Ltail63
|
||||
ret
|
||||
|
||||
/*
|
||||
* Critical loop. Start at a new cache line boundary. Assuming
|
||||
* 64 bytes per line this ensures the entire loop is in one line.
|
||||
*/
|
||||
.p2align L1_CACHE_SHIFT
|
||||
.Lcpy_body_large:
|
||||
/* pre-load 64 bytes data. */
|
||||
ldp A_l, A_h, [src, #-16]
|
||||
ldp B_l, B_h, [src, #-32]
|
||||
ldp C_l, C_h, [src, #-48]
|
||||
ldp D_l, D_h, [src, #-64]!
|
||||
1:
|
||||
/*
|
||||
* interlace the load of next 64 bytes data block with store of the last
|
||||
* loaded 64 bytes data.
|
||||
*/
|
||||
stp A_l, A_h, [dst, #-16]
|
||||
ldp A_l, A_h, [src, #-16]
|
||||
stp B_l, B_h, [dst, #-32]
|
||||
ldp B_l, B_h, [src, #-32]
|
||||
stp C_l, C_h, [dst, #-48]
|
||||
ldp C_l, C_h, [src, #-48]
|
||||
stp D_l, D_h, [dst, #-64]!
|
||||
ldp D_l, D_h, [src, #-64]!
|
||||
subs count, count, #64
|
||||
b.ge 1b
|
||||
stp A_l, A_h, [dst, #-16]
|
||||
stp B_l, B_h, [dst, #-32]
|
||||
stp C_l, C_h, [dst, #-48]
|
||||
stp D_l, D_h, [dst, #-64]!
|
||||
|
||||
tst count, #0x3f
|
||||
b.ne .Ltail63
|
||||
ret
|
||||
ENDPROC(memmove)
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -16,6 +24,7 @@
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/cache.h>
|
||||
|
||||
/*
|
||||
* Fill in the buffer with character c (alignment handled by the hardware)
|
||||
@@ -27,27 +36,181 @@
|
||||
* Returns:
|
||||
* x0 - buf
|
||||
*/
|
||||
|
||||
dstin .req x0
|
||||
val .req w1
|
||||
count .req x2
|
||||
tmp1 .req x3
|
||||
tmp1w .req w3
|
||||
tmp2 .req x4
|
||||
tmp2w .req w4
|
||||
zva_len_x .req x5
|
||||
zva_len .req w5
|
||||
zva_bits_x .req x6
|
||||
|
||||
A_l .req x7
|
||||
A_lw .req w7
|
||||
dst .req x8
|
||||
tmp3w .req w9
|
||||
tmp3 .req x9
|
||||
|
||||
ENTRY(memset)
|
||||
mov x4, x0
|
||||
and w1, w1, #0xff
|
||||
orr w1, w1, w1, lsl #8
|
||||
orr w1, w1, w1, lsl #16
|
||||
orr x1, x1, x1, lsl #32
|
||||
subs x2, x2, #8
|
||||
b.mi 2f
|
||||
1: str x1, [x4], #8
|
||||
subs x2, x2, #8
|
||||
b.pl 1b
|
||||
2: adds x2, x2, #4
|
||||
b.mi 3f
|
||||
sub x2, x2, #4
|
||||
str w1, [x4], #4
|
||||
3: adds x2, x2, #2
|
||||
b.mi 4f
|
||||
sub x2, x2, #2
|
||||
strh w1, [x4], #2
|
||||
4: adds x2, x2, #1
|
||||
b.mi 5f
|
||||
strb w1, [x4]
|
||||
5: ret
|
||||
mov dst, dstin /* Preserve return value. */
|
||||
and A_lw, val, #255
|
||||
orr A_lw, A_lw, A_lw, lsl #8
|
||||
orr A_lw, A_lw, A_lw, lsl #16
|
||||
orr A_l, A_l, A_l, lsl #32
|
||||
|
||||
cmp count, #15
|
||||
b.hi .Lover16_proc
|
||||
/*All store maybe are non-aligned..*/
|
||||
tbz count, #3, 1f
|
||||
str A_l, [dst], #8
|
||||
1:
|
||||
tbz count, #2, 2f
|
||||
str A_lw, [dst], #4
|
||||
2:
|
||||
tbz count, #1, 3f
|
||||
strh A_lw, [dst], #2
|
||||
3:
|
||||
tbz count, #0, 4f
|
||||
strb A_lw, [dst]
|
||||
4:
|
||||
ret
|
||||
|
||||
.Lover16_proc:
|
||||
/*Whether the start address is aligned with 16.*/
|
||||
neg tmp2, dst
|
||||
ands tmp2, tmp2, #15
|
||||
b.eq .Laligned
|
||||
/*
|
||||
* The count is not less than 16, we can use stp to store the start 16 bytes,
|
||||
* then adjust the dst aligned with 16.This process will make the current
|
||||
* memory address at alignment boundary.
|
||||
*/
|
||||
stp A_l, A_l, [dst] /*non-aligned store..*/
|
||||
/*make the dst aligned..*/
|
||||
sub count, count, tmp2
|
||||
add dst, dst, tmp2
|
||||
|
||||
.Laligned:
|
||||
cbz A_l, .Lzero_mem
|
||||
|
||||
.Ltail_maybe_long:
|
||||
cmp count, #64
|
||||
b.ge .Lnot_short
|
||||
.Ltail63:
|
||||
ands tmp1, count, #0x30
|
||||
b.eq 3f
|
||||
cmp tmp1w, #0x20
|
||||
b.eq 1f
|
||||
b.lt 2f
|
||||
stp A_l, A_l, [dst], #16
|
||||
1:
|
||||
stp A_l, A_l, [dst], #16
|
||||
2:
|
||||
stp A_l, A_l, [dst], #16
|
||||
/*
|
||||
* The last store length is less than 16,use stp to write last 16 bytes.
|
||||
* It will lead some bytes written twice and the access is non-aligned.
|
||||
*/
|
||||
3:
|
||||
ands count, count, #15
|
||||
cbz count, 4f
|
||||
add dst, dst, count
|
||||
stp A_l, A_l, [dst, #-16] /* Repeat some/all of last store. */
|
||||
4:
|
||||
ret
|
||||
|
||||
/*
|
||||
* Critical loop. Start at a new cache line boundary. Assuming
|
||||
* 64 bytes per line, this ensures the entire loop is in one line.
|
||||
*/
|
||||
.p2align L1_CACHE_SHIFT
|
||||
.Lnot_short:
|
||||
sub dst, dst, #16/* Pre-bias. */
|
||||
sub count, count, #64
|
||||
1:
|
||||
stp A_l, A_l, [dst, #16]
|
||||
stp A_l, A_l, [dst, #32]
|
||||
stp A_l, A_l, [dst, #48]
|
||||
stp A_l, A_l, [dst, #64]!
|
||||
subs count, count, #64
|
||||
b.ge 1b
|
||||
tst count, #0x3f
|
||||
add dst, dst, #16
|
||||
b.ne .Ltail63
|
||||
.Lexitfunc:
|
||||
ret
|
||||
|
||||
/*
|
||||
* For zeroing memory, check to see if we can use the ZVA feature to
|
||||
* zero entire 'cache' lines.
|
||||
*/
|
||||
.Lzero_mem:
|
||||
cmp count, #63
|
||||
b.le .Ltail63
|
||||
/*
|
||||
* For zeroing small amounts of memory, it's not worth setting up
|
||||
* the line-clear code.
|
||||
*/
|
||||
cmp count, #128
|
||||
b.lt .Lnot_short /*count is at least 128 bytes*/
|
||||
|
||||
mrs tmp1, dczid_el0
|
||||
tbnz tmp1, #4, .Lnot_short
|
||||
mov tmp3w, #4
|
||||
and zva_len, tmp1w, #15 /* Safety: other bits reserved. */
|
||||
lsl zva_len, tmp3w, zva_len
|
||||
|
||||
ands tmp3w, zva_len, #63
|
||||
/*
|
||||
* ensure the zva_len is not less than 64.
|
||||
* It is not meaningful to use ZVA if the block size is less than 64.
|
||||
*/
|
||||
b.ne .Lnot_short
|
||||
.Lzero_by_line:
|
||||
/*
|
||||
* Compute how far we need to go to become suitably aligned. We're
|
||||
* already at quad-word alignment.
|
||||
*/
|
||||
cmp count, zva_len_x
|
||||
b.lt .Lnot_short /* Not enough to reach alignment. */
|
||||
sub zva_bits_x, zva_len_x, #1
|
||||
neg tmp2, dst
|
||||
ands tmp2, tmp2, zva_bits_x
|
||||
b.eq 2f /* Already aligned. */
|
||||
/* Not aligned, check that there's enough to copy after alignment.*/
|
||||
sub tmp1, count, tmp2
|
||||
/*
|
||||
* grantee the remain length to be ZVA is bigger than 64,
|
||||
* avoid to make the 2f's process over mem range.*/
|
||||
cmp tmp1, #64
|
||||
ccmp tmp1, zva_len_x, #8, ge /* NZCV=0b1000 */
|
||||
b.lt .Lnot_short
|
||||
/*
|
||||
* We know that there's at least 64 bytes to zero and that it's safe
|
||||
* to overrun by 64 bytes.
|
||||
*/
|
||||
mov count, tmp1
|
||||
1:
|
||||
stp A_l, A_l, [dst]
|
||||
stp A_l, A_l, [dst, #16]
|
||||
stp A_l, A_l, [dst, #32]
|
||||
subs tmp2, tmp2, #64
|
||||
stp A_l, A_l, [dst, #48]
|
||||
add dst, dst, #64
|
||||
b.ge 1b
|
||||
/* We've overrun a bit, so adjust dst downwards.*/
|
||||
add dst, dst, tmp2
|
||||
2:
|
||||
sub count, count, zva_len_x
|
||||
3:
|
||||
dc zva, dst
|
||||
add dst, dst, zva_len_x
|
||||
subs count, count, zva_len_x
|
||||
b.ge 3b
|
||||
ands count, count, zva_bits_x
|
||||
b.ne .Ltail_maybe_long
|
||||
ret
|
||||
ENDPROC(memset)
|
||||
|
||||
234
arch/arm64/lib/strcmp.S
Normal file
234
arch/arm64/lib/strcmp.S
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* compare two strings
|
||||
*
|
||||
* Parameters:
|
||||
* x0 - const string 1 pointer
|
||||
* x1 - const string 2 pointer
|
||||
* Returns:
|
||||
* x0 - an integer less than, equal to, or greater than zero
|
||||
* if s1 is found, respectively, to be less than, to match,
|
||||
* or be greater than s2.
|
||||
*/
|
||||
|
||||
#define REP8_01 0x0101010101010101
|
||||
#define REP8_7f 0x7f7f7f7f7f7f7f7f
|
||||
#define REP8_80 0x8080808080808080
|
||||
|
||||
/* Parameters and result. */
|
||||
src1 .req x0
|
||||
src2 .req x1
|
||||
result .req x0
|
||||
|
||||
/* Internal variables. */
|
||||
data1 .req x2
|
||||
data1w .req w2
|
||||
data2 .req x3
|
||||
data2w .req w3
|
||||
has_nul .req x4
|
||||
diff .req x5
|
||||
syndrome .req x6
|
||||
tmp1 .req x7
|
||||
tmp2 .req x8
|
||||
tmp3 .req x9
|
||||
zeroones .req x10
|
||||
pos .req x11
|
||||
|
||||
ENTRY(strcmp)
|
||||
eor tmp1, src1, src2
|
||||
mov zeroones, #REP8_01
|
||||
tst tmp1, #7
|
||||
b.ne .Lmisaligned8
|
||||
ands tmp1, src1, #7
|
||||
b.ne .Lmutual_align
|
||||
|
||||
/*
|
||||
* NUL detection works on the principle that (X - 1) & (~X) & 0x80
|
||||
* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
|
||||
* can be done in parallel across the entire word.
|
||||
*/
|
||||
.Lloop_aligned:
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
.Lstart_realigned:
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
bic has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
|
||||
orr syndrome, diff, has_nul
|
||||
cbz syndrome, .Lloop_aligned
|
||||
b .Lcal_cmpresult
|
||||
|
||||
.Lmutual_align:
|
||||
/*
|
||||
* Sources are mutually aligned, but are not currently at an
|
||||
* alignment boundary. Round down the addresses and then mask off
|
||||
* the bytes that preceed the start point.
|
||||
*/
|
||||
bic src1, src1, #7
|
||||
bic src2, src2, #7
|
||||
lsl tmp1, tmp1, #3 /* Bytes beyond alignment -> bits. */
|
||||
ldr data1, [src1], #8
|
||||
neg tmp1, tmp1 /* Bits to alignment -64. */
|
||||
ldr data2, [src2], #8
|
||||
mov tmp2, #~0
|
||||
/* Big-endian. Early bytes are at MSB. */
|
||||
CPU_BE( lsl tmp2, tmp2, tmp1 ) /* Shift (tmp1 & 63). */
|
||||
/* Little-endian. Early bytes are at LSB. */
|
||||
CPU_LE( lsr tmp2, tmp2, tmp1 ) /* Shift (tmp1 & 63). */
|
||||
|
||||
orr data1, data1, tmp2
|
||||
orr data2, data2, tmp2
|
||||
b .Lstart_realigned
|
||||
|
||||
.Lmisaligned8:
|
||||
/*
|
||||
* Get the align offset length to compare per byte first.
|
||||
* After this process, one string's address will be aligned.
|
||||
*/
|
||||
and tmp1, src1, #7
|
||||
neg tmp1, tmp1
|
||||
add tmp1, tmp1, #8
|
||||
and tmp2, src2, #7
|
||||
neg tmp2, tmp2
|
||||
add tmp2, tmp2, #8
|
||||
subs tmp3, tmp1, tmp2
|
||||
csel pos, tmp1, tmp2, hi /*Choose the maximum. */
|
||||
.Ltinycmp:
|
||||
ldrb data1w, [src1], #1
|
||||
ldrb data2w, [src2], #1
|
||||
subs pos, pos, #1
|
||||
ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */
|
||||
ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
|
||||
b.eq .Ltinycmp
|
||||
cbnz pos, 1f /*find the null or unequal...*/
|
||||
cmp data1w, #1
|
||||
ccmp data1w, data2w, #0, cs
|
||||
b.eq .Lstart_align /*the last bytes are equal....*/
|
||||
1:
|
||||
sub result, data1, data2
|
||||
ret
|
||||
|
||||
.Lstart_align:
|
||||
ands xzr, src1, #7
|
||||
b.eq .Lrecal_offset
|
||||
/*process more leading bytes to make str1 aligned...*/
|
||||
add src1, src1, tmp3
|
||||
add src2, src2, tmp3
|
||||
/*load 8 bytes from aligned str1 and non-aligned str2..*/
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
bic has_nul, tmp1, tmp2
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
orr syndrome, diff, has_nul
|
||||
cbnz syndrome, .Lcal_cmpresult
|
||||
/*How far is the current str2 from the alignment boundary...*/
|
||||
and tmp3, tmp3, #7
|
||||
.Lrecal_offset:
|
||||
neg pos, tmp3
|
||||
.Lloopcmp_proc:
|
||||
/*
|
||||
* Divide the eight bytes into two parts. First,backwards the src2
|
||||
* to an alignment boundary,load eight bytes from the SRC2 alignment
|
||||
* boundary,then compare with the relative bytes from SRC1.
|
||||
* If all 8 bytes are equal,then start the second part's comparison.
|
||||
* Otherwise finish the comparison.
|
||||
* This special handle can garantee all the accesses are in the
|
||||
* thread/task space in avoid to overrange access.
|
||||
*/
|
||||
ldr data1, [src1,pos]
|
||||
ldr data2, [src2,pos]
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
bic has_nul, tmp1, tmp2
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
orr syndrome, diff, has_nul
|
||||
cbnz syndrome, .Lcal_cmpresult
|
||||
|
||||
/*The second part process*/
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
bic has_nul, tmp1, tmp2
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
orr syndrome, diff, has_nul
|
||||
cbz syndrome, .Lloopcmp_proc
|
||||
|
||||
.Lcal_cmpresult:
|
||||
/*
|
||||
* reversed the byte-order as big-endian,then CLZ can find the most
|
||||
* significant zero bits.
|
||||
*/
|
||||
CPU_LE( rev syndrome, syndrome )
|
||||
CPU_LE( rev data1, data1 )
|
||||
CPU_LE( rev data2, data2 )
|
||||
|
||||
/*
|
||||
* For big-endian we cannot use the trick with the syndrome value
|
||||
* as carry-propagation can corrupt the upper bits if the trailing
|
||||
* bytes in the string contain 0x01.
|
||||
* However, if there is no NUL byte in the dword, we can generate
|
||||
* the result directly. We ca not just subtract the bytes as the
|
||||
* MSB might be significant.
|
||||
*/
|
||||
CPU_BE( cbnz has_nul, 1f )
|
||||
CPU_BE( cmp data1, data2 )
|
||||
CPU_BE( cset result, ne )
|
||||
CPU_BE( cneg result, result, lo )
|
||||
CPU_BE( ret )
|
||||
CPU_BE( 1: )
|
||||
/*Re-compute the NUL-byte detection, using a byte-reversed value. */
|
||||
CPU_BE( rev tmp3, data1 )
|
||||
CPU_BE( sub tmp1, tmp3, zeroones )
|
||||
CPU_BE( orr tmp2, tmp3, #REP8_7f )
|
||||
CPU_BE( bic has_nul, tmp1, tmp2 )
|
||||
CPU_BE( rev has_nul, has_nul )
|
||||
CPU_BE( orr syndrome, diff, has_nul )
|
||||
|
||||
clz pos, syndrome
|
||||
/*
|
||||
* The MS-non-zero bit of the syndrome marks either the first bit
|
||||
* that is different, or the top bit of the first zero byte.
|
||||
* Shifting left now will bring the critical information into the
|
||||
* top bits.
|
||||
*/
|
||||
lsl data1, data1, pos
|
||||
lsl data2, data2, pos
|
||||
/*
|
||||
* But we need to zero-extend (char is unsigned) the value and then
|
||||
* perform a signed 32-bit subtraction.
|
||||
*/
|
||||
lsr data1, data1, #56
|
||||
sub result, data1, data2, lsr #56
|
||||
ret
|
||||
ENDPROC(strcmp)
|
||||
126
arch/arm64/lib/strlen.S
Normal file
126
arch/arm64/lib/strlen.S
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* calculate the length of a string
|
||||
*
|
||||
* Parameters:
|
||||
* x0 - const string pointer
|
||||
* Returns:
|
||||
* x0 - the return length of specific string
|
||||
*/
|
||||
|
||||
/* Arguments and results. */
|
||||
srcin .req x0
|
||||
len .req x0
|
||||
|
||||
/* Locals and temporaries. */
|
||||
src .req x1
|
||||
data1 .req x2
|
||||
data2 .req x3
|
||||
data2a .req x4
|
||||
has_nul1 .req x5
|
||||
has_nul2 .req x6
|
||||
tmp1 .req x7
|
||||
tmp2 .req x8
|
||||
tmp3 .req x9
|
||||
tmp4 .req x10
|
||||
zeroones .req x11
|
||||
pos .req x12
|
||||
|
||||
#define REP8_01 0x0101010101010101
|
||||
#define REP8_7f 0x7f7f7f7f7f7f7f7f
|
||||
#define REP8_80 0x8080808080808080
|
||||
|
||||
ENTRY(strlen)
|
||||
mov zeroones, #REP8_01
|
||||
bic src, srcin, #15
|
||||
ands tmp1, srcin, #15
|
||||
b.ne .Lmisaligned
|
||||
/*
|
||||
* NUL detection works on the principle that (X - 1) & (~X) & 0x80
|
||||
* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
|
||||
* can be done in parallel across the entire word.
|
||||
*/
|
||||
/*
|
||||
* The inner loop deals with two Dwords at a time. This has a
|
||||
* slightly higher start-up cost, but we should win quite quickly,
|
||||
* especially on cores with a high number of issue slots per
|
||||
* cycle, as we get much better parallelism out of the operations.
|
||||
*/
|
||||
.Lloop:
|
||||
ldp data1, data2, [src], #16
|
||||
.Lrealigned:
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
sub tmp3, data2, zeroones
|
||||
orr tmp4, data2, #REP8_7f
|
||||
bic has_nul1, tmp1, tmp2
|
||||
bics has_nul2, tmp3, tmp4
|
||||
ccmp has_nul1, #0, #0, eq /* NZCV = 0000 */
|
||||
b.eq .Lloop
|
||||
|
||||
sub len, src, srcin
|
||||
cbz has_nul1, .Lnul_in_data2
|
||||
CPU_BE( mov data2, data1 ) /*prepare data to re-calculate the syndrome*/
|
||||
sub len, len, #8
|
||||
mov has_nul2, has_nul1
|
||||
.Lnul_in_data2:
|
||||
/*
|
||||
* For big-endian, carry propagation (if the final byte in the
|
||||
* string is 0x01) means we cannot use has_nul directly. The
|
||||
* easiest way to get the correct byte is to byte-swap the data
|
||||
* and calculate the syndrome a second time.
|
||||
*/
|
||||
CPU_BE( rev data2, data2 )
|
||||
CPU_BE( sub tmp1, data2, zeroones )
|
||||
CPU_BE( orr tmp2, data2, #REP8_7f )
|
||||
CPU_BE( bic has_nul2, tmp1, tmp2 )
|
||||
|
||||
sub len, len, #8
|
||||
rev has_nul2, has_nul2
|
||||
clz pos, has_nul2
|
||||
add len, len, pos, lsr #3 /* Bits to bytes. */
|
||||
ret
|
||||
|
||||
.Lmisaligned:
|
||||
cmp tmp1, #8
|
||||
neg tmp1, tmp1
|
||||
ldp data1, data2, [src], #16
|
||||
lsl tmp1, tmp1, #3 /* Bytes beyond alignment -> bits. */
|
||||
mov tmp2, #~0
|
||||
/* Big-endian. Early bytes are at MSB. */
|
||||
CPU_BE( lsl tmp2, tmp2, tmp1 ) /* Shift (tmp1 & 63). */
|
||||
/* Little-endian. Early bytes are at LSB. */
|
||||
CPU_LE( lsr tmp2, tmp2, tmp1 ) /* Shift (tmp1 & 63). */
|
||||
|
||||
orr data1, data1, tmp2
|
||||
orr data2a, data2, tmp2
|
||||
csinv data1, data1, xzr, le
|
||||
csel data2, data2, data2a, le
|
||||
b .Lrealigned
|
||||
ENDPROC(strlen)
|
||||
310
arch/arm64/lib/strncmp.S
Normal file
310
arch/arm64/lib/strncmp.S
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* compare two strings
|
||||
*
|
||||
* Parameters:
|
||||
* x0 - const string 1 pointer
|
||||
* x1 - const string 2 pointer
|
||||
* x2 - the maximal length to be compared
|
||||
* Returns:
|
||||
* x0 - an integer less than, equal to, or greater than zero if s1 is found,
|
||||
* respectively, to be less than, to match, or be greater than s2.
|
||||
*/
|
||||
|
||||
#define REP8_01 0x0101010101010101
|
||||
#define REP8_7f 0x7f7f7f7f7f7f7f7f
|
||||
#define REP8_80 0x8080808080808080
|
||||
|
||||
/* Parameters and result. */
|
||||
src1 .req x0
|
||||
src2 .req x1
|
||||
limit .req x2
|
||||
result .req x0
|
||||
|
||||
/* Internal variables. */
|
||||
data1 .req x3
|
||||
data1w .req w3
|
||||
data2 .req x4
|
||||
data2w .req w4
|
||||
has_nul .req x5
|
||||
diff .req x6
|
||||
syndrome .req x7
|
||||
tmp1 .req x8
|
||||
tmp2 .req x9
|
||||
tmp3 .req x10
|
||||
zeroones .req x11
|
||||
pos .req x12
|
||||
limit_wd .req x13
|
||||
mask .req x14
|
||||
endloop .req x15
|
||||
|
||||
ENTRY(strncmp)
|
||||
cbz limit, .Lret0
|
||||
eor tmp1, src1, src2
|
||||
mov zeroones, #REP8_01
|
||||
tst tmp1, #7
|
||||
b.ne .Lmisaligned8
|
||||
ands tmp1, src1, #7
|
||||
b.ne .Lmutual_align
|
||||
/* Calculate the number of full and partial words -1. */
|
||||
/*
|
||||
* when limit is mulitply of 8, if not sub 1,
|
||||
* the judgement of last dword will wrong.
|
||||
*/
|
||||
sub limit_wd, limit, #1 /* limit != 0, so no underflow. */
|
||||
lsr limit_wd, limit_wd, #3 /* Convert to Dwords. */
|
||||
|
||||
/*
|
||||
* NUL detection works on the principle that (X - 1) & (~X) & 0x80
|
||||
* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
|
||||
* can be done in parallel across the entire word.
|
||||
*/
|
||||
.Lloop_aligned:
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
.Lstart_realigned:
|
||||
subs limit_wd, limit_wd, #1
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
csinv endloop, diff, xzr, pl /* Last Dword or differences.*/
|
||||
bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
|
||||
ccmp endloop, #0, #0, eq
|
||||
b.eq .Lloop_aligned
|
||||
|
||||
/*Not reached the limit, must have found the end or a diff. */
|
||||
tbz limit_wd, #63, .Lnot_limit
|
||||
|
||||
/* Limit % 8 == 0 => all bytes significant. */
|
||||
ands limit, limit, #7
|
||||
b.eq .Lnot_limit
|
||||
|
||||
lsl limit, limit, #3 /* Bits -> bytes. */
|
||||
mov mask, #~0
|
||||
CPU_BE( lsr mask, mask, limit )
|
||||
CPU_LE( lsl mask, mask, limit )
|
||||
bic data1, data1, mask
|
||||
bic data2, data2, mask
|
||||
|
||||
/* Make sure that the NUL byte is marked in the syndrome. */
|
||||
orr has_nul, has_nul, mask
|
||||
|
||||
.Lnot_limit:
|
||||
orr syndrome, diff, has_nul
|
||||
b .Lcal_cmpresult
|
||||
|
||||
.Lmutual_align:
|
||||
/*
|
||||
* Sources are mutually aligned, but are not currently at an
|
||||
* alignment boundary. Round down the addresses and then mask off
|
||||
* the bytes that precede the start point.
|
||||
* We also need to adjust the limit calculations, but without
|
||||
* overflowing if the limit is near ULONG_MAX.
|
||||
*/
|
||||
bic src1, src1, #7
|
||||
bic src2, src2, #7
|
||||
ldr data1, [src1], #8
|
||||
neg tmp3, tmp1, lsl #3 /* 64 - bits(bytes beyond align). */
|
||||
ldr data2, [src2], #8
|
||||
mov tmp2, #~0
|
||||
sub limit_wd, limit, #1 /* limit != 0, so no underflow. */
|
||||
/* Big-endian. Early bytes are at MSB. */
|
||||
CPU_BE( lsl tmp2, tmp2, tmp3 ) /* Shift (tmp1 & 63). */
|
||||
/* Little-endian. Early bytes are at LSB. */
|
||||
CPU_LE( lsr tmp2, tmp2, tmp3 ) /* Shift (tmp1 & 63). */
|
||||
|
||||
and tmp3, limit_wd, #7
|
||||
lsr limit_wd, limit_wd, #3
|
||||
/* Adjust the limit. Only low 3 bits used, so overflow irrelevant.*/
|
||||
add limit, limit, tmp1
|
||||
add tmp3, tmp3, tmp1
|
||||
orr data1, data1, tmp2
|
||||
orr data2, data2, tmp2
|
||||
add limit_wd, limit_wd, tmp3, lsr #3
|
||||
b .Lstart_realigned
|
||||
|
||||
/*when src1 offset is not equal to src2 offset...*/
|
||||
.Lmisaligned8:
|
||||
cmp limit, #8
|
||||
b.lo .Ltiny8proc /*limit < 8... */
|
||||
/*
|
||||
* Get the align offset length to compare per byte first.
|
||||
* After this process, one string's address will be aligned.*/
|
||||
and tmp1, src1, #7
|
||||
neg tmp1, tmp1
|
||||
add tmp1, tmp1, #8
|
||||
and tmp2, src2, #7
|
||||
neg tmp2, tmp2
|
||||
add tmp2, tmp2, #8
|
||||
subs tmp3, tmp1, tmp2
|
||||
csel pos, tmp1, tmp2, hi /*Choose the maximum. */
|
||||
/*
|
||||
* Here, limit is not less than 8, so directly run .Ltinycmp
|
||||
* without checking the limit.*/
|
||||
sub limit, limit, pos
|
||||
.Ltinycmp:
|
||||
ldrb data1w, [src1], #1
|
||||
ldrb data2w, [src2], #1
|
||||
subs pos, pos, #1
|
||||
ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */
|
||||
ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
|
||||
b.eq .Ltinycmp
|
||||
cbnz pos, 1f /*find the null or unequal...*/
|
||||
cmp data1w, #1
|
||||
ccmp data1w, data2w, #0, cs
|
||||
b.eq .Lstart_align /*the last bytes are equal....*/
|
||||
1:
|
||||
sub result, data1, data2
|
||||
ret
|
||||
|
||||
.Lstart_align:
|
||||
lsr limit_wd, limit, #3
|
||||
cbz limit_wd, .Lremain8
|
||||
/*process more leading bytes to make str1 aligned...*/
|
||||
ands xzr, src1, #7
|
||||
b.eq .Lrecal_offset
|
||||
add src1, src1, tmp3 /*tmp3 is positive in this branch.*/
|
||||
add src2, src2, tmp3
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
|
||||
sub limit, limit, tmp3
|
||||
lsr limit_wd, limit, #3
|
||||
subs limit_wd, limit_wd, #1
|
||||
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/
|
||||
bics has_nul, tmp1, tmp2
|
||||
ccmp endloop, #0, #0, eq /*has_null is ZERO: no null byte*/
|
||||
b.ne .Lunequal_proc
|
||||
/*How far is the current str2 from the alignment boundary...*/
|
||||
and tmp3, tmp3, #7
|
||||
.Lrecal_offset:
|
||||
neg pos, tmp3
|
||||
.Lloopcmp_proc:
|
||||
/*
|
||||
* Divide the eight bytes into two parts. First,backwards the src2
|
||||
* to an alignment boundary,load eight bytes from the SRC2 alignment
|
||||
* boundary,then compare with the relative bytes from SRC1.
|
||||
* If all 8 bytes are equal,then start the second part's comparison.
|
||||
* Otherwise finish the comparison.
|
||||
* This special handle can garantee all the accesses are in the
|
||||
* thread/task space in avoid to overrange access.
|
||||
*/
|
||||
ldr data1, [src1,pos]
|
||||
ldr data2, [src2,pos]
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
csinv endloop, diff, xzr, eq
|
||||
cbnz endloop, .Lunequal_proc
|
||||
|
||||
/*The second part process*/
|
||||
ldr data1, [src1], #8
|
||||
ldr data2, [src2], #8
|
||||
subs limit_wd, limit_wd, #1
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
eor diff, data1, data2 /* Non-zero if differences found. */
|
||||
csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/
|
||||
bics has_nul, tmp1, tmp2
|
||||
ccmp endloop, #0, #0, eq /*has_null is ZERO: no null byte*/
|
||||
b.eq .Lloopcmp_proc
|
||||
|
||||
.Lunequal_proc:
|
||||
orr syndrome, diff, has_nul
|
||||
cbz syndrome, .Lremain8
|
||||
.Lcal_cmpresult:
|
||||
/*
|
||||
* reversed the byte-order as big-endian,then CLZ can find the most
|
||||
* significant zero bits.
|
||||
*/
|
||||
CPU_LE( rev syndrome, syndrome )
|
||||
CPU_LE( rev data1, data1 )
|
||||
CPU_LE( rev data2, data2 )
|
||||
/*
|
||||
* For big-endian we cannot use the trick with the syndrome value
|
||||
* as carry-propagation can corrupt the upper bits if the trailing
|
||||
* bytes in the string contain 0x01.
|
||||
* However, if there is no NUL byte in the dword, we can generate
|
||||
* the result directly. We can't just subtract the bytes as the
|
||||
* MSB might be significant.
|
||||
*/
|
||||
CPU_BE( cbnz has_nul, 1f )
|
||||
CPU_BE( cmp data1, data2 )
|
||||
CPU_BE( cset result, ne )
|
||||
CPU_BE( cneg result, result, lo )
|
||||
CPU_BE( ret )
|
||||
CPU_BE( 1: )
|
||||
/* Re-compute the NUL-byte detection, using a byte-reversed value.*/
|
||||
CPU_BE( rev tmp3, data1 )
|
||||
CPU_BE( sub tmp1, tmp3, zeroones )
|
||||
CPU_BE( orr tmp2, tmp3, #REP8_7f )
|
||||
CPU_BE( bic has_nul, tmp1, tmp2 )
|
||||
CPU_BE( rev has_nul, has_nul )
|
||||
CPU_BE( orr syndrome, diff, has_nul )
|
||||
/*
|
||||
* The MS-non-zero bit of the syndrome marks either the first bit
|
||||
* that is different, or the top bit of the first zero byte.
|
||||
* Shifting left now will bring the critical information into the
|
||||
* top bits.
|
||||
*/
|
||||
clz pos, syndrome
|
||||
lsl data1, data1, pos
|
||||
lsl data2, data2, pos
|
||||
/*
|
||||
* But we need to zero-extend (char is unsigned) the value and then
|
||||
* perform a signed 32-bit subtraction.
|
||||
*/
|
||||
lsr data1, data1, #56
|
||||
sub result, data1, data2, lsr #56
|
||||
ret
|
||||
|
||||
.Lremain8:
|
||||
/* Limit % 8 == 0 => all bytes significant. */
|
||||
ands limit, limit, #7
|
||||
b.eq .Lret0
|
||||
.Ltiny8proc:
|
||||
ldrb data1w, [src1], #1
|
||||
ldrb data2w, [src2], #1
|
||||
subs limit, limit, #1
|
||||
|
||||
ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */
|
||||
ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
|
||||
b.eq .Ltiny8proc
|
||||
sub result, data1, data2
|
||||
ret
|
||||
|
||||
.Lret0:
|
||||
mov result, #0
|
||||
ret
|
||||
ENDPROC(strncmp)
|
||||
171
arch/arm64/lib/strnlen.S
Normal file
171
arch/arm64/lib/strnlen.S
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
* Copyright (C) 2013 Linaro.
|
||||
*
|
||||
* This code is based on glibc cortex strings work originally authored by Linaro
|
||||
* and re-licensed under GPLv2 for the Linux kernel. The original code can
|
||||
* be found @
|
||||
*
|
||||
* http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/
|
||||
* files/head:/src/aarch64/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
/*
|
||||
* determine the length of a fixed-size string
|
||||
*
|
||||
* Parameters:
|
||||
* x0 - const string pointer
|
||||
* x1 - maximal string length
|
||||
* Returns:
|
||||
* x0 - the return length of specific string
|
||||
*/
|
||||
|
||||
/* Arguments and results. */
|
||||
srcin .req x0
|
||||
len .req x0
|
||||
limit .req x1
|
||||
|
||||
/* Locals and temporaries. */
|
||||
src .req x2
|
||||
data1 .req x3
|
||||
data2 .req x4
|
||||
data2a .req x5
|
||||
has_nul1 .req x6
|
||||
has_nul2 .req x7
|
||||
tmp1 .req x8
|
||||
tmp2 .req x9
|
||||
tmp3 .req x10
|
||||
tmp4 .req x11
|
||||
zeroones .req x12
|
||||
pos .req x13
|
||||
limit_wd .req x14
|
||||
|
||||
#define REP8_01 0x0101010101010101
|
||||
#define REP8_7f 0x7f7f7f7f7f7f7f7f
|
||||
#define REP8_80 0x8080808080808080
|
||||
|
||||
ENTRY(strnlen)
|
||||
cbz limit, .Lhit_limit
|
||||
mov zeroones, #REP8_01
|
||||
bic src, srcin, #15
|
||||
ands tmp1, srcin, #15
|
||||
b.ne .Lmisaligned
|
||||
/* Calculate the number of full and partial words -1. */
|
||||
sub limit_wd, limit, #1 /* Limit != 0, so no underflow. */
|
||||
lsr limit_wd, limit_wd, #4 /* Convert to Qwords. */
|
||||
|
||||
/*
|
||||
* NUL detection works on the principle that (X - 1) & (~X) & 0x80
|
||||
* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
|
||||
* can be done in parallel across the entire word.
|
||||
*/
|
||||
/*
|
||||
* The inner loop deals with two Dwords at a time. This has a
|
||||
* slightly higher start-up cost, but we should win quite quickly,
|
||||
* especially on cores with a high number of issue slots per
|
||||
* cycle, as we get much better parallelism out of the operations.
|
||||
*/
|
||||
.Lloop:
|
||||
ldp data1, data2, [src], #16
|
||||
.Lrealigned:
|
||||
sub tmp1, data1, zeroones
|
||||
orr tmp2, data1, #REP8_7f
|
||||
sub tmp3, data2, zeroones
|
||||
orr tmp4, data2, #REP8_7f
|
||||
bic has_nul1, tmp1, tmp2
|
||||
bic has_nul2, tmp3, tmp4
|
||||
subs limit_wd, limit_wd, #1
|
||||
orr tmp1, has_nul1, has_nul2
|
||||
ccmp tmp1, #0, #0, pl /* NZCV = 0000 */
|
||||
b.eq .Lloop
|
||||
|
||||
cbz tmp1, .Lhit_limit /* No null in final Qword. */
|
||||
|
||||
/*
|
||||
* We know there's a null in the final Qword. The easiest thing
|
||||
* to do now is work out the length of the string and return
|
||||
* MIN (len, limit).
|
||||
*/
|
||||
sub len, src, srcin
|
||||
cbz has_nul1, .Lnul_in_data2
|
||||
CPU_BE( mov data2, data1 ) /*perpare data to re-calculate the syndrome*/
|
||||
|
||||
sub len, len, #8
|
||||
mov has_nul2, has_nul1
|
||||
.Lnul_in_data2:
|
||||
/*
|
||||
* For big-endian, carry propagation (if the final byte in the
|
||||
* string is 0x01) means we cannot use has_nul directly. The
|
||||
* easiest way to get the correct byte is to byte-swap the data
|
||||
* and calculate the syndrome a second time.
|
||||
*/
|
||||
CPU_BE( rev data2, data2 )
|
||||
CPU_BE( sub tmp1, data2, zeroones )
|
||||
CPU_BE( orr tmp2, data2, #REP8_7f )
|
||||
CPU_BE( bic has_nul2, tmp1, tmp2 )
|
||||
|
||||
sub len, len, #8
|
||||
rev has_nul2, has_nul2
|
||||
clz pos, has_nul2
|
||||
add len, len, pos, lsr #3 /* Bits to bytes. */
|
||||
cmp len, limit
|
||||
csel len, len, limit, ls /* Return the lower value. */
|
||||
ret
|
||||
|
||||
.Lmisaligned:
|
||||
/*
|
||||
* Deal with a partial first word.
|
||||
* We're doing two things in parallel here;
|
||||
* 1) Calculate the number of words (but avoiding overflow if
|
||||
* limit is near ULONG_MAX) - to do this we need to work out
|
||||
* limit + tmp1 - 1 as a 65-bit value before shifting it;
|
||||
* 2) Load and mask the initial data words - we force the bytes
|
||||
* before the ones we are interested in to 0xff - this ensures
|
||||
* early bytes will not hit any zero detection.
|
||||
*/
|
||||
ldp data1, data2, [src], #16
|
||||
|
||||
sub limit_wd, limit, #1
|
||||
and tmp3, limit_wd, #15
|
||||
lsr limit_wd, limit_wd, #4
|
||||
|
||||
add tmp3, tmp3, tmp1
|
||||
add limit_wd, limit_wd, tmp3, lsr #4
|
||||
|
||||
neg tmp4, tmp1
|
||||
lsl tmp4, tmp4, #3 /* Bytes beyond alignment -> bits. */
|
||||
|
||||
mov tmp2, #~0
|
||||
/* Big-endian. Early bytes are at MSB. */
|
||||
CPU_BE( lsl tmp2, tmp2, tmp4 ) /* Shift (tmp1 & 63). */
|
||||
/* Little-endian. Early bytes are at LSB. */
|
||||
CPU_LE( lsr tmp2, tmp2, tmp4 ) /* Shift (tmp1 & 63). */
|
||||
|
||||
cmp tmp1, #8
|
||||
|
||||
orr data1, data1, tmp2
|
||||
orr data2a, data2, tmp2
|
||||
|
||||
csinv data1, data1, xzr, le
|
||||
csel data2, data2, data2a, le
|
||||
b .Lrealigned
|
||||
|
||||
.Lhit_limit:
|
||||
mov len, limit
|
||||
ret
|
||||
ENDPROC(strnlen)
|
||||
@@ -1,5 +1,5 @@
|
||||
obj-y := dma-mapping.o extable.o fault.o init.o \
|
||||
cache.o copypage.o flush.o \
|
||||
ioremap.o mmap.o pgd.o mmu.o \
|
||||
context.o proc.o
|
||||
context.o proc.o pageattr.o
|
||||
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
* Corrupted registers: x0-x7, x9-x11
|
||||
*/
|
||||
__flush_dcache_all:
|
||||
dsb sy // ensure ordering with previous memory accesses
|
||||
dmb sy // ensure ordering with previous memory accesses
|
||||
mrs x0, clidr_el1 // read clidr
|
||||
and x3, x0, #0x7000000 // extract loc from clidr
|
||||
lsr x3, x3, #23 // left align loc bit field
|
||||
@@ -128,7 +128,7 @@ USER(9f, dc cvau, x4 ) // clean D line to PoU
|
||||
add x4, x4, x2
|
||||
cmp x4, x1
|
||||
b.lo 1b
|
||||
dsb sy
|
||||
dsb ish
|
||||
|
||||
icache_line_size x2, x3
|
||||
sub x3, x2, #1
|
||||
@@ -139,7 +139,7 @@ USER(9f, ic ivau, x4 ) // invalidate I line PoU
|
||||
cmp x4, x1
|
||||
b.lo 1b
|
||||
9: // ignore any faulting cache operation
|
||||
dsb sy
|
||||
dsb ish
|
||||
isb
|
||||
ret
|
||||
ENDPROC(flush_icache_range)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/of.h>
|
||||
@@ -43,6 +44,54 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
|
||||
return prot;
|
||||
}
|
||||
|
||||
static struct gen_pool *atomic_pool;
|
||||
|
||||
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
|
||||
static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
|
||||
|
||||
static int __init early_coherent_pool(char *p)
|
||||
{
|
||||
atomic_pool_size = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("coherent_pool", early_coherent_pool);
|
||||
|
||||
static void *__alloc_from_pool(size_t size, struct page **ret_page)
|
||||
{
|
||||
unsigned long val;
|
||||
void *ptr = NULL;
|
||||
|
||||
if (!atomic_pool) {
|
||||
WARN(1, "coherent pool not initialised!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = gen_pool_alloc(atomic_pool, size);
|
||||
if (val) {
|
||||
phys_addr_t phys = gen_pool_virt_to_phys(atomic_pool, val);
|
||||
|
||||
*ret_page = phys_to_page(phys);
|
||||
ptr = (void *)val;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static bool __in_atomic_pool(void *start, size_t size)
|
||||
{
|
||||
return addr_in_gen_pool(atomic_pool, (unsigned long)start, size);
|
||||
}
|
||||
|
||||
static int __free_from_pool(void *start, size_t size)
|
||||
{
|
||||
if (!__in_atomic_pool(start, size))
|
||||
return 0;
|
||||
|
||||
gen_pool_free(atomic_pool, (unsigned long)start, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags,
|
||||
struct dma_attrs *attrs)
|
||||
@@ -50,7 +99,7 @@ static void *__dma_alloc_coherent(struct device *dev, size_t size,
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) &&
|
||||
dev->coherent_dma_mask <= DMA_BIT_MASK(32))
|
||||
flags |= GFP_DMA;
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA)) {
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA) && (flags & __GFP_WAIT)) {
|
||||
struct page *page;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
@@ -70,50 +119,54 @@ static void __dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
bool freed;
|
||||
phys_addr_t paddr = dma_to_phys(dev, dma_handle);
|
||||
|
||||
if (dev == NULL) {
|
||||
WARN_ONCE(1, "Use an actual device structure for DMA allocation\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA)) {
|
||||
phys_addr_t paddr = dma_to_phys(dev, dma_handle);
|
||||
|
||||
dma_release_from_contiguous(dev,
|
||||
freed = dma_release_from_contiguous(dev,
|
||||
phys_to_page(paddr),
|
||||
size >> PAGE_SHIFT);
|
||||
} else {
|
||||
if (!freed)
|
||||
swiotlb_free_coherent(dev, size, vaddr, dma_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void *__dma_alloc_noncoherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct page *page, **map;
|
||||
struct page *page;
|
||||
void *ptr, *coherent_ptr;
|
||||
int order, i;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
order = get_order(size);
|
||||
|
||||
if (!(flags & __GFP_WAIT)) {
|
||||
struct page *page = NULL;
|
||||
void *addr = __alloc_from_pool(size, &page);
|
||||
|
||||
if (addr)
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
|
||||
return addr;
|
||||
|
||||
}
|
||||
|
||||
ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs);
|
||||
if (!ptr)
|
||||
goto no_mem;
|
||||
map = kmalloc(sizeof(struct page *) << order, flags & ~GFP_DMA);
|
||||
if (!map)
|
||||
goto no_map;
|
||||
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
__dma_flush_range(ptr, ptr + size);
|
||||
|
||||
/* create a coherent mapping */
|
||||
page = virt_to_page(ptr);
|
||||
for (i = 0; i < (size >> PAGE_SHIFT); i++)
|
||||
map[i] = page + i;
|
||||
coherent_ptr = vmap(map, size >> PAGE_SHIFT, VM_MAP,
|
||||
__get_dma_pgprot(attrs, __pgprot(PROT_NORMAL_NC), false));
|
||||
kfree(map);
|
||||
coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP,
|
||||
__get_dma_pgprot(attrs,
|
||||
__pgprot(PROT_NORMAL_NC), false),
|
||||
NULL);
|
||||
if (!coherent_ptr)
|
||||
goto no_map;
|
||||
|
||||
@@ -132,6 +185,8 @@ static void __dma_free_noncoherent(struct device *dev, size_t size,
|
||||
{
|
||||
void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle));
|
||||
|
||||
if (__free_from_pool(vaddr, size))
|
||||
return;
|
||||
vunmap(vaddr);
|
||||
__dma_free_coherent(dev, size, swiotlb_addr, dma_handle, attrs);
|
||||
}
|
||||
@@ -329,6 +384,67 @@ static struct notifier_block amba_bus_nb = {
|
||||
|
||||
extern int swiotlb_late_init_with_default_size(size_t default_size);
|
||||
|
||||
static int __init atomic_pool_init(void)
|
||||
{
|
||||
pgprot_t prot = __pgprot(PROT_NORMAL_NC);
|
||||
unsigned long nr_pages = atomic_pool_size >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
void *addr;
|
||||
unsigned int pool_size_order = get_order(atomic_pool_size);
|
||||
|
||||
if (dev_get_cma_area(NULL))
|
||||
page = dma_alloc_from_contiguous(NULL, nr_pages,
|
||||
pool_size_order);
|
||||
else
|
||||
page = alloc_pages(GFP_DMA, pool_size_order);
|
||||
|
||||
if (page) {
|
||||
int ret;
|
||||
void *page_addr = page_address(page);
|
||||
|
||||
memset(page_addr, 0, atomic_pool_size);
|
||||
__dma_flush_range(page_addr, page_addr + atomic_pool_size);
|
||||
|
||||
atomic_pool = gen_pool_create(PAGE_SHIFT, -1);
|
||||
if (!atomic_pool)
|
||||
goto free_page;
|
||||
|
||||
addr = dma_common_contiguous_remap(page, atomic_pool_size,
|
||||
VM_USERMAP, prot, atomic_pool_init);
|
||||
|
||||
if (!addr)
|
||||
goto destroy_genpool;
|
||||
|
||||
ret = gen_pool_add_virt(atomic_pool, (unsigned long)addr,
|
||||
page_to_phys(page),
|
||||
atomic_pool_size, -1);
|
||||
if (ret)
|
||||
goto remove_mapping;
|
||||
|
||||
gen_pool_set_algo(atomic_pool,
|
||||
gen_pool_first_fit_order_align,
|
||||
(void *)PAGE_SHIFT);
|
||||
|
||||
pr_info("DMA: preallocated %zu KiB pool for atomic allocations\n",
|
||||
atomic_pool_size / 1024);
|
||||
return 0;
|
||||
}
|
||||
goto out;
|
||||
|
||||
remove_mapping:
|
||||
dma_common_free_remap(addr, atomic_pool_size, VM_USERMAP);
|
||||
destroy_genpool:
|
||||
gen_pool_destroy(atomic_pool);
|
||||
atomic_pool = NULL;
|
||||
free_page:
|
||||
if (!dma_release_from_contiguous(NULL, page, nr_pages))
|
||||
__free_pages(page, pool_size_order);
|
||||
out:
|
||||
pr_err("DMA: failed to allocate %zu KiB pool for atomic coherent allocation\n",
|
||||
atomic_pool_size / 1024);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init swiotlb_late_init(void)
|
||||
{
|
||||
size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
|
||||
@@ -343,7 +459,17 @@ static int __init swiotlb_late_init(void)
|
||||
|
||||
return swiotlb_late_init_with_default_size(swiotlb_size);
|
||||
}
|
||||
arch_initcall(swiotlb_late_init);
|
||||
|
||||
static int __init arm64_dma_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret |= swiotlb_late_init();
|
||||
ret |= atomic_pool_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(arm64_dma_init);
|
||||
|
||||
#define PREALLOC_DMA_DEBUG_ENTRIES 4096
|
||||
|
||||
|
||||
@@ -51,7 +51,11 @@ int pmd_huge(pmd_t pmd)
|
||||
|
||||
int pud_huge(pud_t pud)
|
||||
{
|
||||
#ifndef __PAGETABLE_PMD_FOLDED
|
||||
return !(pud_val(pud) & PUD_TABLE_BIT);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static __init int setup_hugepagesz(char *opt)
|
||||
|
||||
@@ -68,6 +68,17 @@ static int __init early_initrd(char *p)
|
||||
}
|
||||
early_param("initrd", early_initrd);
|
||||
|
||||
/*
|
||||
* Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It
|
||||
* currently assumes that for memory starting above 4G, 32-bit devices will
|
||||
* use a DMA offset.
|
||||
*/
|
||||
static phys_addr_t max_zone_dma_phys(void)
|
||||
{
|
||||
phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
|
||||
return min(offset + (1ULL << 32), memblock_end_of_DRAM());
|
||||
}
|
||||
|
||||
static void __init zone_sizes_init(unsigned long min, unsigned long max)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
@@ -78,9 +89,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
|
||||
|
||||
/* 4GB maximum for 32-bit only capable devices */
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA)) {
|
||||
unsigned long max_dma_phys =
|
||||
(unsigned long)dma_to_phys(NULL, DMA_BIT_MASK(32) + 1);
|
||||
max_dma = max(min, min(max, max_dma_phys >> PAGE_SHIFT));
|
||||
max_dma = PFN_DOWN(max_zone_dma_phys());
|
||||
zone_size[ZONE_DMA] = max_dma - min;
|
||||
}
|
||||
zone_size[ZONE_NORMAL] = max - max_dma;
|
||||
@@ -153,6 +162,7 @@ void __init rockchip_ion_reserve_bit64(void)
|
||||
void __init arm64_memblock_init(void)
|
||||
{
|
||||
u64 *reserve_map, base, size;
|
||||
phys_addr_t dma_phys_limit = 0;
|
||||
|
||||
/*
|
||||
* Register the kernel text, kernel data, initrd, and initial
|
||||
@@ -192,7 +202,11 @@ void __init arm64_memblock_init(void)
|
||||
#ifdef CONFIG_ARCH_ROCKCHIP
|
||||
rockchip_ion_reserve_bit64();
|
||||
#endif
|
||||
dma_contiguous_reserve(0);
|
||||
|
||||
/* 4GB maximum for 32-bit only capable devices */
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA))
|
||||
dma_phys_limit = max_zone_dma_phys();
|
||||
dma_contiguous_reserve(dma_phys_limit);
|
||||
|
||||
memblock_allow_resize();
|
||||
memblock_dump_all();
|
||||
|
||||
@@ -98,19 +98,25 @@ void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size)
|
||||
}
|
||||
EXPORT_SYMBOL(ioremap_cache);
|
||||
|
||||
#ifndef CONFIG_ARM64_64K_PAGES
|
||||
static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
|
||||
#ifndef CONFIG_ARM64_64K_PAGES
|
||||
static pte_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss;
|
||||
#endif
|
||||
|
||||
static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
|
||||
static inline pud_t * __init early_ioremap_pud(unsigned long addr)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
|
||||
pgd = pgd_offset_k(addr);
|
||||
BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd));
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
return pud_offset(pgd, addr);
|
||||
}
|
||||
|
||||
static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
|
||||
{
|
||||
pud_t *pud = early_ioremap_pud(addr);
|
||||
|
||||
BUG_ON(pud_none(*pud) || pud_bad(*pud));
|
||||
|
||||
return pmd_offset(pud, addr);
|
||||
@@ -127,13 +133,17 @@ static inline pte_t * __init early_ioremap_pte(unsigned long addr)
|
||||
|
||||
void __init early_ioremap_init(void)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
unsigned long addr = fix_to_virt(FIX_BTMAP_BEGIN);
|
||||
|
||||
pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
|
||||
#ifndef CONFIG_ARM64_64K_PAGES
|
||||
/* need to populate pmd for 4k pagesize only */
|
||||
pgd = pgd_offset_k(addr);
|
||||
pud = pud_offset(pgd, addr);
|
||||
pud_populate(&init_mm, pud, bm_pmd);
|
||||
pmd = pmd_offset(pud, addr);
|
||||
pmd_populate_kernel(&init_mm, pmd, bm_pte);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The boot-ioremap range spans multiple pmds, for which
|
||||
* we are not prepared:
|
||||
|
||||
96
arch/arm64/mm/pageattr.c
Normal file
96
arch/arm64/mm/pageattr.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
struct page_change_data {
|
||||
pgprot_t set_mask;
|
||||
pgprot_t clear_mask;
|
||||
};
|
||||
|
||||
static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
|
||||
void *data)
|
||||
{
|
||||
struct page_change_data *cdata = data;
|
||||
pte_t pte = *ptep;
|
||||
|
||||
pte = clear_pte_bit(pte, cdata->clear_mask);
|
||||
pte = set_pte_bit(pte, cdata->set_mask);
|
||||
|
||||
set_pte(ptep, pte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int change_memory_common(unsigned long addr, int numpages,
|
||||
pgprot_t set_mask, pgprot_t clear_mask)
|
||||
{
|
||||
unsigned long start = addr;
|
||||
unsigned long size = PAGE_SIZE*numpages;
|
||||
unsigned long end = start + size;
|
||||
int ret;
|
||||
struct page_change_data data;
|
||||
|
||||
if (!IS_ALIGNED(addr, PAGE_SIZE)) {
|
||||
addr &= PAGE_MASK;
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
if (!is_module_address(start) || !is_module_address(end - 1))
|
||||
return -EINVAL;
|
||||
|
||||
data.set_mask = set_mask;
|
||||
data.clear_mask = clear_mask;
|
||||
|
||||
ret = apply_to_page_range(&init_mm, start, size, change_page_range,
|
||||
&data);
|
||||
|
||||
flush_tlb_kernel_range(start, end);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_memory_ro(unsigned long addr, int numpages)
|
||||
{
|
||||
return change_memory_common(addr, numpages,
|
||||
__pgprot(PTE_RDONLY),
|
||||
__pgprot(PTE_WRITE));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_memory_ro);
|
||||
|
||||
int set_memory_rw(unsigned long addr, int numpages)
|
||||
{
|
||||
return change_memory_common(addr, numpages,
|
||||
__pgprot(PTE_WRITE),
|
||||
__pgprot(PTE_RDONLY));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_memory_rw);
|
||||
|
||||
int set_memory_nx(unsigned long addr, int numpages)
|
||||
{
|
||||
return change_memory_common(addr, numpages,
|
||||
__pgprot(PTE_PXN),
|
||||
__pgprot(0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_memory_nx);
|
||||
|
||||
int set_memory_x(unsigned long addr, int numpages)
|
||||
{
|
||||
return change_memory_common(addr, numpages,
|
||||
__pgprot(0),
|
||||
__pgprot(PTE_PXN));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_memory_x);
|
||||
@@ -69,6 +69,21 @@ ENTRY(cpu_reset)
|
||||
ret x0
|
||||
ENDPROC(cpu_reset)
|
||||
|
||||
ENTRY(cpu_soft_restart)
|
||||
/* Save address of cpu_reset() and reset address */
|
||||
mov x19, x0
|
||||
mov x20, x1
|
||||
|
||||
/* Turn D-cache off */
|
||||
bl cpu_cache_off
|
||||
|
||||
/* Push out all dirty data, and ensure cache is empty */
|
||||
bl flush_cache_all
|
||||
|
||||
mov x0, x20
|
||||
ret x19
|
||||
ENDPROC(cpu_soft_restart)
|
||||
|
||||
/*
|
||||
* cpu_do_idle()
|
||||
*
|
||||
@@ -175,7 +190,7 @@ ENDPROC(cpu_do_switch_mm)
|
||||
ENTRY(__cpu_setup)
|
||||
ic iallu // I+BTB cache invalidate
|
||||
tlbi vmalle1is // invalidate I + D TLBs
|
||||
dsb sy
|
||||
dsb ish
|
||||
|
||||
mov x0, #3 << 20
|
||||
msr cpacr_el1, x0 // Enable FP/ASIMD
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
V_FUNCTION_BEGIN(__kernel_getcpu)
|
||||
.cfi_startproc
|
||||
mfspr r5,SPRN_USPRG3
|
||||
cmpdi cr0,r3,0
|
||||
cmpdi cr1,r4,0
|
||||
cmpwi cr0,r3,0
|
||||
cmpwi cr1,r4,0
|
||||
clrlwi r6,r5,16
|
||||
rlwinm r7,r5,16,31-15,31-0
|
||||
beq cr0,1f
|
||||
|
||||
@@ -248,7 +248,7 @@ asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist)
|
||||
struct group_info *group_info;
|
||||
int retval;
|
||||
|
||||
if (!capable(CAP_SETGID))
|
||||
if (!may_setgroups())
|
||||
return -EPERM;
|
||||
if ((unsigned)gidsetsize > NGROUPS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -34,7 +34,7 @@ static inline unsigned int __getcpu(void)
|
||||
native_read_tscp(&p);
|
||||
} else {
|
||||
/* Load per CPU data from GDT */
|
||||
asm("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));
|
||||
asm volatile ("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
@@ -28,6 +28,13 @@ struct user_desc {
|
||||
unsigned int seg_not_present:1;
|
||||
unsigned int useable:1;
|
||||
#ifdef __x86_64__
|
||||
/*
|
||||
* Because this bit is not present in 32-bit user code, user
|
||||
* programs can pass uninitialized values here. Therefore, in
|
||||
* any context in which a user_desc comes from a 32-bit program,
|
||||
* the kernel must act as though lm == 0, regardless of the
|
||||
* actual value.
|
||||
*/
|
||||
unsigned int lm:1;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#define EXIT_REASON_EOI_INDUCED 45
|
||||
#define EXIT_REASON_EPT_VIOLATION 48
|
||||
#define EXIT_REASON_EPT_MISCONFIG 49
|
||||
#define EXIT_REASON_INVEPT 50
|
||||
#define EXIT_REASON_PREEMPTION_TIMER 52
|
||||
#define EXIT_REASON_WBINVD 54
|
||||
#define EXIT_REASON_XSETBV 55
|
||||
|
||||
@@ -2657,6 +2657,17 @@ static struct intel_uncore_box *uncore_event_to_box(struct perf_event *event)
|
||||
return uncore_pmu_to_box(uncore_event_to_pmu(event), smp_processor_id());
|
||||
}
|
||||
|
||||
/*
|
||||
* Using uncore_pmu_event_init pmu event_init callback
|
||||
* as a detection point for uncore events.
|
||||
*/
|
||||
static int uncore_pmu_event_init(struct perf_event *event);
|
||||
|
||||
static bool is_uncore_event(struct perf_event *event)
|
||||
{
|
||||
return event->pmu->event_init == uncore_pmu_event_init;
|
||||
}
|
||||
|
||||
static int
|
||||
uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader, bool dogrp)
|
||||
{
|
||||
@@ -2671,13 +2682,18 @@ uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader, b
|
||||
return -EINVAL;
|
||||
|
||||
n = box->n_events;
|
||||
box->event_list[n] = leader;
|
||||
n++;
|
||||
|
||||
if (is_uncore_event(leader)) {
|
||||
box->event_list[n] = leader;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!dogrp)
|
||||
return n;
|
||||
|
||||
list_for_each_entry(event, &leader->sibling_list, group_entry) {
|
||||
if (event->state <= PERF_EVENT_STATE_OFF)
|
||||
if (!is_uncore_event(event) ||
|
||||
event->state <= PERF_EVENT_STATE_OFF)
|
||||
continue;
|
||||
|
||||
if (n >= max_count)
|
||||
|
||||
@@ -279,7 +279,14 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
|
||||
static void __init paravirt_ops_setup(void)
|
||||
{
|
||||
pv_info.name = "KVM";
|
||||
pv_info.paravirt_enabled = 1;
|
||||
|
||||
/*
|
||||
* KVM isn't paravirt in the sense of paravirt_enabled. A KVM
|
||||
* guest kernel works like a bare metal kernel with additional
|
||||
* features, and paravirt_enabled is about features that are
|
||||
* missing.
|
||||
*/
|
||||
pv_info.paravirt_enabled = 0;
|
||||
|
||||
if (kvm_para_has_feature(KVM_FEATURE_NOP_IO_DELAY))
|
||||
pv_cpu_ops.io_delay = kvm_io_delay;
|
||||
|
||||
@@ -265,7 +265,6 @@ void __init kvmclock_init(void)
|
||||
#endif
|
||||
kvm_get_preset_lpj();
|
||||
clocksource_register_hz(&kvm_clock, NSEC_PER_SEC);
|
||||
pv_info.paravirt_enabled = 1;
|
||||
pv_info.name = "KVM";
|
||||
|
||||
if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE_STABLE_BIT))
|
||||
|
||||
@@ -279,24 +279,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||
|
||||
fpu = switch_fpu_prepare(prev_p, next_p, cpu);
|
||||
|
||||
/*
|
||||
* Reload esp0, LDT and the page table pointer:
|
||||
*/
|
||||
/* Reload esp0 and ss1. */
|
||||
load_sp0(tss, next);
|
||||
|
||||
/*
|
||||
* Switch DS and ES.
|
||||
* This won't pick up thread selector changes, but I guess that is ok.
|
||||
*/
|
||||
savesegment(es, prev->es);
|
||||
if (unlikely(next->es | prev->es))
|
||||
loadsegment(es, next->es);
|
||||
|
||||
savesegment(ds, prev->ds);
|
||||
if (unlikely(next->ds | prev->ds))
|
||||
loadsegment(ds, next->ds);
|
||||
|
||||
|
||||
/* We must save %fs and %gs before load_TLS() because
|
||||
* %fs and %gs may be cleared by load_TLS().
|
||||
*
|
||||
@@ -305,41 +290,101 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||
savesegment(fs, fsindex);
|
||||
savesegment(gs, gsindex);
|
||||
|
||||
/*
|
||||
* Load TLS before restoring any segments so that segment loads
|
||||
* reference the correct GDT entries.
|
||||
*/
|
||||
load_TLS(next, cpu);
|
||||
|
||||
/*
|
||||
* Leave lazy mode, flushing any hypercalls made here.
|
||||
* This must be done before restoring TLS segments so
|
||||
* the GDT and LDT are properly updated, and must be
|
||||
* done before math_state_restore, so the TS bit is up
|
||||
* to date.
|
||||
* Leave lazy mode, flushing any hypercalls made here. This
|
||||
* must be done after loading TLS entries in the GDT but before
|
||||
* loading segments that might reference them, and and it must
|
||||
* be done before math_state_restore, so the TS bit is up to
|
||||
* date.
|
||||
*/
|
||||
arch_end_context_switch(next_p);
|
||||
|
||||
/* Switch DS and ES.
|
||||
*
|
||||
* Reading them only returns the selectors, but writing them (if
|
||||
* nonzero) loads the full descriptor from the GDT or LDT. The
|
||||
* LDT for next is loaded in switch_mm, and the GDT is loaded
|
||||
* above.
|
||||
*
|
||||
* We therefore need to write new values to the segment
|
||||
* registers on every context switch unless both the new and old
|
||||
* values are zero.
|
||||
*
|
||||
* Note that we don't need to do anything for CS and SS, as
|
||||
* those are saved and restored as part of pt_regs.
|
||||
*/
|
||||
savesegment(es, prev->es);
|
||||
if (unlikely(next->es | prev->es))
|
||||
loadsegment(es, next->es);
|
||||
|
||||
savesegment(ds, prev->ds);
|
||||
if (unlikely(next->ds | prev->ds))
|
||||
loadsegment(ds, next->ds);
|
||||
|
||||
/*
|
||||
* Switch FS and GS.
|
||||
*
|
||||
* Segment register != 0 always requires a reload. Also
|
||||
* reload when it has changed. When prev process used 64bit
|
||||
* base always reload to avoid an information leak.
|
||||
* These are even more complicated than FS and GS: they have
|
||||
* 64-bit bases are that controlled by arch_prctl. Those bases
|
||||
* only differ from the values in the GDT or LDT if the selector
|
||||
* is 0.
|
||||
*
|
||||
* Loading the segment register resets the hidden base part of
|
||||
* the register to 0 or the value from the GDT / LDT. If the
|
||||
* next base address zero, writing 0 to the segment register is
|
||||
* much faster than using wrmsr to explicitly zero the base.
|
||||
*
|
||||
* The thread_struct.fs and thread_struct.gs values are 0
|
||||
* if the fs and gs bases respectively are not overridden
|
||||
* from the values implied by fsindex and gsindex. They
|
||||
* are nonzero, and store the nonzero base addresses, if
|
||||
* the bases are overridden.
|
||||
*
|
||||
* (fs != 0 && fsindex != 0) || (gs != 0 && gsindex != 0) should
|
||||
* be impossible.
|
||||
*
|
||||
* Therefore we need to reload the segment registers if either
|
||||
* the old or new selector is nonzero, and we need to override
|
||||
* the base address if next thread expects it to be overridden.
|
||||
*
|
||||
* This code is unnecessarily slow in the case where the old and
|
||||
* new indexes are zero and the new base is nonzero -- it will
|
||||
* unnecessarily write 0 to the selector before writing the new
|
||||
* base address.
|
||||
*
|
||||
* Note: This all depends on arch_prctl being the only way that
|
||||
* user code can override the segment base. Once wrfsbase and
|
||||
* wrgsbase are enabled, most of this code will need to change.
|
||||
*/
|
||||
if (unlikely(fsindex | next->fsindex | prev->fs)) {
|
||||
loadsegment(fs, next->fsindex);
|
||||
|
||||
/*
|
||||
* Check if the user used a selector != 0; if yes
|
||||
* clear 64bit base, since overloaded base is always
|
||||
* mapped to the Null selector
|
||||
* If user code wrote a nonzero value to FS, then it also
|
||||
* cleared the overridden base address.
|
||||
*
|
||||
* XXX: if user code wrote 0 to FS and cleared the base
|
||||
* address itself, we won't notice and we'll incorrectly
|
||||
* restore the prior base address next time we reschdule
|
||||
* the process.
|
||||
*/
|
||||
if (fsindex)
|
||||
prev->fs = 0;
|
||||
}
|
||||
/* when next process has a 64bit base use it */
|
||||
if (next->fs)
|
||||
wrmsrl(MSR_FS_BASE, next->fs);
|
||||
prev->fsindex = fsindex;
|
||||
|
||||
if (unlikely(gsindex | next->gsindex | prev->gs)) {
|
||||
load_gs_index(next->gsindex);
|
||||
|
||||
/* This works (and fails) the same way as fsindex above. */
|
||||
if (gsindex)
|
||||
prev->gs = 0;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ void (*pm_power_off)(void);
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
static const struct desc_ptr no_idt = {};
|
||||
static int reboot_mode;
|
||||
static enum reboot_mode reboot_mode;
|
||||
enum reboot_type reboot_type = BOOT_ACPI;
|
||||
int reboot_force;
|
||||
|
||||
@@ -88,11 +88,11 @@ static int __init reboot_setup(char *str)
|
||||
|
||||
switch (*str) {
|
||||
case 'w':
|
||||
reboot_mode = 0x1234;
|
||||
reboot_mode = REBOOT_WARM;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
reboot_mode = 0;
|
||||
reboot_mode = REBOOT_COLD;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@@ -552,6 +552,7 @@ static void native_machine_emergency_restart(void)
|
||||
int i;
|
||||
int attempt = 0;
|
||||
int orig_reboot_type = reboot_type;
|
||||
unsigned short mode;
|
||||
|
||||
if (reboot_emergency)
|
||||
emergency_vmx_disable_all();
|
||||
@@ -559,7 +560,8 @@ static void native_machine_emergency_restart(void)
|
||||
tboot_shutdown(TB_SHUTDOWN_REBOOT);
|
||||
|
||||
/* Tell the BIOS if we want cold or warm reboot */
|
||||
*((unsigned short *)__va(0x472)) = reboot_mode;
|
||||
mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
|
||||
*((unsigned short *)__va(0x472)) = mode;
|
||||
|
||||
for (;;) {
|
||||
/* Could also try the reset bit in the Hammer NB */
|
||||
@@ -601,7 +603,7 @@ static void native_machine_emergency_restart(void)
|
||||
|
||||
case BOOT_EFI:
|
||||
if (efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
efi.reset_system(reboot_mode ?
|
||||
efi.reset_system(reboot_mode == REBOOT_WARM ?
|
||||
EFI_RESET_WARM :
|
||||
EFI_RESET_COLD,
|
||||
EFI_SUCCESS, 0, NULL);
|
||||
|
||||
@@ -27,6 +27,37 @@ static int get_free_idx(void)
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
static bool tls_desc_okay(const struct user_desc *info)
|
||||
{
|
||||
if (LDT_empty(info))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* espfix is required for 16-bit data segments, but espfix
|
||||
* only works for LDT segments.
|
||||
*/
|
||||
if (!info->seg_32bit)
|
||||
return false;
|
||||
|
||||
/* Only allow data segments in the TLS array. */
|
||||
if (info->contents > 1)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Non-present segments with DPL 3 present an interesting attack
|
||||
* surface. The kernel should handle such segments correctly,
|
||||
* but TLS is very difficult to protect in a sandbox, so prevent
|
||||
* such segments from being created.
|
||||
*
|
||||
* If userspace needs to remove a TLS entry, it can still delete
|
||||
* it outright.
|
||||
*/
|
||||
if (info->seg_not_present)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_tls_desc(struct task_struct *p, int idx,
|
||||
const struct user_desc *info, int n)
|
||||
{
|
||||
@@ -66,6 +97,9 @@ int do_set_thread_area(struct task_struct *p, int idx,
|
||||
if (copy_from_user(&info, u_info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!tls_desc_okay(&info))
|
||||
return -EINVAL;
|
||||
|
||||
if (idx == -1)
|
||||
idx = info.entry_number;
|
||||
|
||||
@@ -192,6 +226,7 @@ int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
|
||||
{
|
||||
struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES];
|
||||
const struct user_desc *info;
|
||||
int i;
|
||||
|
||||
if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
|
||||
(pos % sizeof(struct user_desc)) != 0 ||
|
||||
@@ -205,6 +240,10 @@ int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
|
||||
else
|
||||
info = infobuf;
|
||||
|
||||
for (i = 0; i < count / sizeof(struct user_desc); i++)
|
||||
if (!tls_desc_okay(info + i))
|
||||
return -EINVAL;
|
||||
|
||||
set_tls_desc(target,
|
||||
GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)),
|
||||
info, count / sizeof(struct user_desc));
|
||||
|
||||
@@ -6242,6 +6242,12 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int handle_invept(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_queue_exception(vcpu, UD_VECTOR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The exit handlers return 1 if the exit was handled fully and guest execution
|
||||
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
|
||||
@@ -6286,6 +6292,7 @@ static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
|
||||
[EXIT_REASON_PAUSE_INSTRUCTION] = handle_pause,
|
||||
[EXIT_REASON_MWAIT_INSTRUCTION] = handle_invalid_op,
|
||||
[EXIT_REASON_MONITOR_INSTRUCTION] = handle_invalid_op,
|
||||
[EXIT_REASON_INVEPT] = handle_invept,
|
||||
};
|
||||
|
||||
static const int kvm_vmx_max_exit_handlers =
|
||||
@@ -6512,6 +6519,7 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu)
|
||||
case EXIT_REASON_VMPTRST: case EXIT_REASON_VMREAD:
|
||||
case EXIT_REASON_VMRESUME: case EXIT_REASON_VMWRITE:
|
||||
case EXIT_REASON_VMOFF: case EXIT_REASON_VMON:
|
||||
case EXIT_REASON_INVEPT:
|
||||
/*
|
||||
* VMX instructions trap unconditionally. This allows L1 to
|
||||
* emulate them for its L2 guest, i.e., allows 3-level nesting!
|
||||
|
||||
@@ -117,30 +117,45 @@ subsys_initcall(init_vdso);
|
||||
|
||||
struct linux_binprm;
|
||||
|
||||
/* Put the vdso above the (randomized) stack with another randomized offset.
|
||||
This way there is no hole in the middle of address space.
|
||||
To save memory make sure it is still in the same PTE as the stack top.
|
||||
This doesn't give that many random bits */
|
||||
/*
|
||||
* Put the vdso above the (randomized) stack with another randomized
|
||||
* offset. This way there is no hole in the middle of address space.
|
||||
* To save memory make sure it is still in the same PTE as the stack
|
||||
* top. This doesn't give that many random bits.
|
||||
*
|
||||
* Note that this algorithm is imperfect: the distribution of the vdso
|
||||
* start address within a PMD is biased toward the end.
|
||||
*
|
||||
* Only used for the 64-bit and x32 vdsos.
|
||||
*/
|
||||
static unsigned long vdso_addr(unsigned long start, unsigned len)
|
||||
{
|
||||
unsigned long addr, end;
|
||||
unsigned offset;
|
||||
end = (start + PMD_SIZE - 1) & PMD_MASK;
|
||||
|
||||
/*
|
||||
* Round up the start address. It can start out unaligned as a result
|
||||
* of stack start randomization.
|
||||
*/
|
||||
start = PAGE_ALIGN(start);
|
||||
|
||||
/* Round the lowest possible end address up to a PMD boundary. */
|
||||
end = (start + len + PMD_SIZE - 1) & PMD_MASK;
|
||||
if (end >= TASK_SIZE_MAX)
|
||||
end = TASK_SIZE_MAX;
|
||||
end -= len;
|
||||
/* This loses some more bits than a modulo, but is cheaper */
|
||||
offset = get_random_int() & (PTRS_PER_PTE - 1);
|
||||
addr = start + (offset << PAGE_SHIFT);
|
||||
if (addr >= end)
|
||||
addr = end;
|
||||
|
||||
if (end > start) {
|
||||
offset = get_random_int() % (((end - start) >> PAGE_SHIFT) + 1);
|
||||
addr = start + (offset << PAGE_SHIFT);
|
||||
} else {
|
||||
addr = start;
|
||||
}
|
||||
|
||||
/*
|
||||
* page-align it here so that get_unmapped_area doesn't
|
||||
* align it wrongfully again to the next page. addr can come in 4K
|
||||
* unaligned here as a result of stack start randomization.
|
||||
* Forcibly align the final address in case we have a hardware
|
||||
* issue that requires alignment for performance reasons.
|
||||
*/
|
||||
addr = PAGE_ALIGN(addr);
|
||||
addr = align_vdso_addr(addr);
|
||||
|
||||
return addr;
|
||||
|
||||
@@ -1070,9 +1070,16 @@ int disk_expand_part_tbl(struct gendisk *disk, int partno)
|
||||
struct disk_part_tbl *old_ptbl = disk->part_tbl;
|
||||
struct disk_part_tbl *new_ptbl;
|
||||
int len = old_ptbl ? old_ptbl->len : 0;
|
||||
int target = partno + 1;
|
||||
int i, target;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* check for int overflow, since we can get here from blkpg_ioctl()
|
||||
* with a user passed 'partno'.
|
||||
*/
|
||||
target = partno + 1;
|
||||
if (target < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* disk_max_parts() is zero during initialization, ignore if so */
|
||||
if (disk_max_parts(disk) && target > disk_max_parts(disk))
|
||||
|
||||
@@ -449,6 +449,9 @@ void af_alg_complete(struct crypto_async_request *req, int err)
|
||||
{
|
||||
struct af_alg_completion *completion = req->data;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
completion->err = err;
|
||||
complete(&completion->completion);
|
||||
}
|
||||
|
||||
@@ -157,4 +157,5 @@ obj-$(CONFIG_IPACK_BUS) += ipack/
|
||||
obj-$(CONFIG_NTB) += ntb/
|
||||
|
||||
obj-$(CONFIG_GATOR) += gator/
|
||||
obj-y += headset_observe/
|
||||
obj-y += headset_observe/
|
||||
obj-$(CONFIG_CORESIGHT) += coresight/
|
||||
|
||||
@@ -508,7 +508,7 @@ int amba_device_add(struct amba_device *dev, struct resource *parent)
|
||||
|
||||
// amba_put_disable_pclk(dev);
|
||||
|
||||
if (cid == AMBA_CID)
|
||||
if (cid == AMBA_CID || cid == CORESIGHT_CID)
|
||||
dev->periphid = pid;
|
||||
|
||||
if (!dev->periphid)
|
||||
|
||||
@@ -320,6 +320,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
|
||||
@@ -491,6 +494,7 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
||||
* enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
|
||||
*/
|
||||
{ PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
|
||||
{ PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
|
||||
|
||||
/* Enmotus */
|
||||
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
|
||||
|
||||
@@ -1501,7 +1501,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
|
||||
host_priv->csr_base = csr_base;
|
||||
|
||||
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
|
||||
if (irq < 0) {
|
||||
if (!irq) {
|
||||
dev_err(&ofdev->dev, "invalid irq from platform\n");
|
||||
goto error_exit_with_cleanup;
|
||||
}
|
||||
|
||||
@@ -242,13 +242,15 @@ static ssize_t store_drivers_probe(struct bus_type *bus,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct device *dev;
|
||||
int err = -EINVAL;
|
||||
|
||||
dev = bus_find_device_by_name(bus, NULL, buf);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (bus_rescan_devices_helper(dev, NULL) != 0)
|
||||
return -EINVAL;
|
||||
return count;
|
||||
if (bus_rescan_devices_helper(dev, NULL) == 0)
|
||||
err = count;
|
||||
put_device(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct device *next_device(struct klist_iter *i)
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm-generic/dma-coherent.h>
|
||||
|
||||
/*
|
||||
@@ -267,3 +269,73 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_common_mmap);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
/*
|
||||
* remaps an array of PAGE_SIZE pages into another vm_area
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
void *dma_common_pages_remap(struct page **pages, size_t size,
|
||||
unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
|
||||
area = get_vm_area_caller(size, vm_flags, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
|
||||
if (map_vm_area(area, prot, &pages)) {
|
||||
vunmap(area->addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* remaps an allocated contiguous region into another vm_area.
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
|
||||
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
||||
unsigned long vm_flags,
|
||||
pgprot_t prot, const void *caller)
|
||||
{
|
||||
int i;
|
||||
struct page **pages;
|
||||
void *ptr;
|
||||
unsigned long pfn;
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
for (i = 0, pfn = page_to_pfn(page); i < (size >> PAGE_SHIFT); i++)
|
||||
pages[i] = pfn_to_page(pfn + i);
|
||||
|
||||
ptr = dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
|
||||
kfree(pages);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmaps a range previously mapped by dma_common_*_remap
|
||||
*/
|
||||
void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
|
||||
{
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
|
||||
if (!area || (area->flags & vm_flags) != vm_flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
11
drivers/coresight/Makefile
Normal file
11
drivers/coresight/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Makefile for CoreSight drivers.
|
||||
#
|
||||
obj-$(CONFIG_CORESIGHT) += coresight.o
|
||||
obj-$(CONFIG_OF) += of_coresight.o
|
||||
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
|
||||
obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
|
||||
obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
|
||||
obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
|
||||
coresight-replicator.o
|
||||
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
|
||||
537
drivers/coresight/coresight-etb10.c
Normal file
537
drivers/coresight/coresight-etb10.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
#define ETB_RAM_DEPTH_REG 0x004
|
||||
#define ETB_STATUS_REG 0x00c
|
||||
#define ETB_RAM_READ_DATA_REG 0x010
|
||||
#define ETB_RAM_READ_POINTER 0x014
|
||||
#define ETB_RAM_WRITE_POINTER 0x018
|
||||
#define ETB_TRG 0x01c
|
||||
#define ETB_CTL_REG 0x020
|
||||
#define ETB_RWD_REG 0x024
|
||||
#define ETB_FFSR 0x300
|
||||
#define ETB_FFCR 0x304
|
||||
#define ETB_ITMISCOP0 0xee0
|
||||
#define ETB_ITTRFLINACK 0xee4
|
||||
#define ETB_ITTRFLIN 0xee8
|
||||
#define ETB_ITATBDATA0 0xeeC
|
||||
#define ETB_ITATBCTR2 0xef0
|
||||
#define ETB_ITATBCTR1 0xef4
|
||||
#define ETB_ITATBCTR0 0xef8
|
||||
|
||||
/* register description */
|
||||
/* STS - 0x00C */
|
||||
#define ETB_STATUS_RAM_FULL BIT(0)
|
||||
/* CTL - 0x020 */
|
||||
#define ETB_CTL_CAPT_EN BIT(0)
|
||||
/* FFCR - 0x304 */
|
||||
#define ETB_FFCR_EN_FTC BIT(0)
|
||||
#define ETB_FFCR_FON_MAN BIT(6)
|
||||
#define ETB_FFCR_STOP_FI BIT(12)
|
||||
#define ETB_FFCR_STOP_TRIGGER BIT(13)
|
||||
|
||||
#define ETB_FFCR_BIT 6
|
||||
#define ETB_FFSR_BIT 1
|
||||
#define ETB_FRAME_SIZE_WORDS 4
|
||||
|
||||
/**
|
||||
* struct etb_drvdata - specifics associated to an ETB component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @miscdev: specifics to handle "/dev/xyz.etb" entry.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @in_use: synchronise user space access to etb buffer.
|
||||
* @buf: area of memory where ETB buffer content gets sent.
|
||||
* @buffer_depth: size of @buf.
|
||||
* @enable: this ETB is being used.
|
||||
* @trigger_cntr: amount of words to store after a trigger.
|
||||
*/
|
||||
struct etb_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
atomic_t in_use;
|
||||
u8 *buf;
|
||||
u32 buffer_depth;
|
||||
bool enable;
|
||||
u32 trigger_cntr;
|
||||
};
|
||||
|
||||
static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
|
||||
{
|
||||
int ret;
|
||||
u32 depth = 0;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* RO registers don't need locking */
|
||||
depth = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
return depth;
|
||||
}
|
||||
|
||||
static void etb_enable_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
int i;
|
||||
u32 depth;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
depth = drvdata->buffer_depth;
|
||||
/* reset write RAM pointer address */
|
||||
writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
|
||||
/* clear entire RAM buffer */
|
||||
for (i = 0; i < depth; i++)
|
||||
writel_relaxed(0x0, drvdata->base + ETB_RWD_REG);
|
||||
|
||||
/* reset write RAM pointer address */
|
||||
writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
|
||||
/* reset read RAM pointer address */
|
||||
writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
|
||||
|
||||
writel_relaxed(drvdata->trigger_cntr, drvdata->base + ETB_TRG);
|
||||
writel_relaxed(ETB_FFCR_EN_FTC | ETB_FFCR_STOP_TRIGGER,
|
||||
drvdata->base + ETB_FFCR);
|
||||
/* ETB trace capture enable */
|
||||
writel_relaxed(ETB_CTL_CAPT_EN, drvdata->base + ETB_CTL_REG);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int etb_enable(struct coresight_device *csdev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
etb_enable_hw(drvdata);
|
||||
drvdata->enable = true;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_info(drvdata->dev, "ETB enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etb_disable_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
u32 ffcr;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
ffcr = readl_relaxed(drvdata->base + ETB_FFCR);
|
||||
/* stop formatter when a stop has completed */
|
||||
ffcr |= ETB_FFCR_STOP_FI;
|
||||
writel_relaxed(ffcr, drvdata->base + ETB_FFCR);
|
||||
/* manually generate a flush of the system */
|
||||
ffcr |= ETB_FFCR_FON_MAN;
|
||||
writel_relaxed(ffcr, drvdata->base + ETB_FFCR);
|
||||
|
||||
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
ETB_FFCR);
|
||||
}
|
||||
|
||||
/* disable trace capture */
|
||||
writel_relaxed(0x0, drvdata->base + ETB_CTL_REG);
|
||||
|
||||
if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
ETB_FFCR);
|
||||
}
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void etb_dump_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
int i;
|
||||
u8 *buf_ptr;
|
||||
u32 read_data, depth;
|
||||
u32 read_ptr, write_ptr;
|
||||
u32 frame_off, frame_endoff;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
|
||||
write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
|
||||
|
||||
frame_off = write_ptr % ETB_FRAME_SIZE_WORDS;
|
||||
frame_endoff = ETB_FRAME_SIZE_WORDS - frame_off;
|
||||
if (frame_off) {
|
||||
dev_err(drvdata->dev,
|
||||
"write_ptr: %lu not aligned to formatter frame size\n",
|
||||
(unsigned long)write_ptr);
|
||||
dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n",
|
||||
(unsigned long)frame_off, (unsigned long)frame_endoff);
|
||||
write_ptr += frame_endoff;
|
||||
}
|
||||
|
||||
if ((readl_relaxed(drvdata->base + ETB_STATUS_REG)
|
||||
& ETB_STATUS_RAM_FULL) == 0)
|
||||
writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
|
||||
else
|
||||
writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER);
|
||||
|
||||
depth = drvdata->buffer_depth;
|
||||
buf_ptr = drvdata->buf;
|
||||
for (i = 0; i < depth; i++) {
|
||||
read_data = readl_relaxed(drvdata->base +
|
||||
ETB_RAM_READ_DATA_REG);
|
||||
*buf_ptr++ = read_data >> 0;
|
||||
*buf_ptr++ = read_data >> 8;
|
||||
*buf_ptr++ = read_data >> 16;
|
||||
*buf_ptr++ = read_data >> 24;
|
||||
}
|
||||
|
||||
if (frame_off) {
|
||||
buf_ptr -= (frame_endoff * 4);
|
||||
for (i = 0; i < frame_endoff; i++) {
|
||||
*buf_ptr++ = 0x0;
|
||||
*buf_ptr++ = 0x0;
|
||||
*buf_ptr++ = 0x0;
|
||||
*buf_ptr++ = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void etb_disable(struct coresight_device *csdev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
drvdata->enable = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
dev_info(drvdata->dev, "ETB disabled\n");
|
||||
}
|
||||
|
||||
static const struct coresight_ops_sink etb_sink_ops = {
|
||||
.enable = etb_enable,
|
||||
.disable = etb_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops etb_cs_ops = {
|
||||
.sink_ops = &etb_sink_ops,
|
||||
};
|
||||
|
||||
static void etb_dump(struct etb_drvdata *drvdata)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->enable) {
|
||||
etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
etb_enable_hw(drvdata);
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_info(drvdata->dev, "ETB dumped\n");
|
||||
}
|
||||
|
||||
static int etb_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct etb_drvdata *drvdata = container_of(file->private_data,
|
||||
struct etb_drvdata, miscdev);
|
||||
|
||||
if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
|
||||
return -EBUSY;
|
||||
|
||||
dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t etb_read(struct file *file, char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
u32 depth;
|
||||
struct etb_drvdata *drvdata = container_of(file->private_data,
|
||||
struct etb_drvdata, miscdev);
|
||||
|
||||
etb_dump(drvdata);
|
||||
|
||||
depth = drvdata->buffer_depth;
|
||||
if (*ppos + len > depth * 4)
|
||||
len = depth * 4 - *ppos;
|
||||
|
||||
if (copy_to_user(data, drvdata->buf + *ppos, len)) {
|
||||
dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*ppos += len;
|
||||
|
||||
dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
|
||||
__func__, len, (int) (depth * 4 - *ppos));
|
||||
return len;
|
||||
}
|
||||
|
||||
static int etb_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct etb_drvdata *drvdata = container_of(file->private_data,
|
||||
struct etb_drvdata, miscdev);
|
||||
atomic_set(&drvdata->in_use, 0);
|
||||
|
||||
dev_dbg(drvdata->dev, "%s: released\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations etb_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = etb_open,
|
||||
.read = etb_read,
|
||||
.release = etb_release,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
u32 etb_rdr, etb_sr, etb_rrp, etb_rwp;
|
||||
u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr;
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG);
|
||||
etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG);
|
||||
etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
|
||||
etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
|
||||
etb_trg = readl_relaxed(drvdata->base + ETB_TRG);
|
||||
etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG);
|
||||
etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR);
|
||||
etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
return sprintf(buf,
|
||||
"Depth:\t\t0x%x\n"
|
||||
"Status:\t\t0x%x\n"
|
||||
"RAM read ptr:\t0x%x\n"
|
||||
"RAM wrt ptr:\t0x%x\n"
|
||||
"Trigger cnt:\t0x%x\n"
|
||||
"Control:\t0x%x\n"
|
||||
"Flush status:\t0x%x\n"
|
||||
"Flush ctrl:\t0x%x\n",
|
||||
etb_rdr, etb_sr, etb_rrp, etb_rwp,
|
||||
etb_trg, etb_cr, etb_ffsr, etb_ffcr);
|
||||
out:
|
||||
return -EINVAL;
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
static ssize_t trigger_cntr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
unsigned long val = drvdata->trigger_cntr;
|
||||
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
|
||||
static ssize_t trigger_cntr_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val;
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = kstrtoul(buf, 16, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->trigger_cntr = val;
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(trigger_cntr);
|
||||
|
||||
static struct attribute *coresight_etb_attrs[] = {
|
||||
&dev_attr_trigger_cntr.attr,
|
||||
&dev_attr_status.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(coresight_etb);
|
||||
|
||||
static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret;
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct etb_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
if (drvdata->buffer_depth < 0)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata->buf = devm_kzalloc(dev,
|
||||
drvdata->buffer_depth * 4, GFP_KERNEL);
|
||||
if (!drvdata->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc->ops = &etb_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_etb_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
drvdata->miscdev.name = pdata->name;
|
||||
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
drvdata->miscdev.fops = &etb_fops;
|
||||
ret = misc_register(&drvdata->miscdev);
|
||||
if (ret)
|
||||
goto err_misc_register;
|
||||
|
||||
dev_info(dev, "ETB initialized\n");
|
||||
return 0;
|
||||
|
||||
err_misc_register:
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etb_remove(struct amba_device *adev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
misc_deregister(&drvdata->miscdev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id etb_ids[] = {
|
||||
{
|
||||
.id = 0x0003b907,
|
||||
.mask = 0x0003ffff,
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
static struct amba_driver etb_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-etb10",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = etb_probe,
|
||||
.remove = etb_remove,
|
||||
.id_table = etb_ids,
|
||||
};
|
||||
|
||||
static int __init etb_init(void)
|
||||
{
|
||||
return amba_driver_register(&etb_driver);
|
||||
}
|
||||
module_init(etb_init);
|
||||
|
||||
static void __exit etb_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&etb_driver);
|
||||
}
|
||||
module_exit(etb_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver");
|
||||
591
drivers/coresight/coresight-etm-cp14.c
Normal file
591
drivers/coresight/coresight-etm-cp14.c
Normal file
@@ -0,0 +1,591 @@
|
||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bug.h>
|
||||
#include <asm/hardware/cp14.h>
|
||||
|
||||
#include "coresight-etm.h"
|
||||
|
||||
int etm_readl_cp14(u32 reg, unsigned int *val)
|
||||
{
|
||||
switch (reg) {
|
||||
case ETMCR:
|
||||
*val = etm_read(ETMCR);
|
||||
return 0;
|
||||
case ETMCCR:
|
||||
*val = etm_read(ETMCCR);
|
||||
return 0;
|
||||
case ETMTRIGGER:
|
||||
*val = etm_read(ETMTRIGGER);
|
||||
return 0;
|
||||
case ETMSR:
|
||||
*val = etm_read(ETMSR);
|
||||
return 0;
|
||||
case ETMSCR:
|
||||
*val = etm_read(ETMSCR);
|
||||
return 0;
|
||||
case ETMTSSCR:
|
||||
*val = etm_read(ETMTSSCR);
|
||||
return 0;
|
||||
case ETMTEEVR:
|
||||
*val = etm_read(ETMTEEVR);
|
||||
return 0;
|
||||
case ETMTECR1:
|
||||
*val = etm_read(ETMTECR1);
|
||||
return 0;
|
||||
case ETMFFLR:
|
||||
*val = etm_read(ETMFFLR);
|
||||
return 0;
|
||||
case ETMACVRn(0):
|
||||
*val = etm_read(ETMACVR0);
|
||||
return 0;
|
||||
case ETMACVRn(1):
|
||||
*val = etm_read(ETMACVR1);
|
||||
return 0;
|
||||
case ETMACVRn(2):
|
||||
*val = etm_read(ETMACVR2);
|
||||
return 0;
|
||||
case ETMACVRn(3):
|
||||
*val = etm_read(ETMACVR3);
|
||||
return 0;
|
||||
case ETMACVRn(4):
|
||||
*val = etm_read(ETMACVR4);
|
||||
return 0;
|
||||
case ETMACVRn(5):
|
||||
*val = etm_read(ETMACVR5);
|
||||
return 0;
|
||||
case ETMACVRn(6):
|
||||
*val = etm_read(ETMACVR6);
|
||||
return 0;
|
||||
case ETMACVRn(7):
|
||||
*val = etm_read(ETMACVR7);
|
||||
return 0;
|
||||
case ETMACVRn(8):
|
||||
*val = etm_read(ETMACVR8);
|
||||
return 0;
|
||||
case ETMACVRn(9):
|
||||
*val = etm_read(ETMACVR9);
|
||||
return 0;
|
||||
case ETMACVRn(10):
|
||||
*val = etm_read(ETMACVR10);
|
||||
return 0;
|
||||
case ETMACVRn(11):
|
||||
*val = etm_read(ETMACVR11);
|
||||
return 0;
|
||||
case ETMACVRn(12):
|
||||
*val = etm_read(ETMACVR12);
|
||||
return 0;
|
||||
case ETMACVRn(13):
|
||||
*val = etm_read(ETMACVR13);
|
||||
return 0;
|
||||
case ETMACVRn(14):
|
||||
*val = etm_read(ETMACVR14);
|
||||
return 0;
|
||||
case ETMACVRn(15):
|
||||
*val = etm_read(ETMACVR15);
|
||||
return 0;
|
||||
case ETMACTRn(0):
|
||||
*val = etm_read(ETMACTR0);
|
||||
return 0;
|
||||
case ETMACTRn(1):
|
||||
*val = etm_read(ETMACTR1);
|
||||
return 0;
|
||||
case ETMACTRn(2):
|
||||
*val = etm_read(ETMACTR2);
|
||||
return 0;
|
||||
case ETMACTRn(3):
|
||||
*val = etm_read(ETMACTR3);
|
||||
return 0;
|
||||
case ETMACTRn(4):
|
||||
*val = etm_read(ETMACTR4);
|
||||
return 0;
|
||||
case ETMACTRn(5):
|
||||
*val = etm_read(ETMACTR5);
|
||||
return 0;
|
||||
case ETMACTRn(6):
|
||||
*val = etm_read(ETMACTR6);
|
||||
return 0;
|
||||
case ETMACTRn(7):
|
||||
*val = etm_read(ETMACTR7);
|
||||
return 0;
|
||||
case ETMACTRn(8):
|
||||
*val = etm_read(ETMACTR8);
|
||||
return 0;
|
||||
case ETMACTRn(9):
|
||||
*val = etm_read(ETMACTR9);
|
||||
return 0;
|
||||
case ETMACTRn(10):
|
||||
*val = etm_read(ETMACTR10);
|
||||
return 0;
|
||||
case ETMACTRn(11):
|
||||
*val = etm_read(ETMACTR11);
|
||||
return 0;
|
||||
case ETMACTRn(12):
|
||||
*val = etm_read(ETMACTR12);
|
||||
return 0;
|
||||
case ETMACTRn(13):
|
||||
*val = etm_read(ETMACTR13);
|
||||
return 0;
|
||||
case ETMACTRn(14):
|
||||
*val = etm_read(ETMACTR14);
|
||||
return 0;
|
||||
case ETMACTRn(15):
|
||||
*val = etm_read(ETMACTR15);
|
||||
return 0;
|
||||
case ETMCNTRLDVRn(0):
|
||||
*val = etm_read(ETMCNTRLDVR0);
|
||||
return 0;
|
||||
case ETMCNTRLDVRn(1):
|
||||
*val = etm_read(ETMCNTRLDVR1);
|
||||
return 0;
|
||||
case ETMCNTRLDVRn(2):
|
||||
*val = etm_read(ETMCNTRLDVR2);
|
||||
return 0;
|
||||
case ETMCNTRLDVRn(3):
|
||||
*val = etm_read(ETMCNTRLDVR3);
|
||||
return 0;
|
||||
case ETMCNTENRn(0):
|
||||
*val = etm_read(ETMCNTENR0);
|
||||
return 0;
|
||||
case ETMCNTENRn(1):
|
||||
*val = etm_read(ETMCNTENR1);
|
||||
return 0;
|
||||
case ETMCNTENRn(2):
|
||||
*val = etm_read(ETMCNTENR2);
|
||||
return 0;
|
||||
case ETMCNTENRn(3):
|
||||
*val = etm_read(ETMCNTENR3);
|
||||
return 0;
|
||||
case ETMCNTRLDEVRn(0):
|
||||
*val = etm_read(ETMCNTRLDEVR0);
|
||||
return 0;
|
||||
case ETMCNTRLDEVRn(1):
|
||||
*val = etm_read(ETMCNTRLDEVR1);
|
||||
return 0;
|
||||
case ETMCNTRLDEVRn(2):
|
||||
*val = etm_read(ETMCNTRLDEVR2);
|
||||
return 0;
|
||||
case ETMCNTRLDEVRn(3):
|
||||
*val = etm_read(ETMCNTRLDEVR3);
|
||||
return 0;
|
||||
case ETMCNTVRn(0):
|
||||
*val = etm_read(ETMCNTVR0);
|
||||
return 0;
|
||||
case ETMCNTVRn(1):
|
||||
*val = etm_read(ETMCNTVR1);
|
||||
return 0;
|
||||
case ETMCNTVRn(2):
|
||||
*val = etm_read(ETMCNTVR2);
|
||||
return 0;
|
||||
case ETMCNTVRn(3):
|
||||
*val = etm_read(ETMCNTVR3);
|
||||
return 0;
|
||||
case ETMSQ12EVR:
|
||||
*val = etm_read(ETMSQ12EVR);
|
||||
return 0;
|
||||
case ETMSQ21EVR:
|
||||
*val = etm_read(ETMSQ21EVR);
|
||||
return 0;
|
||||
case ETMSQ23EVR:
|
||||
*val = etm_read(ETMSQ23EVR);
|
||||
return 0;
|
||||
case ETMSQ31EVR:
|
||||
*val = etm_read(ETMSQ31EVR);
|
||||
return 0;
|
||||
case ETMSQ32EVR:
|
||||
*val = etm_read(ETMSQ32EVR);
|
||||
return 0;
|
||||
case ETMSQ13EVR:
|
||||
*val = etm_read(ETMSQ13EVR);
|
||||
return 0;
|
||||
case ETMSQR:
|
||||
*val = etm_read(ETMSQR);
|
||||
return 0;
|
||||
case ETMEXTOUTEVRn(0):
|
||||
*val = etm_read(ETMEXTOUTEVR0);
|
||||
return 0;
|
||||
case ETMEXTOUTEVRn(1):
|
||||
*val = etm_read(ETMEXTOUTEVR1);
|
||||
return 0;
|
||||
case ETMEXTOUTEVRn(2):
|
||||
*val = etm_read(ETMEXTOUTEVR2);
|
||||
return 0;
|
||||
case ETMEXTOUTEVRn(3):
|
||||
*val = etm_read(ETMEXTOUTEVR3);
|
||||
return 0;
|
||||
case ETMCIDCVRn(0):
|
||||
*val = etm_read(ETMCIDCVR0);
|
||||
return 0;
|
||||
case ETMCIDCVRn(1):
|
||||
*val = etm_read(ETMCIDCVR1);
|
||||
return 0;
|
||||
case ETMCIDCVRn(2):
|
||||
*val = etm_read(ETMCIDCVR2);
|
||||
return 0;
|
||||
case ETMCIDCMR:
|
||||
*val = etm_read(ETMCIDCMR);
|
||||
return 0;
|
||||
case ETMIMPSPEC0:
|
||||
*val = etm_read(ETMIMPSPEC0);
|
||||
return 0;
|
||||
case ETMIMPSPEC1:
|
||||
*val = etm_read(ETMIMPSPEC1);
|
||||
return 0;
|
||||
case ETMIMPSPEC2:
|
||||
*val = etm_read(ETMIMPSPEC2);
|
||||
return 0;
|
||||
case ETMIMPSPEC3:
|
||||
*val = etm_read(ETMIMPSPEC3);
|
||||
return 0;
|
||||
case ETMIMPSPEC4:
|
||||
*val = etm_read(ETMIMPSPEC4);
|
||||
return 0;
|
||||
case ETMIMPSPEC5:
|
||||
*val = etm_read(ETMIMPSPEC5);
|
||||
return 0;
|
||||
case ETMIMPSPEC6:
|
||||
*val = etm_read(ETMIMPSPEC6);
|
||||
return 0;
|
||||
case ETMIMPSPEC7:
|
||||
*val = etm_read(ETMIMPSPEC7);
|
||||
return 0;
|
||||
case ETMSYNCFR:
|
||||
*val = etm_read(ETMSYNCFR);
|
||||
return 0;
|
||||
case ETMIDR:
|
||||
*val = etm_read(ETMIDR);
|
||||
return 0;
|
||||
case ETMCCER:
|
||||
*val = etm_read(ETMCCER);
|
||||
return 0;
|
||||
case ETMEXTINSELR:
|
||||
*val = etm_read(ETMEXTINSELR);
|
||||
return 0;
|
||||
case ETMTESSEICR:
|
||||
*val = etm_read(ETMTESSEICR);
|
||||
return 0;
|
||||
case ETMEIBCR:
|
||||
*val = etm_read(ETMEIBCR);
|
||||
return 0;
|
||||
case ETMTSEVR:
|
||||
*val = etm_read(ETMTSEVR);
|
||||
return 0;
|
||||
case ETMAUXCR:
|
||||
*val = etm_read(ETMAUXCR);
|
||||
return 0;
|
||||
case ETMTRACEIDR:
|
||||
*val = etm_read(ETMTRACEIDR);
|
||||
return 0;
|
||||
case ETMVMIDCVR:
|
||||
*val = etm_read(ETMVMIDCVR);
|
||||
return 0;
|
||||
case ETMOSLSR:
|
||||
*val = etm_read(ETMOSLSR);
|
||||
return 0;
|
||||
case ETMOSSRR:
|
||||
*val = etm_read(ETMOSSRR);
|
||||
return 0;
|
||||
case ETMPDCR:
|
||||
*val = etm_read(ETMPDCR);
|
||||
return 0;
|
||||
case ETMPDSR:
|
||||
*val = etm_read(ETMPDSR);
|
||||
return 0;
|
||||
default:
|
||||
*val = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int etm_writel_cp14(u32 reg, u32 val)
|
||||
{
|
||||
switch (reg) {
|
||||
case ETMCR:
|
||||
etm_write(val, ETMCR);
|
||||
break;
|
||||
case ETMTRIGGER:
|
||||
etm_write(val, ETMTRIGGER);
|
||||
break;
|
||||
case ETMSR:
|
||||
etm_write(val, ETMSR);
|
||||
break;
|
||||
case ETMTSSCR:
|
||||
etm_write(val, ETMTSSCR);
|
||||
break;
|
||||
case ETMTEEVR:
|
||||
etm_write(val, ETMTEEVR);
|
||||
break;
|
||||
case ETMTECR1:
|
||||
etm_write(val, ETMTECR1);
|
||||
break;
|
||||
case ETMFFLR:
|
||||
etm_write(val, ETMFFLR);
|
||||
break;
|
||||
case ETMACVRn(0):
|
||||
etm_write(val, ETMACVR0);
|
||||
break;
|
||||
case ETMACVRn(1):
|
||||
etm_write(val, ETMACVR1);
|
||||
break;
|
||||
case ETMACVRn(2):
|
||||
etm_write(val, ETMACVR2);
|
||||
break;
|
||||
case ETMACVRn(3):
|
||||
etm_write(val, ETMACVR3);
|
||||
break;
|
||||
case ETMACVRn(4):
|
||||
etm_write(val, ETMACVR4);
|
||||
break;
|
||||
case ETMACVRn(5):
|
||||
etm_write(val, ETMACVR5);
|
||||
break;
|
||||
case ETMACVRn(6):
|
||||
etm_write(val, ETMACVR6);
|
||||
break;
|
||||
case ETMACVRn(7):
|
||||
etm_write(val, ETMACVR7);
|
||||
break;
|
||||
case ETMACVRn(8):
|
||||
etm_write(val, ETMACVR8);
|
||||
break;
|
||||
case ETMACVRn(9):
|
||||
etm_write(val, ETMACVR9);
|
||||
break;
|
||||
case ETMACVRn(10):
|
||||
etm_write(val, ETMACVR10);
|
||||
break;
|
||||
case ETMACVRn(11):
|
||||
etm_write(val, ETMACVR11);
|
||||
break;
|
||||
case ETMACVRn(12):
|
||||
etm_write(val, ETMACVR12);
|
||||
break;
|
||||
case ETMACVRn(13):
|
||||
etm_write(val, ETMACVR13);
|
||||
break;
|
||||
case ETMACVRn(14):
|
||||
etm_write(val, ETMACVR14);
|
||||
break;
|
||||
case ETMACVRn(15):
|
||||
etm_write(val, ETMACVR15);
|
||||
break;
|
||||
case ETMACTRn(0):
|
||||
etm_write(val, ETMACTR0);
|
||||
break;
|
||||
case ETMACTRn(1):
|
||||
etm_write(val, ETMACTR1);
|
||||
break;
|
||||
case ETMACTRn(2):
|
||||
etm_write(val, ETMACTR2);
|
||||
break;
|
||||
case ETMACTRn(3):
|
||||
etm_write(val, ETMACTR3);
|
||||
break;
|
||||
case ETMACTRn(4):
|
||||
etm_write(val, ETMACTR4);
|
||||
break;
|
||||
case ETMACTRn(5):
|
||||
etm_write(val, ETMACTR5);
|
||||
break;
|
||||
case ETMACTRn(6):
|
||||
etm_write(val, ETMACTR6);
|
||||
break;
|
||||
case ETMACTRn(7):
|
||||
etm_write(val, ETMACTR7);
|
||||
break;
|
||||
case ETMACTRn(8):
|
||||
etm_write(val, ETMACTR8);
|
||||
break;
|
||||
case ETMACTRn(9):
|
||||
etm_write(val, ETMACTR9);
|
||||
break;
|
||||
case ETMACTRn(10):
|
||||
etm_write(val, ETMACTR10);
|
||||
break;
|
||||
case ETMACTRn(11):
|
||||
etm_write(val, ETMACTR11);
|
||||
break;
|
||||
case ETMACTRn(12):
|
||||
etm_write(val, ETMACTR12);
|
||||
break;
|
||||
case ETMACTRn(13):
|
||||
etm_write(val, ETMACTR13);
|
||||
break;
|
||||
case ETMACTRn(14):
|
||||
etm_write(val, ETMACTR14);
|
||||
break;
|
||||
case ETMACTRn(15):
|
||||
etm_write(val, ETMACTR15);
|
||||
break;
|
||||
case ETMCNTRLDVRn(0):
|
||||
etm_write(val, ETMCNTRLDVR0);
|
||||
break;
|
||||
case ETMCNTRLDVRn(1):
|
||||
etm_write(val, ETMCNTRLDVR1);
|
||||
break;
|
||||
case ETMCNTRLDVRn(2):
|
||||
etm_write(val, ETMCNTRLDVR2);
|
||||
break;
|
||||
case ETMCNTRLDVRn(3):
|
||||
etm_write(val, ETMCNTRLDVR3);
|
||||
break;
|
||||
case ETMCNTENRn(0):
|
||||
etm_write(val, ETMCNTENR0);
|
||||
break;
|
||||
case ETMCNTENRn(1):
|
||||
etm_write(val, ETMCNTENR1);
|
||||
break;
|
||||
case ETMCNTENRn(2):
|
||||
etm_write(val, ETMCNTENR2);
|
||||
break;
|
||||
case ETMCNTENRn(3):
|
||||
etm_write(val, ETMCNTENR3);
|
||||
break;
|
||||
case ETMCNTRLDEVRn(0):
|
||||
etm_write(val, ETMCNTRLDEVR0);
|
||||
break;
|
||||
case ETMCNTRLDEVRn(1):
|
||||
etm_write(val, ETMCNTRLDEVR1);
|
||||
break;
|
||||
case ETMCNTRLDEVRn(2):
|
||||
etm_write(val, ETMCNTRLDEVR2);
|
||||
break;
|
||||
case ETMCNTRLDEVRn(3):
|
||||
etm_write(val, ETMCNTRLDEVR3);
|
||||
break;
|
||||
case ETMCNTVRn(0):
|
||||
etm_write(val, ETMCNTVR0);
|
||||
break;
|
||||
case ETMCNTVRn(1):
|
||||
etm_write(val, ETMCNTVR1);
|
||||
break;
|
||||
case ETMCNTVRn(2):
|
||||
etm_write(val, ETMCNTVR2);
|
||||
break;
|
||||
case ETMCNTVRn(3):
|
||||
etm_write(val, ETMCNTVR3);
|
||||
break;
|
||||
case ETMSQ12EVR:
|
||||
etm_write(val, ETMSQ12EVR);
|
||||
break;
|
||||
case ETMSQ21EVR:
|
||||
etm_write(val, ETMSQ21EVR);
|
||||
break;
|
||||
case ETMSQ23EVR:
|
||||
etm_write(val, ETMSQ23EVR);
|
||||
break;
|
||||
case ETMSQ31EVR:
|
||||
etm_write(val, ETMSQ31EVR);
|
||||
break;
|
||||
case ETMSQ32EVR:
|
||||
etm_write(val, ETMSQ32EVR);
|
||||
break;
|
||||
case ETMSQ13EVR:
|
||||
etm_write(val, ETMSQ13EVR);
|
||||
break;
|
||||
case ETMSQR:
|
||||
etm_write(val, ETMSQR);
|
||||
break;
|
||||
case ETMEXTOUTEVRn(0):
|
||||
etm_write(val, ETMEXTOUTEVR0);
|
||||
break;
|
||||
case ETMEXTOUTEVRn(1):
|
||||
etm_write(val, ETMEXTOUTEVR1);
|
||||
break;
|
||||
case ETMEXTOUTEVRn(2):
|
||||
etm_write(val, ETMEXTOUTEVR2);
|
||||
break;
|
||||
case ETMEXTOUTEVRn(3):
|
||||
etm_write(val, ETMEXTOUTEVR3);
|
||||
break;
|
||||
case ETMCIDCVRn(0):
|
||||
etm_write(val, ETMCIDCVR0);
|
||||
break;
|
||||
case ETMCIDCVRn(1):
|
||||
etm_write(val, ETMCIDCVR1);
|
||||
break;
|
||||
case ETMCIDCVRn(2):
|
||||
etm_write(val, ETMCIDCVR2);
|
||||
break;
|
||||
case ETMCIDCMR:
|
||||
etm_write(val, ETMCIDCMR);
|
||||
break;
|
||||
case ETMIMPSPEC0:
|
||||
etm_write(val, ETMIMPSPEC0);
|
||||
break;
|
||||
case ETMIMPSPEC1:
|
||||
etm_write(val, ETMIMPSPEC1);
|
||||
break;
|
||||
case ETMIMPSPEC2:
|
||||
etm_write(val, ETMIMPSPEC2);
|
||||
break;
|
||||
case ETMIMPSPEC3:
|
||||
etm_write(val, ETMIMPSPEC3);
|
||||
break;
|
||||
case ETMIMPSPEC4:
|
||||
etm_write(val, ETMIMPSPEC4);
|
||||
break;
|
||||
case ETMIMPSPEC5:
|
||||
etm_write(val, ETMIMPSPEC5);
|
||||
break;
|
||||
case ETMIMPSPEC6:
|
||||
etm_write(val, ETMIMPSPEC6);
|
||||
break;
|
||||
case ETMIMPSPEC7:
|
||||
etm_write(val, ETMIMPSPEC7);
|
||||
break;
|
||||
case ETMSYNCFR:
|
||||
etm_write(val, ETMSYNCFR);
|
||||
break;
|
||||
case ETMEXTINSELR:
|
||||
etm_write(val, ETMEXTINSELR);
|
||||
break;
|
||||
case ETMTESSEICR:
|
||||
etm_write(val, ETMTESSEICR);
|
||||
break;
|
||||
case ETMEIBCR:
|
||||
etm_write(val, ETMEIBCR);
|
||||
break;
|
||||
case ETMTSEVR:
|
||||
etm_write(val, ETMTSEVR);
|
||||
break;
|
||||
case ETMAUXCR:
|
||||
etm_write(val, ETMAUXCR);
|
||||
break;
|
||||
case ETMTRACEIDR:
|
||||
etm_write(val, ETMTRACEIDR);
|
||||
break;
|
||||
case ETMVMIDCVR:
|
||||
etm_write(val, ETMVMIDCVR);
|
||||
break;
|
||||
case ETMOSLAR:
|
||||
etm_write(val, ETMOSLAR);
|
||||
break;
|
||||
case ETMOSSRR:
|
||||
etm_write(val, ETMOSSRR);
|
||||
break;
|
||||
case ETMPDCR:
|
||||
etm_write(val, ETMPDCR);
|
||||
break;
|
||||
case ETMPDSR:
|
||||
etm_write(val, ETMPDSR);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
251
drivers/coresight/coresight-etm.h
Normal file
251
drivers/coresight/coresight-etm.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
||||
#define _CORESIGHT_CORESIGHT_ETM_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "coresight-priv.h"
|
||||
|
||||
/*
|
||||
* Device registers:
|
||||
* 0x000 - 0x2FC: Trace registers
|
||||
* 0x300 - 0x314: Management registers
|
||||
* 0x318 - 0xEFC: Trace registers
|
||||
*
|
||||
* Coresight registers
|
||||
* 0xF00 - 0xF9C: Management registers
|
||||
* 0xFA0 - 0xFA4: Management registers in PFTv1.0
|
||||
* Trace registers in PFTv1.1
|
||||
* 0xFA8 - 0xFFC: Management registers
|
||||
*/
|
||||
|
||||
/* Trace registers (0x000-0x2FC) */
|
||||
#define ETMCR 0x000
|
||||
#define ETMCCR 0x004
|
||||
#define ETMTRIGGER 0x008
|
||||
#define ETMSR 0x010
|
||||
#define ETMSCR 0x014
|
||||
#define ETMTSSCR 0x018
|
||||
#define ETMTECR2 0x01c
|
||||
#define ETMTEEVR 0x020
|
||||
#define ETMTECR1 0x024
|
||||
#define ETMFFLR 0x02c
|
||||
#define ETMACVRn(n) (0x040 + (n * 4))
|
||||
#define ETMACTRn(n) (0x080 + (n * 4))
|
||||
#define ETMCNTRLDVRn(n) (0x140 + (n * 4))
|
||||
#define ETMCNTENRn(n) (0x150 + (n * 4))
|
||||
#define ETMCNTRLDEVRn(n) (0x160 + (n * 4))
|
||||
#define ETMCNTVRn(n) (0x170 + (n * 4))
|
||||
#define ETMSQ12EVR 0x180
|
||||
#define ETMSQ21EVR 0x184
|
||||
#define ETMSQ23EVR 0x188
|
||||
#define ETMSQ31EVR 0x18c
|
||||
#define ETMSQ32EVR 0x190
|
||||
#define ETMSQ13EVR 0x194
|
||||
#define ETMSQR 0x19c
|
||||
#define ETMEXTOUTEVRn(n) (0x1a0 + (n * 4))
|
||||
#define ETMCIDCVRn(n) (0x1b0 + (n * 4))
|
||||
#define ETMCIDCMR 0x1bc
|
||||
#define ETMIMPSPEC0 0x1c0
|
||||
#define ETMIMPSPEC1 0x1c4
|
||||
#define ETMIMPSPEC2 0x1c8
|
||||
#define ETMIMPSPEC3 0x1cc
|
||||
#define ETMIMPSPEC4 0x1d0
|
||||
#define ETMIMPSPEC5 0x1d4
|
||||
#define ETMIMPSPEC6 0x1d8
|
||||
#define ETMIMPSPEC7 0x1dc
|
||||
#define ETMSYNCFR 0x1e0
|
||||
#define ETMIDR 0x1e4
|
||||
#define ETMCCER 0x1e8
|
||||
#define ETMEXTINSELR 0x1ec
|
||||
#define ETMTESSEICR 0x1f0
|
||||
#define ETMEIBCR 0x1f4
|
||||
#define ETMTSEVR 0x1f8
|
||||
#define ETMAUXCR 0x1fc
|
||||
#define ETMTRACEIDR 0x200
|
||||
#define ETMVMIDCVR 0x240
|
||||
/* Management registers (0x300-0x314) */
|
||||
#define ETMOSLAR 0x300
|
||||
#define ETMOSLSR 0x304
|
||||
#define ETMOSSRR 0x308
|
||||
#define ETMPDCR 0x310
|
||||
#define ETMPDSR 0x314
|
||||
#define ETM_MAX_ADDR_CMP 16
|
||||
#define ETM_MAX_CNTR 4
|
||||
#define ETM_MAX_CTXID_CMP 3
|
||||
|
||||
/* Register definition */
|
||||
/* ETMCR - 0x00 */
|
||||
#define ETMCR_PWD_DWN BIT(0)
|
||||
#define ETMCR_STALL_MODE BIT(7)
|
||||
#define ETMCR_ETM_PRG BIT(10)
|
||||
#define ETMCR_ETM_EN BIT(11)
|
||||
#define ETMCR_CYC_ACC BIT(12)
|
||||
#define ETMCR_CTXID_SIZE (BIT(14)|BIT(15))
|
||||
#define ETMCR_TIMESTAMP_EN BIT(28)
|
||||
/* ETMCCR - 0x04 */
|
||||
#define ETMCCR_FIFOFULL BIT(23)
|
||||
/* ETMPDCR - 0x310 */
|
||||
#define ETMPDCR_PWD_UP BIT(3)
|
||||
/* ETMTECR1 - 0x024 */
|
||||
#define ETMTECR1_ADDR_COMP_1 BIT(0)
|
||||
#define ETMTECR1_INC_EXC BIT(24)
|
||||
#define ETMTECR1_START_STOP BIT(25)
|
||||
/* ETMCCER - 0x1E8 */
|
||||
#define ETMCCER_TIMESTAMP BIT(22)
|
||||
|
||||
#define ETM_MODE_EXCLUDE BIT(0)
|
||||
#define ETM_MODE_CYCACC BIT(1)
|
||||
#define ETM_MODE_STALL BIT(2)
|
||||
#define ETM_MODE_TIMESTAMP BIT(3)
|
||||
#define ETM_MODE_CTXID BIT(4)
|
||||
#define ETM_MODE_ALL 0x1f
|
||||
|
||||
#define ETM_SQR_MASK 0x3
|
||||
#define ETM_TRACEID_MASK 0x3f
|
||||
#define ETM_EVENT_MASK 0x1ffff
|
||||
#define ETM_SYNC_MASK 0xfff
|
||||
#define ETM_ALL_MASK 0xffffffff
|
||||
|
||||
#define ETMSR_PROG_BIT 1
|
||||
#define ETM_SEQ_STATE_MAX_VAL (0x2)
|
||||
#define PORT_SIZE_MASK (GENMASK(21, 21) | GENMASK(6, 4))
|
||||
|
||||
#define ETM_HARD_WIRE_RES_A /* Hard wired, always true */ \
|
||||
((0x0f << 0) | \
|
||||
/* Resource index A */ \
|
||||
(0x06 << 4))
|
||||
|
||||
#define ETM_ADD_COMP_0 /* Single addr comparator 1 */ \
|
||||
((0x00 << 7) | \
|
||||
/* Resource index B */ \
|
||||
(0x00 << 11))
|
||||
|
||||
#define ETM_EVENT_NOT_A BIT(14) /* NOT(A) */
|
||||
|
||||
#define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \
|
||||
ETM_ADD_COMP_0 | \
|
||||
ETM_EVENT_NOT_A)
|
||||
/**
|
||||
* struct etm_drvdata - specifics associated to an ETM component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @cpu: the cpu this component is affined to.
|
||||
* @port_size: port size as reported by ETMCR bit 4-6 and 21.
|
||||
* @arch: ETM/PTM version number.
|
||||
* @use_cpu14: true if management registers need to be accessed via CP14.
|
||||
* @enable: is this ETM/PTM currently tracing.
|
||||
* @sticky_enable: true if ETM base configuration has been done.
|
||||
* @boot_enable:true if we should start tracing at boot time.
|
||||
* @os_unlock: true if access to management registers is allowed.
|
||||
* @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
|
||||
* @nr_cntr: Number of counters as found in ETMCCR bit 13-15.
|
||||
* @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19.
|
||||
* @nr_ext_out: Number of external output as found in ETMCCR bit 20-22.
|
||||
* @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
|
||||
* @etmccr: value of register ETMCCR.
|
||||
* @etmccer: value of register ETMCCER.
|
||||
* @traceid: value of the current ID for this component.
|
||||
* @mode: controls various modes supported by this ETM/PTM.
|
||||
* @ctrl: used in conjunction with @mode.
|
||||
* @trigger_event: setting for register ETMTRIGGER.
|
||||
* @startstop_ctrl: setting for register ETMTSSCR.
|
||||
* @enable_event: setting for register ETMTEEVR.
|
||||
* @enable_ctrl1: setting for register ETMTECR1.
|
||||
* @fifofull_level: setting for register ETMFFLR.
|
||||
* @addr_idx: index for the address comparator selection.
|
||||
* @addr_val: value for address comparator register.
|
||||
* @addr_acctype: access type for address comparator register.
|
||||
* @addr_type: current status of the comparator register.
|
||||
* @cntr_idx: index for the counter register selection.
|
||||
* @cntr_rld_val: reload value of a counter register.
|
||||
* @cntr_event: control for counter enable register.
|
||||
* @cntr_rld_event: value for counter reload event register.
|
||||
* @cntr_val: counter value register.
|
||||
* @seq_12_event: event causing the transition from 1 to 2.
|
||||
* @seq_21_event: event causing the transition from 2 to 1.
|
||||
* @seq_23_event: event causing the transition from 2 to 3.
|
||||
* @seq_31_event: event causing the transition from 3 to 1.
|
||||
* @seq_32_event: event causing the transition from 3 to 2.
|
||||
* @seq_13_event: event causing the transition from 1 to 3.
|
||||
* @seq_curr_state: current value of the sequencer register.
|
||||
* @ctxid_idx: index for the context ID registers.
|
||||
* @ctxid_val: value for the context ID to trigger on.
|
||||
* @ctxid_mask: mask applicable to all the context IDs.
|
||||
* @sync_freq: Synchronisation frequency.
|
||||
* @timestamp_event: Defines an event that requests the insertion
|
||||
of a timestamp into the trace stream.
|
||||
*/
|
||||
struct etm_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
int cpu;
|
||||
int port_size;
|
||||
u8 arch;
|
||||
bool use_cp14;
|
||||
bool enable;
|
||||
bool sticky_enable;
|
||||
bool boot_enable;
|
||||
bool os_unlock;
|
||||
u8 nr_addr_cmp;
|
||||
u8 nr_cntr;
|
||||
u8 nr_ext_inp;
|
||||
u8 nr_ext_out;
|
||||
u8 nr_ctxid_cmp;
|
||||
u32 etmccr;
|
||||
u32 etmccer;
|
||||
u32 traceid;
|
||||
u32 mode;
|
||||
u32 ctrl;
|
||||
u32 trigger_event;
|
||||
u32 startstop_ctrl;
|
||||
u32 enable_event;
|
||||
u32 enable_ctrl1;
|
||||
u32 fifofull_level;
|
||||
u8 addr_idx;
|
||||
u32 addr_val[ETM_MAX_ADDR_CMP];
|
||||
u32 addr_acctype[ETM_MAX_ADDR_CMP];
|
||||
u32 addr_type[ETM_MAX_ADDR_CMP];
|
||||
u8 cntr_idx;
|
||||
u32 cntr_rld_val[ETM_MAX_CNTR];
|
||||
u32 cntr_event[ETM_MAX_CNTR];
|
||||
u32 cntr_rld_event[ETM_MAX_CNTR];
|
||||
u32 cntr_val[ETM_MAX_CNTR];
|
||||
u32 seq_12_event;
|
||||
u32 seq_21_event;
|
||||
u32 seq_23_event;
|
||||
u32 seq_31_event;
|
||||
u32 seq_32_event;
|
||||
u32 seq_13_event;
|
||||
u32 seq_curr_state;
|
||||
u8 ctxid_idx;
|
||||
u32 ctxid_val[ETM_MAX_CTXID_CMP];
|
||||
u32 ctxid_mask;
|
||||
u32 sync_freq;
|
||||
u32 timestamp_event;
|
||||
};
|
||||
|
||||
enum etm_addr_type {
|
||||
ETM_ADDR_TYPE_NONE,
|
||||
ETM_ADDR_TYPE_SINGLE,
|
||||
ETM_ADDR_TYPE_RANGE,
|
||||
ETM_ADDR_TYPE_START,
|
||||
ETM_ADDR_TYPE_STOP,
|
||||
};
|
||||
#endif
|
||||
1928
drivers/coresight/coresight-etm3x.c
Normal file
1928
drivers/coresight/coresight-etm3x.c
Normal file
File diff suppressed because it is too large
Load Diff
268
drivers/coresight/coresight-funnel.c
Normal file
268
drivers/coresight/coresight-funnel.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
#define FUNNEL_FUNCTL 0x000
|
||||
#define FUNNEL_PRICTL 0x004
|
||||
|
||||
#define FUNNEL_HOLDTIME_MASK 0xf00
|
||||
#define FUNNEL_HOLDTIME_SHFT 0x8
|
||||
#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
|
||||
|
||||
/**
|
||||
* struct funnel_drvdata - specifics associated to a funnel component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @priority: port selection order.
|
||||
*/
|
||||
struct funnel_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
unsigned long priority;
|
||||
};
|
||||
|
||||
static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
|
||||
{
|
||||
u32 functl;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
|
||||
functl &= ~FUNNEL_HOLDTIME_MASK;
|
||||
functl |= FUNNEL_HOLDTIME;
|
||||
functl |= (1 << port);
|
||||
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
|
||||
writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int funnel_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
funnel_enable_hw(drvdata, inport);
|
||||
|
||||
dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
|
||||
{
|
||||
u32 functl;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
|
||||
functl &= ~(1 << inport);
|
||||
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void funnel_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
funnel_disable_hw(drvdata, inport);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link funnel_link_ops = {
|
||||
.enable = funnel_enable,
|
||||
.disable = funnel_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops funnel_cs_ops = {
|
||||
.link_ops = &funnel_link_ops,
|
||||
};
|
||||
|
||||
static ssize_t priority_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
unsigned long val = drvdata->priority;
|
||||
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
|
||||
static ssize_t priority_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val;
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = kstrtoul(buf, 16, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->priority = val;
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(priority);
|
||||
|
||||
static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
|
||||
{
|
||||
u32 functl;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
return functl;
|
||||
}
|
||||
|
||||
static ssize_t funnel_ctrl_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = get_funnel_ctrl_hw(drvdata);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
return sprintf(buf, "%#x\n", val);
|
||||
}
|
||||
static DEVICE_ATTR_RO(funnel_ctrl);
|
||||
|
||||
static struct attribute *coresight_funnel_attrs[] = {
|
||||
&dev_attr_funnel_ctrl.attr,
|
||||
&dev_attr_priority.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(coresight_funnel);
|
||||
|
||||
static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct funnel_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
|
||||
desc->ops = &funnel_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_funnel_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
dev_info(dev, "FUNNEL initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int funnel_remove(struct amba_device *adev)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id funnel_ids[] = {
|
||||
{
|
||||
.id = 0x0003b908,
|
||||
.mask = 0x0003ffff,
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
static struct amba_driver funnel_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-funnel",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = funnel_probe,
|
||||
.remove = funnel_remove,
|
||||
.id_table = funnel_ids,
|
||||
};
|
||||
|
||||
static int __init funnel_init(void)
|
||||
{
|
||||
return amba_driver_register(&funnel_driver);
|
||||
}
|
||||
module_init(funnel_init);
|
||||
|
||||
static void __exit funnel_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&funnel_driver);
|
||||
}
|
||||
module_exit(funnel_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Funnel driver");
|
||||
63
drivers/coresight/coresight-priv.h
Normal file
63
drivers/coresight/coresight-priv.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _CORESIGHT_PRIV_H
|
||||
#define _CORESIGHT_PRIV_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/coresight.h>
|
||||
|
||||
/*
|
||||
* Coresight management registers (0xf00-0xfcc)
|
||||
* 0xfa0 - 0xfa4: Management registers in PFTv1.0
|
||||
* Trace registers in PFTv1.1
|
||||
*/
|
||||
#define CORESIGHT_ITCTRL 0xf00
|
||||
#define CORESIGHT_CLAIMSET 0xfa0
|
||||
#define CORESIGHT_CLAIMCLR 0xfa4
|
||||
#define CORESIGHT_LAR 0xfb0
|
||||
#define CORESIGHT_LSR 0xfb4
|
||||
#define CORESIGHT_AUTHSTATUS 0xfb8
|
||||
#define CORESIGHT_DEVID 0xfc8
|
||||
#define CORESIGHT_DEVTYPE 0xfcc
|
||||
|
||||
#define TIMEOUT_US 100
|
||||
#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb)
|
||||
|
||||
static inline void CS_LOCK(void __iomem *addr)
|
||||
{
|
||||
do {
|
||||
/* Wait for things to settle */
|
||||
mb();
|
||||
writel_relaxed(0x0, addr + CORESIGHT_LAR);
|
||||
} while (0);
|
||||
}
|
||||
|
||||
static inline void CS_UNLOCK(void __iomem *addr)
|
||||
{
|
||||
do {
|
||||
writel_relaxed(CORESIGHT_UNLOCK, addr + CORESIGHT_LAR);
|
||||
/* Make sure everyone has seen this */
|
||||
mb();
|
||||
} while (0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
|
||||
extern int etm_readl_cp14(u32 off, unsigned int *val);
|
||||
extern int etm_writel_cp14(u32 off, u32 val);
|
||||
#else
|
||||
static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
|
||||
static inline int etm_writel_cp14(u32 val, u32 off) { return 0; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
137
drivers/coresight/coresight-replicator.c
Normal file
137
drivers/coresight/coresight-replicator.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/coresight.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
/**
|
||||
* struct replicator_drvdata - specifics associated to a replicator component
|
||||
* @dev: the device entity associated with this component
|
||||
* @csdev: component vitals needed by the framework
|
||||
*/
|
||||
struct replicator_drvdata {
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
};
|
||||
|
||||
static int replicator_enable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
dev_info(drvdata->dev, "REPLICATOR enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void replicator_disable(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
dev_info(drvdata->dev, "REPLICATOR disabled\n");
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link replicator_link_ops = {
|
||||
.enable = replicator_enable,
|
||||
.disable = replicator_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops replicator_cs_ops = {
|
||||
.link_ops = &replicator_link_ops,
|
||||
};
|
||||
|
||||
static int replicator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct replicator_drvdata *drvdata;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
pdev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc->ops = &replicator_cs_ops;
|
||||
desc->pdata = pdev->dev.platform_data;
|
||||
desc->dev = &pdev->dev;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
dev_info(dev, "REPLICATOR initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replicator_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct replicator_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id replicator_match[] = {
|
||||
{.compatible = "arm,coresight-replicator"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver replicator_driver = {
|
||||
.probe = replicator_probe,
|
||||
.remove = replicator_remove,
|
||||
.driver = {
|
||||
.name = "coresight-replicator",
|
||||
.of_match_table = replicator_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init replicator_init(void)
|
||||
{
|
||||
return platform_driver_register(&replicator_driver);
|
||||
}
|
||||
module_init(replicator_init);
|
||||
|
||||
static void __exit replicator_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&replicator_driver);
|
||||
}
|
||||
module_exit(replicator_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Replicator driver");
|
||||
776
drivers/coresight/coresight-tmc.c
Normal file
776
drivers/coresight/coresight-tmc.c
Normal file
@@ -0,0 +1,776 @@
|
||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
#define TMC_RSZ 0x004
|
||||
#define TMC_STS 0x00c
|
||||
#define TMC_RRD 0x010
|
||||
#define TMC_RRP 0x014
|
||||
#define TMC_RWP 0x018
|
||||
#define TMC_TRG 0x01c
|
||||
#define TMC_CTL 0x020
|
||||
#define TMC_RWD 0x024
|
||||
#define TMC_MODE 0x028
|
||||
#define TMC_LBUFLEVEL 0x02c
|
||||
#define TMC_CBUFLEVEL 0x030
|
||||
#define TMC_BUFWM 0x034
|
||||
#define TMC_RRPHI 0x038
|
||||
#define TMC_RWPHI 0x03c
|
||||
#define TMC_AXICTL 0x110
|
||||
#define TMC_DBALO 0x118
|
||||
#define TMC_DBAHI 0x11c
|
||||
#define TMC_FFSR 0x300
|
||||
#define TMC_FFCR 0x304
|
||||
#define TMC_PSCR 0x308
|
||||
#define TMC_ITMISCOP0 0xee0
|
||||
#define TMC_ITTRFLIN 0xee8
|
||||
#define TMC_ITATBDATA0 0xeec
|
||||
#define TMC_ITATBCTR2 0xef0
|
||||
#define TMC_ITATBCTR1 0xef4
|
||||
#define TMC_ITATBCTR0 0xef8
|
||||
|
||||
/* register description */
|
||||
/* TMC_CTL - 0x020 */
|
||||
#define TMC_CTL_CAPT_EN BIT(0)
|
||||
/* TMC_STS - 0x00C */
|
||||
#define TMC_STS_TRIGGERED BIT(1)
|
||||
/* TMC_AXICTL - 0x110 */
|
||||
#define TMC_AXICTL_PROT_CTL_B0 BIT(0)
|
||||
#define TMC_AXICTL_PROT_CTL_B1 BIT(1)
|
||||
#define TMC_AXICTL_SCT_GAT_MODE BIT(7)
|
||||
#define TMC_AXICTL_WR_BURST_LEN 0xF00
|
||||
/* TMC_FFCR - 0x304 */
|
||||
#define TMC_FFCR_EN_FMT BIT(0)
|
||||
#define TMC_FFCR_EN_TI BIT(1)
|
||||
#define TMC_FFCR_FON_FLIN BIT(4)
|
||||
#define TMC_FFCR_FON_TRIG_EVT BIT(5)
|
||||
#define TMC_FFCR_FLUSHMAN BIT(6)
|
||||
#define TMC_FFCR_TRIGON_TRIGIN BIT(8)
|
||||
#define TMC_FFCR_STOP_ON_FLUSH BIT(12)
|
||||
|
||||
#define TMC_STS_TRIGGERED_BIT 2
|
||||
#define TMC_FFCR_FLUSHMAN_BIT 6
|
||||
|
||||
enum tmc_config_type {
|
||||
TMC_CONFIG_TYPE_ETB,
|
||||
TMC_CONFIG_TYPE_ETR,
|
||||
TMC_CONFIG_TYPE_ETF,
|
||||
};
|
||||
|
||||
enum tmc_mode {
|
||||
TMC_MODE_CIRCULAR_BUFFER,
|
||||
TMC_MODE_SOFTWARE_FIFO,
|
||||
TMC_MODE_HARDWARE_FIFO,
|
||||
};
|
||||
|
||||
enum tmc_mem_intf_width {
|
||||
TMC_MEM_INTF_WIDTH_32BITS = 0x2,
|
||||
TMC_MEM_INTF_WIDTH_64BITS = 0x3,
|
||||
TMC_MEM_INTF_WIDTH_128BITS = 0x4,
|
||||
TMC_MEM_INTF_WIDTH_256BITS = 0x5,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tmc_drvdata - specifics associated to an TMC component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
|
||||
* @clk: the clock this component is associated to.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @read_count: manages preparation of buffer for reading.
|
||||
* @buf: area of memory where trace data get sent.
|
||||
* @paddr: DMA start location in RAM.
|
||||
* @vaddr: virtual representation of @paddr.
|
||||
* @size: @buf size.
|
||||
* @enable: this TMC is being used.
|
||||
* @config_type: TMC variant, must be of type @tmc_config_type.
|
||||
* @trigger_cntr: amount of words to store after a trigger.
|
||||
*/
|
||||
struct tmc_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
struct clk *clk;
|
||||
spinlock_t spinlock;
|
||||
int read_count;
|
||||
bool reading;
|
||||
char *buf;
|
||||
dma_addr_t paddr;
|
||||
void __iomem *vaddr;
|
||||
u32 size;
|
||||
bool enable;
|
||||
enum tmc_config_type config_type;
|
||||
u32 trigger_cntr;
|
||||
};
|
||||
|
||||
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
/* Ensure formatter, unformatter and hardware fifo are empty */
|
||||
if (coresight_timeout(drvdata->base,
|
||||
TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TMC_STS);
|
||||
}
|
||||
}
|
||||
|
||||
static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
u32 ffcr;
|
||||
|
||||
ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
|
||||
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
|
||||
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
|
||||
ffcr |= TMC_FFCR_FLUSHMAN;
|
||||
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
|
||||
/* Ensure flush completes */
|
||||
if (coresight_timeout(drvdata->base,
|
||||
TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TMC_FFCR);
|
||||
}
|
||||
|
||||
tmc_wait_for_ready(drvdata);
|
||||
}
|
||||
|
||||
static void tmc_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
|
||||
}
|
||||
|
||||
static void tmc_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
writel_relaxed(0x0, drvdata->base + TMC_CTL);
|
||||
}
|
||||
|
||||
static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
/* Zero out the memory to help with debug */
|
||||
memset(drvdata->buf, 0, drvdata->size);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
|
||||
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
|
||||
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
|
||||
TMC_FFCR_TRIGON_TRIGIN,
|
||||
drvdata->base + TMC_FFCR);
|
||||
|
||||
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
|
||||
tmc_enable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
u32 axictl;
|
||||
|
||||
/* Zero out the memory to help with debug */
|
||||
memset(drvdata->vaddr, 0, drvdata->size);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
|
||||
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
|
||||
|
||||
axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
|
||||
axictl |= TMC_AXICTL_WR_BURST_LEN;
|
||||
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
|
||||
axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
|
||||
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
|
||||
axictl = (axictl &
|
||||
~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
|
||||
TMC_AXICTL_PROT_CTL_B1;
|
||||
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
|
||||
|
||||
writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
|
||||
writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
|
||||
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
|
||||
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
|
||||
TMC_FFCR_TRIGON_TRIGIN,
|
||||
drvdata->base + TMC_FFCR);
|
||||
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
|
||||
tmc_enable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
|
||||
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
|
||||
drvdata->base + TMC_FFCR);
|
||||
writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
|
||||
tmc_enable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
tmc_etb_enable_hw(drvdata);
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
tmc_etr_enable_hw(drvdata);
|
||||
} else {
|
||||
if (mode == TMC_MODE_CIRCULAR_BUFFER)
|
||||
tmc_etb_enable_hw(drvdata);
|
||||
else
|
||||
tmc_etf_enable_hw(drvdata);
|
||||
}
|
||||
drvdata->enable = true;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_info(drvdata->dev, "TMC enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_enable_sink(struct coresight_device *csdev)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
|
||||
}
|
||||
|
||||
static int tmc_enable_link(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
|
||||
}
|
||||
|
||||
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
enum tmc_mem_intf_width memwidth;
|
||||
u8 memwords;
|
||||
char *bufp;
|
||||
u32 read_data;
|
||||
int i;
|
||||
|
||||
memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
|
||||
if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
|
||||
memwords = 1;
|
||||
else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
|
||||
memwords = 2;
|
||||
else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
|
||||
memwords = 4;
|
||||
else
|
||||
memwords = 8;
|
||||
|
||||
bufp = drvdata->buf;
|
||||
while (1) {
|
||||
for (i = 0; i < memwords; i++) {
|
||||
read_data = readl_relaxed(drvdata->base + TMC_RRD);
|
||||
if (read_data == 0xFFFFFFFF)
|
||||
return;
|
||||
memcpy(bufp, &read_data, 4);
|
||||
bufp += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
tmc_flush_and_stop(drvdata);
|
||||
tmc_etb_dump_hw(drvdata);
|
||||
tmc_disable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
u32 rwp, val;
|
||||
|
||||
rwp = readl_relaxed(drvdata->base + TMC_RWP);
|
||||
val = readl_relaxed(drvdata->base + TMC_STS);
|
||||
|
||||
/* How much memory do we still have */
|
||||
if (val & BIT(0))
|
||||
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
|
||||
else
|
||||
drvdata->buf = drvdata->vaddr;
|
||||
}
|
||||
|
||||
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
tmc_flush_and_stop(drvdata);
|
||||
tmc_etr_dump_hw(drvdata);
|
||||
tmc_disable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
tmc_flush_and_stop(drvdata);
|
||||
tmc_disable_hw(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading)
|
||||
goto out;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
tmc_etb_disable_hw(drvdata);
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
tmc_etr_disable_hw(drvdata);
|
||||
} else {
|
||||
if (mode == TMC_MODE_CIRCULAR_BUFFER)
|
||||
tmc_etb_disable_hw(drvdata);
|
||||
else
|
||||
tmc_etf_disable_hw(drvdata);
|
||||
}
|
||||
out:
|
||||
drvdata->enable = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
dev_info(drvdata->dev, "TMC disabled\n");
|
||||
}
|
||||
|
||||
static void tmc_disable_sink(struct coresight_device *csdev)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
|
||||
}
|
||||
|
||||
static void tmc_disable_link(struct coresight_device *csdev, int inport,
|
||||
int outport)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_sink tmc_sink_ops = {
|
||||
.enable = tmc_enable_sink,
|
||||
.disable = tmc_disable_sink,
|
||||
};
|
||||
|
||||
static const struct coresight_ops_link tmc_link_ops = {
|
||||
.enable = tmc_enable_link,
|
||||
.disable = tmc_disable_link,
|
||||
};
|
||||
|
||||
static const struct coresight_ops tmc_etb_cs_ops = {
|
||||
.sink_ops = &tmc_sink_ops,
|
||||
};
|
||||
|
||||
static const struct coresight_ops tmc_etr_cs_ops = {
|
||||
.sink_ops = &tmc_sink_ops,
|
||||
};
|
||||
|
||||
static const struct coresight_ops tmc_etf_cs_ops = {
|
||||
.sink_ops = &tmc_sink_ops,
|
||||
.link_ops = &tmc_link_ops,
|
||||
};
|
||||
|
||||
static int tmc_read_prepare(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
enum tmc_mode mode;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (!drvdata->enable)
|
||||
goto out;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
tmc_etb_disable_hw(drvdata);
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
tmc_etr_disable_hw(drvdata);
|
||||
} else {
|
||||
mode = readl_relaxed(drvdata->base + TMC_MODE);
|
||||
if (mode == TMC_MODE_CIRCULAR_BUFFER) {
|
||||
tmc_etb_disable_hw(drvdata);
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
out:
|
||||
drvdata->reading = true;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_info(drvdata->dev, "TMC read start\n");
|
||||
return 0;
|
||||
err:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
unsigned long flags;
|
||||
enum tmc_mode mode;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (!drvdata->enable)
|
||||
goto out;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
tmc_etb_enable_hw(drvdata);
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
tmc_etr_enable_hw(drvdata);
|
||||
} else {
|
||||
mode = readl_relaxed(drvdata->base + TMC_MODE);
|
||||
if (mode == TMC_MODE_CIRCULAR_BUFFER)
|
||||
tmc_etb_enable_hw(drvdata);
|
||||
}
|
||||
out:
|
||||
drvdata->reading = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_info(drvdata->dev, "TMC read end\n");
|
||||
}
|
||||
|
||||
static int tmc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata, miscdev);
|
||||
int ret = 0;
|
||||
|
||||
if (drvdata->read_count++)
|
||||
goto out;
|
||||
|
||||
ret = tmc_read_prepare(drvdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
out:
|
||||
nonseekable_open(inode, file);
|
||||
|
||||
dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata, miscdev);
|
||||
char *bufp = drvdata->buf + *ppos;
|
||||
|
||||
if (*ppos + len > drvdata->size)
|
||||
len = drvdata->size - *ppos;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
if (bufp == (char *)(drvdata->vaddr + drvdata->size))
|
||||
bufp = drvdata->vaddr;
|
||||
else if (bufp > (char *)(drvdata->vaddr + drvdata->size))
|
||||
bufp -= drvdata->size;
|
||||
if ((bufp + len) > (char *)(drvdata->vaddr + drvdata->size))
|
||||
len = (char *)(drvdata->vaddr + drvdata->size) - bufp;
|
||||
}
|
||||
|
||||
if (copy_to_user(data, bufp, len)) {
|
||||
dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*ppos += len;
|
||||
|
||||
dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
|
||||
__func__, len, (int) (drvdata->size - *ppos));
|
||||
return len;
|
||||
}
|
||||
|
||||
static int tmc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata, miscdev);
|
||||
|
||||
if (--drvdata->read_count) {
|
||||
if (drvdata->read_count < 0) {
|
||||
dev_err(drvdata->dev, "mismatched close\n");
|
||||
drvdata->read_count = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmc_read_unprepare(drvdata);
|
||||
out:
|
||||
dev_dbg(drvdata->dev, "%s: released\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations tmc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tmc_open,
|
||||
.read = tmc_read,
|
||||
.release = tmc_release,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t trigger_cntr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
unsigned long val = drvdata->trigger_cntr;
|
||||
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
|
||||
static ssize_t trigger_cntr_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
unsigned long val;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = kstrtoul(buf, 16, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->trigger_cntr = val;
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(trigger_cntr);
|
||||
|
||||
static struct attribute *coresight_etb_attrs[] = {
|
||||
&dev_attr_trigger_cntr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(coresight_etb);
|
||||
|
||||
static struct attribute *coresight_etr_attrs[] = {
|
||||
&dev_attr_trigger_cntr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(coresight_etr);
|
||||
|
||||
static struct attribute *coresight_etf_attrs[] = {
|
||||
&dev_attr_trigger_cntr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(coresight_etf);
|
||||
|
||||
static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 devid;
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct tmc_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
||||
drvdata->config_type = BMVAL(devid, 6, 7);
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
if (np)
|
||||
ret = of_property_read_u32(np,
|
||||
"arm,buffer-size",
|
||||
&drvdata->size);
|
||||
if (ret)
|
||||
drvdata->size = SZ_1M;
|
||||
} else {
|
||||
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
|
||||
&drvdata->paddr, GFP_KERNEL);
|
||||
if (!drvdata->vaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(drvdata->vaddr, 0, drvdata->size);
|
||||
drvdata->buf = drvdata->vaddr;
|
||||
} else {
|
||||
drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
|
||||
if (!drvdata->buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_devm_kzalloc;
|
||||
}
|
||||
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->ops = &tmc_etb_cs_ops;
|
||||
desc->groups = coresight_etb_groups;
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->ops = &tmc_etr_cs_ops;
|
||||
desc->groups = coresight_etr_groups;
|
||||
} else {
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
|
||||
desc->ops = &tmc_etf_cs_ops;
|
||||
desc->groups = coresight_etf_groups;
|
||||
}
|
||||
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto err_devm_kzalloc;
|
||||
}
|
||||
|
||||
drvdata->miscdev.name = pdata->name;
|
||||
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
drvdata->miscdev.fops = &tmc_fops;
|
||||
ret = misc_register(&drvdata->miscdev);
|
||||
if (ret)
|
||||
goto err_misc_register;
|
||||
|
||||
dev_info(dev, "TMC initialized\n");
|
||||
return 0;
|
||||
|
||||
err_misc_register:
|
||||
coresight_unregister(drvdata->csdev);
|
||||
err_devm_kzalloc:
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
|
||||
dma_free_coherent(dev, drvdata->size,
|
||||
&drvdata->paddr, GFP_KERNEL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tmc_remove(struct amba_device *adev)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
misc_deregister(&drvdata->miscdev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
|
||||
dma_free_coherent(drvdata->dev, drvdata->size,
|
||||
&drvdata->paddr, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id tmc_ids[] = {
|
||||
{
|
||||
.id = 0x0003b961,
|
||||
.mask = 0x0003ffff,
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
static struct amba_driver tmc_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-tmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tmc_probe,
|
||||
.remove = tmc_remove,
|
||||
.id_table = tmc_ids,
|
||||
};
|
||||
|
||||
static int __init tmc_init(void)
|
||||
{
|
||||
return amba_driver_register(&tmc_driver);
|
||||
}
|
||||
module_init(tmc_init);
|
||||
|
||||
static void __exit tmc_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&tmc_driver);
|
||||
}
|
||||
module_exit(tmc_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver");
|
||||
217
drivers/coresight/coresight-tpiu.c
Normal file
217
drivers/coresight/coresight-tpiu.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
#define TPIU_SUPP_PORTSZ 0x000
|
||||
#define TPIU_CURR_PORTSZ 0x004
|
||||
#define TPIU_SUPP_TRIGMODES 0x100
|
||||
#define TPIU_TRIG_CNTRVAL 0x104
|
||||
#define TPIU_TRIG_MULT 0x108
|
||||
#define TPIU_SUPP_TESTPATM 0x200
|
||||
#define TPIU_CURR_TESTPATM 0x204
|
||||
#define TPIU_TEST_PATREPCNTR 0x208
|
||||
#define TPIU_FFSR 0x300
|
||||
#define TPIU_FFCR 0x304
|
||||
#define TPIU_FSYNC_CNTR 0x308
|
||||
#define TPIU_EXTCTL_INPORT 0x400
|
||||
#define TPIU_EXTCTL_OUTPORT 0x404
|
||||
#define TPIU_ITTRFLINACK 0xee4
|
||||
#define TPIU_ITTRFLIN 0xee8
|
||||
#define TPIU_ITATBDATA0 0xeec
|
||||
#define TPIU_ITATBCTR2 0xef0
|
||||
#define TPIU_ITATBCTR1 0xef4
|
||||
#define TPIU_ITATBCTR0 0xef8
|
||||
|
||||
/** register definition **/
|
||||
/* FFCR - 0x304 */
|
||||
#define FFCR_FON_MAN BIT(6)
|
||||
|
||||
/**
|
||||
* @base: memory mapped base address for this component.
|
||||
* @dev: the device entity associated to this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @clk: the clock this component is associated to.
|
||||
*/
|
||||
struct tpiu_drvdata {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void tpiu_enable_hw(struct tpiu_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* TODO: fill this up */
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static int tpiu_enable(struct coresight_device *csdev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tpiu_enable_hw(drvdata);
|
||||
|
||||
dev_info(drvdata->dev, "TPIU enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpiu_disable_hw(struct tpiu_drvdata *drvdata)
|
||||
{
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* Clear formatter controle reg. */
|
||||
writel_relaxed(0x0, drvdata->base + TPIU_FFCR);
|
||||
/* Generate manual flush */
|
||||
writel_relaxed(FFCR_FON_MAN, drvdata->base + TPIU_FFCR);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void tpiu_disable(struct coresight_device *csdev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
tpiu_disable_hw(drvdata);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
dev_info(drvdata->dev, "TPIU disabled\n");
|
||||
}
|
||||
|
||||
static const struct coresight_ops_sink tpiu_sink_ops = {
|
||||
.enable = tpiu_enable,
|
||||
.disable = tpiu_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops tpiu_cs_ops = {
|
||||
.sink_ops = &tpiu_sink_ops,
|
||||
};
|
||||
|
||||
static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret;
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct tpiu_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->base = base;
|
||||
|
||||
drvdata->clk = adev->pclk;
|
||||
ret = clk_prepare_enable(drvdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable tpiu to support older devices */
|
||||
tpiu_disable_hw(drvdata);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
|
||||
desc->ops = &tpiu_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
dev_info(dev, "TPIU initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpiu_remove(struct amba_device *adev)
|
||||
{
|
||||
struct tpiu_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id tpiu_ids[] = {
|
||||
{
|
||||
.id = 0x0003b912,
|
||||
.mask = 0x0003ffff,
|
||||
},
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
static struct amba_driver tpiu_driver = {
|
||||
.drv = {
|
||||
.name = "coresight-tpiu",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tpiu_probe,
|
||||
.remove = tpiu_remove,
|
||||
.id_table = tpiu_ids,
|
||||
};
|
||||
|
||||
static int __init tpiu_init(void)
|
||||
{
|
||||
return amba_driver_register(&tpiu_driver);
|
||||
}
|
||||
module_init(tpiu_init);
|
||||
|
||||
static void __exit tpiu_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&tpiu_driver);
|
||||
}
|
||||
module_exit(tpiu_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user