mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 14:42:37 +02:00
Merge remote-tracking branch 'lsk/v3.10/topic/tc2' into linux-linaro-lsk
Conflicts (look like simple add/add stuff): arch/arm/Kconfig arch/arm/common/Makefile
This commit is contained in:
commit
9bca3d7f7c
172
Documentation/devicetree/bindings/arm/cci.txt
Normal file
172
Documentation/devicetree/bindings/arm/cci.txt
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
=======================================================
|
||||
ARM CCI cache coherent interconnect binding description
|
||||
=======================================================
|
||||
|
||||
ARM multi-cluster systems maintain intra-cluster coherency through a
|
||||
cache coherent interconnect (CCI) that is capable of monitoring bus
|
||||
transactions and manage coherency, TLB invalidations and memory barriers.
|
||||
|
||||
It allows snooping and distributed virtual memory message broadcast across
|
||||
clusters, through memory mapped interface, with a global control register
|
||||
space and multiple sets of interface control registers, one per slave
|
||||
interface.
|
||||
|
||||
Bindings for the CCI node follow the ePAPR standard, available from:
|
||||
|
||||
www.power.org/documentation/epapr-version-1-1/
|
||||
|
||||
with the addition of the bindings described in this document which are
|
||||
specific to ARM.
|
||||
|
||||
* CCI interconnect node
|
||||
|
||||
Description: Describes a CCI cache coherent Interconnect component
|
||||
|
||||
Node name must be "cci".
|
||||
Node's parent must be the root node /, and the address space visible
|
||||
through the CCI interconnect is the same as the one seen from the
|
||||
root node (ie from CPUs perspective as per DT standard).
|
||||
Every CCI node has to define the following properties:
|
||||
|
||||
- compatible
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be set to
|
||||
"arm,cci-400"
|
||||
|
||||
- reg
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: A standard property. Specifies base physical
|
||||
address of CCI control registers common to all
|
||||
interfaces.
|
||||
|
||||
- ranges:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: A standard property. Follow rules in the ePAPR for
|
||||
hierarchical bus addressing. CCI interfaces
|
||||
addresses refer to the parent node addressing
|
||||
scheme to declare their register bases.
|
||||
|
||||
CCI interconnect node can define the following child nodes:
|
||||
|
||||
- CCI control interface nodes
|
||||
|
||||
Node name must be "slave-if".
|
||||
Parent node must be CCI interconnect node.
|
||||
|
||||
A CCI control interface node must contain the following
|
||||
properties:
|
||||
|
||||
- compatible
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be set to
|
||||
"arm,cci-400-ctrl-if"
|
||||
|
||||
- interface-type:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be set to one of {"ace", "ace-lite"}
|
||||
depending on the interface type the node
|
||||
represents.
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: the base address and size of the
|
||||
corresponding interface programming
|
||||
registers.
|
||||
|
||||
* CCI interconnect bus masters
|
||||
|
||||
Description: masters in the device tree connected to a CCI port
|
||||
(inclusive of CPUs and their cpu nodes).
|
||||
|
||||
A CCI interconnect bus master node must contain the following
|
||||
properties:
|
||||
|
||||
- cci-control-port:
|
||||
Usage: required
|
||||
Value type: <phandle>
|
||||
Definition: a phandle containing the CCI control interface node
|
||||
the master is connected to.
|
||||
|
||||
Example:
|
||||
|
||||
cpus {
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
|
||||
CPU0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
cci-control-port = <&cci_control1>;
|
||||
reg = <0x0>;
|
||||
};
|
||||
|
||||
CPU1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
cci-control-port = <&cci_control1>;
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
CPU2: cpu@100 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
cci-control-port = <&cci_control2>;
|
||||
reg = <0x100>;
|
||||
};
|
||||
|
||||
CPU3: cpu@101 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
cci-control-port = <&cci_control2>;
|
||||
reg = <0x101>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
dma0: dma@3000000 {
|
||||
compatible = "arm,pl330", "arm,primecell";
|
||||
cci-control-port = <&cci_control0>;
|
||||
reg = <0x0 0x3000000 0x0 0x1000>;
|
||||
interrupts = <10>;
|
||||
#dma-cells = <1>;
|
||||
#dma-channels = <8>;
|
||||
#dma-requests = <32>;
|
||||
};
|
||||
|
||||
cci@2c090000 {
|
||||
compatible = "arm,cci-400";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x0 0x2c090000 0 0x1000>;
|
||||
ranges = <0x0 0x0 0x2c090000 0x6000>;
|
||||
|
||||
cci_control0: slave-if@1000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace-lite";
|
||||
reg = <0x1000 0x1000>;
|
||||
};
|
||||
|
||||
cci_control1: slave-if@4000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x4000 0x1000>;
|
||||
};
|
||||
|
||||
cci_control2: slave-if@5000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x5000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
This CCI node corresponds to a CCI component whose control registers sits
|
||||
at address 0x000000002c090000.
|
||||
CCI slave interface @0x000000002c091000 is connected to dma controller dma0.
|
||||
CCI slave interface @0x000000002c094000 is connected to CPUs {CPU0, CPU1};
|
||||
CCI slave interface @0x000000002c095000 is connected to CPUs {CPU2, CPU3};
|
||||
19
Documentation/devicetree/bindings/arm/rtsm-dcscb.txt
Normal file
19
Documentation/devicetree/bindings/arm/rtsm-dcscb.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
ARM Dual Cluster System Configuration Block
|
||||
-------------------------------------------
|
||||
|
||||
The Dual Cluster System Configuration Block (DCSCB) provides basic
|
||||
functionality for controlling clocks, resets and configuration pins in
|
||||
the Dual Cluster System implemented by the Real-Time System Model (RTSM).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "arm,rtsm,dcscb"
|
||||
|
||||
- reg : physical base address and the size of the registers window
|
||||
|
||||
Example:
|
||||
|
||||
dcscb@60000000 {
|
||||
compatible = "arm,rtsm,dcscb";
|
||||
reg = <0x60000000 0x1000>;
|
||||
};
|
||||
35
Documentation/devicetree/bindings/mfd/vexpress-spc.txt
Normal file
35
Documentation/devicetree/bindings/mfd/vexpress-spc.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
* ARM Versatile Express Serial Power Controller device tree bindings
|
||||
|
||||
Latest ARM development boards implement a power management interface (serial
|
||||
power controller - SPC) that is capable of managing power/voltage and
|
||||
operating point transitions, through memory mapped registers interface.
|
||||
|
||||
On testchips like TC2 it also provides a configuration interface that can
|
||||
be used to read/write values which cannot be read/written through simple
|
||||
memory mapped reads/writes.
|
||||
|
||||
- spc node
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must be
|
||||
"arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc"
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encode-array>
|
||||
Definition: A standard property that specifies the base address
|
||||
and the size of the SPC address space
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: SPC interrupt configuration. A standard property
|
||||
that follows ePAPR interrupts specifications
|
||||
|
||||
Example:
|
||||
|
||||
spc: spc@7fff0000 {
|
||||
compatible = "arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc";
|
||||
reg = <0 0x7FFF0000 0 0x1000>;
|
||||
interrupts = <0 95 4>;
|
||||
};
|
||||
|
|
@ -202,7 +202,14 @@ dtb-$(CONFIG_ARCH_VERSATILE) += versatile-ab.dtb \
|
|||
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
|
||||
vexpress-v2p-ca9.dtb \
|
||||
vexpress-v2p-ca15-tc1.dtb \
|
||||
vexpress-v2p-ca15_a7.dtb
|
||||
vexpress-v2p-ca15_a7.dtb \
|
||||
rtsm_ve-cortex_a9x2.dtb \
|
||||
rtsm_ve-cortex_a9x4.dtb \
|
||||
rtsm_ve-cortex_a15x1.dtb \
|
||||
rtsm_ve-cortex_a15x2.dtb \
|
||||
rtsm_ve-cortex_a15x4.dtb \
|
||||
rtsm_ve-v2p-ca15x1-ca7x1.dtb \
|
||||
rtsm_ve-v2p-ca15x4-ca7x4.dtb
|
||||
dtb-$(CONFIG_ARCH_VIRT) += xenvm-4.2.dtb
|
||||
dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
|
||||
wm8505-ref.dtb \
|
||||
|
|
|
|||
52
arch/arm/boot/dts/clcd-panels.dtsi
Normal file
52
arch/arm/boot/dts/clcd-panels.dtsi
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* ARM Ltd. Versatile Express
|
||||
*
|
||||
*/
|
||||
|
||||
/ {
|
||||
panels {
|
||||
panel@0 {
|
||||
compatible = "panel";
|
||||
mode = "VGA";
|
||||
refresh = <60>;
|
||||
xres = <640>;
|
||||
yres = <480>;
|
||||
pixclock = <39721>;
|
||||
left_margin = <40>;
|
||||
right_margin = <24>;
|
||||
upper_margin = <32>;
|
||||
lower_margin = <11>;
|
||||
hsync_len = <96>;
|
||||
vsync_len = <2>;
|
||||
sync = <0>;
|
||||
vmode = "FB_VMODE_NONINTERLACED";
|
||||
|
||||
tim2 = "TIM2_BCD", "TIM2_IPC";
|
||||
cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)";
|
||||
caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888";
|
||||
bpp = <16>;
|
||||
};
|
||||
|
||||
panel@1 {
|
||||
compatible = "panel";
|
||||
mode = "XVGA";
|
||||
refresh = <60>;
|
||||
xres = <1024>;
|
||||
yres = <768>;
|
||||
pixclock = <15748>;
|
||||
left_margin = <152>;
|
||||
right_margin = <48>;
|
||||
upper_margin = <23>;
|
||||
lower_margin = <3>;
|
||||
hsync_len = <104>;
|
||||
vsync_len = <4>;
|
||||
sync = <0>;
|
||||
vmode = "FB_VMODE_NONINTERLACED";
|
||||
|
||||
tim2 = "TIM2_BCD", "TIM2_IPC";
|
||||
cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)";
|
||||
caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888";
|
||||
bpp = <16>;
|
||||
};
|
||||
};
|
||||
};
|
||||
159
arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts
Normal file
159
arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA15x1CT
|
||||
*
|
||||
* RTSM_VE_Cortex_A15x1.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA15x1";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a15x1", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0 0x2c001000 0 0x1000>,
|
||||
<0 0x2c002000 0 0x1000>,
|
||||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0 0x08000000 0x04000000>,
|
||||
<1 0 0 0x14000000 0x04000000>,
|
||||
<2 0 0 0x18000000 0x04000000>,
|
||||
<3 0 0 0x1c000000 0x04000000>,
|
||||
<4 0 0 0x0c000000 0x04000000>,
|
||||
<5 0 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
165
arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts
Normal file
165
arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA15x2CT
|
||||
*
|
||||
* RTSM_VE_Cortex_A15x2.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA15x2";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a15x2", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0 0x2c001000 0 0x1000>,
|
||||
<0 0x2c002000 0 0x1000>,
|
||||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0 0x08000000 0x04000000>,
|
||||
<1 0 0 0x14000000 0x04000000>,
|
||||
<2 0 0 0x18000000 0x04000000>,
|
||||
<3 0 0 0x1c000000 0x04000000>,
|
||||
<4 0 0 0x0c000000 0x04000000>,
|
||||
<5 0 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
177
arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts
Normal file
177
arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA15x4CT
|
||||
*
|
||||
* RTSM_VE_Cortex_A15x4.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA15x4";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a15x4", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0 0x2c001000 0 0x1000>,
|
||||
<0 0x2c002000 0 0x1000>,
|
||||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0 0x08000000 0x04000000>,
|
||||
<1 0 0 0x14000000 0x04000000>,
|
||||
<2 0 0 0x18000000 0x04000000>,
|
||||
<3 0 0 0x1c000000 0x04000000>,
|
||||
<4 0 0 0x0c000000 0x04000000>,
|
||||
<5 0 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
171
arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts
Normal file
171
arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA9MPx2CT
|
||||
*
|
||||
* RTSM_VE_Cortex_A9x2.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA9x2";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a9x2", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x80000000 0x80000000>;
|
||||
};
|
||||
|
||||
scu@2c000000 {
|
||||
compatible = "arm,cortex-a9-scu";
|
||||
reg = <0x2c000000 0x58>;
|
||||
};
|
||||
|
||||
timer@2c000600 {
|
||||
compatible = "arm,cortex-a9-twd-timer";
|
||||
reg = <0x2c000600 0x20>;
|
||||
interrupts = <1 13 0xf04>;
|
||||
};
|
||||
|
||||
watchdog@2c000620 {
|
||||
compatible = "arm,cortex-a9-twd-wdt";
|
||||
reg = <0x2c000620 0x20>;
|
||||
interrupts = <1 14 0xf04>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0x2c001000 0x1000>,
|
||||
<0x2c000100 0x100>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x04000000>,
|
||||
<1 0 0x14000000 0x04000000>,
|
||||
<2 0 0x18000000 0x04000000>,
|
||||
<3 0 0x1c000000 0x04000000>,
|
||||
<4 0 0x0c000000 0x04000000>,
|
||||
<5 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
183
arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts
Normal file
183
arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA9MPx4CT
|
||||
*
|
||||
* RTSM_VE_Cortex_A9x4.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA9x4";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a9x4", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a9";
|
||||
reg = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x80000000 0x80000000>;
|
||||
};
|
||||
|
||||
scu@2c000000 {
|
||||
compatible = "arm,cortex-a9-scu";
|
||||
reg = <0x2c000000 0x58>;
|
||||
};
|
||||
|
||||
timer@2c000600 {
|
||||
compatible = "arm,cortex-a9-twd-timer";
|
||||
reg = <0x2c000600 0x20>;
|
||||
interrupts = <1 13 0xf04>;
|
||||
};
|
||||
|
||||
watchdog@2c000620 {
|
||||
compatible = "arm,cortex-a9-twd-wdt";
|
||||
reg = <0x2c000620 0x20>;
|
||||
interrupts = <1 14 0xf04>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0x2c001000 0x1000>,
|
||||
<0x2c000100 0x100>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x04000000>,
|
||||
<1 0 0x14000000 0x04000000>,
|
||||
<2 0 0x18000000 0x04000000>,
|
||||
<3 0 0x1c000000 0x04000000>,
|
||||
<4 0 0x0c000000 0x04000000>,
|
||||
<5 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
224
arch/arm/boot/dts/rtsm_ve-motherboard.dtsi
Normal file
224
arch/arm/boot/dts/rtsm_ve-motherboard.dtsi
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* Motherboard component
|
||||
*
|
||||
* VEMotherBoard.lisa
|
||||
*/
|
||||
|
||||
motherboard {
|
||||
compatible = "arm,vexpress,v2m-p1", "simple-bus";
|
||||
arm,hbi = <0x190>;
|
||||
arm,vexpress,site = <0>;
|
||||
arm,v2m-memory-map = "rs1";
|
||||
#address-cells = <2>; /* SMB chipselect number and offset */
|
||||
#size-cells = <1>;
|
||||
#interrupt-cells = <1>;
|
||||
ranges;
|
||||
|
||||
flash@0,00000000 {
|
||||
compatible = "arm,vexpress-flash", "cfi-flash";
|
||||
reg = <0 0x00000000 0x04000000>,
|
||||
<4 0x00000000 0x04000000>;
|
||||
bank-width = <4>;
|
||||
};
|
||||
|
||||
vram@2,00000000 {
|
||||
compatible = "arm,vexpress-vram";
|
||||
reg = <2 0x00000000 0x00800000>;
|
||||
};
|
||||
|
||||
ethernet@2,02000000 {
|
||||
compatible = "smsc,lan91c111";
|
||||
reg = <2 0x02000000 0x10000>;
|
||||
interrupts = <15>;
|
||||
};
|
||||
|
||||
iofpga@3,00000000 {
|
||||
compatible = "arm,amba-bus", "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 3 0 0x200000>;
|
||||
|
||||
v2m_sysreg: sysreg@010000 {
|
||||
compatible = "arm,vexpress-sysreg";
|
||||
reg = <0x010000 0x1000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
v2m_sysctl: sysctl@020000 {
|
||||
compatible = "arm,sp810", "arm,primecell";
|
||||
reg = <0x020000 0x1000>;
|
||||
clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&smbclk>;
|
||||
clock-names = "refclk", "timclk", "apb_pclk";
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3";
|
||||
};
|
||||
|
||||
aaci@040000 {
|
||||
compatible = "arm,pl041", "arm,primecell";
|
||||
reg = <0x040000 0x1000>;
|
||||
interrupts = <11>;
|
||||
clocks = <&smbclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
mmci@050000 {
|
||||
compatible = "arm,pl180", "arm,primecell";
|
||||
reg = <0x050000 0x1000>;
|
||||
interrupts = <9 10>;
|
||||
cd-gpios = <&v2m_sysreg 0 0>;
|
||||
wp-gpios = <&v2m_sysreg 1 0>;
|
||||
max-frequency = <12000000>;
|
||||
vmmc-supply = <&v2m_fixed_3v3>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "mclk", "apb_pclk";
|
||||
};
|
||||
|
||||
kmi@060000 {
|
||||
compatible = "arm,pl050", "arm,primecell";
|
||||
reg = <0x060000 0x1000>;
|
||||
interrupts = <12>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "KMIREFCLK", "apb_pclk";
|
||||
};
|
||||
|
||||
kmi@070000 {
|
||||
compatible = "arm,pl050", "arm,primecell";
|
||||
reg = <0x070000 0x1000>;
|
||||
interrupts = <13>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "KMIREFCLK", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_serial0: uart@090000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x090000 0x1000>;
|
||||
interrupts = <5>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_serial1: uart@0a0000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x0a0000 0x1000>;
|
||||
interrupts = <6>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_serial2: uart@0b0000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x0b0000 0x1000>;
|
||||
interrupts = <7>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_serial3: uart@0c0000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x0c0000 0x1000>;
|
||||
interrupts = <8>;
|
||||
clocks = <&v2m_clk24mhz>, <&smbclk>;
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
};
|
||||
|
||||
wdt@0f0000 {
|
||||
compatible = "arm,sp805", "arm,primecell";
|
||||
reg = <0x0f0000 0x1000>;
|
||||
interrupts = <0>;
|
||||
clocks = <&v2m_refclk32khz>, <&smbclk>;
|
||||
clock-names = "wdogclk", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_timer01: timer@110000 {
|
||||
compatible = "arm,sp804", "arm,primecell";
|
||||
reg = <0x110000 0x1000>;
|
||||
interrupts = <2>;
|
||||
clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&smbclk>;
|
||||
clock-names = "timclken1", "timclken2", "apb_pclk";
|
||||
};
|
||||
|
||||
v2m_timer23: timer@120000 {
|
||||
compatible = "arm,sp804", "arm,primecell";
|
||||
reg = <0x120000 0x1000>;
|
||||
interrupts = <3>;
|
||||
clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&smbclk>;
|
||||
clock-names = "timclken1", "timclken2", "apb_pclk";
|
||||
};
|
||||
|
||||
rtc@170000 {
|
||||
compatible = "arm,pl031", "arm,primecell";
|
||||
reg = <0x170000 0x1000>;
|
||||
interrupts = <4>;
|
||||
clocks = <&smbclk>;
|
||||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
clcd@1f0000 {
|
||||
compatible = "arm,pl111", "arm,primecell";
|
||||
reg = <0x1f0000 0x1000>;
|
||||
interrupts = <14>;
|
||||
clocks = <&v2m_oscclk1>, <&smbclk>;
|
||||
clock-names = "v2m:oscclk1", "apb_pclk";
|
||||
mode = "VGA";
|
||||
use_dma = <0>;
|
||||
framebuffer = <0x18000000 0x00180000>;
|
||||
};
|
||||
};
|
||||
|
||||
v2m_fixed_3v3: fixedregulator@0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "3V3";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
v2m_clk24mhz: clk24mhz {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <24000000>;
|
||||
clock-output-names = "v2m:clk24mhz";
|
||||
};
|
||||
|
||||
v2m_refclk1mhz: refclk1mhz {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <1000000>;
|
||||
clock-output-names = "v2m:refclk1mhz";
|
||||
};
|
||||
|
||||
v2m_refclk32khz: refclk32khz {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
clock-output-names = "v2m:refclk32khz";
|
||||
};
|
||||
|
||||
mcc {
|
||||
compatible = "simple-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
v2m_oscclk1: osc@1 {
|
||||
/* CLCD clock */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <23750000 63500000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "v2m:oscclk1";
|
||||
};
|
||||
|
||||
muxfpga@0 {
|
||||
compatible = "arm,vexpress-muxfpga";
|
||||
arm,vexpress-sysreg,func = <7 0>;
|
||||
};
|
||||
|
||||
shutdown@0 {
|
||||
compatible = "arm,vexpress-shutdown";
|
||||
arm,vexpress-sysreg,func = <8 0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
244
arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts
Normal file
244
arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA15x4CT
|
||||
* ARMCortexA7x4CT
|
||||
* RTSM_VE_Cortex_A15x1_A7x1.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/memreserve/ 0xff000000 0x01000000;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA15x1-A7x1";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a15x1_a7x1", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
clusters {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cluster0: cluster@0 {
|
||||
reg = <0>;
|
||||
// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core0: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
cluster1: cluster@1 {
|
||||
reg = <1>;
|
||||
// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core1: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core0>;
|
||||
// clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x100>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core1>;
|
||||
// clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
cci@2c090000 {
|
||||
compatible = "arm,cci-400", "arm,cci";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0 0x2c090000 0 0x1000>;
|
||||
ranges = <0x0 0x0 0x2c090000 0x10000>;
|
||||
|
||||
cci_control1: slave-if@4000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x4000 0x1000>;
|
||||
};
|
||||
|
||||
cci_control2: slave-if@5000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x5000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
dcscb@60000000 {
|
||||
compatible = "arm,rtsm,dcscb";
|
||||
reg = <0 0x60000000 0 0x1000>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0 0x2c001000 0 0x1000>,
|
||||
<0 0x2c002000 0 0x1000>,
|
||||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
|
||||
gic-cpuif@0 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <0>;
|
||||
cpu = <&cpu0>;
|
||||
};
|
||||
gic-cpuif@1 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <1>;
|
||||
cpu = <&cpu1>;
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0 0x08000000 0x04000000>,
|
||||
<1 0 0 0x14000000 0x04000000>,
|
||||
<2 0 0 0x18000000 0x04000000>,
|
||||
<3 0 0 0x1c000000 0x04000000>,
|
||||
<4 0 0 0x0c000000 0x04000000>,
|
||||
<5 0 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
358
arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts
Normal file
358
arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* ARM Ltd. Fast Models
|
||||
*
|
||||
* Versatile Express (VE) system model
|
||||
* ARMCortexA15x4CT
|
||||
* ARMCortexA7x4CT
|
||||
* RTSM_VE_Cortex_A15x4_A7x4.lisa
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/memreserve/ 0xff000000 0x01000000;
|
||||
|
||||
/ {
|
||||
model = "RTSM_VE_CortexA15x4-A7x4";
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,rtsm_ve,cortex_a15x4_a7x4", "arm,vexpress";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
chosen { };
|
||||
|
||||
aliases {
|
||||
serial0 = &v2m_serial0;
|
||||
serial1 = &v2m_serial1;
|
||||
serial2 = &v2m_serial2;
|
||||
serial3 = &v2m_serial3;
|
||||
};
|
||||
|
||||
clusters {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cluster0: cluster@0 {
|
||||
reg = <0>;
|
||||
// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core0: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
core1: core@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
core2: core@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
core3: core@3 {
|
||||
reg = <3>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
cluster1: cluster@1 {
|
||||
reg = <1>;
|
||||
// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core4: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
core5: core@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
core6: core@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
core7: core@3 {
|
||||
reg = <3>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core0>;
|
||||
// clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core1>;
|
||||
// clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu2: cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <2>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core2>;
|
||||
// clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu3: cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <3>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core3>;
|
||||
// clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu4: cpu@4 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x100>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core4>;
|
||||
// clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu5: cpu@5 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x101>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core5>;
|
||||
// clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu6: cpu@6 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x102>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core6>;
|
||||
// clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu7: cpu@7 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x103>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core7>;
|
||||
// clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
cci@2c090000 {
|
||||
compatible = "arm,cci-400", "arm,cci";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0 0x2c090000 0 0x1000>;
|
||||
ranges = <0x0 0x0 0x2c090000 0x10000>;
|
||||
|
||||
cci_control1: slave-if@4000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x4000 0x1000>;
|
||||
};
|
||||
|
||||
cci_control2: slave-if@5000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x5000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
dcscb@60000000 {
|
||||
compatible = "arm,rtsm,dcscb";
|
||||
reg = <0 0x60000000 0 0x1000>;
|
||||
};
|
||||
|
||||
gic: interrupt-controller@2c001000 {
|
||||
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
|
||||
#interrupt-cells = <3>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0 0x2c001000 0 0x1000>,
|
||||
<0 0x2c002000 0 0x1000>,
|
||||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
|
||||
gic-cpuif@0 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <0>;
|
||||
cpu = <&cpu0>;
|
||||
};
|
||||
gic-cpuif@1 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <1>;
|
||||
cpu = <&cpu1>;
|
||||
};
|
||||
gic-cpuif@2 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <2>;
|
||||
cpu = <&cpu2>;
|
||||
};
|
||||
gic-cpuif@3 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <3>;
|
||||
cpu = <&cpu3>;
|
||||
};
|
||||
gic-cpuif@4 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <4>;
|
||||
cpu = <&cpu4>;
|
||||
};
|
||||
gic-cpuif@5 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <5>;
|
||||
cpu = <&cpu5>;
|
||||
};
|
||||
gic-cpuif@6 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <6>;
|
||||
cpu = <&cpu6>;
|
||||
};
|
||||
gic-cpuif@7 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <7>;
|
||||
cpu = <&cpu7>;
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
<1 14 0xf08>,
|
||||
<1 11 0xf08>,
|
||||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
||||
osc@0 {
|
||||
/* ACLK clock to the AXI master port on the test chip */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 0>;
|
||||
freq-range = <30000000 50000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "extsaxiclk";
|
||||
};
|
||||
|
||||
oscclk1: osc@1 {
|
||||
/* Reference clock for the CLCD */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 1>;
|
||||
freq-range = <10000000 80000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "clcdclk";
|
||||
};
|
||||
|
||||
smbclk: oscclk2: osc@2 {
|
||||
/* Reference clock for the test chip internal PLLs */
|
||||
compatible = "arm,vexpress-osc";
|
||||
arm,vexpress-sysreg,func = <1 2>;
|
||||
freq-range = <33000000 100000000>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "tcrefclk";
|
||||
};
|
||||
};
|
||||
|
||||
smb {
|
||||
compatible = "simple-bus";
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0 0x08000000 0x04000000>,
|
||||
<1 0 0 0x14000000 0x04000000>,
|
||||
<2 0 0 0x18000000 0x04000000>,
|
||||
<3 0 0 0x1c000000 0x04000000>,
|
||||
<4 0 0 0x0c000000 0x04000000>,
|
||||
<5 0 0 0x10000000 0x04000000>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 63>;
|
||||
interrupt-map = <0 0 0 &gic 0 0 4>,
|
||||
<0 0 1 &gic 0 1 4>,
|
||||
<0 0 2 &gic 0 2 4>,
|
||||
<0 0 3 &gic 0 3 4>,
|
||||
<0 0 4 &gic 0 4 4>,
|
||||
<0 0 5 &gic 0 5 4>,
|
||||
<0 0 6 &gic 0 6 4>,
|
||||
<0 0 7 &gic 0 7 4>,
|
||||
<0 0 8 &gic 0 8 4>,
|
||||
<0 0 9 &gic 0 9 4>,
|
||||
<0 0 10 &gic 0 10 4>,
|
||||
<0 0 11 &gic 0 11 4>,
|
||||
<0 0 12 &gic 0 12 4>,
|
||||
<0 0 13 &gic 0 13 4>,
|
||||
<0 0 14 &gic 0 14 4>,
|
||||
<0 0 15 &gic 0 15 4>,
|
||||
<0 0 16 &gic 0 16 4>,
|
||||
<0 0 17 &gic 0 17 4>,
|
||||
<0 0 18 &gic 0 18 4>,
|
||||
<0 0 19 &gic 0 19 4>,
|
||||
<0 0 20 &gic 0 20 4>,
|
||||
<0 0 21 &gic 0 21 4>,
|
||||
<0 0 22 &gic 0 22 4>,
|
||||
<0 0 23 &gic 0 23 4>,
|
||||
<0 0 24 &gic 0 24 4>,
|
||||
<0 0 25 &gic 0 25 4>,
|
||||
<0 0 26 &gic 0 26 4>,
|
||||
<0 0 27 &gic 0 27 4>,
|
||||
<0 0 28 &gic 0 28 4>,
|
||||
<0 0 29 &gic 0 29 4>,
|
||||
<0 0 30 &gic 0 30 4>,
|
||||
<0 0 31 &gic 0 31 4>,
|
||||
<0 0 32 &gic 0 32 4>,
|
||||
<0 0 33 &gic 0 33 4>,
|
||||
<0 0 34 &gic 0 34 4>,
|
||||
<0 0 35 &gic 0 35 4>,
|
||||
<0 0 36 &gic 0 36 4>,
|
||||
<0 0 37 &gic 0 37 4>,
|
||||
<0 0 38 &gic 0 38 4>,
|
||||
<0 0 39 &gic 0 39 4>,
|
||||
<0 0 40 &gic 0 40 4>,
|
||||
<0 0 41 &gic 0 41 4>,
|
||||
<0 0 42 &gic 0 42 4>;
|
||||
|
||||
/include/ "rtsm_ve-motherboard.dtsi"
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
|
|
@ -228,6 +228,7 @@ compact-flash@1a0000 {
|
|||
};
|
||||
|
||||
clcd@1f0000 {
|
||||
status = "disabled";
|
||||
compatible = "arm,pl111", "arm,primecell";
|
||||
reg = <0x1f0000 0x1000>;
|
||||
interrupts = <14>;
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ compact-flash@1a000 {
|
|||
};
|
||||
|
||||
clcd@1f000 {
|
||||
status = "disabled";
|
||||
compatible = "arm,pl111", "arm,primecell";
|
||||
reg = <0x1f000 0x1000>;
|
||||
interrupts = <14>;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
/dts-v1/;
|
||||
|
||||
/memreserve/ 0xbf000000 0x01000000;
|
||||
|
||||
/ {
|
||||
model = "V2P-CA15";
|
||||
arm,hbi = <0x237>;
|
||||
|
|
@ -57,6 +59,8 @@ hdlcd@2b000000 {
|
|||
interrupts = <0 85 4>;
|
||||
clocks = <&oscclk5>;
|
||||
clock-names = "pxlclk";
|
||||
mode = "1024x768-16@60";
|
||||
framebuffer = <0 0xff000000 0 0x01000000>;
|
||||
};
|
||||
|
||||
memory-controller@2b0a0000 {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
/dts-v1/;
|
||||
|
||||
/memreserve/ 0xff000000 0x01000000;
|
||||
|
||||
/ {
|
||||
model = "V2P-CA15_CA7";
|
||||
arm,hbi = <0x249>;
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress";
|
||||
compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress", "arm,generic";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
|
@ -29,44 +31,106 @@ aliases {
|
|||
i2c1 = &v2m_i2c_pcie;
|
||||
};
|
||||
|
||||
cpus {
|
||||
clusters {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
cluster0: cluster@0 {
|
||||
reg = <0>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core0: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
core1: core@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
cpu1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
cluster1: cluster@1 {
|
||||
reg = <1>;
|
||||
cores {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
core2: core@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
core3: core@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
core4: core@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu2: cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x100>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core2>;
|
||||
clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu3: cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x101>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core3>;
|
||||
clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu4: cpu@4 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
reg = <0x102>;
|
||||
cluster = <&cluster1>;
|
||||
core = <&core4>;
|
||||
clock-frequency = <800000000>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
};
|
||||
|
||||
cpu0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core0>;
|
||||
clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
|
||||
cpu1: cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
cluster = <&cluster0>;
|
||||
core = <&core1>;
|
||||
clock-frequency = <1000000000>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0 0x80000000 0 0x40000000>;
|
||||
reg = <0 0x80000000 0 0x80000000>;
|
||||
};
|
||||
|
||||
wdt@2a490000 {
|
||||
|
|
@ -81,6 +145,8 @@ hdlcd@2b000000 {
|
|||
compatible = "arm,hdlcd";
|
||||
reg = <0 0x2b000000 0 0x1000>;
|
||||
interrupts = <0 85 4>;
|
||||
mode = "1024x768-16@60";
|
||||
framebuffer = <0 0xff000000 0 0x01000000>;
|
||||
clocks = <&oscclk5>;
|
||||
clock-names = "pxlclk";
|
||||
};
|
||||
|
|
@ -102,6 +168,64 @@ gic: interrupt-controller@2c001000 {
|
|||
<0 0x2c004000 0 0x2000>,
|
||||
<0 0x2c006000 0 0x2000>;
|
||||
interrupts = <1 9 0xf04>;
|
||||
|
||||
gic-cpuif@0 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <0>;
|
||||
cpu = <&cpu0>;
|
||||
};
|
||||
gic-cpuif@1 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <1>;
|
||||
cpu = <&cpu1>;
|
||||
};
|
||||
gic-cpuif@2 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <2>;
|
||||
cpu = <&cpu2>;
|
||||
};
|
||||
|
||||
gic-cpuif@3 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <3>;
|
||||
cpu = <&cpu3>;
|
||||
};
|
||||
|
||||
gic-cpuif@4 {
|
||||
compatible = "arm,gic-cpuif";
|
||||
cpuif-id = <4>;
|
||||
cpu = <&cpu4>;
|
||||
};
|
||||
};
|
||||
|
||||
cci@2c090000 {
|
||||
compatible = "arm,cci-400";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0 0x2c090000 0 0x1000>;
|
||||
ranges = <0x0 0x0 0x2c090000 0x10000>;
|
||||
|
||||
cci_control1: slave-if@4000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x4000 0x1000>;
|
||||
};
|
||||
|
||||
cci_control2: slave-if@5000 {
|
||||
compatible = "arm,cci-400-ctrl-if";
|
||||
interface-type = "ace";
|
||||
reg = <0x5000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
cci-pmu@2c099000 {
|
||||
compatible = "arm,cci-400-pmu";
|
||||
reg = <0 0x2c099000 0 0x6000>;
|
||||
interrupts = <0 101 4>,
|
||||
<0 102 4>,
|
||||
<0 103 4>,
|
||||
<0 104 4>,
|
||||
<0 105 4>;
|
||||
};
|
||||
|
||||
memory-controller@7ffd0000 {
|
||||
|
|
@ -125,6 +249,12 @@ dma@7ff00000 {
|
|||
clock-names = "apb_pclk";
|
||||
};
|
||||
|
||||
spc@7fff0000 {
|
||||
compatible = "arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc";
|
||||
reg = <0 0x7fff0000 0 0x1000>;
|
||||
interrupts = <0 95 4>;
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <1 13 0xf08>,
|
||||
|
|
@ -133,12 +263,21 @@ timer {
|
|||
<1 10 0xf08>;
|
||||
};
|
||||
|
||||
pmu {
|
||||
pmu_a15 {
|
||||
compatible = "arm,cortex-a15-pmu";
|
||||
cluster = <&cluster0>;
|
||||
interrupts = <0 68 4>,
|
||||
<0 69 4>;
|
||||
};
|
||||
|
||||
pmu_a7 {
|
||||
compatible = "arm,cortex-a7-pmu";
|
||||
cluster = <&cluster1>;
|
||||
interrupts = <0 128 4>,
|
||||
<0 129 4>,
|
||||
<0 130 4>;
|
||||
};
|
||||
|
||||
oscclk6a: oscclk6a {
|
||||
/* Reference 24MHz clock */
|
||||
compatible = "fixed-clock";
|
||||
|
|
@ -147,6 +286,15 @@ oscclk6a: oscclk6a {
|
|||
clock-output-names = "oscclk6a";
|
||||
};
|
||||
|
||||
psci {
|
||||
compatible = "arm,psci";
|
||||
method = "smc";
|
||||
cpu_suspend = <0x80100001>;
|
||||
cpu_off = <0x80100002>;
|
||||
cpu_on = <0x80100003>;
|
||||
migrate = <0x80100004>;
|
||||
};
|
||||
|
||||
dcc {
|
||||
compatible = "arm,vexpress,config-bus";
|
||||
arm,vexpress,config-bridge = <&v2m_sysreg>;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
/dts-v1/;
|
||||
|
||||
/memreserve/ 0xbf000000 0x01000000;
|
||||
|
||||
/ {
|
||||
model = "V2P-CA5s";
|
||||
arm,hbi = <0x225>;
|
||||
|
|
@ -59,6 +61,8 @@ hdlcd@2a110000 {
|
|||
interrupts = <0 85 4>;
|
||||
clocks = <&oscclk3>;
|
||||
clock-names = "pxlclk";
|
||||
mode = "640x480-16@60";
|
||||
framebuffer = <0xbf000000 0x01000000>;
|
||||
};
|
||||
|
||||
memory-controller@2a150000 {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
/dts-v1/;
|
||||
|
||||
/include/ "clcd-panels.dtsi"
|
||||
|
||||
/ {
|
||||
model = "V2P-CA9";
|
||||
arm,hbi = <0x191>;
|
||||
|
|
@ -73,6 +75,8 @@ clcd@10020000 {
|
|||
interrupts = <0 44 4>;
|
||||
clocks = <&oscclk1>, <&oscclk2>;
|
||||
clock-names = "clcdclk", "apb_pclk";
|
||||
mode = "XVGA";
|
||||
use_dma = <1>;
|
||||
};
|
||||
|
||||
memory-controller@100e0000 {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
|
|||
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
|
||||
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
|
||||
obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o
|
||||
AFLAGS_mcpm_head.o := -march=armv7-a
|
||||
AFLAGS_vlock.o := -march=armv7-a
|
||||
obj-$(CONFIG_BL_SWITCHER) += bL_switcher.o
|
||||
obj-$(CONFIG_BL_SWITCHER_DUMMY_IF) += bL_switcher_dummy_if.o
|
||||
|
||||
AFLAGS_mcpm_head.o := -march=armv7-a
|
||||
AFLAGS_vlock.o := -march=armv7-a
|
||||
CFLAGS_REMOVE_mcpm_entry.o = -pg
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ static inline void __cpuinit arch_counter_set_user_access(void)
|
|||
asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
|
||||
|
||||
/* disable user access to everything */
|
||||
cntkctl &= ~((3 << 8) | (7 << 0));
|
||||
cntkctl &= ~((3 << 8) | (3 << 0));
|
||||
|
||||
asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,20 @@ static inline void set_cr(unsigned int val)
|
|||
isb();
|
||||
}
|
||||
|
||||
static inline unsigned int get_auxcr(void)
|
||||
{
|
||||
unsigned int val;
|
||||
asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void set_auxcr(unsigned int val)
|
||||
{
|
||||
asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR"
|
||||
: : "r" (val));
|
||||
isb();
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
extern void adjust_cr(unsigned long mask, unsigned long set);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct tag;
|
||||
|
|
@ -16,8 +18,10 @@ struct pt_regs;
|
|||
struct smp_operations;
|
||||
#ifdef CONFIG_SMP
|
||||
#define smp_ops(ops) (&(ops))
|
||||
#define smp_init_ops(ops) (&(ops))
|
||||
#else
|
||||
#define smp_ops(ops) (struct smp_operations *)NULL
|
||||
#define smp_init_ops(ops) (bool (*)(void))NULL
|
||||
#endif
|
||||
|
||||
struct machine_desc {
|
||||
|
|
@ -41,6 +45,7 @@ struct machine_desc {
|
|||
unsigned char reserve_lp2 :1; /* never has lp2 */
|
||||
char restart_mode; /* default restart mode */
|
||||
struct smp_operations *smp; /* SMP operations */
|
||||
bool (*smp_init)(void);
|
||||
void (*fixup)(struct tag *, char **,
|
||||
struct meminfo *);
|
||||
void (*reserve)(void);/* reserve mem blocks */
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#define PSCI_POWER_STATE_TYPE_STANDBY 0
|
||||
#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
|
||||
#define PSCI_POWER_STATE_AFFINITY_LEVEL0 0
|
||||
#define PSCI_POWER_STATE_AFFINITY_LEVEL1 1
|
||||
#define PSCI_POWER_STATE_AFFINITY_LEVEL2 2
|
||||
#define PSCI_POWER_STATE_AFFINITY_LEVEL3 3
|
||||
|
||||
struct psci_power_state {
|
||||
u16 id;
|
||||
|
|
@ -32,5 +36,22 @@ struct psci_operations {
|
|||
};
|
||||
|
||||
extern struct psci_operations psci_ops;
|
||||
extern struct smp_operations psci_smp_ops;
|
||||
|
||||
#ifdef CONFIG_ARM_PSCI
|
||||
void psci_init(void);
|
||||
bool psci_smp_available(void);
|
||||
#else
|
||||
static inline void psci_init(void) { }
|
||||
static inline bool psci_smp_available(void) { return false; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_PSCI
|
||||
extern int __init psci_probe(void);
|
||||
#else
|
||||
static inline int psci_probe(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
#endif /* __ASM_ARM_PSCI_H */
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ obj-$(CONFIG_DEBUG_LL) += debug.o
|
|||
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
|
||||
obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
|
||||
obj-$(CONFIG_ARM_PSCI) += psci.o
|
||||
ifeq ($(CONFIG_ARM_PSCI),y)
|
||||
obj-y += psci.o
|
||||
obj-$(CONFIG_SMP) += psci_smp.o
|
||||
endif
|
||||
|
||||
extra-y := $(head-y) vmlinux.lds
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/compiler.h>
|
||||
#include <asm/errno.h>
|
||||
|
|
@ -26,6 +27,11 @@
|
|||
|
||||
struct psci_operations psci_ops;
|
||||
|
||||
/* Type of psci support. Currently can only be enabled or disabled */
|
||||
#define PSCI_SUP_DISABLED 0
|
||||
#define PSCI_SUP_ENABLED 1
|
||||
|
||||
static unsigned int psci;
|
||||
static int (*invoke_psci_fn)(u32, u32, u32, u32);
|
||||
|
||||
enum psci_function {
|
||||
|
|
@ -42,6 +48,7 @@ static u32 psci_function_id[PSCI_FN_MAX];
|
|||
#define PSCI_RET_EOPNOTSUPP -1
|
||||
#define PSCI_RET_EINVAL -2
|
||||
#define PSCI_RET_EPERM -3
|
||||
#define PSCI_RET_EALREADYON -4
|
||||
|
||||
static int psci_to_linux_errno(int errno)
|
||||
{
|
||||
|
|
@ -54,6 +61,8 @@ static int psci_to_linux_errno(int errno)
|
|||
return -EINVAL;
|
||||
case PSCI_RET_EPERM:
|
||||
return -EPERM;
|
||||
case PSCI_RET_EALREADYON:
|
||||
return -EAGAIN;
|
||||
};
|
||||
|
||||
return -EINVAL;
|
||||
|
|
@ -158,15 +167,18 @@ static const struct of_device_id psci_of_match[] __initconst = {
|
|||
{},
|
||||
};
|
||||
|
||||
static int __init psci_init(void)
|
||||
void __init psci_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
const char *method;
|
||||
u32 id;
|
||||
|
||||
if (psci == PSCI_SUP_DISABLED)
|
||||
return;
|
||||
|
||||
np = of_find_matching_node(NULL, psci_of_match);
|
||||
if (!np)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
pr_info("probing function IDs from device-tree\n");
|
||||
|
||||
|
|
@ -206,6 +218,35 @@ static int __init psci_init(void)
|
|||
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
early_initcall(psci_init);
|
||||
|
||||
int __init psci_probe(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (psci == PSCI_SUP_ENABLED) {
|
||||
np = of_find_matching_node(NULL, psci_of_match);
|
||||
if (np)
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init early_psci(char *val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (strcmp(val, "enable") == 0)
|
||||
psci = PSCI_SUP_ENABLED;
|
||||
else if (strcmp(val, "disable") == 0)
|
||||
psci = PSCI_SUP_DISABLED;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
early_param("psci", early_psci);
|
||||
|
|
|
|||
84
arch/arm/kernel/psci_smp.c
Normal file
84
arch/arm/kernel/psci_smp.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Limited
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/psci.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
/*
|
||||
* psci_smp assumes that the following is true about PSCI:
|
||||
*
|
||||
* cpu_suspend Suspend the execution on a CPU
|
||||
* @state we don't currently describe affinity levels, so just pass 0.
|
||||
* @entry_point the first instruction to be executed on return
|
||||
* returns 0 success, < 0 on failure
|
||||
*
|
||||
* cpu_off Power down a CPU
|
||||
* @state we don't currently describe affinity levels, so just pass 0.
|
||||
* no return on successful call
|
||||
*
|
||||
* cpu_on Power up a CPU
|
||||
* @cpuid cpuid of target CPU, as from MPIDR
|
||||
* @entry_point the first instruction to be executed on return
|
||||
* returns 0 success, < 0 on failure
|
||||
*
|
||||
* migrate Migrate the context to a different CPU
|
||||
* @cpuid cpuid of target CPU, as from MPIDR
|
||||
* returns 0 success, < 0 on failure
|
||||
*
|
||||
*/
|
||||
|
||||
extern void secondary_startup(void);
|
||||
|
||||
static int __cpuinit psci_boot_secondary(unsigned int cpu,
|
||||
struct task_struct *idle)
|
||||
{
|
||||
if (psci_ops.cpu_on)
|
||||
return psci_ops.cpu_on(cpu_logical_map(cpu),
|
||||
__pa(secondary_startup));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
void __ref psci_cpu_die(unsigned int cpu)
|
||||
{
|
||||
const struct psci_power_state ps = {
|
||||
.type = PSCI_POWER_STATE_TYPE_POWER_DOWN,
|
||||
};
|
||||
|
||||
if (psci_ops.cpu_off)
|
||||
psci_ops.cpu_off(ps);
|
||||
|
||||
/* We should never return */
|
||||
panic("psci: cpu %d failed to shutdown\n", cpu);
|
||||
}
|
||||
#else
|
||||
#define psci_cpu_die NULL
|
||||
#endif
|
||||
|
||||
bool __init psci_smp_available(void)
|
||||
{
|
||||
/* is cpu_on available at least? */
|
||||
return (psci_ops.cpu_on != NULL);
|
||||
}
|
||||
|
||||
struct smp_operations __initdata psci_smp_ops = {
|
||||
.smp_boot_secondary = psci_boot_secondary,
|
||||
.cpu_die = psci_cpu_die,
|
||||
};
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
#include <asm/cputype.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/procinfo.h>
|
||||
#include <asm/psci.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
|
@ -261,6 +262,19 @@ static int cpu_has_aliasing_icache(unsigned int arch)
|
|||
int aliasing_icache;
|
||||
unsigned int id_reg, num_sets, line_size;
|
||||
|
||||
#ifdef CONFIG_BIG_LITTLE
|
||||
/*
|
||||
* We expect a combination of Cortex-A15 and Cortex-A7 cores.
|
||||
* A7 = VIPT aliasing I-cache
|
||||
* A15 = PIPT (non-aliasing) I-cache
|
||||
* To cater for this discrepancy, let's assume aliasing I-cache
|
||||
* all the time. This means unneeded extra work on the A15 but
|
||||
* only ptrace is affected which is not performance critical.
|
||||
*/
|
||||
if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* PIPT caches never alias. */
|
||||
if (icache_is_pipt())
|
||||
return 0;
|
||||
|
|
@ -796,9 +810,15 @@ void __init setup_arch(char **cmdline_p)
|
|||
unflatten_device_tree();
|
||||
|
||||
arm_dt_init_cpu_maps();
|
||||
psci_init();
|
||||
#ifdef CONFIG_SMP
|
||||
if (is_smp()) {
|
||||
smp_set_ops(mdesc->smp);
|
||||
if (!mdesc->smp_init || !mdesc->smp_init()) {
|
||||
if (psci_smp_available())
|
||||
smp_set_ops(&psci_smp_ops);
|
||||
else if (mdesc->smp)
|
||||
smp_set_ops(mdesc->smp);
|
||||
}
|
||||
smp_init_cpus();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <asm/assembler.h>
|
||||
#include <asm/glue-cache.h>
|
||||
#include <asm/glue-proc.h>
|
||||
#include "entry-header.S"
|
||||
.text
|
||||
|
||||
/*
|
||||
|
|
@ -30,9 +31,8 @@ ENTRY(__cpu_suspend)
|
|||
mov r2, r5 @ virtual SP
|
||||
ldr r3, =sleep_save_sp
|
||||
#ifdef CONFIG_SMP
|
||||
ALT_SMP(mrc p15, 0, lr, c0, c0, 5)
|
||||
ALT_UP(mov lr, #0)
|
||||
and lr, lr, #15
|
||||
get_thread_info r5
|
||||
ldr lr, [r5, #TI_CPU] @ cpu logical index
|
||||
add r3, r3, lr, lsl #2
|
||||
#endif
|
||||
bl __cpu_suspend_save
|
||||
|
|
@ -82,10 +82,13 @@ ENDPROC(cpu_resume_after_mmu)
|
|||
.align
|
||||
ENTRY(cpu_resume)
|
||||
#ifdef CONFIG_SMP
|
||||
mov r1, #0 @ fall-back logical index for UP
|
||||
ALT_SMP(mrc p15, 0, r0, c0, c0, 5)
|
||||
ALT_UP_B(1f)
|
||||
bic r0, #0xff000000
|
||||
bl cpu_logical_index @ return logical index in r1
|
||||
1:
|
||||
adr r0, sleep_save_sp
|
||||
ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
|
||||
ALT_UP(mov r1, #0)
|
||||
and r1, r1, #15
|
||||
ldr r0, [r0, r1, lsl #2] @ stack phys addr
|
||||
#else
|
||||
ldr r0, sleep_save_sp @ stack phys addr
|
||||
|
|
@ -102,3 +105,20 @@ sleep_save_sp:
|
|||
.rept CONFIG_NR_CPUS
|
||||
.long 0 @ preserve stack phys ptr here
|
||||
.endr
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpu_logical_index:
|
||||
adr r3, cpu_map_ptr
|
||||
ldr r2, [r3]
|
||||
add r3, r3, r2 @ virt_to_phys(__cpu_logical_map)
|
||||
mov r1, #0
|
||||
1:
|
||||
ldr r2, [r3, r1, lsl #2]
|
||||
cmp r2, r0
|
||||
moveq pc, lr
|
||||
add r1, r1, #1
|
||||
b 1b
|
||||
|
||||
cpu_map_ptr:
|
||||
.long __cpu_logical_map - .
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
config ARCH_VEXPRESS
|
||||
bool "ARM Ltd. Versatile Express family" if ARCH_MULTI_V7
|
||||
select ARCH_HAS_CPUFREQ
|
||||
select ARCH_HAS_OPP
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARM_AMBA
|
||||
select ARM_GIC
|
||||
|
|
@ -56,5 +58,23 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA
|
|||
|
||||
config ARCH_VEXPRESS_CA9X4
|
||||
bool "Versatile Express Cortex-A9x4 tile"
|
||||
select ARM_ERRATA_643719
|
||||
|
||||
config ARCH_VEXPRESS_DCSCB
|
||||
bool "Dual Cluster System Control Block (DCSCB) support"
|
||||
depends on MCPM
|
||||
select ARM_CCI
|
||||
help
|
||||
Support for the Dual Cluster System Configuration Block (DCSCB).
|
||||
This is needed to provide CPU and cluster power management
|
||||
on RTSM implementing big.LITTLE.
|
||||
|
||||
config ARCH_VEXPRESS_TC2
|
||||
bool "TC2 cluster management"
|
||||
depends on MCPM
|
||||
select VEXPRESS_SPC
|
||||
select ARM_CCI
|
||||
help
|
||||
Support for CPU and cluster power management on TC2.
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -6,5 +6,13 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
|
|||
|
||||
obj-y := v2m.o
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o
|
||||
CFLAGS_REMOVE_dcscb.o = -pg
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o
|
||||
CFLAGS_REMOVE_tc2_pm.o = -pg
|
||||
ifeq ($(CONFIG_ARCH_VEXPRESS_TC2),y)
|
||||
obj-$(CONFIG_ARM_PSCI) += tc2_pm_psci.o
|
||||
CFLAGS_REMOVE_tc2_pm_psci.o = -pg
|
||||
endif
|
||||
obj-$(CONFIG_SMP) += platsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
void vexpress_dt_smp_map_io(void);
|
||||
|
||||
bool vexpress_smp_init_ops(void);
|
||||
|
||||
extern struct smp_operations vexpress_smp_ops;
|
||||
|
||||
extern void vexpress_cpu_die(unsigned int cpu);
|
||||
|
|
|
|||
260
arch/arm/mach-vexpress/dcscb.c
Normal file
260
arch/arm/mach-vexpress/dcscb.c
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block
|
||||
*
|
||||
* Created by: Nicolas Pitre, May 2012
|
||||
* Copyright: (C) 2012-2013 Linaro Limited
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/vexpress.h>
|
||||
#include <linux/arm-cci.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/psci.h>
|
||||
|
||||
|
||||
#define RST_HOLD0 0x0
|
||||
#define RST_HOLD1 0x4
|
||||
#define SYS_SWRESET 0x8
|
||||
#define RST_STAT0 0xc
|
||||
#define RST_STAT1 0x10
|
||||
#define EAG_CFG_R 0x20
|
||||
#define EAG_CFG_W 0x24
|
||||
#define KFC_CFG_R 0x28
|
||||
#define KFC_CFG_W 0x2c
|
||||
#define DCS_CFG_R 0x30
|
||||
|
||||
/*
|
||||
* We can't use regular spinlocks. In the switcher case, it is possible
|
||||
* for an outbound CPU to call power_down() while its inbound counterpart
|
||||
* is already live using the same logical CPU number which trips lockdep
|
||||
* debugging.
|
||||
*/
|
||||
static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static void __iomem *dcscb_base;
|
||||
static int dcscb_use_count[4][2];
|
||||
static int dcscb_allcpus_mask[2];
|
||||
|
||||
static int dcscb_power_up(unsigned int cpu, unsigned int cluster)
|
||||
{
|
||||
unsigned int rst_hold, cpumask = (1 << cpu);
|
||||
unsigned int all_mask = dcscb_allcpus_mask[cluster];
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
if (cpu >= 4 || cluster >= 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Since this is called with IRQs enabled, and no arch_spin_lock_irq
|
||||
* variant exists, we need to disable IRQs manually here.
|
||||
*/
|
||||
local_irq_disable();
|
||||
arch_spin_lock(&dcscb_lock);
|
||||
|
||||
dcscb_use_count[cpu][cluster]++;
|
||||
if (dcscb_use_count[cpu][cluster] == 1) {
|
||||
rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
|
||||
if (rst_hold & (1 << 8)) {
|
||||
/* remove cluster reset and add individual CPU's reset */
|
||||
rst_hold &= ~(1 << 8);
|
||||
rst_hold |= all_mask;
|
||||
}
|
||||
rst_hold &= ~(cpumask | (cpumask << 4));
|
||||
writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
|
||||
} else if (dcscb_use_count[cpu][cluster] != 2) {
|
||||
/*
|
||||
* The only possible values are:
|
||||
* 0 = CPU down
|
||||
* 1 = CPU (still) up
|
||||
* 2 = CPU requested to be up before it had a chance
|
||||
* to actually make itself down.
|
||||
* Any other value is a bug.
|
||||
*/
|
||||
BUG();
|
||||
}
|
||||
|
||||
arch_spin_unlock(&dcscb_lock);
|
||||
local_irq_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dcscb_power_down(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster, rst_hold, cpumask, all_mask;
|
||||
bool last_man = false, skip_wfi = false;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
cpumask = (1 << cpu);
|
||||
all_mask = dcscb_allcpus_mask[cluster];
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cpu >= 4 || cluster >= 2);
|
||||
|
||||
__mcpm_cpu_going_down(cpu, cluster);
|
||||
|
||||
arch_spin_lock(&dcscb_lock);
|
||||
BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
|
||||
dcscb_use_count[cpu][cluster]--;
|
||||
if (dcscb_use_count[cpu][cluster] == 0) {
|
||||
rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
|
||||
rst_hold |= cpumask;
|
||||
if (((rst_hold | (rst_hold >> 4)) & all_mask) == all_mask) {
|
||||
rst_hold |= (1 << 8);
|
||||
last_man = true;
|
||||
}
|
||||
writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
|
||||
} else if (dcscb_use_count[cpu][cluster] == 1) {
|
||||
/*
|
||||
* A power_up request went ahead of us.
|
||||
* Even if we do not want to shut this CPU down,
|
||||
* the caller expects a certain state as if the WFI
|
||||
* was aborted. So let's continue with cache cleaning.
|
||||
*/
|
||||
skip_wfi = true;
|
||||
} else
|
||||
BUG();
|
||||
|
||||
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
|
||||
arch_spin_unlock(&dcscb_lock);
|
||||
|
||||
/*
|
||||
* Flush all cache levels for this cluster.
|
||||
*
|
||||
* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need
|
||||
* a preliminary flush here for those CPUs. At least, that's
|
||||
* the theory -- without the extra flush, Linux explodes on
|
||||
* RTSM (to be investigated).
|
||||
*/
|
||||
flush_cache_all();
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_all();
|
||||
|
||||
/*
|
||||
* This is a harmless no-op. On platforms with a real
|
||||
* outer cache this might either be needed or not,
|
||||
* depending on where the outer cache sits.
|
||||
*/
|
||||
outer_flush_all();
|
||||
|
||||
/* Disable local coherency by clearing the ACTLR "SMP" bit: */
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
|
||||
/*
|
||||
* Disable cluster-level coherency by masking
|
||||
* incoming snoops and DVM messages:
|
||||
*/
|
||||
cci_disable_port_by_cpu(mpidr);
|
||||
|
||||
__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
|
||||
} else {
|
||||
arch_spin_unlock(&dcscb_lock);
|
||||
|
||||
/*
|
||||
* Flush the local CPU cache.
|
||||
*
|
||||
* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need
|
||||
* a preliminary flush here for those CPUs. At least, that's
|
||||
* the theory -- without the extra flush, Linux explodes on
|
||||
* RTSM (to be investigated).
|
||||
*/
|
||||
flush_cache_louis();
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_louis();
|
||||
|
||||
/* Disable local coherency by clearing the ACTLR "SMP" bit: */
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
}
|
||||
|
||||
__mcpm_cpu_down(cpu, cluster);
|
||||
|
||||
/* Now we are prepared for power-down, do it: */
|
||||
dsb();
|
||||
if (!skip_wfi)
|
||||
wfi();
|
||||
|
||||
/* Not dead at this point? Let our caller cope. */
|
||||
}
|
||||
|
||||
static const struct mcpm_platform_ops dcscb_power_ops = {
|
||||
.power_up = dcscb_power_up,
|
||||
.power_down = dcscb_power_down,
|
||||
};
|
||||
|
||||
static void __init dcscb_usage_count_init(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cpu >= 4 || cluster >= 2);
|
||||
dcscb_use_count[cpu][cluster] = 1;
|
||||
}
|
||||
|
||||
extern void dcscb_power_up_setup(unsigned int affinity_level);
|
||||
|
||||
static int __init dcscb_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
unsigned int cfg;
|
||||
int ret;
|
||||
|
||||
ret = psci_probe();
|
||||
if (!ret) {
|
||||
pr_debug("psci found. Aborting native init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!cci_probed())
|
||||
return -ENODEV;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb");
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
dcscb_base = of_iomap(node, 0);
|
||||
if (!dcscb_base)
|
||||
return -EADDRNOTAVAIL;
|
||||
cfg = readl_relaxed(dcscb_base + DCS_CFG_R);
|
||||
dcscb_allcpus_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1;
|
||||
dcscb_allcpus_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1;
|
||||
dcscb_usage_count_init();
|
||||
|
||||
ret = mcpm_platform_register(&dcscb_power_ops);
|
||||
if (!ret)
|
||||
ret = mcpm_sync_init(dcscb_power_up_setup);
|
||||
if (ret) {
|
||||
iounmap(dcscb_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("VExpress DCSCB support installed\n");
|
||||
|
||||
/*
|
||||
* Future entries into the kernel can now go
|
||||
* through the cluster entry vectors.
|
||||
*/
|
||||
vexpress_flags_set(virt_to_phys(mcpm_entry_point));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_initcall(dcscb_init);
|
||||
38
arch/arm/mach-vexpress/dcscb_setup.S
Normal file
38
arch/arm/mach-vexpress/dcscb_setup.S
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* arch/arm/include/asm/dcscb_setup.S
|
||||
*
|
||||
* Created by: Dave Martin, 2012-06-22
|
||||
* Copyright: (C) 2012-2013 Linaro Limited
|
||||
*
|
||||
* 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/linkage.h>
|
||||
|
||||
|
||||
ENTRY(dcscb_power_up_setup)
|
||||
|
||||
cmp r0, #0 @ check affinity level
|
||||
beq 2f
|
||||
|
||||
/*
|
||||
* Enable cluster-level coherency, in preparation for turning on the MMU.
|
||||
* The ACTLR SMP bit does not need to be set here, because cpu_resume()
|
||||
* already restores that.
|
||||
*
|
||||
* A15/A7 may not require explicit L2 invalidation on reset, dependent
|
||||
* on hardware integration decisions.
|
||||
* For now, this code assumes that L2 is either already invalidated,
|
||||
* or invalidation is not required.
|
||||
*/
|
||||
|
||||
b cci_enable_port_for_self
|
||||
|
||||
2: @ Implementation-specific local CPU setup operations should go here,
|
||||
@ if any. In this case, there is nothing to do.
|
||||
|
||||
bx lr
|
||||
|
||||
ENDPROC(dcscb_power_up_setup)
|
||||
10
arch/arm/mach-vexpress/include/mach/tc2.h
Normal file
10
arch/arm/mach-vexpress/include/mach/tc2.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __MACH_TC2_H
|
||||
#define __MACH_TC2_H
|
||||
|
||||
/*
|
||||
* cpu and cluster limits
|
||||
*/
|
||||
#define TC2_MAX_CPUS 3
|
||||
#define TC2_MAX_CLUSTERS 2
|
||||
|
||||
#endif
|
||||
|
|
@ -12,9 +12,11 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
|
|
@ -203,3 +205,21 @@ struct smp_operations __initdata vexpress_smp_ops = {
|
|||
.cpu_die = vexpress_cpu_die,
|
||||
#endif
|
||||
};
|
||||
|
||||
bool __init vexpress_smp_init_ops(void)
|
||||
{
|
||||
#ifdef CONFIG_MCPM
|
||||
/*
|
||||
* The best way to detect a multi-cluster configuration at the moment
|
||||
* is to look for the presence of a CCI in the system.
|
||||
* Override the default vexpress_smp_ops if so.
|
||||
*/
|
||||
struct device_node *node;
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
|
||||
if (node && of_device_is_available(node)) {
|
||||
mcpm_smp_set_ops();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
271
arch/arm/mach-vexpress/tc2_pm.c
Normal file
271
arch/arm/mach-vexpress/tc2_pm.c
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* arch/arm/mach-vexpress/tc2_pm.c - TC2 power management support
|
||||
*
|
||||
* Created by: Nicolas Pitre, October 2012
|
||||
* Copyright: (C) 2012 Linaro Limited
|
||||
*
|
||||
* Some portions of this file were originally written by Achin Gupta
|
||||
* Copyright: (C) 2012 ARM Limited
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/psci.h>
|
||||
|
||||
#include <mach/motherboard.h>
|
||||
#include <mach/tc2.h>
|
||||
|
||||
#include <linux/vexpress.h>
|
||||
#include <linux/arm-cci.h>
|
||||
|
||||
/*
|
||||
* We can't use regular spinlocks. In the switcher case, it is possible
|
||||
* for an outbound CPU to call power_down() after its inbound counterpart
|
||||
* is already live using the same logical CPU number which trips lockdep
|
||||
* debugging.
|
||||
*/
|
||||
static arch_spinlock_t tc2_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static int tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS];
|
||||
|
||||
static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster)
|
||||
{
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
if (cluster >= TC2_MAX_CLUSTERS ||
|
||||
cpu >= vexpress_spc_get_nb_cpus(cluster))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Since this is called with IRQs enabled, and no arch_spin_lock_irq
|
||||
* variant exists, we need to disable IRQs manually here.
|
||||
*/
|
||||
local_irq_disable();
|
||||
arch_spin_lock(&tc2_pm_lock);
|
||||
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster])
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
|
||||
tc2_pm_use_count[cpu][cluster]++;
|
||||
if (tc2_pm_use_count[cpu][cluster] == 1) {
|
||||
vexpress_spc_write_resume_reg(cluster, cpu,
|
||||
virt_to_phys(mcpm_entry_point));
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1);
|
||||
} else if (tc2_pm_use_count[cpu][cluster] != 2) {
|
||||
/*
|
||||
* The only possible values are:
|
||||
* 0 = CPU down
|
||||
* 1 = CPU (still) up
|
||||
* 2 = CPU requested to be up before it had a chance
|
||||
* to actually make itself down.
|
||||
* Any other value is a bug.
|
||||
*/
|
||||
BUG();
|
||||
}
|
||||
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
local_irq_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc2_pm_down(u64 residency)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
bool last_man = false, skip_wfi = false;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cluster >= TC2_MAX_CLUSTERS ||
|
||||
cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
__mcpm_cpu_going_down(cpu, cluster);
|
||||
|
||||
arch_spin_lock(&tc2_pm_lock);
|
||||
BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
|
||||
tc2_pm_use_count[cpu][cluster]--;
|
||||
if (tc2_pm_use_count[cpu][cluster] == 0) {
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1);
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster] &&
|
||||
(!residency || residency > 5000)) {
|
||||
vexpress_spc_powerdown_enable(cluster, 1);
|
||||
vexpress_spc_set_global_wakeup_intr(1);
|
||||
last_man = true;
|
||||
}
|
||||
} else if (tc2_pm_use_count[cpu][cluster] == 1) {
|
||||
/*
|
||||
* A power_up request went ahead of us.
|
||||
* Even if we do not want to shut this CPU down,
|
||||
* the caller expects a certain state as if the WFI
|
||||
* was aborted. So let's continue with cache cleaning.
|
||||
*/
|
||||
skip_wfi = true;
|
||||
} else
|
||||
BUG();
|
||||
|
||||
gic_cpu_if_down();
|
||||
|
||||
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_all();
|
||||
asm volatile ("clrex");
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
|
||||
cci_disable_port_by_cpu(mpidr);
|
||||
|
||||
/*
|
||||
* Ensure that both C & I bits are disabled in the SCTLR
|
||||
* before disabling ACE snoops. This ensures that no
|
||||
* coherency traffic will originate from this cpu after
|
||||
* ACE snoops are turned off.
|
||||
*/
|
||||
cpu_proc_fin();
|
||||
|
||||
__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
|
||||
} else {
|
||||
/*
|
||||
* If last man then undo any setup done previously.
|
||||
*/
|
||||
if (last_man) {
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
vexpress_spc_set_global_wakeup_intr(0);
|
||||
}
|
||||
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_louis();
|
||||
asm volatile ("clrex");
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
}
|
||||
|
||||
__mcpm_cpu_down(cpu, cluster);
|
||||
|
||||
/* Now we are prepared for power-down, do it: */
|
||||
if (!skip_wfi)
|
||||
wfi();
|
||||
|
||||
/* Not dead at this point? Let our caller cope. */
|
||||
}
|
||||
|
||||
static void tc2_pm_power_down(void)
|
||||
{
|
||||
tc2_pm_down(0);
|
||||
}
|
||||
|
||||
static void tc2_pm_suspend(u64 residency)
|
||||
{
|
||||
extern void tc2_resume(void);
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
vexpress_spc_write_resume_reg(cluster, cpu,
|
||||
virt_to_phys(tc2_resume));
|
||||
|
||||
tc2_pm_down(residency);
|
||||
}
|
||||
|
||||
static void tc2_pm_powered_up(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
unsigned long flags;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cluster >= TC2_MAX_CLUSTERS ||
|
||||
cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
local_irq_save(flags);
|
||||
arch_spin_lock(&tc2_pm_lock);
|
||||
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster]) {
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
vexpress_spc_set_global_wakeup_intr(0);
|
||||
}
|
||||
|
||||
if (!tc2_pm_use_count[cpu][cluster])
|
||||
tc2_pm_use_count[cpu][cluster] = 1;
|
||||
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 0);
|
||||
vexpress_spc_write_resume_reg(cluster, cpu, 0);
|
||||
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static const struct mcpm_platform_ops tc2_pm_power_ops = {
|
||||
.power_up = tc2_pm_power_up,
|
||||
.power_down = tc2_pm_power_down,
|
||||
.suspend = tc2_pm_suspend,
|
||||
.powered_up = tc2_pm_powered_up,
|
||||
};
|
||||
|
||||
static void __init tc2_pm_usage_count_init(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cluster >= TC2_MAX_CLUSTERS ||
|
||||
cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
tc2_pm_use_count[cpu][cluster] = 1;
|
||||
}
|
||||
|
||||
extern void tc2_pm_power_up_setup(unsigned int affinity_level);
|
||||
|
||||
static int __init tc2_pm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = psci_probe();
|
||||
if (!ret) {
|
||||
pr_debug("psci found. Aborting native init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!vexpress_spc_check_loaded())
|
||||
return -ENODEV;
|
||||
|
||||
tc2_pm_usage_count_init();
|
||||
|
||||
ret = mcpm_platform_register(&tc2_pm_power_ops);
|
||||
if (!ret)
|
||||
ret = mcpm_sync_init(tc2_pm_power_up_setup);
|
||||
if (!ret)
|
||||
pr_info("TC2 power management initialized\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
early_initcall(tc2_pm_init);
|
||||
173
arch/arm/mach-vexpress/tc2_pm_psci.c
Normal file
173
arch/arm/mach-vexpress/tc2_pm_psci.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* arch/arm/mach-vexpress/tc2_pm_psci.c - TC2 PSCI support
|
||||
*
|
||||
* Created by: Achin Gupta, December 2012
|
||||
* Copyright: (C) 2012 ARM Limited
|
||||
*
|
||||
* Some portions of this file were originally written by Nicolas Pitre
|
||||
* Copyright: (C) 2012 Linaro Limited
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/psci.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cp15.h>
|
||||
|
||||
#include <mach/motherboard.h>
|
||||
#include <mach/tc2.h>
|
||||
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
/*
|
||||
* Platform specific state id understood by the firmware and used to
|
||||
* program the power controller
|
||||
*/
|
||||
#define PSCI_POWER_STATE_ID 0
|
||||
|
||||
static atomic_t tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS];
|
||||
|
||||
static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster)
|
||||
{
|
||||
unsigned int mpidr = (cluster << 8) | cpu;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(!psci_ops.cpu_on);
|
||||
|
||||
switch (atomic_inc_return(&tc2_pm_use_count[cpu][cluster])) {
|
||||
case 1:
|
||||
/*
|
||||
* This is a request to power up a cpu that linux thinks has
|
||||
* been powered down. Retries are needed if the firmware has
|
||||
* seen the power down request as yet.
|
||||
*/
|
||||
do
|
||||
ret = psci_ops.cpu_on(mpidr,
|
||||
virt_to_phys(mcpm_entry_point));
|
||||
while (ret == -EAGAIN);
|
||||
|
||||
return ret;
|
||||
case 2:
|
||||
/* This power up request has overtaken a power down request */
|
||||
return ret;
|
||||
default:
|
||||
/* Any other value is a bug */
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static void tc2_pm_psci_power_down(void)
|
||||
{
|
||||
struct psci_power_state power_state;
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
BUG_ON(!psci_ops.cpu_off);
|
||||
|
||||
switch (atomic_dec_return(&tc2_pm_use_count[cpu][cluster])) {
|
||||
case 1:
|
||||
/*
|
||||
* Overtaken by a power up. Flush caches, exit coherency,
|
||||
* return & fake a reset
|
||||
*/
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
|
||||
flush_cache_louis();
|
||||
|
||||
asm volatile ("clrex");
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
|
||||
return;
|
||||
case 0:
|
||||
/* A normal request to possibly power down the cluster */
|
||||
power_state.id = PSCI_POWER_STATE_ID;
|
||||
power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN;
|
||||
power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1;
|
||||
|
||||
psci_ops.cpu_off(power_state);
|
||||
|
||||
/* On success this function never returns */
|
||||
default:
|
||||
/* Any other value is a bug */
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static void tc2_pm_psci_suspend(u64 unused)
|
||||
{
|
||||
struct psci_power_state power_state;
|
||||
|
||||
BUG_ON(!psci_ops.cpu_suspend);
|
||||
|
||||
/* On TC2 always attempt to power down the cluster */
|
||||
power_state.id = PSCI_POWER_STATE_ID;
|
||||
power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN;
|
||||
power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1;
|
||||
|
||||
psci_ops.cpu_suspend(power_state, virt_to_phys(mcpm_entry_point));
|
||||
|
||||
/* On success this function never returns */
|
||||
BUG();
|
||||
}
|
||||
|
||||
static const struct mcpm_platform_ops tc2_pm_power_ops = {
|
||||
.power_up = tc2_pm_psci_power_up,
|
||||
.power_down = tc2_pm_psci_power_down,
|
||||
.suspend = tc2_pm_psci_suspend,
|
||||
};
|
||||
|
||||
static void __init tc2_pm_usage_count_init(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
BUG_ON(cluster >= TC2_MAX_CLUSTERS ||
|
||||
cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
atomic_set(&tc2_pm_use_count[cpu][cluster], 1);
|
||||
}
|
||||
|
||||
static int __init tc2_pm_psci_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = psci_probe();
|
||||
if (ret) {
|
||||
pr_debug("psci not found. Aborting psci init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!vexpress_spc_check_loaded()) {
|
||||
pr_debug("spc not found. Aborting psci init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tc2_pm_usage_count_init();
|
||||
|
||||
ret = mcpm_platform_register(&tc2_pm_power_ops);
|
||||
if (!ret)
|
||||
ret = mcpm_sync_init(NULL);
|
||||
if (!ret)
|
||||
pr_info("TC2 power management initialized\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
early_initcall(tc2_pm_psci_init);
|
||||
68
arch/arm/mach-vexpress/tc2_pm_setup.S
Normal file
68
arch/arm/mach-vexpress/tc2_pm_setup.S
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* arch/arm/include/asm/tc2_pm_setup.S
|
||||
*
|
||||
* Created by: Nicolas Pitre, October 2012
|
||||
( (based on dcscb_setup.S by Dave Martin)
|
||||
* Copyright: (C) 2012 Linaro Limited
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <asm/mcpm.h>
|
||||
|
||||
|
||||
#define SPC_PHYS_BASE 0x7FFF0000
|
||||
#define SPC_WAKE_INT_STAT 0xb2c
|
||||
|
||||
#define SNOOP_CTL_A15 0x404
|
||||
#define SNOOP_CTL_A7 0x504
|
||||
|
||||
#define A15_SNOOP_MASK (0x3 << 7)
|
||||
#define A7_SNOOP_MASK (0x1 << 13)
|
||||
|
||||
#define A15_BX_ADDR0 0xB68
|
||||
|
||||
|
||||
ENTRY(tc2_resume)
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
ubfx r1, r0, #0, #4 @ r1 = cpu
|
||||
ubfx r2, r0, #8, #4 @ r2 = cluster
|
||||
add r1, r1, r2, lsl #2 @ r1 = index of CPU in WAKE_INT_STAT
|
||||
ldr r3, =SPC_PHYS_BASE + SPC_WAKE_INT_STAT
|
||||
ldr r3, [r3]
|
||||
lsr r3, r1
|
||||
tst r3, #1
|
||||
wfieq @ if no pending IRQ reenters wfi
|
||||
b mcpm_entry_point
|
||||
ENDPROC(tc2_resume)
|
||||
|
||||
/*
|
||||
* Enable cluster-level coherency, in preparation for turning on the MMU.
|
||||
* The ACTLR SMP bit does not need to be set here, because cpu_resume()
|
||||
* already restores that.
|
||||
*/
|
||||
|
||||
ENTRY(tc2_pm_power_up_setup)
|
||||
|
||||
cmp r0, #0
|
||||
beq 2f
|
||||
|
||||
b cci_enable_port_for_self
|
||||
|
||||
2: @ Clear the BX addr register
|
||||
ldr r3, =SPC_PHYS_BASE + A15_BX_ADDR0
|
||||
mrc p15, 0, r0, c0, c0, 5 @ MPIDR
|
||||
ubfx r1, r0, #8, #4 @ cluster
|
||||
ubfx r0, r0, #0, #4 @ cpu
|
||||
add r3, r3, r1, lsl #4
|
||||
mov r1, #0
|
||||
str r1, [r3, r0, lsl #2]
|
||||
dsb
|
||||
|
||||
bx lr
|
||||
|
||||
ENDPROC(tc2_pm_power_up_setup)
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/smp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
|
@ -373,6 +374,31 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express")
|
|||
.init_machine = v2m_init,
|
||||
MACHINE_END
|
||||
|
||||
static void __init v2m_dt_hdlcd_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
int len, na, ns;
|
||||
const __be32 *prop;
|
||||
phys_addr_t fb_base, fb_size;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,hdlcd");
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
na = of_n_addr_cells(node);
|
||||
ns = of_n_size_cells(node);
|
||||
|
||||
prop = of_get_property(node, "framebuffer", &len);
|
||||
if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop)))
|
||||
return;
|
||||
|
||||
fb_base = of_read_number(prop, na);
|
||||
fb_size = of_read_number(prop + na, ns);
|
||||
|
||||
if (WARN_ON(memblock_remove(fb_base, fb_size)))
|
||||
return;
|
||||
};
|
||||
|
||||
static struct map_desc v2m_rs1_io_desc __initdata = {
|
||||
.virtual = V2M_PERIPH,
|
||||
.pfn = __phys_to_pfn(0x1c000000),
|
||||
|
|
@ -423,6 +449,8 @@ void __init v2m_dt_init_early(void)
|
|||
pr_warning("vexpress: DT HBI (%x) is not matching "
|
||||
"hardware (%x)!\n", dt_hbi, hbi);
|
||||
}
|
||||
|
||||
v2m_dt_hdlcd_init();
|
||||
}
|
||||
|
||||
static void __init v2m_dt_timer_init(void)
|
||||
|
|
@ -456,6 +484,7 @@ static const char * const v2m_dt_match[] __initconst = {
|
|||
DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
|
||||
.dt_compat = v2m_dt_match,
|
||||
.smp = smp_ops(vexpress_smp_ops),
|
||||
.smp_init = smp_init_ops(vexpress_smp_init_ops),
|
||||
.map_io = v2m_dt_map_io,
|
||||
.init_early = v2m_dt_init_early,
|
||||
.init_irq = irqchip_init,
|
||||
|
|
|
|||
|
|
@ -3,4 +3,3 @@
|
|||
#
|
||||
|
||||
obj-y := virt.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Dummy Virtual Machine - does what it says on the tin.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Ltd
|
||||
* Author: Will Deacon <will.deacon@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.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/psci.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
extern void secondary_startup(void);
|
||||
|
||||
static void __init virt_smp_init_cpus(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void __init virt_smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
}
|
||||
|
||||
static int __cpuinit virt_boot_secondary(unsigned int cpu,
|
||||
struct task_struct *idle)
|
||||
{
|
||||
if (psci_ops.cpu_on)
|
||||
return psci_ops.cpu_on(cpu_logical_map(cpu),
|
||||
__pa(secondary_startup));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
struct smp_operations __initdata virt_smp_ops = {
|
||||
.smp_init_cpus = virt_smp_init_cpus,
|
||||
.smp_prepare_cpus = virt_smp_prepare_cpus,
|
||||
.smp_boot_secondary = virt_boot_secondary,
|
||||
};
|
||||
|
|
@ -36,11 +36,8 @@ static const char *virt_dt_match[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
extern struct smp_operations virt_smp_ops;
|
||||
|
||||
DT_MACHINE_START(VIRT, "Dummy Virtual Machine")
|
||||
.init_irq = irqchip_init,
|
||||
.init_machine = virt_init,
|
||||
.smp = smp_ops(virt_smp_ops),
|
||||
.dt_compat = virt_dt_match,
|
||||
MACHINE_END
|
||||
|
|
|
|||
|
|
@ -446,8 +446,16 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
|
|||
|
||||
if (pud_none(*pud_k))
|
||||
goto bad_area;
|
||||
if (!pud_present(*pud))
|
||||
if (!pud_present(*pud)) {
|
||||
set_pud(pud, *pud_k);
|
||||
/*
|
||||
* There is a small window during free_pgtables() where the
|
||||
* user *pud entry is 0 but the TLB has not been invalidated
|
||||
* and we get a level 2 (pmd) translation fault caused by the
|
||||
* intermediate TLB caching of the old level 1 (pud) entry.
|
||||
*/
|
||||
flush_tlb_kernel_page(addr);
|
||||
}
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
pmd_k = pmd_offset(pud_k, addr);
|
||||
|
|
@ -470,8 +478,9 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
|
|||
#endif
|
||||
if (pmd_none(pmd_k[index]))
|
||||
goto bad_area;
|
||||
if (!pmd_present(pmd[index]))
|
||||
copy_pmd(pmd, pmd_k);
|
||||
|
||||
copy_pmd(pmd, pmd_k);
|
||||
return 0;
|
||||
|
||||
bad_area:
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
__INIT
|
||||
|
||||
/*
|
||||
* Realview/Versatile Express specific entry point for secondary CPUs.
|
||||
* This provides a "holding pen" into which all secondary cores are held
|
||||
|
|
|
|||
|
|
@ -26,4 +26,11 @@ config OMAP_INTERCONNECT
|
|||
|
||||
help
|
||||
Driver to enable OMAP interconnect error handling driver.
|
||||
|
||||
config ARM_CCI
|
||||
bool "ARM CCI driver support"
|
||||
depends on ARM
|
||||
help
|
||||
Driver supporting the CCI cache coherent interconnect for ARM
|
||||
platforms.
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
|||
|
||||
# Interconnect bus driver for OMAP SoCs.
|
||||
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||
# CCI cache coherent interconnect for ARM platforms
|
||||
obj-$(CONFIG_ARM_CCI) += arm-cci.o
|
||||
|
|
|
|||
947
drivers/bus/arm-cci.c
Normal file
947
drivers/bus/arm-cci.c
Normal file
|
|
@ -0,0 +1,947 @@
|
|||
/*
|
||||
* CCI cache coherent interconnect driver
|
||||
*
|
||||
* Copyright (C) 2013 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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/arm-cci.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/irq_regs.h>
|
||||
#include <asm/pmu.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
#define DRIVER_NAME "CCI"
|
||||
|
||||
#define CCI_PORT_CTRL 0x0
|
||||
#define CCI_CTRL_STATUS 0xc
|
||||
|
||||
#define CCI_ENABLE_SNOOP_REQ 0x1
|
||||
#define CCI_ENABLE_DVM_REQ 0x2
|
||||
#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ)
|
||||
|
||||
struct cci_nb_ports {
|
||||
unsigned int nb_ace;
|
||||
unsigned int nb_ace_lite;
|
||||
};
|
||||
|
||||
enum cci_ace_port_type {
|
||||
ACE_INVALID_PORT = 0x0,
|
||||
ACE_PORT,
|
||||
ACE_LITE_PORT,
|
||||
};
|
||||
|
||||
struct cci_ace_port {
|
||||
void __iomem *base;
|
||||
unsigned long phys;
|
||||
enum cci_ace_port_type type;
|
||||
struct device_node *dn;
|
||||
};
|
||||
|
||||
static struct cci_ace_port *ports;
|
||||
static unsigned int nb_cci_ports;
|
||||
|
||||
static void __iomem *cci_ctrl_base;
|
||||
static unsigned long cci_ctrl_phys;
|
||||
|
||||
#ifdef CONFIG_HW_PERF_EVENTS
|
||||
|
||||
static void __iomem *cci_pmu_base;
|
||||
|
||||
#define CCI400_PMCR 0x0100
|
||||
|
||||
#define CCI400_PMU_CYCLE_CNTR_BASE 0x0000
|
||||
#define CCI400_PMU_CNTR_BASE(idx) (CCI400_PMU_CYCLE_CNTR_BASE + (idx) * 0x1000)
|
||||
|
||||
#define CCI400_PMCR_CEN 0x00000001
|
||||
#define CCI400_PMCR_RST 0x00000002
|
||||
#define CCI400_PMCR_CCR 0x00000004
|
||||
#define CCI400_PMCR_CCD 0x00000008
|
||||
#define CCI400_PMCR_EX 0x00000010
|
||||
#define CCI400_PMCR_DP 0x00000020
|
||||
#define CCI400_PMCR_NCNT_MASK 0x0000F800
|
||||
#define CCI400_PMCR_NCNT_SHIFT 11
|
||||
|
||||
#define CCI400_PMU_EVT_SEL 0x000
|
||||
#define CCI400_PMU_CNTR 0x004
|
||||
#define CCI400_PMU_CNTR_CTRL 0x008
|
||||
#define CCI400_PMU_OVERFLOW 0x00C
|
||||
|
||||
#define CCI400_PMU_OVERFLOW_FLAG 1
|
||||
|
||||
enum cci400_perf_events {
|
||||
CCI400_PMU_CYCLES = 0xFF
|
||||
};
|
||||
|
||||
#define CCI400_PMU_EVENT_MASK 0xff
|
||||
#define CCI400_PMU_EVENT_SOURCE(event) ((event >> 5) & 0x7)
|
||||
#define CCI400_PMU_EVENT_CODE(event) (event & 0x1f)
|
||||
|
||||
#define CCI400_PMU_EVENT_SOURCE_S0 0
|
||||
#define CCI400_PMU_EVENT_SOURCE_S4 4
|
||||
#define CCI400_PMU_EVENT_SOURCE_M0 5
|
||||
#define CCI400_PMU_EVENT_SOURCE_M2 7
|
||||
|
||||
#define CCI400_PMU_EVENT_SLAVE_MIN 0x0
|
||||
#define CCI400_PMU_EVENT_SLAVE_MAX 0x13
|
||||
|
||||
#define CCI400_PMU_EVENT_MASTER_MIN 0x14
|
||||
#define CCI400_PMU_EVENT_MASTER_MAX 0x1A
|
||||
|
||||
#define CCI400_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */
|
||||
|
||||
#define CCI400_PMU_CYCLE_COUNTER_IDX 0
|
||||
#define CCI400_PMU_COUNTER0_IDX 1
|
||||
#define CCI400_PMU_COUNTER_LAST(cci_pmu) (CCI400_PMU_CYCLE_COUNTER_IDX + cci_pmu->num_events - 1)
|
||||
|
||||
|
||||
static struct perf_event *events[CCI400_PMU_MAX_HW_EVENTS];
|
||||
static unsigned long used_mask[BITS_TO_LONGS(CCI400_PMU_MAX_HW_EVENTS)];
|
||||
static struct pmu_hw_events cci_hw_events = {
|
||||
.events = events,
|
||||
.used_mask = used_mask,
|
||||
};
|
||||
|
||||
static int cci_pmu_validate_hw_event(u8 hw_event)
|
||||
{
|
||||
u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event);
|
||||
u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event);
|
||||
|
||||
if (ev_source <= CCI400_PMU_EVENT_SOURCE_S4 &&
|
||||
ev_code <= CCI400_PMU_EVENT_SLAVE_MAX)
|
||||
return hw_event;
|
||||
else if (CCI400_PMU_EVENT_SOURCE_M0 <= ev_source &&
|
||||
ev_source <= CCI400_PMU_EVENT_SOURCE_M2 &&
|
||||
CCI400_PMU_EVENT_MASTER_MIN <= ev_code &&
|
||||
ev_code <= CCI400_PMU_EVENT_MASTER_MAX)
|
||||
return hw_event;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int cci_pmu_counter_is_valid(struct arm_pmu *cci_pmu, int idx)
|
||||
{
|
||||
return CCI400_PMU_CYCLE_COUNTER_IDX <= idx &&
|
||||
idx <= CCI400_PMU_COUNTER_LAST(cci_pmu);
|
||||
}
|
||||
|
||||
static inline u32 cci_pmu_read_register(int idx, unsigned int offset)
|
||||
{
|
||||
return readl_relaxed(cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset);
|
||||
}
|
||||
|
||||
static inline void cci_pmu_write_register(u32 value, int idx, unsigned int offset)
|
||||
{
|
||||
return writel_relaxed(value, cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset);
|
||||
}
|
||||
|
||||
static inline void cci_pmu_disable_counter(int idx)
|
||||
{
|
||||
cci_pmu_write_register(0, idx, CCI400_PMU_CNTR_CTRL);
|
||||
}
|
||||
|
||||
static inline void cci_pmu_enable_counter(int idx)
|
||||
{
|
||||
cci_pmu_write_register(1, idx, CCI400_PMU_CNTR_CTRL);
|
||||
}
|
||||
|
||||
static inline void cci_pmu_select_event(int idx, unsigned long event)
|
||||
{
|
||||
event &= CCI400_PMU_EVENT_MASK;
|
||||
cci_pmu_write_register(event, idx, CCI400_PMU_EVT_SEL);
|
||||
}
|
||||
|
||||
static u32 cci_pmu_get_max_counters(void)
|
||||
{
|
||||
u32 n_cnts = (readl_relaxed(cci_ctrl_base + CCI400_PMCR) &
|
||||
CCI400_PMCR_NCNT_MASK) >> CCI400_PMCR_NCNT_SHIFT;
|
||||
|
||||
/* add 1 for cycle counter */
|
||||
return n_cnts + 1;
|
||||
}
|
||||
|
||||
static struct pmu_hw_events *cci_pmu_get_hw_events(void)
|
||||
{
|
||||
return &cci_hw_events;
|
||||
}
|
||||
|
||||
static int cci_pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_event = &event->hw;
|
||||
unsigned long cci_event = hw_event->config_base & CCI400_PMU_EVENT_MASK;
|
||||
int idx;
|
||||
|
||||
if (cci_event == CCI400_PMU_CYCLES) {
|
||||
if (test_and_set_bit(CCI400_PMU_CYCLE_COUNTER_IDX, hw->used_mask))
|
||||
return -EAGAIN;
|
||||
|
||||
return CCI400_PMU_CYCLE_COUNTER_IDX;
|
||||
}
|
||||
|
||||
for (idx = CCI400_PMU_COUNTER0_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); ++idx) {
|
||||
if (!test_and_set_bit(idx, hw->used_mask))
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* No counters available */
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int cci_pmu_map_event(struct perf_event *event)
|
||||
{
|
||||
int mapping;
|
||||
u8 config = event->attr.config & CCI400_PMU_EVENT_MASK;
|
||||
|
||||
if (event->attr.type < PERF_TYPE_MAX)
|
||||
return -ENOENT;
|
||||
|
||||
/* 0xff is used to represent CCI Cycles */
|
||||
if (config == 0xff)
|
||||
mapping = config;
|
||||
else
|
||||
mapping = cci_pmu_validate_hw_event(config);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static int cci_pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler)
|
||||
{
|
||||
int irq, err, i = 0;
|
||||
struct platform_device *pmu_device = cci_pmu->plat_device;
|
||||
|
||||
if (unlikely(!pmu_device))
|
||||
return -ENODEV;
|
||||
|
||||
/* CCI exports 6 interrupts - 1 nERRORIRQ + 5 nEVNTCNTOVERFLOW (PMU)
|
||||
nERRORIRQ will be handled by secure firmware on TC2. So we
|
||||
assume that all CCI interrupts listed in the linux device
|
||||
tree are PMU interrupts.
|
||||
|
||||
The following code should then be able to handle different routing
|
||||
of the CCI PMU interrupts.
|
||||
*/
|
||||
while ((irq = platform_get_irq(pmu_device, i)) > 0) {
|
||||
err = request_irq(irq, handler, 0, "arm-cci-pmu", cci_pmu);
|
||||
if (err) {
|
||||
dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n",
|
||||
irq);
|
||||
return err;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t cci_pmu_handle_irq(int irq_num, void *dev)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = (struct arm_pmu *)dev;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs *regs;
|
||||
int idx;
|
||||
|
||||
regs = get_irq_regs();
|
||||
|
||||
/* Iterate over counters and update the corresponding perf events.
|
||||
This should work regardless of whether we have per-counter overflow
|
||||
interrupt or a combined overflow interrupt. */
|
||||
for (idx = CCI400_PMU_CYCLE_COUNTER_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); idx++) {
|
||||
struct perf_event *event = events->events[idx];
|
||||
struct hw_perf_event *hw_counter;
|
||||
|
||||
if (!event)
|
||||
continue;
|
||||
|
||||
hw_counter = &event->hw;
|
||||
|
||||
/* Did this counter overflow? */
|
||||
if (!(cci_pmu_read_register(idx, CCI400_PMU_OVERFLOW) & CCI400_PMU_OVERFLOW_FLAG))
|
||||
continue;
|
||||
cci_pmu_write_register(CCI400_PMU_OVERFLOW_FLAG, idx, CCI400_PMU_OVERFLOW);
|
||||
|
||||
armpmu_event_update(event);
|
||||
perf_sample_data_init(&data, 0, hw_counter->last_period);
|
||||
if (!armpmu_event_set_period(event))
|
||||
continue;
|
||||
|
||||
if (perf_event_overflow(event, &data, regs))
|
||||
cci_pmu->disable(event);
|
||||
}
|
||||
|
||||
irq_work_run();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void cci_pmu_free_irq(struct arm_pmu *cci_pmu)
|
||||
{
|
||||
int irq, i = 0;
|
||||
struct platform_device *pmu_device = cci_pmu->plat_device;
|
||||
|
||||
while ((irq = platform_get_irq(pmu_device, i)) > 0) {
|
||||
free_irq(irq, cci_pmu);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cci_pmu_enable_event(struct perf_event *event)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
|
||||
/* Configure the event to count, unless you are counting cycles */
|
||||
if (idx != CCI400_PMU_CYCLE_COUNTER_IDX)
|
||||
cci_pmu_select_event(idx, hw_counter->config_base);
|
||||
|
||||
cci_pmu_enable_counter(idx);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void cci_pmu_disable_event(struct perf_event *event)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
|
||||
cci_pmu_disable_counter(idx);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void cci_pmu_start(struct arm_pmu *cci_pmu)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
|
||||
/* Enable all the PMU counters. */
|
||||
val = readl(cci_ctrl_base + CCI400_PMCR) | CCI400_PMCR_CEN;
|
||||
writel(val, cci_ctrl_base + CCI400_PMCR);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static void cci_pmu_stop(struct arm_pmu *cci_pmu)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
struct pmu_hw_events *events = cci_pmu->get_hw_events();
|
||||
|
||||
raw_spin_lock_irqsave(&events->pmu_lock, flags);
|
||||
|
||||
/* Disable all the PMU counters. */
|
||||
val = readl(cci_ctrl_base + CCI400_PMCR) & ~CCI400_PMCR_CEN;
|
||||
writel(val, cci_ctrl_base + CCI400_PMCR);
|
||||
|
||||
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
|
||||
}
|
||||
|
||||
static u32 cci_pmu_read_counter(struct perf_event *event)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
u32 value;
|
||||
|
||||
if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) {
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
return 0;
|
||||
}
|
||||
value = cci_pmu_read_register(idx, CCI400_PMU_CNTR);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void cci_pmu_write_counter(struct perf_event *event, u32 value)
|
||||
{
|
||||
struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hw_counter = &event->hw;
|
||||
int idx = hw_counter->idx;
|
||||
|
||||
if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx)))
|
||||
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
|
||||
else
|
||||
cci_pmu_write_register(value, idx, CCI400_PMU_CNTR);
|
||||
}
|
||||
|
||||
static struct arm_pmu cci_pmu = {
|
||||
.name = DRIVER_NAME,
|
||||
.max_period = (1LLU << 32) - 1,
|
||||
.get_hw_events = cci_pmu_get_hw_events,
|
||||
.get_event_idx = cci_pmu_get_event_idx,
|
||||
.map_event = cci_pmu_map_event,
|
||||
.request_irq = cci_pmu_request_irq,
|
||||
.handle_irq = cci_pmu_handle_irq,
|
||||
.free_irq = cci_pmu_free_irq,
|
||||
.enable = cci_pmu_enable_event,
|
||||
.disable = cci_pmu_disable_event,
|
||||
.start = cci_pmu_start,
|
||||
.stop = cci_pmu_stop,
|
||||
.read_counter = cci_pmu_read_counter,
|
||||
.write_counter = cci_pmu_write_counter,
|
||||
};
|
||||
|
||||
static int cci_pmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
cci_pmu_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(cci_pmu_base))
|
||||
return PTR_ERR(cci_pmu_base);
|
||||
|
||||
cci_pmu.plat_device = pdev;
|
||||
cci_pmu.num_events = cci_pmu_get_max_counters();
|
||||
raw_spin_lock_init(&cci_hw_events.pmu_lock);
|
||||
cpumask_setall(&cci_pmu.valid_cpus);
|
||||
|
||||
return armpmu_register(&cci_pmu, -1);
|
||||
}
|
||||
|
||||
static const struct of_device_id arm_cci_pmu_matches[] = {
|
||||
{.compatible = "arm,cci-400-pmu"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver cci_pmu_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = arm_cci_pmu_matches,
|
||||
},
|
||||
.probe = cci_pmu_probe,
|
||||
};
|
||||
|
||||
static int __init cci_pmu_init(void)
|
||||
{
|
||||
if (platform_driver_register(&cci_pmu_platform_driver))
|
||||
WARN(1, "unable to register CCI platform driver\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int __init cci_pmu_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cci_pmu_destroy(void) { }
|
||||
|
||||
#endif /* CONFIG_HW_PERF_EVENTS */
|
||||
|
||||
struct cpu_port {
|
||||
u64 mpidr;
|
||||
u32 port;
|
||||
};
|
||||
|
||||
/*
|
||||
* Use the port MSB as valid flag, shift can be made dynamic
|
||||
* by computing number of bits required for port indexes.
|
||||
* Code disabling CCI cpu ports runs with D-cache invalidated
|
||||
* and SCTLR bit clear so data accesses must be kept to a minimum
|
||||
* to improve performance; for now shift is left static to
|
||||
* avoid one more data access while disabling the CCI port.
|
||||
*/
|
||||
#define PORT_VALID_SHIFT 31
|
||||
#define PORT_VALID (0x1 << PORT_VALID_SHIFT)
|
||||
|
||||
static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr)
|
||||
{
|
||||
port->port = PORT_VALID | index;
|
||||
port->mpidr = mpidr;
|
||||
}
|
||||
|
||||
static inline bool cpu_port_is_valid(struct cpu_port *port)
|
||||
{
|
||||
return !!(port->port & PORT_VALID);
|
||||
}
|
||||
|
||||
static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr)
|
||||
{
|
||||
return port->mpidr == (mpidr & MPIDR_HWID_BITMASK);
|
||||
}
|
||||
|
||||
static struct cpu_port cpu_port[NR_CPUS];
|
||||
|
||||
/**
|
||||
* __cci_ace_get_port - Function to retrieve the port index connected to
|
||||
* a cpu or device.
|
||||
*
|
||||
* @dn: device node of the device to look-up
|
||||
* @type: port type
|
||||
*
|
||||
* Return value:
|
||||
* - CCI port index if success
|
||||
* - -ENODEV if failure
|
||||
*/
|
||||
static int __cci_ace_get_port(struct device_node *dn, int type)
|
||||
{
|
||||
int i;
|
||||
bool ace_match;
|
||||
struct device_node *cci_portn;
|
||||
|
||||
cci_portn = of_parse_phandle(dn, "cci-control-port", 0);
|
||||
for (i = 0; i < nb_cci_ports; i++) {
|
||||
ace_match = ports[i].type == type;
|
||||
if (ace_match && cci_portn == ports[i].dn)
|
||||
return i;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int cci_ace_get_port(struct device_node *dn)
|
||||
{
|
||||
return __cci_ace_get_port(dn, ACE_LITE_PORT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cci_ace_get_port);
|
||||
|
||||
static void __init cci_ace_init_ports(void)
|
||||
{
|
||||
int port, ac, cpu;
|
||||
u64 hwid;
|
||||
const u32 *cell;
|
||||
struct device_node *cpun, *cpus;
|
||||
|
||||
cpus = of_find_node_by_path("/cpus");
|
||||
if (WARN(!cpus, "Missing cpus node, bailing out\n"))
|
||||
return;
|
||||
|
||||
if (WARN_ON(of_property_read_u32(cpus, "#address-cells", &ac)))
|
||||
ac = of_n_addr_cells(cpus);
|
||||
|
||||
/*
|
||||
* Port index look-up speeds up the function disabling ports by CPU,
|
||||
* since the logical to port index mapping is done once and does
|
||||
* not change after system boot.
|
||||
* The stashed index array is initialized for all possible CPUs
|
||||
* at probe time.
|
||||
*/
|
||||
for_each_child_of_node(cpus, cpun) {
|
||||
if (of_node_cmp(cpun->type, "cpu"))
|
||||
continue;
|
||||
cell = of_get_property(cpun, "reg", NULL);
|
||||
if (WARN(!cell, "%s: missing reg property\n", cpun->full_name))
|
||||
continue;
|
||||
|
||||
hwid = of_read_number(cell, ac);
|
||||
cpu = get_logical_index(hwid & MPIDR_HWID_BITMASK);
|
||||
|
||||
if (cpu < 0 || !cpu_possible(cpu))
|
||||
continue;
|
||||
port = __cci_ace_get_port(cpun, ACE_PORT);
|
||||
if (port < 0)
|
||||
continue;
|
||||
|
||||
init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu));
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
WARN(!cpu_port_is_valid(&cpu_port[cpu]),
|
||||
"CPU %u does not have an associated CCI port\n",
|
||||
cpu);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Functions to enable/disable a CCI interconnect slave port
|
||||
*
|
||||
* They are called by low-level power management code to disable slave
|
||||
* interfaces snoops and DVM broadcast.
|
||||
* Since they may execute with cache data allocation disabled and
|
||||
* after the caches have been cleaned and invalidated the functions provide
|
||||
* no explicit locking since they may run with D-cache disabled, so normal
|
||||
* cacheable kernel locks based on ldrex/strex may not work.
|
||||
* Locking has to be provided by BSP implementations to ensure proper
|
||||
* operations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* cci_port_control() - function to control a CCI port
|
||||
*
|
||||
* @port: index of the port to setup
|
||||
* @enable: if true enables the port, if false disables it
|
||||
*/
|
||||
static void notrace cci_port_control(unsigned int port, bool enable)
|
||||
{
|
||||
void __iomem *base = ports[port].base;
|
||||
|
||||
writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL);
|
||||
/*
|
||||
* This function is called from power down procedures
|
||||
* and must not execute any instruction that might
|
||||
* cause the processor to be put in a quiescent state
|
||||
* (eg wfi). Hence, cpu_relax() can not be added to this
|
||||
* read loop to optimize power, since it might hide possibly
|
||||
* disruptive operations.
|
||||
*/
|
||||
while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* cci_disable_port_by_cpu() - function to disable a CCI port by CPU
|
||||
* reference
|
||||
*
|
||||
* @mpidr: mpidr of the CPU whose CCI port should be disabled
|
||||
*
|
||||
* Disabling a CCI port for a CPU implies disabling the CCI port
|
||||
* controlling that CPU cluster. Code disabling CPU CCI ports
|
||||
* must make sure that the CPU running the code is the last active CPU
|
||||
* in the cluster ie all other CPUs are quiescent in a low power state.
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENODEV on port look-up failure
|
||||
*/
|
||||
int notrace cci_disable_port_by_cpu(u64 mpidr)
|
||||
{
|
||||
int cpu;
|
||||
bool is_valid;
|
||||
for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
|
||||
is_valid = cpu_port_is_valid(&cpu_port[cpu]);
|
||||
if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) {
|
||||
cci_port_control(cpu_port[cpu].port, false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu);
|
||||
|
||||
/**
|
||||
* cci_enable_port_for_self() - enable a CCI port for calling CPU
|
||||
*
|
||||
* Enabling a CCI port for the calling CPU implies enabling the CCI
|
||||
* port controlling that CPU's cluster. Caller must make sure that the
|
||||
* CPU running the code is the first active CPU in the cluster and all
|
||||
* other CPUs are quiescent in a low power state or waiting for this CPU
|
||||
* to complete the CCI initialization.
|
||||
*
|
||||
* Because this is called when the MMU is still off and with no stack,
|
||||
* the code must be position independent and ideally rely on callee
|
||||
* clobbered registers only. To achieve this we must code this function
|
||||
* entirely in assembler.
|
||||
*
|
||||
* On success this returns with the proper CCI port enabled. In case of
|
||||
* any failure this never returns as the inability to enable the CCI is
|
||||
* fatal and there is no possible recovery at this stage.
|
||||
*/
|
||||
asmlinkage void __naked cci_enable_port_for_self(void)
|
||||
{
|
||||
asm volatile ("\n"
|
||||
|
||||
" mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n"
|
||||
" and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n"
|
||||
" adr r1, 5f \n"
|
||||
" ldr r2, [r1] \n"
|
||||
" add r1, r1, r2 @ &cpu_port \n"
|
||||
" add ip, r1, %[sizeof_cpu_port] \n"
|
||||
|
||||
/* Loop over the cpu_port array looking for a matching MPIDR */
|
||||
"1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n"
|
||||
" cmp r2, r0 @ compare MPIDR \n"
|
||||
" bne 2f \n"
|
||||
|
||||
/* Found a match, now test port validity */
|
||||
" ldr r3, [r1, %[offsetof_cpu_port_port]] \n"
|
||||
" tst r3, #"__stringify(PORT_VALID)" \n"
|
||||
" bne 3f \n"
|
||||
|
||||
/* no match, loop with the next cpu_port entry */
|
||||
"2: add r1, r1, %[sizeof_struct_cpu_port] \n"
|
||||
" cmp r1, ip @ done? \n"
|
||||
" blo 1b \n"
|
||||
|
||||
/* CCI port not found -- cheaply try to stall this CPU */
|
||||
"cci_port_not_found: \n"
|
||||
" wfi \n"
|
||||
" wfe \n"
|
||||
" b cci_port_not_found \n"
|
||||
|
||||
/* Use matched port index to look up the corresponding ports entry */
|
||||
"3: bic r3, r3, #"__stringify(PORT_VALID)" \n"
|
||||
" adr r0, 6f \n"
|
||||
" ldmia r0, {r1, r2} \n"
|
||||
" sub r1, r1, r0 @ virt - phys \n"
|
||||
" ldr r0, [r0, r2] @ *(&ports) \n"
|
||||
" mov r2, %[sizeof_struct_ace_port] \n"
|
||||
" mla r0, r2, r3, r0 @ &ports[index] \n"
|
||||
" sub r0, r0, r1 @ virt_to_phys() \n"
|
||||
|
||||
/* Enable the CCI port */
|
||||
" ldr r0, [r0, %[offsetof_port_phys]] \n"
|
||||
" mov r3, #"__stringify(CCI_ENABLE_REQ)" \n"
|
||||
" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n"
|
||||
|
||||
/* poll the status reg for completion */
|
||||
" adr r1, 7f \n"
|
||||
" ldr r0, [r1] \n"
|
||||
" ldr r0, [r0, r1] @ cci_ctrl_base \n"
|
||||
"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n"
|
||||
" tst r1, #1 \n"
|
||||
" bne 4b \n"
|
||||
|
||||
" mov r0, #0 \n"
|
||||
" bx lr \n"
|
||||
|
||||
" .align 2 \n"
|
||||
"5: .word cpu_port - . \n"
|
||||
"6: .word . \n"
|
||||
" .word ports - 6b \n"
|
||||
"7: .word cci_ctrl_phys - . \n"
|
||||
: :
|
||||
[sizeof_cpu_port] "i" (sizeof(cpu_port)),
|
||||
#ifndef __ARMEB__
|
||||
[offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)),
|
||||
#else
|
||||
[offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4),
|
||||
#endif
|
||||
[offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)),
|
||||
[sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)),
|
||||
[sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)),
|
||||
[offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) );
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* __cci_control_port_by_device() - function to control a CCI port by device
|
||||
* reference
|
||||
*
|
||||
* @dn: device node pointer of the device whose CCI port should be
|
||||
* controlled
|
||||
* @enable: if true enables the port, if false disables it
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENODEV on port look-up failure
|
||||
*/
|
||||
int notrace __cci_control_port_by_device(struct device_node *dn, bool enable)
|
||||
{
|
||||
int port;
|
||||
|
||||
if (!dn)
|
||||
return -ENODEV;
|
||||
|
||||
port = __cci_ace_get_port(dn, ACE_LITE_PORT);
|
||||
if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n",
|
||||
dn->full_name))
|
||||
return -ENODEV;
|
||||
cci_port_control(port, enable);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__cci_control_port_by_device);
|
||||
|
||||
/**
|
||||
* __cci_control_port_by_index() - function to control a CCI port by port index
|
||||
*
|
||||
* @port: port index previously retrieved with cci_ace_get_port()
|
||||
* @enable: if true enables the port, if false disables it
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENODEV on port index out of range
|
||||
* -EPERM if operation carried out on an ACE PORT
|
||||
*/
|
||||
int notrace __cci_control_port_by_index(u32 port, bool enable)
|
||||
{
|
||||
if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT)
|
||||
return -ENODEV;
|
||||
/*
|
||||
* CCI control for ports connected to CPUS is extremely fragile
|
||||
* and must be made to go through a specific and controlled
|
||||
* interface (ie cci_disable_port_by_cpu(); control by general purpose
|
||||
* indexing is therefore disabled for ACE ports.
|
||||
*/
|
||||
if (ports[port].type == ACE_PORT)
|
||||
return -EPERM;
|
||||
|
||||
cci_port_control(port, enable);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__cci_control_port_by_index);
|
||||
|
||||
static const struct cci_nb_ports cci400_ports = {
|
||||
.nb_ace = 2,
|
||||
.nb_ace_lite = 3
|
||||
};
|
||||
|
||||
static const struct of_device_id arm_cci_matches[] = {
|
||||
{.compatible = "arm,cci-400", .data = &cci400_ports },
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct of_device_id arm_cci_ctrl_if_matches[] = {
|
||||
{.compatible = "arm,cci-400-ctrl-if", },
|
||||
{},
|
||||
};
|
||||
|
||||
static int __init cci_probe(void)
|
||||
{
|
||||
struct cci_nb_ports const *cci_config;
|
||||
int ret, i, nb_ace = 0, nb_ace_lite = 0;
|
||||
struct device_node *np, *cp;
|
||||
struct resource res;
|
||||
const char *match_str;
|
||||
bool is_ace;
|
||||
|
||||
np = of_find_matching_node(NULL, arm_cci_matches);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
cci_config = of_match_node(arm_cci_matches, np)->data;
|
||||
if (!cci_config)
|
||||
return -ENODEV;
|
||||
|
||||
nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite;
|
||||
|
||||
ports = kcalloc(sizeof(*ports), nb_cci_ports, GFP_KERNEL);
|
||||
if (!ports)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
if (!ret) {
|
||||
cci_ctrl_base = ioremap(res.start, resource_size(&res));
|
||||
cci_ctrl_phys = res.start;
|
||||
}
|
||||
if (ret || !cci_ctrl_base) {
|
||||
WARN(1, "unable to ioremap CCI ctrl\n");
|
||||
ret = -ENXIO;
|
||||
goto memalloc_err;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, cp) {
|
||||
if (!of_match_node(arm_cci_ctrl_if_matches, cp))
|
||||
continue;
|
||||
|
||||
i = nb_ace + nb_ace_lite;
|
||||
|
||||
if (i >= nb_cci_ports)
|
||||
break;
|
||||
|
||||
if (of_property_read_string(cp, "interface-type",
|
||||
&match_str)) {
|
||||
WARN(1, "node %s missing interface-type property\n",
|
||||
cp->full_name);
|
||||
continue;
|
||||
}
|
||||
is_ace = strcmp(match_str, "ace") == 0;
|
||||
if (!is_ace && strcmp(match_str, "ace-lite")) {
|
||||
WARN(1, "node %s containing invalid interface-type property, skipping it\n",
|
||||
cp->full_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(cp, 0, &res);
|
||||
if (!ret) {
|
||||
ports[i].base = ioremap(res.start, resource_size(&res));
|
||||
ports[i].phys = res.start;
|
||||
}
|
||||
if (ret || !ports[i].base) {
|
||||
WARN(1, "unable to ioremap CCI port %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_ace) {
|
||||
if (WARN_ON(nb_ace >= cci_config->nb_ace))
|
||||
continue;
|
||||
ports[i].type = ACE_PORT;
|
||||
++nb_ace;
|
||||
} else {
|
||||
if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite))
|
||||
continue;
|
||||
ports[i].type = ACE_LITE_PORT;
|
||||
++nb_ace_lite;
|
||||
}
|
||||
ports[i].dn = cp;
|
||||
}
|
||||
|
||||
/* initialize a stashed array of ACE ports to speed-up look-up */
|
||||
cci_ace_init_ports();
|
||||
|
||||
/*
|
||||
* Multi-cluster systems may need this data when non-coherent, during
|
||||
* cluster power-up/power-down. Make sure it reaches main memory.
|
||||
*/
|
||||
sync_cache_w(&cci_ctrl_base);
|
||||
sync_cache_w(&cci_ctrl_phys);
|
||||
sync_cache_w(&ports);
|
||||
sync_cache_w(&cpu_port);
|
||||
__sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports);
|
||||
pr_info("ARM CCI driver probed\n");
|
||||
return 0;
|
||||
|
||||
memalloc_err:
|
||||
|
||||
kfree(ports);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cci_init_status = -EAGAIN;
|
||||
static DEFINE_MUTEX(cci_probing);
|
||||
|
||||
static int __init cci_init(void)
|
||||
{
|
||||
if (cci_init_status != -EAGAIN)
|
||||
return cci_init_status;
|
||||
|
||||
mutex_lock(&cci_probing);
|
||||
if (cci_init_status == -EAGAIN)
|
||||
cci_init_status = cci_probe();
|
||||
mutex_unlock(&cci_probing);
|
||||
return cci_init_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* To sort out early init calls ordering a helper function is provided to
|
||||
* check if the CCI driver has beed initialized. Function check if the driver
|
||||
* has been initialized, if not it calls the init function that probes
|
||||
* the driver and updates the return value.
|
||||
*/
|
||||
bool __init cci_probed(void)
|
||||
{
|
||||
return cci_init() == 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cci_probed);
|
||||
|
||||
early_initcall(cci_init);
|
||||
core_initcall(cci_pmu_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ARM CCI support");
|
||||
|
|
@ -42,7 +42,7 @@ config COMMON_CLK_WM831X
|
|||
|
||||
config COMMON_CLK_VERSATILE
|
||||
bool "Clock driver for ARM Reference designs"
|
||||
depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS
|
||||
depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64
|
||||
---help---
|
||||
Supports clocking on ARM Reference designs:
|
||||
- Integrator/AP and Integrator/CP
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
|
|||
obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
|
||||
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
|
||||
obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o clk-vexpress-spc.o
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ void __init vexpress_osc_of_setup(struct device_node *node)
|
|||
osc->func = vexpress_config_func_get_by_node(node);
|
||||
if (!osc->func) {
|
||||
pr_err("Failed to obtain config func for node '%s'!\n",
|
||||
node->name);
|
||||
node->full_name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ void __init vexpress_osc_of_setup(struct device_node *node)
|
|||
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
if (!init.name)
|
||||
init.name = node->name;
|
||||
init.name = node->full_name;
|
||||
|
||||
init.ops = &vexpress_osc_ops;
|
||||
init.flags = CLK_IS_ROOT;
|
||||
|
|
|
|||
131
drivers/clk/versatile/clk-vexpress-spc.c
Normal file
131
drivers/clk/versatile/clk-vexpress-spc.c
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (C) 2012 ARM Limited
|
||||
* Copyright (C) 2012 Linaro
|
||||
*
|
||||
* Author: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
/* SPC clock programming interface for Vexpress cpus */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
struct clk_spc {
|
||||
struct clk_hw hw;
|
||||
spinlock_t *lock;
|
||||
int cluster;
|
||||
};
|
||||
|
||||
#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw)
|
||||
|
||||
static unsigned long spc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_spc *spc = to_clk_spc(hw);
|
||||
u32 freq;
|
||||
|
||||
if (vexpress_spc_get_performance(spc->cluster, &freq)) {
|
||||
return -EIO;
|
||||
pr_err("%s: Failed", __func__);
|
||||
}
|
||||
|
||||
return freq * 1000;
|
||||
}
|
||||
|
||||
static long spc_round_rate(struct clk_hw *hw, unsigned long drate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return drate;
|
||||
}
|
||||
|
||||
static int spc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_spc *spc = to_clk_spc(hw);
|
||||
|
||||
return vexpress_spc_set_performance(spc->cluster, rate / 1000);
|
||||
}
|
||||
|
||||
static struct clk_ops clk_spc_ops = {
|
||||
.recalc_rate = spc_recalc_rate,
|
||||
.round_rate = spc_round_rate,
|
||||
.set_rate = spc_set_rate,
|
||||
};
|
||||
|
||||
struct clk *vexpress_clk_register_spc(const char *name, int cluster_id)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_spc *spc;
|
||||
struct clk *clk;
|
||||
|
||||
if (!name) {
|
||||
pr_err("Invalid name passed");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
spc = kzalloc(sizeof(*spc), GFP_KERNEL);
|
||||
if (!spc) {
|
||||
pr_err("could not allocate spc clk\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
spc->hw.init = &init;
|
||||
spc->cluster = cluster_id;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_spc_ops;
|
||||
init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
|
||||
init.num_parents = 0;
|
||||
|
||||
clk = clk_register(NULL, &spc->hw);
|
||||
if (!IS_ERR_OR_NULL(clk))
|
||||
return clk;
|
||||
|
||||
pr_err("clk register failed\n");
|
||||
kfree(spc);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
void __init vexpress_clk_of_register_spc(void)
|
||||
{
|
||||
char name[14] = "cpu-cluster.";
|
||||
struct device_node *node = NULL;
|
||||
struct clk *clk;
|
||||
const u32 *val;
|
||||
int cluster_id = 0, len;
|
||||
|
||||
if (!of_find_compatible_node(NULL, NULL, "arm,vexpress-spc")) {
|
||||
pr_debug("%s: No SPC found, Exiting!!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((node = of_find_node_by_name(node, "cluster"))) {
|
||||
val = of_get_property(node, "reg", &len);
|
||||
if (val && len == 4)
|
||||
cluster_id = be32_to_cpup(val);
|
||||
|
||||
name[12] = cluster_id + '0';
|
||||
clk = vexpress_clk_register_spc(name, cluster_id);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
pr_debug("Registered clock '%s'\n", name);
|
||||
clk_register_clkdev(clk, NULL, name);
|
||||
}
|
||||
}
|
||||
CLK_OF_DECLARE(spc, "arm,vexpress-spc", vexpress_clk_of_register_spc);
|
||||
#endif
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
config ARM_BIG_LITTLE_CPUFREQ
|
||||
tristate "Generic ARM big LITTLE CPUfreq driver"
|
||||
depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK
|
||||
depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK && BIG_LITTLE
|
||||
help
|
||||
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
|
||||
|
||||
|
|
@ -15,6 +15,14 @@ config ARM_DT_BL_CPUFREQ
|
|||
This enables probing via DT for Generic CPUfreq driver for ARM
|
||||
big.LITTLE platform. This gets frequency tables from DT.
|
||||
|
||||
config ARM_VEXPRESS_BL_CPUFREQ
|
||||
tristate "ARM Vexpress big LITTLE CPUfreq driver"
|
||||
select ARM_BIG_LITTLE_CPUFREQ
|
||||
depends on VEXPRESS_SPC
|
||||
help
|
||||
This enables the CPUfreq driver for ARM Vexpress big.LITTLE platform.
|
||||
If in doubt, say N.
|
||||
|
||||
config ARM_EXYNOS_CPUFREQ
|
||||
bool "SAMSUNG EXYNOS SoCs"
|
||||
depends on ARCH_EXYNOS
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o
|
|||
obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
|
||||
# big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big
|
||||
# LITTLE drivers, so that it is probed last.
|
||||
obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_big_little.o
|
||||
obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
|
||||
|
||||
obj-$(CONFIG_ARCH_DAVINCI_DA850) += davinci-cpufreq.o
|
||||
|
|
|
|||
86
drivers/cpufreq/vexpress_big_little.c
Normal file
86
drivers/cpufreq/vexpress_big_little.c
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Vexpress big.LITTLE CPUFreq Interface driver
|
||||
*
|
||||
* It provides necessary ops to arm_big_little cpufreq driver and gets
|
||||
* Frequency information from Device Tree. Freq table in DT must be in KHz.
|
||||
*
|
||||
* Copyright (C) 2013 Linaro.
|
||||
* Viresh Kumar <viresh.kumar@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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/cpufreq.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/opp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vexpress.h>
|
||||
#include "arm_big_little.h"
|
||||
|
||||
static int vexpress_init_opp_table(struct device *cpu_dev)
|
||||
{
|
||||
int i = -1, count, cluster = cpu_to_cluster(cpu_dev->id);
|
||||
u32 *table;
|
||||
int ret;
|
||||
|
||||
count = vexpress_spc_get_freq_table(cluster, &table);
|
||||
if (!table || !count) {
|
||||
pr_err("SPC controller returned invalid freq table");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (++i < count) {
|
||||
/* FIXME: Voltage value */
|
||||
ret = opp_add(cpu_dev, table[i] * 1000, 900000);
|
||||
if (ret) {
|
||||
dev_warn(cpu_dev, "%s: Failed to add OPP %d, err: %d\n",
|
||||
__func__, table[i] * 1000, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vexpress_get_transition_latency(struct device *cpu_dev)
|
||||
{
|
||||
/* 1 ms */
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
static struct cpufreq_arm_bL_ops vexpress_bL_ops = {
|
||||
.name = "vexpress-bL",
|
||||
.get_transition_latency = vexpress_get_transition_latency,
|
||||
.init_opp_table = vexpress_init_opp_table,
|
||||
};
|
||||
|
||||
static int vexpress_bL_init(void)
|
||||
{
|
||||
if (!vexpress_spc_check_loaded()) {
|
||||
pr_info("%s: No SPC found\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return bL_cpufreq_register(&vexpress_bL_ops);
|
||||
}
|
||||
module_init(vexpress_bL_init);
|
||||
|
||||
static void vexpress_bL_exit(void)
|
||||
{
|
||||
return bL_cpufreq_unregister(&vexpress_bL_ops);
|
||||
}
|
||||
module_exit(vexpress_bL_exit);
|
||||
|
||||
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
|
||||
MODULE_DESCRIPTION("ARM Vexpress big LITTLE cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
||||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
|
||||
obj-$(CONFIG_BIG_LITTLE) += arm_big_little.o
|
||||
obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
|
||||
obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
|
||||
|
|
|
|||
183
drivers/cpuidle/arm_big_little.c
Normal file
183
drivers/cpuidle/arm_big_little.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* big.LITTLE CPU idle driver.
|
||||
*
|
||||
* Copyright (C) 2012 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/arm-cci.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/vexpress.h>
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/idmap.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static int bl_cpuidle_simple_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
ktime_t time_start, time_end;
|
||||
s64 diff;
|
||||
|
||||
time_start = ktime_get();
|
||||
|
||||
cpu_do_idle();
|
||||
|
||||
time_end = ktime_get();
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
diff = ktime_to_us(ktime_sub(time_end, time_start));
|
||||
if (diff > INT_MAX)
|
||||
diff = INT_MAX;
|
||||
|
||||
dev->last_residency = (int) diff;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx);
|
||||
|
||||
static struct cpuidle_state bl_cpuidle_set[] __initdata = {
|
||||
[0] = {
|
||||
.enter = bl_cpuidle_simple_enter,
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1,
|
||||
.power_usage = UINT_MAX,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "WFI",
|
||||
.desc = "ARM WFI",
|
||||
},
|
||||
[1] = {
|
||||
.enter = bl_enter_powerdown,
|
||||
.exit_latency = 300,
|
||||
.target_residency = 1000,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "C1",
|
||||
.desc = "ARM power down",
|
||||
},
|
||||
};
|
||||
|
||||
struct cpuidle_driver bl_idle_driver = {
|
||||
.name = "bl_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.safe_state_index = 0
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev);
|
||||
|
||||
static int notrace bl_powerdown_finisher(unsigned long arg)
|
||||
{
|
||||
unsigned int mpidr = read_cpuid_mpidr();
|
||||
unsigned int cluster = (mpidr >> 8) & 0xf;
|
||||
unsigned int cpu = mpidr & 0xf;
|
||||
|
||||
mcpm_set_entry_vector(cpu, cluster, cpu_resume);
|
||||
mcpm_cpu_suspend(0); /* 0 should be replaced with better value here */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* bl_enter_powerdown - Programs CPU to enter the specified state
|
||||
* @dev: cpuidle device
|
||||
* @drv: The target state to be programmed
|
||||
* @idx: state index
|
||||
*
|
||||
* Called from the CPUidle framework to program the device to the
|
||||
* specified target state selected by the governor.
|
||||
*/
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
struct timespec ts_preidle, ts_postidle, ts_idle;
|
||||
int ret;
|
||||
|
||||
/* Used to keep track of the total time in idle */
|
||||
getnstimeofday(&ts_preidle);
|
||||
|
||||
BUG_ON(!irqs_disabled());
|
||||
|
||||
cpu_pm_enter();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher);
|
||||
if (ret)
|
||||
BUG();
|
||||
|
||||
mcpm_cpu_powered_up();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
cpu_pm_exit();
|
||||
|
||||
getnstimeofday(&ts_postidle);
|
||||
local_irq_enable();
|
||||
ts_idle = timespec_sub(ts_postidle, ts_preidle);
|
||||
|
||||
dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
|
||||
ts_idle.tv_sec * USEC_PER_SEC;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* bl_idle_init
|
||||
*
|
||||
* Registers the bl specific cpuidle driver with the cpuidle
|
||||
* framework with the valid set of states.
|
||||
*/
|
||||
int __init bl_idle_init(void)
|
||||
{
|
||||
struct cpuidle_device *dev;
|
||||
int i, cpu_id;
|
||||
struct cpuidle_driver *drv = &bl_idle_driver;
|
||||
|
||||
if (!of_find_compatible_node(NULL, NULL, "arm,generic")) {
|
||||
pr_info("%s: No compatible node found\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drv->state_count = (sizeof(bl_cpuidle_set) /
|
||||
sizeof(struct cpuidle_state));
|
||||
|
||||
for (i = 0; i < drv->state_count; i++) {
|
||||
memcpy(&drv->states[i], &bl_cpuidle_set[i],
|
||||
sizeof(struct cpuidle_state));
|
||||
}
|
||||
|
||||
cpuidle_register_driver(drv);
|
||||
|
||||
for_each_cpu(cpu_id, cpu_online_mask) {
|
||||
pr_err("CPUidle for CPU%d registered\n", cpu_id);
|
||||
dev = &per_cpu(bl_idle_dev, cpu_id);
|
||||
dev->cpu = cpu_id;
|
||||
|
||||
dev->state_count = drv->state_count;
|
||||
|
||||
if (cpuidle_register_device(dev)) {
|
||||
printk(KERN_ERR "%s: Cpuidle register device failed\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(bl_idle_init);
|
||||
|
|
@ -37,20 +37,6 @@
|
|||
extern void highbank_set_cpu_jump(int cpu, void *jump_addr);
|
||||
extern void *scu_base_addr;
|
||||
|
||||
static inline unsigned int get_auxcr(void)
|
||||
{
|
||||
unsigned int val;
|
||||
asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val) : : "cc");
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void set_auxcr(unsigned int val)
|
||||
{
|
||||
asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR"
|
||||
: : "r" (val) : "cc");
|
||||
isb();
|
||||
}
|
||||
|
||||
static noinline void calxeda_idle_restore(void)
|
||||
{
|
||||
set_cr(get_cr() | CR_C);
|
||||
|
|
|
|||
|
|
@ -452,6 +452,12 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
|
|||
writel_relaxed(1, base + GIC_CPU_CTRL);
|
||||
}
|
||||
|
||||
void gic_cpu_if_down(void)
|
||||
{
|
||||
void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
|
||||
writel_relaxed(0, cpu_base + GIC_CPU_CTRL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_PM
|
||||
/*
|
||||
* Saves the GIC distributor registers during suspend or idle. Must be called
|
||||
|
|
|
|||
|
|
@ -1144,7 +1144,15 @@ config MCP_UCB1200_TS
|
|||
endmenu
|
||||
|
||||
config VEXPRESS_CONFIG
|
||||
bool
|
||||
bool "ARM Versatile Express platform infrastructure"
|
||||
depends on ARM || ARM64
|
||||
help
|
||||
Platform configuration infrastructure for the ARM Ltd.
|
||||
Versatile Express.
|
||||
|
||||
config VEXPRESS_SPC
|
||||
bool "Versatile Express SPC driver support"
|
||||
depends on ARM
|
||||
depends on VEXPRESS_CONFIG
|
||||
help
|
||||
Serial Power Controller driver for ARM Ltd. test chips.
|
||||
|
|
|
|||
|
|
@ -153,5 +153,6 @@ obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
|
|||
obj-$(CONFIG_MFD_SYSCON) += syscon.o
|
||||
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
|
||||
obj-$(CONFIG_VEXPRESS_SPC) += vexpress-spc.o
|
||||
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
|
||||
obj-$(CONFIG_MFD_AS3711) += as3711.o
|
||||
|
|
|
|||
|
|
@ -86,29 +86,13 @@ void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
|
|||
}
|
||||
EXPORT_SYMBOL(vexpress_config_bridge_unregister);
|
||||
|
||||
|
||||
struct vexpress_config_func {
|
||||
struct vexpress_config_bridge *bridge;
|
||||
void *func;
|
||||
};
|
||||
|
||||
struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
|
||||
struct device_node *node)
|
||||
static struct vexpress_config_bridge *
|
||||
vexpress_config_bridge_find(struct device_node *node)
|
||||
{
|
||||
struct device_node *bridge_node;
|
||||
struct vexpress_config_func *func;
|
||||
int i;
|
||||
struct vexpress_config_bridge *res = NULL;
|
||||
struct device_node *bridge_node = of_node_get(node);
|
||||
|
||||
if (WARN_ON(dev && node && dev->of_node != node))
|
||||
return NULL;
|
||||
if (dev && !node)
|
||||
node = dev->of_node;
|
||||
|
||||
func = kzalloc(sizeof(*func), GFP_KERNEL);
|
||||
if (!func)
|
||||
return NULL;
|
||||
|
||||
bridge_node = of_node_get(node);
|
||||
while (bridge_node) {
|
||||
const __be32 *prop = of_get_property(bridge_node,
|
||||
"arm,vexpress,config-bridge", NULL);
|
||||
|
|
@ -129,13 +113,46 @@ struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
|
|||
|
||||
if (test_bit(i, vexpress_config_bridges_map) &&
|
||||
bridge->node == bridge_node) {
|
||||
func->bridge = bridge;
|
||||
func->func = bridge->info->func_get(dev, node);
|
||||
res = bridge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&vexpress_config_bridges_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
struct vexpress_config_func {
|
||||
struct vexpress_config_bridge *bridge;
|
||||
void *func;
|
||||
};
|
||||
|
||||
struct vexpress_config_func *__vexpress_config_func_get(
|
||||
struct vexpress_config_bridge *bridge,
|
||||
struct device *dev,
|
||||
struct device_node *node,
|
||||
const char *id)
|
||||
{
|
||||
struct vexpress_config_func *func;
|
||||
|
||||
if (WARN_ON(dev && node && dev->of_node != node))
|
||||
return NULL;
|
||||
if (dev && !node)
|
||||
node = dev->of_node;
|
||||
|
||||
if (!bridge)
|
||||
bridge = vexpress_config_bridge_find(node);
|
||||
if (!bridge)
|
||||
return NULL;
|
||||
|
||||
func = kzalloc(sizeof(*func), GFP_KERNEL);
|
||||
if (!func)
|
||||
return NULL;
|
||||
|
||||
func->bridge = bridge;
|
||||
func->func = bridge->info->func_get(dev, node, id);
|
||||
|
||||
if (!func->func) {
|
||||
of_node_put(node);
|
||||
kfree(func);
|
||||
|
|
|
|||
633
drivers/mfd/vexpress-spc.c
Normal file
633
drivers/mfd/vexpress-spc.c
Normal file
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* Versatile Express Serial Power Controller (SPC) support
|
||||
*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
*
|
||||
* Authors: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
|
||||
* Achin Gupta <achin.gupta@arm.com>
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define SCC_CFGREG19 0x120
|
||||
#define SCC_CFGREG20 0x124
|
||||
#define A15_CONF 0x400
|
||||
#define A7_CONF 0x500
|
||||
#define SYS_INFO 0x700
|
||||
#define PERF_LVL_A15 0xB00
|
||||
#define PERF_REQ_A15 0xB04
|
||||
#define PERF_LVL_A7 0xB08
|
||||
#define PERF_REQ_A7 0xB0c
|
||||
#define SYS_CFGCTRL 0xB10
|
||||
#define SYS_CFGCTRL_REQ 0xB14
|
||||
#define PWC_STATUS 0xB18
|
||||
#define PWC_FLAG 0xB1c
|
||||
#define WAKE_INT_MASK 0xB24
|
||||
#define WAKE_INT_RAW 0xB28
|
||||
#define WAKE_INT_STAT 0xB2c
|
||||
#define A15_PWRDN_EN 0xB30
|
||||
#define A7_PWRDN_EN 0xB34
|
||||
#define A7_PWRDNACK 0xB54
|
||||
#define A15_BX_ADDR0 0xB68
|
||||
#define SYS_CFG_WDATA 0xB70
|
||||
#define SYS_CFG_RDATA 0xB74
|
||||
#define A7_BX_ADDR0 0xB78
|
||||
|
||||
#define GBL_WAKEUP_INT_MSK (0x3 << 10)
|
||||
|
||||
#define CLKF_SHIFT 16
|
||||
#define CLKF_MASK 0x1FFF
|
||||
#define CLKR_SHIFT 0
|
||||
#define CLKR_MASK 0x3F
|
||||
#define CLKOD_SHIFT 8
|
||||
#define CLKOD_MASK 0xF
|
||||
|
||||
#define OPP_FUNCTION 6
|
||||
#define OPP_BASE_DEVICE 0x300
|
||||
#define OPP_A15_OFFSET 0x4
|
||||
#define OPP_A7_OFFSET 0xc
|
||||
|
||||
#define SYS_CFGCTRL_START (1 << 31)
|
||||
#define SYS_CFGCTRL_WRITE (1 << 30)
|
||||
#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
|
||||
#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
|
||||
|
||||
#define MAX_OPPS 8
|
||||
#define MAX_CLUSTERS 2
|
||||
|
||||
enum {
|
||||
A15_OPP_TYPE = 0,
|
||||
A7_OPP_TYPE = 1,
|
||||
SYS_CFGCTRL_TYPE = 2,
|
||||
INVALID_TYPE
|
||||
};
|
||||
|
||||
#define STAT_COMPLETE(type) ((1 << 0) << (type << 2))
|
||||
#define STAT_ERR(type) ((1 << 1) << (type << 2))
|
||||
#define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type))
|
||||
|
||||
struct vexpress_spc_drvdata {
|
||||
void __iomem *baseaddr;
|
||||
u32 a15_clusid;
|
||||
int irq;
|
||||
u32 cur_req_type;
|
||||
u32 freqs[MAX_CLUSTERS][MAX_OPPS];
|
||||
int freqs_cnt[MAX_CLUSTERS];
|
||||
};
|
||||
|
||||
enum spc_func_type {
|
||||
CONFIG_FUNC = 0,
|
||||
PERF_FUNC = 1,
|
||||
};
|
||||
|
||||
struct vexpress_spc_func {
|
||||
enum spc_func_type type;
|
||||
u32 function;
|
||||
u32 device;
|
||||
};
|
||||
|
||||
static struct vexpress_spc_drvdata *info;
|
||||
static u32 *vexpress_spc_config_data;
|
||||
static struct vexpress_config_bridge *vexpress_spc_config_bridge;
|
||||
static struct vexpress_config_func *opp_func, *perf_func;
|
||||
|
||||
static int vexpress_spc_load_result = -EAGAIN;
|
||||
|
||||
static bool vexpress_spc_initialized(void)
|
||||
{
|
||||
return vexpress_spc_load_result == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_write_resume_reg() - set the jump address used for warm boot
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @cpu: mpidr[7:0] bitfield describing cpu affinity level
|
||||
* @addr: physical resume address
|
||||
*/
|
||||
void vexpress_spc_write_resume_reg(u32 cluster, u32 cpu, u32 addr)
|
||||
{
|
||||
void __iomem *baseaddr;
|
||||
|
||||
if (WARN_ON_ONCE(cluster >= MAX_CLUSTERS))
|
||||
return;
|
||||
|
||||
if (cluster != info->a15_clusid)
|
||||
baseaddr = info->baseaddr + A7_BX_ADDR0 + (cpu << 2);
|
||||
else
|
||||
baseaddr = info->baseaddr + A15_BX_ADDR0 + (cpu << 2);
|
||||
|
||||
writel_relaxed(addr, baseaddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_get_nb_cpus() - get number of cpus in a cluster
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
*
|
||||
* Return: number of cpus in the cluster
|
||||
* -EINVAL if cluster number invalid
|
||||
*/
|
||||
int vexpress_spc_get_nb_cpus(u32 cluster)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (WARN_ON_ONCE(cluster >= MAX_CLUSTERS))
|
||||
return -EINVAL;
|
||||
|
||||
val = readl_relaxed(info->baseaddr + SYS_INFO);
|
||||
val = (cluster != info->a15_clusid) ? (val >> 20) : (val >> 16);
|
||||
return val & 0xf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vexpress_spc_get_nb_cpus);
|
||||
|
||||
/**
|
||||
* vexpress_spc_get_performance - get current performance level of cluster
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @freq: pointer to the performance level to be assigned
|
||||
*
|
||||
* Return: 0 on success
|
||||
* < 0 on read error
|
||||
*/
|
||||
int vexpress_spc_get_performance(u32 cluster, u32 *freq)
|
||||
{
|
||||
u32 perf_cfg_reg;
|
||||
int perf, ret;
|
||||
|
||||
if (!vexpress_spc_initialized() || (cluster >= MAX_CLUSTERS))
|
||||
return -EINVAL;
|
||||
|
||||
perf_cfg_reg = cluster != info->a15_clusid ? PERF_LVL_A7 : PERF_LVL_A15;
|
||||
ret = vexpress_config_read(perf_func, perf_cfg_reg, &perf);
|
||||
|
||||
if (!ret)
|
||||
*freq = info->freqs[cluster][perf];
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vexpress_spc_get_performance);
|
||||
|
||||
/**
|
||||
* vexpress_spc_get_perf_index - get performance level corresponding to
|
||||
* a frequency
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @freq: frequency to be looked-up
|
||||
*
|
||||
* Return: perf level index on success
|
||||
* -EINVAL on error
|
||||
*/
|
||||
static int vexpress_spc_find_perf_index(u32 cluster, u32 freq)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < info->freqs_cnt[cluster]; idx++)
|
||||
if (info->freqs[cluster][idx] == freq)
|
||||
break;
|
||||
return (idx == info->freqs_cnt[cluster]) ? -EINVAL : idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_set_performance - set current performance level of cluster
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @freq: performance level to be programmed
|
||||
*
|
||||
* Returns: 0 on success
|
||||
* < 0 on write error
|
||||
*/
|
||||
int vexpress_spc_set_performance(u32 cluster, u32 freq)
|
||||
{
|
||||
int ret, perf, offset;
|
||||
|
||||
if (!vexpress_spc_initialized() || (cluster >= MAX_CLUSTERS))
|
||||
return -EINVAL;
|
||||
|
||||
offset = (cluster != info->a15_clusid) ? PERF_LVL_A7 : PERF_LVL_A15;
|
||||
|
||||
perf = vexpress_spc_find_perf_index(cluster, freq);
|
||||
|
||||
if (perf < 0 || perf >= MAX_OPPS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vexpress_config_write(perf_func, offset, perf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vexpress_spc_set_performance);
|
||||
|
||||
static void vexpress_spc_set_wake_intr(u32 mask)
|
||||
{
|
||||
writel_relaxed(mask & VEXPRESS_SPC_WAKE_INTR_MASK,
|
||||
info->baseaddr + WAKE_INT_MASK);
|
||||
}
|
||||
|
||||
static inline void reg_bitmask(u32 *reg, u32 mask, bool set)
|
||||
{
|
||||
if (set)
|
||||
*reg |= mask;
|
||||
else
|
||||
*reg &= ~mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_set_global_wakeup_intr()
|
||||
*
|
||||
* Function to set/clear global wakeup IRQs. Not protected by locking since
|
||||
* it might be used in code paths where normal cacheable locks are not
|
||||
* working. Locking must be provided by the caller to ensure atomicity.
|
||||
*
|
||||
* @set: if true, global wake-up IRQs are set, if false they are cleared
|
||||
*/
|
||||
void vexpress_spc_set_global_wakeup_intr(bool set)
|
||||
{
|
||||
u32 wake_int_mask_reg = 0;
|
||||
|
||||
wake_int_mask_reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);
|
||||
reg_bitmask(&wake_int_mask_reg, GBL_WAKEUP_INT_MSK, set);
|
||||
vexpress_spc_set_wake_intr(wake_int_mask_reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_set_cpu_wakeup_irq()
|
||||
*
|
||||
* Function to set/clear per-CPU wake-up IRQs. Not protected by locking since
|
||||
* it might be used in code paths where normal cacheable locks are not
|
||||
* working. Locking must be provided by the caller to ensure atomicity.
|
||||
*
|
||||
* @cpu: mpidr[7:0] bitfield describing cpu affinity level
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @set: if true, wake-up IRQs are set, if false they are cleared
|
||||
*/
|
||||
void vexpress_spc_set_cpu_wakeup_irq(u32 cpu, u32 cluster, bool set)
|
||||
{
|
||||
u32 mask = 0;
|
||||
u32 wake_int_mask_reg = 0;
|
||||
|
||||
mask = 1 << cpu;
|
||||
if (info->a15_clusid != cluster)
|
||||
mask <<= 4;
|
||||
|
||||
wake_int_mask_reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);
|
||||
reg_bitmask(&wake_int_mask_reg, mask, set);
|
||||
vexpress_spc_set_wake_intr(wake_int_mask_reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_powerdown_enable()
|
||||
*
|
||||
* Function to enable/disable cluster powerdown. Not protected by locking
|
||||
* since it might be used in code paths where normal cacheable locks are not
|
||||
* working. Locking must be provided by the caller to ensure atomicity.
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @enable: if true enables powerdown, if false disables it
|
||||
*/
|
||||
void vexpress_spc_powerdown_enable(u32 cluster, bool enable)
|
||||
{
|
||||
u32 pwdrn_reg = 0;
|
||||
|
||||
if (cluster >= MAX_CLUSTERS)
|
||||
return;
|
||||
pwdrn_reg = cluster != info->a15_clusid ? A7_PWRDN_EN : A15_PWRDN_EN;
|
||||
writel_relaxed(enable, info->baseaddr + pwdrn_reg);
|
||||
}
|
||||
|
||||
irqreturn_t vexpress_spc_irq_handler(int irq, void *data)
|
||||
{
|
||||
int ret;
|
||||
u32 status = readl_relaxed(info->baseaddr + PWC_STATUS);
|
||||
|
||||
if (!(status & RESPONSE_MASK(info->cur_req_type)))
|
||||
return IRQ_NONE;
|
||||
|
||||
if ((status == STAT_COMPLETE(SYS_CFGCTRL_TYPE))
|
||||
&& vexpress_spc_config_data) {
|
||||
*vexpress_spc_config_data =
|
||||
readl_relaxed(info->baseaddr + SYS_CFG_RDATA);
|
||||
vexpress_spc_config_data = NULL;
|
||||
}
|
||||
|
||||
ret = STAT_COMPLETE(info->cur_req_type) ? 0 : -EIO;
|
||||
info->cur_req_type = INVALID_TYPE;
|
||||
vexpress_config_complete(vexpress_spc_config_bridge, ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the firmware documentation, this is always fixed to 20
|
||||
* All the 4 OSC: A15 PLL0/1, A7 PLL0/1 must be programmed same
|
||||
* values for both control and value registers.
|
||||
* This function uses A15 PLL 0 registers to compute multiple factor
|
||||
* F out = F in * (CLKF + 1) / ((CLKOD + 1) * (CLKR + 1))
|
||||
*/
|
||||
static inline int __get_mult_factor(void)
|
||||
{
|
||||
int i_div, o_div, f_div;
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(info->baseaddr + SCC_CFGREG19);
|
||||
f_div = (tmp >> CLKF_SHIFT) & CLKF_MASK;
|
||||
|
||||
tmp = readl(info->baseaddr + SCC_CFGREG20);
|
||||
o_div = (tmp >> CLKOD_SHIFT) & CLKOD_MASK;
|
||||
i_div = (tmp >> CLKR_SHIFT) & CLKR_MASK;
|
||||
|
||||
return (f_div + 1) / ((o_div + 1) * (i_div + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_populate_opps() - initialize opp tables from microcontroller
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
*
|
||||
* Return: 0 on success
|
||||
* < 0 on error
|
||||
*/
|
||||
static int vexpress_spc_populate_opps(u32 cluster)
|
||||
{
|
||||
u32 data = 0, ret, i, offset;
|
||||
int mult_fact = __get_mult_factor();
|
||||
|
||||
if (WARN_ON_ONCE(cluster >= MAX_CLUSTERS))
|
||||
return -EINVAL;
|
||||
|
||||
offset = cluster != info->a15_clusid ? OPP_A7_OFFSET : OPP_A15_OFFSET;
|
||||
for (i = 0; i < MAX_OPPS; i++) {
|
||||
ret = vexpress_config_read(opp_func, i + offset, &data);
|
||||
if (!ret)
|
||||
info->freqs[cluster][i] = (data & 0xFFFFF) * mult_fact;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
info->freqs_cnt[cluster] = i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vexpress_spc_get_freq_table() - Retrieve a pointer to the frequency
|
||||
* table for a given cluster
|
||||
*
|
||||
* @cluster: mpidr[15:8] bitfield describing cluster affinity level
|
||||
* @fptr: pointer to be initialized
|
||||
* Return: operating points count on success
|
||||
* -EINVAL on pointer error
|
||||
*/
|
||||
int vexpress_spc_get_freq_table(u32 cluster, u32 **fptr)
|
||||
{
|
||||
if (WARN_ON_ONCE(!fptr || cluster >= MAX_CLUSTERS))
|
||||
return -EINVAL;
|
||||
*fptr = info->freqs[cluster];
|
||||
return info->freqs_cnt[cluster];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vexpress_spc_get_freq_table);
|
||||
|
||||
static void *vexpress_spc_func_get(struct device *dev,
|
||||
struct device_node *node, const char *id)
|
||||
{
|
||||
struct vexpress_spc_func *spc_func;
|
||||
u32 func_device[2];
|
||||
int err = 0;
|
||||
|
||||
spc_func = kzalloc(sizeof(*spc_func), GFP_KERNEL);
|
||||
if (!spc_func)
|
||||
return NULL;
|
||||
|
||||
if (strcmp(id, "opp") == 0) {
|
||||
spc_func->type = CONFIG_FUNC;
|
||||
spc_func->function = OPP_FUNCTION;
|
||||
spc_func->device = OPP_BASE_DEVICE;
|
||||
} else if (strcmp(id, "perf") == 0) {
|
||||
spc_func->type = PERF_FUNC;
|
||||
} else if (node) {
|
||||
of_node_get(node);
|
||||
err = of_property_read_u32_array(node,
|
||||
"arm,vexpress-sysreg,func", func_device,
|
||||
ARRAY_SIZE(func_device));
|
||||
of_node_put(node);
|
||||
spc_func->type = CONFIG_FUNC;
|
||||
spc_func->function = func_device[0];
|
||||
spc_func->device = func_device[1];
|
||||
}
|
||||
|
||||
if (WARN_ON(err)) {
|
||||
kfree(spc_func);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pr_debug("func 0x%p = 0x%x, %d %d\n", spc_func,
|
||||
spc_func->function,
|
||||
spc_func->device,
|
||||
spc_func->type);
|
||||
|
||||
return spc_func;
|
||||
}
|
||||
|
||||
static void vexpress_spc_func_put(void *func)
|
||||
{
|
||||
kfree(func);
|
||||
}
|
||||
|
||||
static int vexpress_spc_func_exec(void *func, int offset, bool write,
|
||||
u32 *data)
|
||||
{
|
||||
struct vexpress_spc_func *spc_func = func;
|
||||
u32 command;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Setting and retrieval of operating points is not part of
|
||||
* DCC config interface. It was made to go through the same
|
||||
* code path so that requests to the M3 can be serialized
|
||||
* properly with config reads/writes through the common
|
||||
* vexpress config interface
|
||||
*/
|
||||
switch (spc_func->type) {
|
||||
case PERF_FUNC:
|
||||
if (write) {
|
||||
info->cur_req_type = (offset == PERF_LVL_A15) ?
|
||||
A15_OPP_TYPE : A7_OPP_TYPE;
|
||||
writel_relaxed(*data, info->baseaddr + offset);
|
||||
return VEXPRESS_CONFIG_STATUS_WAIT;
|
||||
} else {
|
||||
*data = readl_relaxed(info->baseaddr + offset);
|
||||
return VEXPRESS_CONFIG_STATUS_DONE;
|
||||
}
|
||||
case CONFIG_FUNC:
|
||||
info->cur_req_type = SYS_CFGCTRL_TYPE;
|
||||
|
||||
command = SYS_CFGCTRL_START;
|
||||
command |= write ? SYS_CFGCTRL_WRITE : 0;
|
||||
command |= SYS_CFGCTRL_FUNC(spc_func->function);
|
||||
command |= SYS_CFGCTRL_DEVICE(spc_func->device + offset);
|
||||
|
||||
pr_debug("command %x\n", command);
|
||||
|
||||
if (!write)
|
||||
vexpress_spc_config_data = data;
|
||||
else
|
||||
writel_relaxed(*data, info->baseaddr + SYS_CFG_WDATA);
|
||||
writel_relaxed(command, info->baseaddr + SYS_CFGCTRL);
|
||||
|
||||
return VEXPRESS_CONFIG_STATUS_WAIT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
struct vexpress_config_bridge_info vexpress_spc_config_bridge_info = {
|
||||
.name = "vexpress-spc",
|
||||
.func_get = vexpress_spc_func_get,
|
||||
.func_put = vexpress_spc_func_put,
|
||||
.func_exec = vexpress_spc_func_exec,
|
||||
};
|
||||
|
||||
static const struct of_device_id vexpress_spc_ids[] __initconst = {
|
||||
{ .compatible = "arm,vexpress-spc,v2p-ca15_a7" },
|
||||
{ .compatible = "arm,vexpress-spc" },
|
||||
{},
|
||||
};
|
||||
|
||||
static int __init vexpress_spc_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *node = of_find_matching_node(NULL,
|
||||
vexpress_spc_ids);
|
||||
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
pr_err("%s: unable to allocate mem\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->cur_req_type = INVALID_TYPE;
|
||||
|
||||
info->baseaddr = of_iomap(node, 0);
|
||||
if (WARN_ON(!info->baseaddr)) {
|
||||
ret = -ENXIO;
|
||||
goto mem_free;
|
||||
}
|
||||
|
||||
info->irq = irq_of_parse_and_map(node, 0);
|
||||
|
||||
if (WARN_ON(!info->irq)) {
|
||||
ret = -ENXIO;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
readl_relaxed(info->baseaddr + PWC_STATUS);
|
||||
|
||||
ret = request_irq(info->irq, vexpress_spc_irq_handler,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"arm-spc", info);
|
||||
|
||||
if (ret) {
|
||||
pr_err("IRQ %d request failed\n", info->irq);
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
info->a15_clusid = readl_relaxed(info->baseaddr + A15_CONF) & 0xf;
|
||||
|
||||
vexpress_spc_config_bridge = vexpress_config_bridge_register(
|
||||
node, &vexpress_spc_config_bridge_info);
|
||||
|
||||
if (WARN_ON(!vexpress_spc_config_bridge)) {
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
opp_func = vexpress_config_func_get(vexpress_spc_config_bridge, "opp");
|
||||
perf_func =
|
||||
vexpress_config_func_get(vexpress_spc_config_bridge, "perf");
|
||||
|
||||
if (!opp_func || !perf_func) {
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
if (vexpress_spc_populate_opps(0) || vexpress_spc_populate_opps(1)) {
|
||||
if (info->irq)
|
||||
free_irq(info->irq, info);
|
||||
pr_err("failed to build OPP table\n");
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
/*
|
||||
* Multi-cluster systems may need this data when non-coherent, during
|
||||
* cluster power-up/power-down. Make sure it reaches main memory:
|
||||
*/
|
||||
sync_cache_w(info);
|
||||
sync_cache_w(&info);
|
||||
pr_info("vexpress-spc loaded at %p\n", info->baseaddr);
|
||||
return 0;
|
||||
|
||||
unmap:
|
||||
iounmap(info->baseaddr);
|
||||
|
||||
mem_free:
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __init __vexpress_spc_check_loaded(void);
|
||||
/*
|
||||
* Pointer spc_check_loaded is swapped after init hence it is safe
|
||||
* to initialize it to a function in the __init section
|
||||
*/
|
||||
static bool (*spc_check_loaded)(void) __refdata = &__vexpress_spc_check_loaded;
|
||||
|
||||
static bool __init __vexpress_spc_check_loaded(void)
|
||||
{
|
||||
if (vexpress_spc_load_result == -EAGAIN)
|
||||
vexpress_spc_load_result = vexpress_spc_init();
|
||||
spc_check_loaded = &vexpress_spc_initialized;
|
||||
return vexpress_spc_initialized();
|
||||
}
|
||||
|
||||
/*
|
||||
* Function exported to manage early_initcall ordering.
|
||||
* SPC code is needed very early in the boot process
|
||||
* to bring CPUs out of reset and initialize power
|
||||
* management back-end. After boot swap pointers to
|
||||
* make the functionality check available to loadable
|
||||
* modules, when early boot init functions have been
|
||||
* already freed from kernel address space.
|
||||
*/
|
||||
bool vexpress_spc_check_loaded(void)
|
||||
{
|
||||
return spc_check_loaded();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vexpress_spc_check_loaded);
|
||||
|
||||
static int __init vexpress_spc_early_init(void)
|
||||
{
|
||||
__vexpress_spc_check_loaded();
|
||||
return vexpress_spc_load_result;
|
||||
}
|
||||
early_initcall(vexpress_spc_early_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Serial Power Controller (SPC) support");
|
||||
|
|
@ -165,7 +165,7 @@ static u32 *vexpress_sysreg_config_data;
|
|||
static int vexpress_sysreg_config_tries;
|
||||
|
||||
static void *vexpress_sysreg_config_func_get(struct device *dev,
|
||||
struct device_node *node)
|
||||
struct device_node *node, const char *id)
|
||||
{
|
||||
struct vexpress_sysreg_config_func *config_func;
|
||||
u32 site;
|
||||
|
|
@ -351,6 +351,8 @@ void __init vexpress_sysreg_of_early_init(void)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
|
||||
#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
|
||||
[VEXPRESS_GPIO_##_name] = { \
|
||||
.reg = _reg, \
|
||||
|
|
@ -445,6 +447,8 @@ struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
|
|||
.leds = vexpress_sysreg_leds,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ssize_t vexpress_sysreg_sys_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
@ -480,6 +484,9 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
|
|||
setup_timer(&vexpress_sysreg_config_timer,
|
||||
vexpress_sysreg_config_complete, 0);
|
||||
|
||||
vexpress_sysreg_dev = &pdev->dev;
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
vexpress_sysreg_gpio_chip.dev = &pdev->dev;
|
||||
err = gpiochip_add(&vexpress_sysreg_gpio_chip);
|
||||
if (err) {
|
||||
|
|
@ -490,11 +497,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
vexpress_sysreg_dev = &pdev->dev;
|
||||
|
||||
platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
|
||||
PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
|
||||
sizeof(vexpress_sysreg_leds_pdata));
|
||||
#endif
|
||||
|
||||
device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -1896,7 +1896,7 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
|
|||
SMC_SELECT_BANK(lp, 1);
|
||||
val = SMC_GET_BASE(lp);
|
||||
val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
|
||||
if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
|
||||
if (((unsigned long)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
|
||||
printk("%s: IOADDR %p doesn't match configuration (%x).\n",
|
||||
CARDNAME, ioaddr, val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ config POWER_RESET_RESTART
|
|||
user presses a key. u-boot then boots into Linux.
|
||||
|
||||
config POWER_RESET_VEXPRESS
|
||||
bool
|
||||
bool "ARM Versatile Express power-off and reset driver"
|
||||
depends on ARM || ARM64
|
||||
depends on POWER_RESET
|
||||
help
|
||||
Power off and reset support for the ARM Ltd. Versatile
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ config VIDEOMODE_HELPERS
|
|||
config HDMI
|
||||
bool
|
||||
|
||||
config VEXPRESS_DVI_CONTROL
|
||||
bool "Versatile Express DVI control"
|
||||
depends on FB && VEXPRESS_CONFIG
|
||||
default y
|
||||
|
||||
menuconfig FB
|
||||
tristate "Support for frame buffer devices"
|
||||
---help---
|
||||
|
|
@ -326,6 +331,21 @@ config FB_ARMCLCD
|
|||
here and read <file:Documentation/kbuild/modules.txt>. The module
|
||||
will be called amba-clcd.
|
||||
|
||||
config FB_ARMHDLCD
|
||||
tristate "ARM High Definition LCD support"
|
||||
depends on FB && ARM
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
help
|
||||
This framebuffer device driver is for the ARM High Definition
|
||||
Colour LCD controller.
|
||||
|
||||
If you want to compile this as a module (=code which can be
|
||||
inserted into and removed from the running kernel), say M
|
||||
here and read <file:Documentation/kbuild/modules.txt>. The module
|
||||
will be called arm-hdlcd.
|
||||
|
||||
config FB_ACORN
|
||||
bool "Acorn VIDC support"
|
||||
depends on (FB = y) && ARM && ARCH_ACORN
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
|
|||
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
|
||||
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
|
||||
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
|
||||
obj-$(CONFIG_FB_ARMHDLCD) += arm-hdlcd.o
|
||||
obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
|
||||
obj-$(CONFIG_FB_68328) += 68328fb.o
|
||||
obj-$(CONFIG_FB_GBE) += gbefb.o
|
||||
|
|
@ -177,3 +178,6 @@ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
|
|||
ifeq ($(CONFIG_OF),y)
|
||||
obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
|
||||
endif
|
||||
|
||||
# platform specific output drivers
|
||||
obj-$(CONFIG_VEXPRESS_DVI_CONTROL) += vexpress-dvi.o
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
|
|
@ -30,6 +33,16 @@
|
|||
|
||||
#define to_clcd(info) container_of(info, struct clcd_fb, fb)
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
#define clcdfb_dma_alloc dma_alloc_writecombine
|
||||
#define clcdfb_dma_free dma_free_writecombine
|
||||
#define clcdfb_dma_mmap dma_mmap_writecombine
|
||||
#else
|
||||
#define clcdfb_dma_alloc dma_alloc_coherent
|
||||
#define clcdfb_dma_free dma_free_coherent
|
||||
#define clcdfb_dma_mmap dma_mmap_coherent
|
||||
#endif
|
||||
|
||||
/* This is limited to 16 characters when displayed by X startup */
|
||||
static const char *clcd_name = "CLCD FB";
|
||||
|
||||
|
|
@ -392,6 +405,44 @@ static int clcdfb_blank(int blank_mode, struct fb_info *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int clcdfb_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vma)
|
||||
{
|
||||
return clcdfb_dma_mmap(&fb->dev->dev, vma,
|
||||
fb->fb.screen_base,
|
||||
fb->fb.fix.smem_start,
|
||||
fb->fb.fix.smem_len);
|
||||
}
|
||||
|
||||
int clcdfb_mmap_io(struct clcd_fb *fb, struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned long user_count, count, pfn, off;
|
||||
|
||||
user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
|
||||
count = PAGE_ALIGN(fb->fb.fix.smem_len) >> PAGE_SHIFT;
|
||||
pfn = fb->fb.fix.smem_start >> PAGE_SHIFT;
|
||||
off = vma->vm_pgoff;
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
if (off < count && user_count <= (count - off))
|
||||
return remap_pfn_range(vma, vma->vm_start, pfn + off,
|
||||
user_count << PAGE_SHIFT,
|
||||
vma->vm_page_prot);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
void clcdfb_remove_dma(struct clcd_fb *fb)
|
||||
{
|
||||
clcdfb_dma_free(&fb->dev->dev, fb->fb.fix.smem_len,
|
||||
fb->fb.screen_base, fb->fb.fix.smem_start);
|
||||
}
|
||||
|
||||
void clcdfb_remove_io(struct clcd_fb *fb)
|
||||
{
|
||||
iounmap(fb->fb.screen_base);
|
||||
}
|
||||
|
||||
static int clcdfb_mmap(struct fb_info *info,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
|
|
@ -542,14 +593,239 @@ static int clcdfb_register(struct clcd_fb *fb)
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct string_lookup {
|
||||
const char *string;
|
||||
const u32 val;
|
||||
};
|
||||
|
||||
static struct string_lookup vmode_lookups[] = {
|
||||
{ "FB_VMODE_NONINTERLACED", FB_VMODE_NONINTERLACED},
|
||||
{ "FB_VMODE_INTERLACED", FB_VMODE_INTERLACED},
|
||||
{ "FB_VMODE_DOUBLE", FB_VMODE_DOUBLE},
|
||||
{ "FB_VMODE_ODD_FLD_FIRST", FB_VMODE_ODD_FLD_FIRST},
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static struct string_lookup tim2_lookups[] = {
|
||||
{ "TIM2_CLKSEL", TIM2_CLKSEL},
|
||||
{ "TIM2_IVS", TIM2_IVS},
|
||||
{ "TIM2_IHS", TIM2_IHS},
|
||||
{ "TIM2_IPC", TIM2_IPC},
|
||||
{ "TIM2_IOE", TIM2_IOE},
|
||||
{ "TIM2_BCD", TIM2_BCD},
|
||||
{ NULL, 0},
|
||||
};
|
||||
static struct string_lookup cntl_lookups[] = {
|
||||
{"CNTL_LCDEN", CNTL_LCDEN},
|
||||
{"CNTL_LCDBPP1", CNTL_LCDBPP1},
|
||||
{"CNTL_LCDBPP2", CNTL_LCDBPP2},
|
||||
{"CNTL_LCDBPP4", CNTL_LCDBPP4},
|
||||
{"CNTL_LCDBPP8", CNTL_LCDBPP8},
|
||||
{"CNTL_LCDBPP16", CNTL_LCDBPP16},
|
||||
{"CNTL_LCDBPP16_565", CNTL_LCDBPP16_565},
|
||||
{"CNTL_LCDBPP16_444", CNTL_LCDBPP16_444},
|
||||
{"CNTL_LCDBPP24", CNTL_LCDBPP24},
|
||||
{"CNTL_LCDBW", CNTL_LCDBW},
|
||||
{"CNTL_LCDTFT", CNTL_LCDTFT},
|
||||
{"CNTL_LCDMONO8", CNTL_LCDMONO8},
|
||||
{"CNTL_LCDDUAL", CNTL_LCDDUAL},
|
||||
{"CNTL_BGR", CNTL_BGR},
|
||||
{"CNTL_BEBO", CNTL_BEBO},
|
||||
{"CNTL_BEPO", CNTL_BEPO},
|
||||
{"CNTL_LCDPWR", CNTL_LCDPWR},
|
||||
{"CNTL_LCDVCOMP(1)", CNTL_LCDVCOMP(1)},
|
||||
{"CNTL_LCDVCOMP(2)", CNTL_LCDVCOMP(2)},
|
||||
{"CNTL_LCDVCOMP(3)", CNTL_LCDVCOMP(3)},
|
||||
{"CNTL_LCDVCOMP(4)", CNTL_LCDVCOMP(4)},
|
||||
{"CNTL_LCDVCOMP(5)", CNTL_LCDVCOMP(5)},
|
||||
{"CNTL_LCDVCOMP(6)", CNTL_LCDVCOMP(6)},
|
||||
{"CNTL_LCDVCOMP(7)", CNTL_LCDVCOMP(7)},
|
||||
{"CNTL_LDMAFIFOTIME", CNTL_LDMAFIFOTIME},
|
||||
{"CNTL_WATERMARK", CNTL_WATERMARK},
|
||||
{ NULL, 0},
|
||||
};
|
||||
static struct string_lookup caps_lookups[] = {
|
||||
{"CLCD_CAP_RGB444", CLCD_CAP_RGB444},
|
||||
{"CLCD_CAP_RGB5551", CLCD_CAP_RGB5551},
|
||||
{"CLCD_CAP_RGB565", CLCD_CAP_RGB565},
|
||||
{"CLCD_CAP_RGB888", CLCD_CAP_RGB888},
|
||||
{"CLCD_CAP_BGR444", CLCD_CAP_BGR444},
|
||||
{"CLCD_CAP_BGR5551", CLCD_CAP_BGR5551},
|
||||
{"CLCD_CAP_BGR565", CLCD_CAP_BGR565},
|
||||
{"CLCD_CAP_BGR888", CLCD_CAP_BGR888},
|
||||
{"CLCD_CAP_444", CLCD_CAP_444},
|
||||
{"CLCD_CAP_5551", CLCD_CAP_5551},
|
||||
{"CLCD_CAP_565", CLCD_CAP_565},
|
||||
{"CLCD_CAP_888", CLCD_CAP_888},
|
||||
{"CLCD_CAP_RGB", CLCD_CAP_RGB},
|
||||
{"CLCD_CAP_BGR", CLCD_CAP_BGR},
|
||||
{"CLCD_CAP_ALL", CLCD_CAP_ALL},
|
||||
{ NULL, 0},
|
||||
};
|
||||
|
||||
u32 parse_setting(struct string_lookup *lookup, const char *name)
|
||||
{
|
||||
int i = 0;
|
||||
while (lookup[i].string != NULL) {
|
||||
if (strcmp(lookup[i].string, name) == 0)
|
||||
return lookup[i].val;
|
||||
++i;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
u32 get_string_lookup(struct device_node *node, const char *name,
|
||||
struct string_lookup *lookup)
|
||||
{
|
||||
const char *string;
|
||||
int count, i, ret = 0;
|
||||
|
||||
count = of_property_count_strings(node, name);
|
||||
if (count >= 0)
|
||||
for (i = 0; i < count; i++)
|
||||
if (of_property_read_string_index(node, name, i,
|
||||
&string) == 0)
|
||||
ret |= parse_setting(lookup, string);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_val(struct device_node *node, const char *string)
|
||||
{
|
||||
u32 ret = 0;
|
||||
|
||||
if (of_property_read_u32(node, string, &ret))
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct clcd_panel *getPanel(struct device_node *node)
|
||||
{
|
||||
static struct clcd_panel panel;
|
||||
|
||||
panel.mode.refresh = get_val(node, "refresh");
|
||||
panel.mode.xres = get_val(node, "xres");
|
||||
panel.mode.yres = get_val(node, "yres");
|
||||
panel.mode.pixclock = get_val(node, "pixclock");
|
||||
panel.mode.left_margin = get_val(node, "left_margin");
|
||||
panel.mode.right_margin = get_val(node, "right_margin");
|
||||
panel.mode.upper_margin = get_val(node, "upper_margin");
|
||||
panel.mode.lower_margin = get_val(node, "lower_margin");
|
||||
panel.mode.hsync_len = get_val(node, "hsync_len");
|
||||
panel.mode.vsync_len = get_val(node, "vsync_len");
|
||||
panel.mode.sync = get_val(node, "sync");
|
||||
panel.bpp = get_val(node, "bpp");
|
||||
panel.width = (signed short) get_val(node, "width");
|
||||
panel.height = (signed short) get_val(node, "height");
|
||||
|
||||
panel.mode.vmode = get_string_lookup(node, "vmode", vmode_lookups);
|
||||
panel.tim2 = get_string_lookup(node, "tim2", tim2_lookups);
|
||||
panel.cntl = get_string_lookup(node, "cntl", cntl_lookups);
|
||||
panel.caps = get_string_lookup(node, "caps", caps_lookups);
|
||||
|
||||
return &panel;
|
||||
}
|
||||
|
||||
struct clcd_panel *clcdfb_get_panel(const char *name)
|
||||
{
|
||||
struct device_node *node = NULL;
|
||||
const char *mode;
|
||||
struct clcd_panel *panel = NULL;
|
||||
|
||||
do {
|
||||
node = of_find_compatible_node(node, NULL, "panel");
|
||||
if (node)
|
||||
if (of_property_read_string(node, "mode", &mode) == 0)
|
||||
if (strcmp(mode, name) == 0) {
|
||||
panel = getPanel(node);
|
||||
panel->mode.name = name;
|
||||
}
|
||||
} while (node != NULL);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int clcdfb_dt_init(struct clcd_fb *fb)
|
||||
{
|
||||
int err = 0;
|
||||
struct device_node *node;
|
||||
const char *mode;
|
||||
dma_addr_t dma;
|
||||
u32 use_dma;
|
||||
const __be32 *prop;
|
||||
int len, na, ns;
|
||||
phys_addr_t fb_base, fb_size;
|
||||
|
||||
node = fb->dev->dev.of_node;
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
|
||||
na = of_n_addr_cells(node);
|
||||
ns = of_n_size_cells(node);
|
||||
|
||||
if (WARN_ON(of_property_read_string(node, "mode", &mode)))
|
||||
return -ENODEV;
|
||||
|
||||
fb->panel = clcdfb_get_panel(mode);
|
||||
if (!fb->panel)
|
||||
return -EINVAL;
|
||||
fb->fb.fix.smem_len = fb->panel->mode.xres * fb->panel->mode.yres * 2;
|
||||
|
||||
fb->board->name = "Device Tree CLCD PL111";
|
||||
fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_565;
|
||||
fb->board->check = clcdfb_check;
|
||||
fb->board->decode = clcdfb_decode;
|
||||
|
||||
if (of_property_read_u32(node, "use_dma", &use_dma))
|
||||
use_dma = 0;
|
||||
|
||||
if (use_dma) {
|
||||
fb->fb.screen_base = clcdfb_dma_alloc(&fb->dev->dev,
|
||||
fb->fb.fix.smem_len,
|
||||
&dma, GFP_KERNEL);
|
||||
if (!fb->fb.screen_base) {
|
||||
pr_err("CLCD: unable to map framebuffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fb->fb.fix.smem_start = dma;
|
||||
fb->board->mmap = clcdfb_mmap_dma;
|
||||
fb->board->remove = clcdfb_remove_dma;
|
||||
} else {
|
||||
prop = of_get_property(node, "framebuffer", &len);
|
||||
if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop)))
|
||||
return -EINVAL;
|
||||
|
||||
fb_base = of_read_number(prop, na);
|
||||
fb_size = of_read_number(prop + na, ns);
|
||||
|
||||
fb->fb.fix.smem_start = fb_base;
|
||||
fb->fb.screen_base = ioremap_wc(fb_base, fb_size);
|
||||
fb->board->mmap = clcdfb_mmap_io;
|
||||
fb->board->remove = clcdfb_remove_io;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
{
|
||||
struct clcd_board *board = dev->dev.platform_data;
|
||||
struct clcd_fb *fb;
|
||||
int ret;
|
||||
|
||||
if (!board)
|
||||
return -EINVAL;
|
||||
if (!board) {
|
||||
#ifdef CONFIG_OF
|
||||
if (dev->dev.of_node) {
|
||||
board = kzalloc(sizeof(struct clcd_board), GFP_KERNEL);
|
||||
if (!board)
|
||||
return -ENOMEM;
|
||||
board->setup = clcdfb_dt_init;
|
||||
} else
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = amba_request_regions(dev, NULL);
|
||||
if (ret) {
|
||||
|
|
|
|||
844
drivers/video/arm-hdlcd.c
Normal file
844
drivers/video/arm-hdlcd.c
Normal file
|
|
@ -0,0 +1,844 @@
|
|||
/*
|
||||
* drivers/video/arm-hdlcd.c
|
||||
*
|
||||
* Copyright (C) 2011 ARM Limited
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* ARM HDLCD Controller
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/arm-hdlcd.h>
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#endif
|
||||
|
||||
#include "edid.h"
|
||||
|
||||
#ifdef CONFIG_SERIAL_AMBA_PCU_UART
|
||||
int get_edid(u8 *msgbuf);
|
||||
#else
|
||||
#endif
|
||||
|
||||
#define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb)
|
||||
|
||||
static struct of_device_id hdlcd_of_matches[] = {
|
||||
{ .compatible = "arm,hdlcd" },
|
||||
{},
|
||||
};
|
||||
|
||||
/* Framebuffer size. */
|
||||
static unsigned long framebuffer_size;
|
||||
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
static unsigned long buffer_underrun_events;
|
||||
static DEFINE_SPINLOCK(hdlcd_underrun_lock);
|
||||
|
||||
static void hdlcd_underrun_set(unsigned long val)
|
||||
{
|
||||
spin_lock(&hdlcd_underrun_lock);
|
||||
buffer_underrun_events = val;
|
||||
spin_unlock(&hdlcd_underrun_lock);
|
||||
}
|
||||
|
||||
static unsigned long hdlcd_underrun_get(void)
|
||||
{
|
||||
unsigned long val;
|
||||
spin_lock(&hdlcd_underrun_lock);
|
||||
val = buffer_underrun_events;
|
||||
spin_unlock(&hdlcd_underrun_lock);
|
||||
return val;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int hdlcd_underrun_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned char underrun_string[32];
|
||||
snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
|
||||
seq_puts(m, underrun_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, hdlcd_underrun_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_hdlcd_underrun_operations = {
|
||||
.open = proc_hdlcd_underrun_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int hdlcd_underrun_init(void)
|
||||
{
|
||||
hdlcd_underrun_set(0);
|
||||
proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
|
||||
return 0;
|
||||
}
|
||||
static void hdlcd_underrun_close(void)
|
||||
{
|
||||
remove_proc_entry("hdlcd_underrun", NULL);
|
||||
}
|
||||
#else
|
||||
static int hdlcd_underrun_init(void) { return 0; }
|
||||
static void hdlcd_underrun_close(void) { }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
|
||||
|
||||
static struct fb_var_screeninfo cached_var_screeninfo;
|
||||
|
||||
static struct fb_videomode hdlcd_default_mode = {
|
||||
.refresh = 60,
|
||||
.xres = 1680,
|
||||
.yres = 1050,
|
||||
.pixclock = 8403,
|
||||
.left_margin = 80,
|
||||
.right_margin = 48,
|
||||
.upper_margin = 21,
|
||||
.lower_margin = 3,
|
||||
.hsync_len = 32,
|
||||
.vsync_len = 6,
|
||||
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
|
||||
.vmode = FB_VMODE_NONINTERLACED
|
||||
};
|
||||
|
||||
static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
|
||||
{
|
||||
dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
|
||||
writel(1, hdlcd->base + HDLCD_REG_COMMAND);
|
||||
}
|
||||
|
||||
static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
|
||||
{
|
||||
dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
|
||||
writel(0, hdlcd->base + HDLCD_REG_COMMAND);
|
||||
}
|
||||
|
||||
static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
|
||||
struct fb_var_screeninfo *var)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
memset(&var->transp, 0, sizeof(var->transp));
|
||||
var->red.msb_right = 0;
|
||||
var->green.msb_right = 0;
|
||||
var->blue.msb_right = 0;
|
||||
var->blue.offset = 0;
|
||||
|
||||
switch (var->bits_per_pixel) {
|
||||
case 8:
|
||||
/* pseudocolor */
|
||||
var->red.length = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.length = 8;
|
||||
break;
|
||||
case 16:
|
||||
/* 565 format */
|
||||
var->red.length = 5;
|
||||
var->green.length = 6;
|
||||
var->blue.length = 5;
|
||||
break;
|
||||
case 32:
|
||||
var->transp.length = 8;
|
||||
case 24:
|
||||
var->red.length = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.length = 8;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if(var->bits_per_pixel != 32)
|
||||
{
|
||||
var->green.offset = var->blue.length;
|
||||
var->red.offset = var->green.offset + var->green.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Previously, the byte ordering for 32-bit color was
|
||||
* (msb)<alpha><red><green><blue>(lsb)
|
||||
* but this does not match what android expects and
|
||||
* the colors are odd. Instead, use
|
||||
* <alpha><blue><green><red>
|
||||
* Since we tell fb what we are doing, console
|
||||
* , X and directfb access should work fine.
|
||||
*/
|
||||
var->green.offset = var->red.length;
|
||||
var->blue.offset = var->green.offset + var->green.length;
|
||||
var->transp.offset = var->blue.offset + var->blue.length;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
int bytes_per_pixel = var->bits_per_pixel / 8;
|
||||
|
||||
#ifdef HDLCD_NO_VIRTUAL_SCREEN
|
||||
var->yres_virtual = var->yres;
|
||||
#else
|
||||
var->yres_virtual = 2 * var->yres;
|
||||
#endif
|
||||
|
||||
if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
|
||||
return -ENOMEM;
|
||||
|
||||
if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure the bitfields are set appropriately */
|
||||
return hdlcd_set_bitfields(hdlcd, var);
|
||||
}
|
||||
|
||||
/* prototype */
|
||||
static int hdlcd_pan_display(struct fb_var_screeninfo *var,
|
||||
struct fb_info *info);
|
||||
|
||||
#define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg))
|
||||
#define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg))
|
||||
|
||||
static int hdlcd_set_par(struct fb_info *info)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
|
||||
int polarities;
|
||||
int old_yoffset;
|
||||
|
||||
/* check for shortcuts */
|
||||
old_yoffset = cached_var_screeninfo.yoffset;
|
||||
cached_var_screeninfo.yoffset = info->var.yoffset;
|
||||
if (!memcmp(&info->var, &cached_var_screeninfo,
|
||||
sizeof(struct fb_var_screeninfo))) {
|
||||
if(old_yoffset != info->var.yoffset) {
|
||||
/* we only changed yoffset, and we already
|
||||
* already recorded it a couple lines up
|
||||
*/
|
||||
hdlcd_pan_display(&info->var, info);
|
||||
}
|
||||
/* or no change */
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
|
||||
|
||||
if (hdlcd->fb.var.bits_per_pixel >= 16)
|
||||
hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
else
|
||||
hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
||||
|
||||
memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo));
|
||||
|
||||
polarities = HDLCD_POLARITY_DATAEN |
|
||||
#ifndef CONFIG_ARCH_TUSCAN
|
||||
HDLCD_POLARITY_PIXELCLK |
|
||||
#endif
|
||||
HDLCD_POLARITY_DATA;
|
||||
polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
|
||||
polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
|
||||
|
||||
hdlcd_disable(hdlcd);
|
||||
|
||||
WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
|
||||
#ifdef HDLCD_RED_DEFAULT_COLOUR
|
||||
WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
|
||||
| hdlcd->fb.var.red.offset);
|
||||
#else
|
||||
WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
|
||||
#endif
|
||||
WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
|
||||
|
||||
clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
|
||||
clk_enable(hdlcd->clk);
|
||||
|
||||
hdlcd_enable(hdlcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
|
||||
unsigned int blue, unsigned int transp, struct fb_info *info)
|
||||
{
|
||||
if (regno < 16) {
|
||||
u32 *pal = info->pseudo_palette;
|
||||
|
||||
pal[regno] = ((red >> 8) << info->var.red.offset) |
|
||||
((green >> 8) << info->var.green.offset) |
|
||||
((blue >> 8) << info->var.blue.offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t hdlcd_irq(int irq, void *data)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = data;
|
||||
unsigned long irq_mask, irq_status;
|
||||
|
||||
irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
|
||||
irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
|
||||
|
||||
/* acknowledge interrupt(s) */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
|
||||
/* increment the count */
|
||||
hdlcd_underrun_set(hdlcd_underrun_get() + 1);
|
||||
}
|
||||
#endif
|
||||
if (irq_status & HDLCD_INTERRUPT_VSYNC) {
|
||||
/* disable future VSYNC interrupts */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
|
||||
|
||||
complete(&hdlcd->vsync_completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hdlcd_wait_for_vsync(struct fb_info *info)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
unsigned long irq_mask;
|
||||
int err;
|
||||
|
||||
/* enable VSYNC interrupt */
|
||||
irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
|
||||
WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
|
||||
|
||||
err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
|
||||
msecs_to_jiffies(100));
|
||||
|
||||
if (!err)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_blank(int blank_mode, struct fb_info *info)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
|
||||
switch (blank_mode) {
|
||||
case FB_BLANK_POWERDOWN:
|
||||
clk_disable(hdlcd->clk);
|
||||
case FB_BLANK_NORMAL:
|
||||
hdlcd_disable(hdlcd);
|
||||
break;
|
||||
case FB_BLANK_UNBLANK:
|
||||
clk_enable(hdlcd->clk);
|
||||
hdlcd_enable(hdlcd);
|
||||
break;
|
||||
case FB_BLANK_VSYNC_SUSPEND:
|
||||
case FB_BLANK_HSYNC_SUSPEND:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdlcd_mmap_open(struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
static void hdlcd_mmap_close(struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
static struct vm_operations_struct hdlcd_mmap_ops = {
|
||||
.open = hdlcd_mmap_open,
|
||||
.close = hdlcd_mmap_close,
|
||||
};
|
||||
|
||||
static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
unsigned long off;
|
||||
unsigned long start;
|
||||
unsigned long len = hdlcd->fb.fix.smem_len;
|
||||
|
||||
if (vma->vm_end - vma->vm_start == 0)
|
||||
return 0;
|
||||
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
||||
return -EINVAL;
|
||||
|
||||
off = vma->vm_pgoff << PAGE_SHIFT;
|
||||
if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
|
||||
return -EINVAL;
|
||||
|
||||
start = hdlcd->fb.fix.smem_start;
|
||||
off += start;
|
||||
|
||||
vma->vm_pgoff = off >> PAGE_SHIFT;
|
||||
vma->vm_flags |= VM_IO;
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
vma->vm_ops = &hdlcd_mmap_ops;
|
||||
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = to_hdlcd_device(info);
|
||||
|
||||
hdlcd->fb.var.yoffset = var->yoffset;
|
||||
WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
|
||||
(var->yoffset * hdlcd->fb.fix.line_length));
|
||||
|
||||
hdlcd_wait_for_vsync(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (cmd) {
|
||||
case FBIO_WAITFORVSYNC:
|
||||
err = hdlcd_wait_for_vsync(info);
|
||||
break;
|
||||
default:
|
||||
err = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct fb_ops hdlcd_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_check_var = hdlcd_check_var,
|
||||
.fb_set_par = hdlcd_set_par,
|
||||
.fb_setcolreg = hdlcd_setcolreg,
|
||||
.fb_blank = hdlcd_blank,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
.fb_mmap = hdlcd_mmap,
|
||||
.fb_pan_display = hdlcd_pan_display,
|
||||
.fb_ioctl = hdlcd_ioctl,
|
||||
.fb_compat_ioctl = hdlcd_ioctl
|
||||
};
|
||||
|
||||
static int hdlcd_setup(struct hdlcd_device *hdlcd)
|
||||
{
|
||||
u32 version;
|
||||
int err = -EFAULT;
|
||||
|
||||
hdlcd->fb.device = hdlcd->dev;
|
||||
|
||||
hdlcd->clk = clk_get(hdlcd->dev, NULL);
|
||||
if (IS_ERR(hdlcd->clk)) {
|
||||
dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
|
||||
return PTR_ERR(hdlcd->clk);
|
||||
}
|
||||
|
||||
err = clk_prepare(hdlcd->clk);
|
||||
if (err)
|
||||
goto clk_prepare_err;
|
||||
|
||||
hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
|
||||
if (!hdlcd->base) {
|
||||
dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
|
||||
goto remap_err;
|
||||
}
|
||||
|
||||
hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
|
||||
if (!hdlcd->fb.pseudo_palette) {
|
||||
dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
|
||||
err = -ENOMEM;
|
||||
goto kmalloc_err;
|
||||
}
|
||||
|
||||
version = readl(hdlcd->base + HDLCD_REG_VERSION);
|
||||
if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
|
||||
dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
|
||||
err = -EINVAL;
|
||||
goto kmalloc_err;
|
||||
}
|
||||
dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
|
||||
(version & HDLCD_VERSION_MAJOR_MASK) >> 8,
|
||||
version & HDLCD_VERSION_MINOR_MASK);
|
||||
|
||||
strcpy(hdlcd->fb.fix.id, "hdlcd");
|
||||
hdlcd->fb.fbops = &hdlcd_ops;
|
||||
hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
|
||||
|
||||
hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
hdlcd->fb.fix.type_aux = 0;
|
||||
hdlcd->fb.fix.xpanstep = 0;
|
||||
hdlcd->fb.fix.ypanstep = 1;
|
||||
hdlcd->fb.fix.ywrapstep = 0;
|
||||
hdlcd->fb.fix.accel = FB_ACCEL_NONE;
|
||||
|
||||
hdlcd->fb.var.nonstd = 0;
|
||||
hdlcd->fb.var.activate = FB_ACTIVATE_NOW;
|
||||
hdlcd->fb.var.height = -1;
|
||||
hdlcd->fb.var.width = -1;
|
||||
hdlcd->fb.var.accel_flags = 0;
|
||||
|
||||
init_completion(&hdlcd->vsync_completion);
|
||||
|
||||
if (hdlcd->edid) {
|
||||
/* build modedb from EDID */
|
||||
fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
|
||||
fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
|
||||
hdlcd->fb.monspecs.modedb_len,
|
||||
&hdlcd->fb.modelist);
|
||||
fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
|
||||
hdlcd->fb.monspecs.modedb,
|
||||
hdlcd->fb.monspecs.modedb_len,
|
||||
&hdlcd_default_mode, 32);
|
||||
} else {
|
||||
hdlcd->fb.monspecs.hfmin = 0;
|
||||
hdlcd->fb.monspecs.hfmax = 100000;
|
||||
hdlcd->fb.monspecs.vfmin = 0;
|
||||
hdlcd->fb.monspecs.vfmax = 400;
|
||||
hdlcd->fb.monspecs.dclkmin = 1000000;
|
||||
hdlcd->fb.monspecs.dclkmax = 100000000;
|
||||
fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
|
||||
}
|
||||
|
||||
dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
|
||||
hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
|
||||
hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
|
||||
hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres;
|
||||
#ifdef HDLCD_NO_VIRTUAL_SCREEN
|
||||
hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres;
|
||||
#else
|
||||
hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2;
|
||||
#endif
|
||||
|
||||
/* initialise and set the palette */
|
||||
if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
|
||||
dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
|
||||
err = -ENOMEM;
|
||||
goto setup_err;
|
||||
}
|
||||
fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
|
||||
|
||||
/* Allow max number of outstanding requests with the largest beat burst */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
|
||||
/* Set the framebuffer base to start of allocated memory */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
/* turn on underrun interrupt for counting */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
|
||||
#else
|
||||
/* Ensure interrupts are disabled */
|
||||
WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
|
||||
#endif
|
||||
fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
|
||||
|
||||
if (!register_framebuffer(&hdlcd->fb)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
|
||||
|
||||
fb_dealloc_cmap(&hdlcd->fb.cmap);
|
||||
setup_err:
|
||||
iounmap(hdlcd->base);
|
||||
kmalloc_err:
|
||||
kfree(hdlcd->fb.pseudo_palette);
|
||||
remap_err:
|
||||
clk_unprepare(hdlcd->clk);
|
||||
clk_prepare_err:
|
||||
clk_put(hdlcd->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline unsigned char atohex(u8 data)
|
||||
{
|
||||
if (!isxdigit(data))
|
||||
return 0;
|
||||
/* truncate the upper nibble and add 9 to non-digit values */
|
||||
return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
|
||||
}
|
||||
|
||||
/* EDID data is passed from devicetree in a literal string that can contain spaces and
|
||||
the hexadecimal dump of the data */
|
||||
static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!edid_data)
|
||||
return -EINVAL;
|
||||
|
||||
hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
if (!hdlcd->edid)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0, j = 0; i < data_len; i++) {
|
||||
if (isspace(edid_data[i]))
|
||||
continue;
|
||||
hdlcd->edid[j++] = atohex(edid_data[i]);
|
||||
if (j >= EDID_LENGTH)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j < EDID_LENGTH) {
|
||||
kfree(hdlcd->edid);
|
||||
hdlcd->edid = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0, i;
|
||||
struct hdlcd_device *hdlcd;
|
||||
struct resource *mem;
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node *of_node;
|
||||
#endif
|
||||
|
||||
memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
|
||||
|
||||
dev_dbg(&pdev->dev, "HDLCD: probing\n");
|
||||
|
||||
hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
|
||||
if (!hdlcd)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
of_node = pdev->dev.of_node;
|
||||
if (of_node) {
|
||||
int len;
|
||||
const u8 *edid;
|
||||
const __be32 *prop = of_get_property(of_node, "mode", &len);
|
||||
if (prop)
|
||||
strncpy(fb_mode, (char *)prop, len);
|
||||
prop = of_get_property(of_node, "framebuffer", &len);
|
||||
if (prop) {
|
||||
hdlcd->fb.fix.smem_start = of_read_ulong(prop,
|
||||
of_n_addr_cells(of_node));
|
||||
prop += of_n_addr_cells(of_node);
|
||||
framebuffer_size = of_read_ulong(prop,
|
||||
of_n_size_cells(of_node));
|
||||
if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
|
||||
framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
|
||||
dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
|
||||
hdlcd->fb.fix.smem_start, framebuffer_size);
|
||||
}
|
||||
edid = of_get_property(of_node, "edid", &len);
|
||||
if (edid) {
|
||||
err = parse_edid_data(hdlcd, edid, len);
|
||||
#ifdef CONFIG_SERIAL_AMBA_PCU_UART
|
||||
} else {
|
||||
/* ask the firmware to fetch the EDID */
|
||||
dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
|
||||
hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
if (!hdlcd->edid)
|
||||
return -ENOMEM;
|
||||
err = get_edid(hdlcd->edid);
|
||||
#endif /* CONFIG_SERIAL_AMBA_PCU_UART */
|
||||
}
|
||||
if (err)
|
||||
dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
|
||||
err = -EINVAL;
|
||||
goto resource_err;
|
||||
}
|
||||
|
||||
i = platform_get_irq(pdev, 0);
|
||||
if (i < 0) {
|
||||
dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
|
||||
err = -ENOENT;
|
||||
goto resource_err;
|
||||
} else {
|
||||
err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
|
||||
goto resource_err;
|
||||
}
|
||||
hdlcd->irq = i;
|
||||
}
|
||||
|
||||
if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) {
|
||||
err = -ENXIO;
|
||||
goto request_err;
|
||||
}
|
||||
|
||||
if (!hdlcd->fb.fix.smem_start) {
|
||||
dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
|
||||
err = -ENOMEM;
|
||||
goto memalloc_err;
|
||||
}
|
||||
hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
|
||||
if (!hdlcd->fb.screen_base) {
|
||||
dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
|
||||
err = -ENOMEM;
|
||||
goto probe_err;
|
||||
}
|
||||
|
||||
hdlcd->fb.screen_size = framebuffer_size;
|
||||
hdlcd->fb.fix.smem_len = framebuffer_size;
|
||||
hdlcd->fb.fix.mmio_start = mem->start;
|
||||
hdlcd->fb.fix.mmio_len = resource_size(mem);
|
||||
|
||||
/* Clear the framebuffer */
|
||||
memset(hdlcd->fb.screen_base, 0, framebuffer_size);
|
||||
|
||||
hdlcd->dev = &pdev->dev;
|
||||
|
||||
dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
|
||||
hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
|
||||
|
||||
err = hdlcd_setup(hdlcd);
|
||||
|
||||
if (err)
|
||||
goto probe_err;
|
||||
|
||||
platform_set_drvdata(pdev, hdlcd);
|
||||
return 0;
|
||||
|
||||
probe_err:
|
||||
iounmap(hdlcd->fb.screen_base);
|
||||
memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
|
||||
|
||||
memalloc_err:
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
|
||||
request_err:
|
||||
free_irq(hdlcd->irq, hdlcd);
|
||||
|
||||
resource_err:
|
||||
kfree(hdlcd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hdlcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable(hdlcd->clk);
|
||||
clk_unprepare(hdlcd->clk);
|
||||
clk_put(hdlcd->clk);
|
||||
|
||||
/* unmap memory */
|
||||
iounmap(hdlcd->fb.screen_base);
|
||||
iounmap(hdlcd->base);
|
||||
|
||||
/* deallocate fb memory */
|
||||
fb_dealloc_cmap(&hdlcd->fb.cmap);
|
||||
kfree(hdlcd->fb.pseudo_palette);
|
||||
memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
|
||||
release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
|
||||
|
||||
free_irq(hdlcd->irq, NULL);
|
||||
kfree(hdlcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
/* not implemented yet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdlcd_resume(struct platform_device *pdev)
|
||||
{
|
||||
/* not implemented yet */
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define hdlcd_suspend NULL
|
||||
#define hdlcd_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver hdlcd_driver = {
|
||||
.probe = hdlcd_probe,
|
||||
.remove = hdlcd_remove,
|
||||
.suspend = hdlcd_suspend,
|
||||
.resume = hdlcd_resume,
|
||||
.driver = {
|
||||
.name = "hdlcd",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = hdlcd_of_matches,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init hdlcd_init(void)
|
||||
{
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
int err = platform_driver_register(&hdlcd_driver);
|
||||
if (!err)
|
||||
hdlcd_underrun_init();
|
||||
return err;
|
||||
#else
|
||||
return platform_driver_register(&hdlcd_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __exit hdlcd_exit(void)
|
||||
{
|
||||
#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
hdlcd_underrun_close();
|
||||
#endif
|
||||
platform_driver_unregister(&hdlcd_driver);
|
||||
}
|
||||
|
||||
module_init(hdlcd_init);
|
||||
module_exit(hdlcd_exit);
|
||||
|
||||
MODULE_AUTHOR("Liviu Dudau");
|
||||
MODULE_DESCRIPTION("ARM HDLCD core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
220
drivers/video/vexpress-dvi.c
Normal file
220
drivers/video/vexpress-dvi.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Limited
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "vexpress-dvi: " fmt
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
|
||||
static struct vexpress_config_func *vexpress_dvimode_func;
|
||||
|
||||
static struct {
|
||||
u32 xres, yres, mode;
|
||||
} vexpress_dvi_dvimodes[] = {
|
||||
{ 640, 480, 0 }, /* VGA */
|
||||
{ 800, 600, 1 }, /* SVGA */
|
||||
{ 1024, 768, 2 }, /* XGA */
|
||||
{ 1280, 1024, 3 }, /* SXGA */
|
||||
{ 1600, 1200, 4 }, /* UXGA */
|
||||
{ 1920, 1080, 5 }, /* HD1080 */
|
||||
};
|
||||
|
||||
static void vexpress_dvi_mode_set(struct fb_info *info, u32 xres, u32 yres)
|
||||
{
|
||||
int err = -ENOENT;
|
||||
int i;
|
||||
|
||||
if (!vexpress_dvimode_func)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vexpress_dvi_dvimodes); i++) {
|
||||
if (vexpress_dvi_dvimodes[i].xres == xres &&
|
||||
vexpress_dvi_dvimodes[i].yres == yres) {
|
||||
pr_debug("mode: %ux%u = %d\n", xres, yres,
|
||||
vexpress_dvi_dvimodes[i].mode);
|
||||
err = vexpress_config_write(vexpress_dvimode_func, 0,
|
||||
vexpress_dvi_dvimodes[i].mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
pr_warn("Failed to set %ux%u mode! (%d)\n", xres, yres, err);
|
||||
}
|
||||
|
||||
|
||||
static struct vexpress_config_func *vexpress_muxfpga_func;
|
||||
static int vexpress_dvi_fb = -1;
|
||||
|
||||
static int vexpress_dvi_mux_set(struct fb_info *info)
|
||||
{
|
||||
int err;
|
||||
u32 site = vexpress_get_site_by_dev(info->device);
|
||||
|
||||
if (!vexpress_muxfpga_func)
|
||||
return -ENXIO;
|
||||
|
||||
err = vexpress_config_write(vexpress_muxfpga_func, 0, site);
|
||||
if (!err) {
|
||||
pr_debug("Selected MUXFPGA input %d (fb%d)\n", site,
|
||||
info->node);
|
||||
vexpress_dvi_fb = info->node;
|
||||
vexpress_dvi_mode_set(info, info->var.xres,
|
||||
info->var.yres);
|
||||
} else {
|
||||
pr_warn("Failed to select MUXFPGA input %d (fb%d)! (%d)\n",
|
||||
site, info->node, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vexpress_dvi_fb_select(int fb)
|
||||
{
|
||||
int err;
|
||||
struct fb_info *info;
|
||||
|
||||
/* fb0 is the default */
|
||||
if (fb < 0)
|
||||
fb = 0;
|
||||
|
||||
info = registered_fb[fb];
|
||||
if (!info || !lock_fb_info(info))
|
||||
return -ENODEV;
|
||||
|
||||
err = vexpress_dvi_mux_set(info);
|
||||
|
||||
unlock_fb_info(info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t vexpress_dvi_fb_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", vexpress_dvi_fb);
|
||||
}
|
||||
|
||||
static ssize_t vexpress_dvi_fb_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
long value;
|
||||
int err = kstrtol(buf, 0, &value);
|
||||
|
||||
if (!err)
|
||||
err = vexpress_dvi_fb_select(value);
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
DEVICE_ATTR(fb, S_IRUGO | S_IWUSR, vexpress_dvi_fb_show,
|
||||
vexpress_dvi_fb_store);
|
||||
|
||||
|
||||
static int vexpress_dvi_fb_event_notify(struct notifier_block *self,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct fb_event *event = data;
|
||||
struct fb_info *info = event->info;
|
||||
struct fb_videomode *mode = event->data;
|
||||
|
||||
switch (action) {
|
||||
case FB_EVENT_FB_REGISTERED:
|
||||
if (vexpress_dvi_fb < 0)
|
||||
vexpress_dvi_mux_set(info);
|
||||
break;
|
||||
case FB_EVENT_MODE_CHANGE:
|
||||
case FB_EVENT_MODE_CHANGE_ALL:
|
||||
if (info->node == vexpress_dvi_fb)
|
||||
vexpress_dvi_mode_set(info, mode->xres, mode->yres);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block vexpress_dvi_fb_notifier = {
|
||||
.notifier_call = vexpress_dvi_fb_event_notify,
|
||||
};
|
||||
static bool vexpress_dvi_fb_notifier_registered;
|
||||
|
||||
|
||||
enum vexpress_dvi_func { FUNC_MUXFPGA, FUNC_DVIMODE };
|
||||
|
||||
static struct of_device_id vexpress_dvi_of_match[] = {
|
||||
{
|
||||
.compatible = "arm,vexpress-muxfpga",
|
||||
.data = (void *)FUNC_MUXFPGA,
|
||||
}, {
|
||||
.compatible = "arm,vexpress-dvimode",
|
||||
.data = (void *)FUNC_DVIMODE,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int vexpress_dvi_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum vexpress_dvi_func func;
|
||||
const struct of_device_id *match =
|
||||
of_match_device(vexpress_dvi_of_match, &pdev->dev);
|
||||
|
||||
if (match)
|
||||
func = (enum vexpress_dvi_func)match->data;
|
||||
else
|
||||
func = pdev->id_entry->driver_data;
|
||||
|
||||
switch (func) {
|
||||
case FUNC_MUXFPGA:
|
||||
vexpress_muxfpga_func =
|
||||
vexpress_config_func_get_by_dev(&pdev->dev);
|
||||
device_create_file(&pdev->dev, &dev_attr_fb);
|
||||
break;
|
||||
case FUNC_DVIMODE:
|
||||
vexpress_dvimode_func =
|
||||
vexpress_config_func_get_by_dev(&pdev->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vexpress_dvi_fb_notifier_registered) {
|
||||
fb_register_client(&vexpress_dvi_fb_notifier);
|
||||
vexpress_dvi_fb_notifier_registered = true;
|
||||
}
|
||||
|
||||
vexpress_dvi_fb_select(vexpress_dvi_fb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id vexpress_dvi_id_table[] = {
|
||||
{ .name = "vexpress-muxfpga", .driver_data = FUNC_MUXFPGA, },
|
||||
{ .name = "vexpress-dvimode", .driver_data = FUNC_DVIMODE, },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver vexpress_dvi_driver = {
|
||||
.probe = vexpress_dvi_probe,
|
||||
.driver = {
|
||||
.name = "vexpress-dvi",
|
||||
.of_match_table = vexpress_dvi_of_match,
|
||||
},
|
||||
.id_table = vexpress_dvi_id_table,
|
||||
};
|
||||
|
||||
static int __init vexpress_dvi_init(void)
|
||||
{
|
||||
return platform_driver_register(&vexpress_dvi_driver);
|
||||
}
|
||||
device_initcall(vexpress_dvi_init);
|
||||
61
include/linux/arm-cci.h
Normal file
61
include/linux/arm-cci.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* CCI cache coherent interconnect support
|
||||
*
|
||||
* Copyright (C) 2013 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ARM_CCI_H
|
||||
#define __LINUX_ARM_CCI_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device_node;
|
||||
|
||||
#ifdef CONFIG_ARM_CCI
|
||||
extern bool cci_probed(void);
|
||||
extern int cci_ace_get_port(struct device_node *dn);
|
||||
extern int cci_disable_port_by_cpu(u64 mpidr);
|
||||
extern int __cci_control_port_by_device(struct device_node *dn, bool enable);
|
||||
extern int __cci_control_port_by_index(u32 port, bool enable);
|
||||
#else
|
||||
static inline bool cci_probed(void) { return false; }
|
||||
static inline int cci_ace_get_port(struct device_node *dn)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int cci_disable_port_by_cpu(u64 mpidr) { return -ENODEV; }
|
||||
static inline int __cci_control_port_by_device(struct device_node *dn,
|
||||
bool enable)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int __cci_control_port_by_index(u32 port, bool enable)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
#define cci_disable_port_by_device(dev) \
|
||||
__cci_control_port_by_device(dev, false)
|
||||
#define cci_enable_port_by_device(dev) \
|
||||
__cci_control_port_by_device(dev, true)
|
||||
#define cci_disable_port_by_index(dev) \
|
||||
__cci_control_port_by_index(dev, false)
|
||||
#define cci_enable_port_by_index(dev) \
|
||||
__cci_control_port_by_index(dev, true)
|
||||
|
||||
#endif
|
||||
122
include/linux/arm-hdlcd.h
Normal file
122
include/linux/arm-hdlcd.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* include/linux/arm-hdlcd.h
|
||||
*
|
||||
* Copyright (C) 2011 ARM Limited
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* ARM HDLCD Controller register definition
|
||||
*/
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/* register offsets */
|
||||
#define HDLCD_REG_VERSION 0x0000 /* ro */
|
||||
#define HDLCD_REG_INT_RAWSTAT 0x0010 /* rw */
|
||||
#define HDLCD_REG_INT_CLEAR 0x0014 /* wo */
|
||||
#define HDLCD_REG_INT_MASK 0x0018 /* rw */
|
||||
#define HDLCD_REG_INT_STATUS 0x001c /* ro */
|
||||
#define HDLCD_REG_USER_OUT 0x0020 /* rw */
|
||||
#define HDLCD_REG_FB_BASE 0x0100 /* rw */
|
||||
#define HDLCD_REG_FB_LINE_LENGTH 0x0104 /* rw */
|
||||
#define HDLCD_REG_FB_LINE_COUNT 0x0108 /* rw */
|
||||
#define HDLCD_REG_FB_LINE_PITCH 0x010c /* rw */
|
||||
#define HDLCD_REG_BUS_OPTIONS 0x0110 /* rw */
|
||||
#define HDLCD_REG_V_SYNC 0x0200 /* rw */
|
||||
#define HDLCD_REG_V_BACK_PORCH 0x0204 /* rw */
|
||||
#define HDLCD_REG_V_DATA 0x0208 /* rw */
|
||||
#define HDLCD_REG_V_FRONT_PORCH 0x020c /* rw */
|
||||
#define HDLCD_REG_H_SYNC 0x0210 /* rw */
|
||||
#define HDLCD_REG_H_BACK_PORCH 0x0214 /* rw */
|
||||
#define HDLCD_REG_H_DATA 0x0218 /* rw */
|
||||
#define HDLCD_REG_H_FRONT_PORCH 0x021c /* rw */
|
||||
#define HDLCD_REG_POLARITIES 0x0220 /* rw */
|
||||
#define HDLCD_REG_COMMAND 0x0230 /* rw */
|
||||
#define HDLCD_REG_PIXEL_FORMAT 0x0240 /* rw */
|
||||
#define HDLCD_REG_BLUE_SELECT 0x0244 /* rw */
|
||||
#define HDLCD_REG_GREEN_SELECT 0x0248 /* rw */
|
||||
#define HDLCD_REG_RED_SELECT 0x024c /* rw */
|
||||
|
||||
/* version */
|
||||
#define HDLCD_PRODUCT_ID 0x1CDC0000
|
||||
#define HDLCD_PRODUCT_MASK 0xFFFF0000
|
||||
#define HDLCD_VERSION_MAJOR_MASK 0x0000FF00
|
||||
#define HDLCD_VERSION_MINOR_MASK 0x000000FF
|
||||
|
||||
/* interrupts */
|
||||
#define HDLCD_INTERRUPT_DMA_END (1 << 0)
|
||||
#define HDLCD_INTERRUPT_BUS_ERROR (1 << 1)
|
||||
#define HDLCD_INTERRUPT_VSYNC (1 << 2)
|
||||
#define HDLCD_INTERRUPT_UNDERRUN (1 << 3)
|
||||
|
||||
/* polarity */
|
||||
#define HDLCD_POLARITY_VSYNC (1 << 0)
|
||||
#define HDLCD_POLARITY_HSYNC (1 << 1)
|
||||
#define HDLCD_POLARITY_DATAEN (1 << 2)
|
||||
#define HDLCD_POLARITY_DATA (1 << 3)
|
||||
#define HDLCD_POLARITY_PIXELCLK (1 << 4)
|
||||
|
||||
/* commands */
|
||||
#define HDLCD_COMMAND_DISABLE (0 << 0)
|
||||
#define HDLCD_COMMAND_ENABLE (1 << 0)
|
||||
|
||||
/* pixel format */
|
||||
#define HDLCD_PIXEL_FMT_LITTLE_ENDIAN (0 << 31)
|
||||
#define HDLCD_PIXEL_FMT_BIG_ENDIAN (1 << 31)
|
||||
#define HDLCD_BYTES_PER_PIXEL_MASK (3 << 3)
|
||||
|
||||
/* bus options */
|
||||
#define HDLCD_BUS_BURST_MASK 0x01f
|
||||
#define HDLCD_BUS_MAX_OUTSTAND 0xf00
|
||||
#define HDLCD_BUS_BURST_NONE (0 << 0)
|
||||
#define HDLCD_BUS_BURST_1 (1 << 0)
|
||||
#define HDLCD_BUS_BURST_2 (1 << 1)
|
||||
#define HDLCD_BUS_BURST_4 (1 << 2)
|
||||
#define HDLCD_BUS_BURST_8 (1 << 3)
|
||||
#define HDLCD_BUS_BURST_16 (1 << 4)
|
||||
|
||||
/* Max resolution supported is 4096x4096, 8 bit per color component,
|
||||
8 bit alpha, but we are going to choose the usual hardware default
|
||||
(2048x2048, 32 bpp) and enable double buffering */
|
||||
#define HDLCD_MAX_XRES 2048
|
||||
#define HDLCD_MAX_YRES 2048
|
||||
#define HDLCD_MAX_FRAMEBUFFER_SIZE (HDLCD_MAX_XRES * HDLCD_MAX_YRES << 2)
|
||||
|
||||
#define HDLCD_MEM_BASE (CONFIG_PAGE_OFFSET - 0x1000000)
|
||||
|
||||
#define NR_PALETTE 256
|
||||
|
||||
/* OEMs using HDLCD may wish to enable these settings if
|
||||
* display disruption is apparent and you suspect HDLCD
|
||||
* access to RAM may be starved.
|
||||
*/
|
||||
/* Turn HDLCD default color red instead of black so
|
||||
* that it's easy to see pixel clock data underruns
|
||||
* (compared to other visual disruption)
|
||||
*/
|
||||
//#define HDLCD_RED_DEFAULT_COLOUR
|
||||
/* Add a counter in the IRQ handler to count buffer underruns
|
||||
* and /proc/hdlcd_underrun to read the counter
|
||||
*/
|
||||
//#define HDLCD_COUNT_BUFFERUNDERRUNS
|
||||
/* Restrict height to 1x screen size
|
||||
*
|
||||
*/
|
||||
//#define HDLCD_NO_VIRTUAL_SCREEN
|
||||
|
||||
#ifdef CONFIG_ANDROID
|
||||
#define HDLCD_NO_VIRTUAL_SCREEN
|
||||
#endif
|
||||
|
||||
struct hdlcd_device {
|
||||
struct fb_info fb;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct completion vsync_completion;
|
||||
unsigned char *edid;
|
||||
};
|
||||
|
|
@ -69,6 +69,8 @@ void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
|
|||
u32 offset, struct device_node *);
|
||||
void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
|
||||
|
||||
void gic_cpu_if_down(void);
|
||||
|
||||
static inline void gic_init(unsigned int nr, int start,
|
||||
void __iomem *dist , void __iomem *cpu)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@
|
|||
*/
|
||||
struct vexpress_config_bridge_info {
|
||||
const char *name;
|
||||
void *(*func_get)(struct device *dev, struct device_node *node);
|
||||
void *(*func_get)(struct device *dev, struct device_node *node,
|
||||
const char *id);
|
||||
void (*func_put)(void *func);
|
||||
int (*func_exec)(void *func, int offset, bool write, u32 *data);
|
||||
};
|
||||
|
|
@ -87,12 +88,17 @@ void vexpress_config_complete(struct vexpress_config_bridge *bridge,
|
|||
|
||||
struct vexpress_config_func;
|
||||
|
||||
struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
|
||||
struct device_node *node);
|
||||
struct vexpress_config_func *__vexpress_config_func_get(
|
||||
struct vexpress_config_bridge *bridge,
|
||||
struct device *dev,
|
||||
struct device_node *node,
|
||||
const char *id);
|
||||
#define vexpress_config_func_get(bridge, id) \
|
||||
__vexpress_config_func_get(bridge, NULL, NULL, id)
|
||||
#define vexpress_config_func_get_by_dev(dev) \
|
||||
__vexpress_config_func_get(dev, NULL)
|
||||
__vexpress_config_func_get(NULL, dev, NULL, NULL)
|
||||
#define vexpress_config_func_get_by_node(node) \
|
||||
__vexpress_config_func_get(NULL, node)
|
||||
__vexpress_config_func_get(NULL, NULL, node, NULL)
|
||||
void vexpress_config_func_put(struct vexpress_config_func *func);
|
||||
|
||||
/* Both may sleep! */
|
||||
|
|
@ -120,7 +126,53 @@ void vexpress_sysreg_of_early_init(void);
|
|||
struct clk *vexpress_osc_setup(struct device *dev);
|
||||
void vexpress_osc_of_setup(struct device_node *node);
|
||||
|
||||
struct clk *vexpress_clk_register_spc(const char *name, int cluster_id);
|
||||
void vexpress_clk_of_register_spc(void);
|
||||
|
||||
void vexpress_clk_init(void __iomem *sp810_base);
|
||||
void vexpress_clk_of_init(void);
|
||||
|
||||
/* SPC */
|
||||
|
||||
#define VEXPRESS_SPC_WAKE_INTR_IRQ(cluster, cpu) \
|
||||
(1 << (4 * (cluster) + (cpu)))
|
||||
#define VEXPRESS_SPC_WAKE_INTR_FIQ(cluster, cpu) \
|
||||
(1 << (7 * (cluster) + (cpu)))
|
||||
#define VEXPRESS_SPC_WAKE_INTR_SWDOG (1 << 10)
|
||||
#define VEXPRESS_SPC_WAKE_INTR_GTIMER (1 << 11)
|
||||
#define VEXPRESS_SPC_WAKE_INTR_MASK 0xFFF
|
||||
|
||||
#ifdef CONFIG_VEXPRESS_SPC
|
||||
extern bool vexpress_spc_check_loaded(void);
|
||||
extern void vexpress_spc_set_cpu_wakeup_irq(u32 cpu, u32 cluster, bool set);
|
||||
extern void vexpress_spc_set_global_wakeup_intr(bool set);
|
||||
extern int vexpress_spc_get_freq_table(u32 cluster, u32 **fptr);
|
||||
extern int vexpress_spc_get_performance(u32 cluster, u32 *freq);
|
||||
extern int vexpress_spc_set_performance(u32 cluster, u32 freq);
|
||||
extern void vexpress_spc_write_resume_reg(u32 cluster, u32 cpu, u32 addr);
|
||||
extern int vexpress_spc_get_nb_cpus(u32 cluster);
|
||||
extern void vexpress_spc_powerdown_enable(u32 cluster, bool enable);
|
||||
#else
|
||||
static inline bool vexpress_spc_check_loaded(void) { return false; }
|
||||
static inline void vexpress_spc_set_cpu_wakeup_irq(u32 cpu, u32 cluster,
|
||||
bool set) { }
|
||||
static inline void vexpress_spc_set_global_wakeup_intr(bool set) { }
|
||||
static inline int vexpress_spc_get_freq_table(u32 cluster, u32 **fptr)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int vexpress_spc_get_performance(u32 cluster, u32 *freq)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int vexpress_spc_set_performance(u32 cluster, u32 freq)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void vexpress_spc_write_resume_reg(u32 cluster,
|
||||
u32 cpu, u32 addr) { }
|
||||
static inline int vexpress_spc_get_nb_cpus(u32 cluster) { return -ENODEV; }
|
||||
static inline void vexpress_spc_powerdown_enable(u32 cluster, bool enable) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
1
linaro/configs/vexpress-tuning.conf
Normal file
1
linaro/configs/vexpress-tuning.conf
Normal file
|
|
@ -0,0 +1 @@
|
|||
# CONFIG_PROVE_LOCKING is not set
|
||||
59
linaro/configs/vexpress.conf
Normal file
59
linaro/configs/vexpress.conf
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
CONFIG_ARCH_VEXPRESS=y
|
||||
CONFIG_ARCH_VEXPRESS_CA9X4=y
|
||||
CONFIG_BIG_LITTLE=y
|
||||
CONFIG_ARCH_VEXPRESS_TC2=y
|
||||
CONFIG_ARCH_VEXPRESS_DCSCB=y
|
||||
CONFIG_ARM_VEXPRESS_BL_CPUFREQ=y
|
||||
CONFIG_PM_OPP=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
CONFIG_ARM_PSCI=y
|
||||
CONFIG_HAVE_ARM_ARCH_TIMER=y
|
||||
CONFIG_NR_CPUS=8
|
||||
CONFIG_HIGHMEM=y
|
||||
CONFIG_HIGHPTE=y
|
||||
CONFIG_CMDLINE="console=ttyAMA0,38400n8 root=/dev/mmcblk0p2 rootwait mmci.fmax=4000000"
|
||||
CONFIG_VFP=y
|
||||
CONFIG_NEON=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_SMSC911X=y
|
||||
CONFIG_SMC91X=y
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
CONFIG_SERIO_AMBAKMI=y
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_FB_ARMCLCD=y
|
||||
CONFIG_FB_ARMHDLCD=y
|
||||
CONFIG_LOGO=y
|
||||
# CONFIG_LOGO_LINUX_MONO is not set
|
||||
# CONFIG_LOGO_LINUX_VGA16 is not set
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SND_ARMAACI=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ISP1760_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ARMMMCI=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_PL031=y
|
||||
CONFIG_NFS_FS=y
|
||||
CONFIG_NFS_V3=y
|
||||
CONFIG_NFS_V3_ACL=y
|
||||
CONFIG_NFS_V4=y
|
||||
CONFIG_ROOT_NFS=y
|
||||
CONFIG_VEXPRESS_CONFIG=y
|
||||
CONFIG_SENSORS_VEXPRESS=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_VEXPRESS=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||
CONFIG_LEDS_TRIGGER_CPU=y
|
||||
Loading…
Reference in New Issue
Block a user