Merge branch 'tracking-armlt-tc2-cpufreq' into lsk-3.10-vexpress
authorJon Medhurst <tixy@linaro.org>
Wed, 17 Jul 2013 11:02:21 +0000 (12:02 +0100)
committerJon Medhurst <tixy@linaro.org>
Wed, 17 Jul 2013 11:02:21 +0000 (12:02 +0100)
73 files changed:
Documentation/devicetree/bindings/arm/cci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/rtsm-dcscb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/vexpress-spc.txt [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/boot/dts/Makefile
arch/arm/boot/dts/clcd-panels.dtsi [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-motherboard.dtsi [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts [new file with mode: 0644]
arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts [new file with mode: 0644]
arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
arch/arm/boot/dts/vexpress-v2m.dtsi
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
arch/arm/boot/dts/vexpress-v2p-ca5s.dts
arch/arm/boot/dts/vexpress-v2p-ca9.dts
arch/arm/common/Makefile
arch/arm/include/asm/arch_timer.h
arch/arm/include/asm/cp15.h
arch/arm/include/asm/mach/arch.h
arch/arm/include/asm/psci.h
arch/arm/kernel/Makefile
arch/arm/kernel/psci.c
arch/arm/kernel/psci_smp.c [new file with mode: 0644]
arch/arm/kernel/setup.c
arch/arm/kernel/sleep.S
arch/arm/mach-vexpress/Kconfig
arch/arm/mach-vexpress/Makefile
arch/arm/mach-vexpress/core.h
arch/arm/mach-vexpress/dcscb.c [new file with mode: 0644]
arch/arm/mach-vexpress/dcscb_setup.S [new file with mode: 0644]
arch/arm/mach-vexpress/include/mach/tc2.h [new file with mode: 0644]
arch/arm/mach-vexpress/platsmp.c
arch/arm/mach-vexpress/tc2_pm.c [new file with mode: 0644]
arch/arm/mach-vexpress/tc2_pm_psci.c [new file with mode: 0644]
arch/arm/mach-vexpress/tc2_pm_setup.S [new file with mode: 0644]
arch/arm/mach-vexpress/v2m.c
arch/arm/mach-virt/Makefile
arch/arm/mach-virt/platsmp.c [deleted file]
arch/arm/mach-virt/virt.c
arch/arm/mm/fault.c
arch/arm/plat-versatile/headsmp.S
drivers/bus/Kconfig
drivers/bus/Makefile
drivers/bus/arm-cci.c [new file with mode: 0644]
drivers/clk/Kconfig
drivers/clk/versatile/clk-vexpress-osc.c
drivers/cpuidle/Makefile
drivers/cpuidle/arm_big_little.c [new file with mode: 0644]
drivers/cpuidle/cpuidle-calxeda.c
drivers/irqchip/irq-gic.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/vexpress-config.c
drivers/mfd/vexpress-spc.c [new file with mode: 0644]
drivers/mfd/vexpress-sysreg.c
drivers/net/ethernet/smsc/smc91x.c
drivers/power/reset/Kconfig
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/amba-clcd.c
drivers/video/arm-hdlcd.c [new file with mode: 0644]
drivers/video/vexpress-dvi.c [new file with mode: 0644]
include/linux/arm-cci.h [new file with mode: 0644]
include/linux/arm-hdlcd.h [new file with mode: 0644]
include/linux/irqchip/arm-gic.h
include/linux/vexpress.h
linaro/configs/vexpress-tuning.conf [new file with mode: 0644]
linaro/configs/vexpress.conf [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/arm/cci.txt b/Documentation/devicetree/bindings/arm/cci.txt
new file mode 100644 (file)
index 0000000..92d36e2
--- /dev/null
@@ -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};
diff --git a/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt b/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt
new file mode 100644 (file)
index 0000000..3b8fbf3
--- /dev/null
@@ -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>;
+       };
diff --git a/Documentation/devicetree/bindings/mfd/vexpress-spc.txt b/Documentation/devicetree/bindings/mfd/vexpress-spc.txt
new file mode 100644 (file)
index 0000000..1d71dc2
--- /dev/null
@@ -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>;
+};
index 136f263ed47b79d010cf4ca06e7b1e2a07e4fd73..c24b364bb8cba7b06a2adc0711e40b51cfc8d044 100644 (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
index f0895c581a89be8668a99db10e6873ae94be0cef..00baf9f5766aebf2c01221e7b9a317a7e024d4c0 100644 (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 \
diff --git a/arch/arm/boot/dts/clcd-panels.dtsi b/arch/arm/boot/dts/clcd-panels.dtsi
new file mode 100644 (file)
index 0000000..0b0ff6e
--- /dev/null
@@ -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>;
+               };
+       };
+};
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts
new file mode 100644 (file)
index 0000000..c9eee91
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts
new file mode 100644 (file)
index 0000000..853a166
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts
new file mode 100644 (file)
index 0000000..c1947a3
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts
new file mode 100644 (file)
index 0000000..fca6b2f
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts
new file mode 100644 (file)
index 0000000..fd8a6ed
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi
new file mode 100644 (file)
index 0000000..6d12566
--- /dev/null
@@ -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>;
+                       };
+               };
+       };
diff --git a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts
new file mode 100644 (file)
index 0000000..fe8cf5d
--- /dev/null
@@ -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"
diff --git a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts
new file mode 100644 (file)
index 0000000..f715285
--- /dev/null
@@ -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"
index ac870fb3fa0d459b6639e5965f9cc2daecae14e2..9584232ee6b6c6d5464e7704bea9eddd2e7d80ce 100644 (file)
                        };
 
                        clcd@1f0000 {
+                               status = "disabled";
                                compatible = "arm,pl111", "arm,primecell";
                                reg = <0x1f0000 0x1000>;
                                interrupts = <14>;
index f1420368355bb2f91715526d4a057660f5480b71..6593398c11ae3ba3c4a51f2c29ef23c4697e5405 100644 (file)
                        };
 
                        clcd@1f000 {
+                               status = "disabled";
                                compatible = "arm,pl111", "arm,primecell";
                                reg = <0x1f000 0x1000>;
                                interrupts = <14>;
index 9420053acc14639ffa24e992dfb3bd3eaf5d9071..cc6a8c0cfe33bc99407ccbf64de783e7514b14d5 100644 (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 {
index d2803be4e1a8f89ac0c9ca6c36429deb43ba65a0..f1dc620c5c4596058df6686227c31cf5d406c80a 100644 (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>;
                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 {
                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";
        };
                      <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 {
                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>,
                             <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";
                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>;
index c544a55045918ee1879d59b0282c37bd23d402eb..cf633ed6a1b485b2fd2c1d0cb6fa19b895fbd770 100644 (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 {
index 62d9b225dcceec8b27464027cd9dba60640c33f5..f83706bd3f9a87bbe3eb8018eb108fc9f32b4bdb 100644 (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 {
index 48434cbe3e89090d18d7bc3e4a799713631c8ac3..f27d6a7af57fc40f734584668bc4574262605fcb 100644 (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
index 7c1bfc0aea0c200a268ec82c62fd4cb624c17ecf..4928cdacedf0169324f2aa6bd36cc2bfef869cf0 100644 (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));
 }
index 1f3262e99d81e6e7be2bf3864b6649d32a39b0da..cedd3721318b2f1a4251717ab383ee865e2aef7c 100644 (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
index 308ad7d6f98b77098dd3c831b8831f9731f49211..75bf07910b8189314d276d5e7ee7ad5c1fe5c782 100644 (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  */
index ce0dbe7c1625d296b165794ab75165a7452ecbfe..f0a8627c9f1c250b63d0569f1b076981175d9f55 100644 (file)
 
 #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 */
index 5f3338eacad21024a75d7b1e7480f3fc58e804b8..dd9d90ab65d0cd7975031c0d67bf2cb8d1aefbb7 100644 (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
index 36531643cc2c01e3d8e997e32e2ffaa9a8b9ce12..0daf4f2522845306424c274594257867a2f545b2 100644 (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>
 
 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;
+}
+
+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_initcall(psci_init);
+early_param("psci", early_psci);
diff --git a/arch/arm/kernel/psci_smp.c b/arch/arm/kernel/psci_smp.c
new file mode 100644 (file)
index 0000000..23a1142
--- /dev/null
@@ -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,
+};
index b4b1d397592b3d6435c0a503541c37d3fffcc016..6002fe06b124688f77f340f7cb5367d171274228 100644 (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
index 987dcf33415c420f70dc08852da45bd654a21ea2..b5c1e636ed8559f22411aa7412d09db526633a18 100644 (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
index f6ba1716e406571c6a7786cccf0f7492d92d58cc..dd3d5975a5c1b9cd1d1eeac01e636d387293d23e 100644 (file)
@@ -58,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
index 42703e8b4d3bcdb674b99f4c01425e0dbcb44a21..14193dc7e6e8e6e8a328dd2ed3b689ff035d2e32 100644 (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
index f134cd4a85f1d98e2ca68baa6fed34d5577b0eab..bde4374ab6d5eb119d24a0e167ea13a20598f5c4 100644 (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);
diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c
new file mode 100644 (file)
index 0000000..948aec0
--- /dev/null
@@ -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);
diff --git a/arch/arm/mach-vexpress/dcscb_setup.S b/arch/arm/mach-vexpress/dcscb_setup.S
new file mode 100644 (file)
index 0000000..4bb7fbe
--- /dev/null
@@ -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)
diff --git a/arch/arm/mach-vexpress/include/mach/tc2.h b/arch/arm/mach-vexpress/include/mach/tc2.h
new file mode 100644 (file)
index 0000000..d3b5a22
--- /dev/null
@@ -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
index dc1ace55d5578bdcb96930fa7dabde0bb692ddd9..993c9ae5dc5e16f01605770da107bfb20b1a027f 100644 (file)
 #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;
+}
diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c
new file mode 100644 (file)
index 0000000..9c742ed
--- /dev/null
@@ -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);
diff --git a/arch/arm/mach-vexpress/tc2_pm_psci.c b/arch/arm/mach-vexpress/tc2_pm_psci.c
new file mode 100644 (file)
index 0000000..c2fdc22
--- /dev/null
@@ -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);
diff --git a/arch/arm/mach-vexpress/tc2_pm_setup.S b/arch/arm/mach-vexpress/tc2_pm_setup.S
new file mode 100644 (file)
index 0000000..a18dafe
--- /dev/null
@@ -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)
index 8802030df98d0fbac53f4cd1f01df30f598f1773..057f99b62eaff24daaee3c70a4f8b7ed3dec231c 100644 (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,
index 042afc1f8c4425f4ce887910dd4a5daddb4e8615..7ddbfa60227f8e267acc58bca381428fdb99e294 100644 (file)
@@ -3,4 +3,3 @@
 #
 
 obj-y                                  := virt.o
-obj-$(CONFIG_SMP)                      += platsmp.o
diff --git a/arch/arm/mach-virt/platsmp.c b/arch/arm/mach-virt/platsmp.c
deleted file mode 100644 (file)
index f4143f5..0000000
+++ /dev/null
@@ -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,
-};
index 061f283f579e891b59f8a0c17c0dcafa7bdc4288..a67d2dd5bb6062e7eede85ef612ac6c072c9e7ea 100644 (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
index 5dbf13f954f6f493aaae525d4f3b93282ac88f75..e207aa5f846f602ec0fc843df89c8a81a5994e7b 100644 (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:
index b178d44e9eaa897c8f8a3a4bb017198cb7014cae..2677bc3762d7ce418b4c116b709dadf0f8e53697 100644 (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
index b05ecab915c4ba30e1ea668f09f700b1d2519937..5286e2d333b09752b0bf7a342dc63f4650b29f63 100644 (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
index 3c7b53c12091cb0cdafa5ab74fd43ddc616ae5c9..670cea4438023cce1d71d2f62af509855ccee694 100644 (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
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
new file mode 100644 (file)
index 0000000..ed284ab
--- /dev/null
@@ -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");
index 0357ac44638ba5f70ee623939697a1f1023b9440..d0d9b2124752486249b5e2adb1ab9713a869a794 100644 (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
index 256c8be74df8483073ac84fe2f9984724a837ac9..2dc8b41a339dba3dc359e7ee34b10de98e8eb3fe 100644 (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;
index 0d8bd55e776f64d5e3f45c2f5d776366d98b9f86..7d8256a5ea978688beae9d354d3751336445c740 100644 (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
diff --git a/drivers/cpuidle/arm_big_little.c b/drivers/cpuidle/arm_big_little.c
new file mode 100644 (file)
index 0000000..e537889
--- /dev/null
@@ -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);
index 223379169cb08d01907906919882da5941cd1843..0e6e408c0a630b71c466f3fabaa7decd1b97fed0 100644 (file)
 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);
index 19ceaa60e0f45c755fd67d840e8ee90de320d1ca..fe44d3e2c702dd1d96922371c97829a352d2db57 100644 (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
index d54e985748b78403956a0b7ba0d2b8634b949244..a5e54f0d6a737b8e30e98e4108542776579eb228 100644 (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.
index 718e94a2a9a7577e5bb6f286c822983c88e2dcf7..3a0120315aa3fcc9e26eab8e1ef9ce627e5f5d39 100644 (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
index 84ce6b9daa3dea13b2adf8d07d907fb128d1175c..1af2b0e0182ffb338466e3a0abbb1990e66bdcdf 100644 (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);
diff --git a/drivers/mfd/vexpress-spc.c b/drivers/mfd/vexpress-spc.c
new file mode 100644 (file)
index 0000000..0c6718a
--- /dev/null
@@ -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");
index 96a020b1dcd14a9321a1358a6dd420c1522e2f60..7f429afce1128520b073e8a58aa0c33a54718243 100644 (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);
 
index dfbf978315dfb31a8cbbd01ce5beae06abc5f398..bdd703c6bf167196b2370120c19abfadcb178269 100644 (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);
        }
index 349e9ae8090a75eaf3c98f8ece4f99695b2308f4..ee039dcead04782a5389ff7827f271e2d3aa2192 100644 (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
index 2e937bdace6f123a1b6f1c0c055b4422dde78875..d0d9c5ea9e3a1565d91f3dd58ec041a2f5b76eeb 100644 (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
index e8bae8dd4804d4bf797f444f239d5005a689390e..33869eea4981580a03e85e758df7faf2586fcec6 100644 (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
index 0a2cce7285be99dd8aaa7983a43bbeae7e6b2550..94a1998338dacb5dd726d44a9683338ef7185c95 100644 (file)
 #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>
 
 #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) {
diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c
new file mode 100644 (file)
index 0000000..cfd631e
--- /dev/null
@@ -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");
diff --git a/drivers/video/vexpress-dvi.c b/drivers/video/vexpress-dvi.c
new file mode 100644 (file)
index 0000000..f087534
--- /dev/null
@@ -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);
diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h
new file mode 100644 (file)
index 0000000..79d6edf
--- /dev/null
@@ -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
diff --git a/include/linux/arm-hdlcd.h b/include/linux/arm-hdlcd.h
new file mode 100644 (file)
index 0000000..939f3a8
--- /dev/null
@@ -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;
+};
index 3e203eb23cc79231f96ae50e1fb56da9cf4125ad..b5696108c06e8aa7705d512b94f951744f13c228 100644 (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)
 {
index 6e7980de37f4ee37dee1de2d0b16edbb007f683b..c1191ab4cb988f441a12c0ec99d8f40afe440ce1 100644 (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! */
@@ -126,4 +132,47 @@ 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
diff --git a/linaro/configs/vexpress-tuning.conf b/linaro/configs/vexpress-tuning.conf
new file mode 100644 (file)
index 0000000..adea6cc
--- /dev/null
@@ -0,0 +1 @@
+# CONFIG_PROVE_LOCKING is not set
diff --git a/linaro/configs/vexpress.conf b/linaro/configs/vexpress.conf
new file mode 100644 (file)
index 0000000..94c36e0
--- /dev/null
@@ -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