Merge branch 'lsk-3.10-vexpress' of git://git.linaro.org/people/tixy/kernel into lsk-v3.10-tc2

This commit is contained in:
Mark Brown
2013-07-17 18:39:13 +01:00
78 changed files with 7017 additions and 137 deletions

View 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};

View 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>;
};

View 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>;
};

View File

@@ -1521,6 +1521,13 @@ config MCPM
for (multi-)cluster based systems, such as big.LITTLE based
systems.
config BIG_LITTLE
bool "big.LITTLE support (Experimental)"
depends on CPU_V7 && SMP
select MCPM
help
This option enables support for the big.LITTLE architecture.
choice
prompt "Memory split"
default VMSPLIT_3G

View File

@@ -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 \

View 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>;
};
};
};

View 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"

View 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"

View 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"

View 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"

View 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"

View 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>;
};
};
};

View 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"

View 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"

View File

@@ -228,6 +228,7 @@
};
clcd@1f0000 {
status = "disabled";
compatible = "arm,pl111", "arm,primecell";
reg = <0x1f0000 0x1000>;
interrupts = <14>;

View File

@@ -227,6 +227,7 @@
};
clcd@1f000 {
status = "disabled";
compatible = "arm,pl111", "arm,primecell";
reg = <0x1f000 0x1000>;
interrupts = <14>;

View File

@@ -9,6 +9,8 @@
/dts-v1/;
/memreserve/ 0xbf000000 0x01000000;
/ {
model = "V2P-CA15";
arm,hbi = <0x237>;
@@ -57,6 +59,8 @@
interrupts = <0 85 4>;
clocks = <&oscclk5>;
clock-names = "pxlclk";
mode = "1024x768-16@60";
framebuffer = <0 0xff000000 0 0x01000000>;
};
memory-controller@2b0a0000 {

View File

@@ -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 @@
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 @@
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 @@
<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 @@
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 @@
<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 @@
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>;

View File

@@ -9,6 +9,8 @@
/dts-v1/;
/memreserve/ 0xbf000000 0x01000000;
/ {
model = "V2P-CA5s";
arm,hbi = <0x225>;
@@ -59,6 +61,8 @@
interrupts = <0 85 4>;
clocks = <&oscclk3>;
clock-names = "pxlclk";
mode = "640x480-16@60";
framebuffer = <0xbf000000 0x01000000>;
};
memory-controller@2a150000 {

View File

@@ -9,6 +9,8 @@
/dts-v1/;
/include/ "clcd-panels.dtsi"
/ {
model = "V2P-CA9";
arm,hbi = <0x191>;
@@ -73,6 +75,8 @@
interrupts = <0 44 4>;
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
mode = "XVGA";
use_dma = <1>;
};
memory-controller@100e0000 {

View File

@@ -16,3 +16,4 @@ 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
CFLAGS_REMOVE_mcpm_entry.o = -pg

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);

View 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,
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View 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);

View 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)

View 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

View File

@@ -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;
}

View 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);

View 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);

View 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)

View File

@@ -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,

View File

@@ -3,4 +3,3 @@
#
obj-y := virt.o
obj-$(CONFIG_SMP) += platsmp.o

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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");

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View 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

View File

@@ -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

View File

@@ -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

View 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");

View File

@@ -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

View 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);

View File

@@ -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);

View File

@@ -453,6 +453,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

View File

@@ -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.

View File

@@ -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

View File

@@ -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
View 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");

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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");

View 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
View 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
View 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;
};

View File

@@ -67,6 +67,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)
{

View File

@@ -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

View File

@@ -0,0 +1 @@
# CONFIG_PROVE_LOCKING is not set

View 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