ARM: Adding support for an energy controller

The energy controller enables DVFS (dynamic voltage and frequency
scaling support) of gem5 for up to 32 independent domains (or clusters).
The changes are modelled somewhat after the VExpress SPC component, but
are specific to gem5.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 96c4d24..cb07596 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -5,6 +5,7 @@
 	select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
 	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
 	select ARCH_HAVE_CUSTOM_GPIO_H
+	select ARCH_HAS_CPUFREQ
 	select ARCH_MIGHT_HAVE_PC_PARPORT
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_1cpus.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_1cpus.dts
new file mode 100644
index 0000000..cffb492
--- /dev/null
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_1cpus.dts
@@ -0,0 +1,347 @@
+/*
+ * ARM Ltd. Versatile Express
+ *
+ * CoreTile Express A15x2 (version with Test Chip 1)
+ * Cortex-A15 MPCore (V2P-CA15)
+ *
+ * HBI-0237A
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xbf000000 0x01000000;
+
+/ {
+	model = "V2P-CA15";
+	arm,hbi = <0x0>;
+	arm,vexpress,site = <0xf>;
+	compatible = "arm,vexpress,v2p-ca15,tc1", "arm,vexpress,v2p-ca15", "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;
+		i2c0 = &v2m_i2c_dvi;
+		i2c1 = &v2m_i2c_pcie;
+	};
+
+	clusters {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cluster0: cluster@0 {
+			reg = <0>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core0: core@0 {
+					reg = <0>;
+				};
+/*
+				core1: core@1 {
+					reg = <1>;
+				};
+*/
+			};
+		};
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0>;
+			cluster = <&cluster0>;
+			core = <&core0>;
+			clock-frequency = <1000000000>;
+		};
+
+/*		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster0>;
+			core = <&core1>;
+			reg = <1>;
+		};
+*/
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x40000000>;
+	};
+
+	hdlcd@2b000000 {
+		compatible = "arm,hdlcd";
+		reg = <0 0x2b000000 0 0x1000>;
+		interrupts = <0 85 4>;
+		clocks = <&oscclk5>;
+		clock-names = "pxlclk";
+		mode = "1024x768-16@60";
+//		mode = "3840x2160M-16@60m";	// 4K mode string
+		framebuffer = <0 0xbf000000 0 0x01000000>;
+	};
+/*
+	memory-controller@2b0a0000 {
+		compatible = "arm,pl341", "arm,primecell";
+		reg = <0 0x2b0a0000 0 0x1000>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	wdt@2b060000 {
+		compatible = "arm,sp805", "arm,primecell";
+		status = "disabled";
+		reg = <0 0x2b060000 0 0x1000>;
+		interrupts = <0 98 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+	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>;
+	};
+/*
+	memory-controller@7ffd0000 {
+		compatible = "arm,pl354", "arm,primecell";
+		reg = <0 0x7ffd0000 0 0x1000>;
+		interrupts = <0 86 4>,
+			     <0 87 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	dma@7ffb0000 {
+		compatible = "arm,pl330", "arm,primecell";
+		reg = <0 0x7ffb0000 0 0x1000>;
+		interrupts = <0 92 4>,
+			     <0 88 4>,
+			     <0 89 4>,
+			     <0 90 4>,
+			     <0 91 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	timer {
+		compatible = "arm,armv7-timer";
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+*/
+
+
+	timer {
+		compatible = "arm,cortex-a15-timer",
+			     "arm,armv7-timer";
+		interrupts = <1 13 0xff01>,
+		             <1 14 0xff01>;
+        clocks = <&oscclk7>;
+        clock-names="apb_pclk";
+	};
+
+
+	/** HACK : cortex-a9-twd-timer hack -- temporary fix */
+	/*timer@2c080000 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0 0x2c080000 0 0x20>;
+		interrupts = <1 13 0xf04>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};*/
+
+	pmu {
+		compatible = "arm,cortex-a15-pmu";
+		interrupts = <0 68 4>,
+			     <0 69 4>;
+	};
+
+	gem5_energy_ctrl@1c080000 {
+		compatible = "arm,gem5-energy-ctrl";
+		reg = <0 0x1c080000 0 0x1C>;
+	};
+
+	dcc {
+		compatible = "arm,vexpress,config-bus";
+		arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+		osc@0 {
+			/* CPU PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 0>;
+			freq-range = <50000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk0";
+		};
+
+		osc@4 {
+			/* Multiplexed AXI master clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 4>;
+			freq-range = <20000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk4";
+		};
+
+		oscclk5: osc@5 {
+			/* HDLCD PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 5>;
+//			freq-range = <23750000 165000000>;	// original
+			freq-range = <23750000 1000000000>;	// for gem5 extended
+								// resolution support
+			#clock-cells = <0>;
+			clock-output-names = "oscclk5";
+		};
+
+		smbclk: osc@6 {
+			/* SMB clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 6>;
+			freq-range = <20000000 50000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk6";
+		};
+
+		oscclk7: osc@7 {
+			/* SYS PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 7>;
+			freq-range = <20000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk7";
+		};
+
+		osc@8 {
+			/* DDR2 PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 8>;
+			freq-range = <40000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk8";
+		};
+
+		volt@0 {
+			/* CPU core voltage */
+			compatible = "arm,vexpress-volt";
+			arm,vexpress-sysreg,func = <2 0>;
+			regulator-name = "Cores";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <1050000>;
+			regulator-always-on;
+			label = "Cores";
+		};
+
+		amp@0 {
+			/* Total current for the two cores */
+			compatible = "arm,vexpress-amp";
+			arm,vexpress-sysreg,func = <3 0>;
+			label = "Cores";
+		};
+
+		temp@0 {
+			/* DCC internal temperature */
+			compatible = "arm,vexpress-temp";
+			arm,vexpress-sysreg,func = <4 0>;
+			label = "DCC";
+		};
+
+		power@0 {
+			/* Total power */
+			compatible = "arm,vexpress-power";
+			arm,vexpress-sysreg,func = <12 0>;
+			label = "Cores";
+		};
+
+		energy@0 {
+			/* Total energy */
+			compatible = "arm,vexpress-energy";
+			arm,vexpress-sysreg,func = <13 0>;
+			label = "Cores";
+		};
+	};
+
+	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/ "vexpress-v2m-rs1-gem5.dtsi"
+	};
+};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_2cpus.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_2cpus.dts
new file mode 100644
index 0000000..7eaba23
--- /dev/null
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_2cpus.dts
@@ -0,0 +1,347 @@
+/*
+ * ARM Ltd. Versatile Express
+ *
+ * CoreTile Express A15x2 (version with Test Chip 1)
+ * Cortex-A15 MPCore (V2P-CA15)
+ *
+ * HBI-0237A
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xbf000000 0x01000000;
+
+/ {
+	model = "V2P-CA15";
+	arm,hbi = <0x0>;
+	arm,vexpress,site = <0xf>;
+	compatible = "arm,vexpress,v2p-ca15,tc1", "arm,vexpress,v2p-ca15", "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;
+		i2c0 = &v2m_i2c_dvi;
+		i2c1 = &v2m_i2c_pcie;
+	};
+
+	clusters {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cluster0: cluster@0 {
+			reg = <0>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core0: core@0 {
+					reg = <0>;
+				};
+
+				core1: core@1 {
+					reg = <1>;
+				};
+
+			};
+		};
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0>;
+			cluster = <&cluster0>;
+			core = <&core0>;
+			clock-frequency = <1000000000>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster0>;
+			core = <&core1>;
+			reg = <1>;
+		};
+
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x40000000>;
+	};
+
+	hdlcd@2b000000 {
+		compatible = "arm,hdlcd";
+		reg = <0 0x2b000000 0 0x1000>;
+		interrupts = <0 85 4>;
+		clocks = <&oscclk5>;
+		clock-names = "pxlclk";
+		mode = "1024x768-16@60";
+//		mode = "3840x2160M-16@60m";	// 4K mode string
+		framebuffer = <0 0xbf000000 0 0x01000000>;
+	};
+/*
+	memory-controller@2b0a0000 {
+		compatible = "arm,pl341", "arm,primecell";
+		reg = <0 0x2b0a0000 0 0x1000>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	wdt@2b060000 {
+		compatible = "arm,sp805", "arm,primecell";
+		status = "disabled";
+		reg = <0 0x2b060000 0 0x1000>;
+		interrupts = <0 98 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+	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>;
+	};
+/*
+	memory-controller@7ffd0000 {
+		compatible = "arm,pl354", "arm,primecell";
+		reg = <0 0x7ffd0000 0 0x1000>;
+		interrupts = <0 86 4>,
+			     <0 87 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	dma@7ffb0000 {
+		compatible = "arm,pl330", "arm,primecell";
+		reg = <0 0x7ffb0000 0 0x1000>;
+		interrupts = <0 92 4>,
+			     <0 88 4>,
+			     <0 89 4>,
+			     <0 90 4>,
+			     <0 91 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	timer {
+		compatible = "arm,armv7-timer";
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+*/
+
+
+	timer {
+		compatible = "arm,cortex-a15-timer",
+			     "arm,armv7-timer";
+		interrupts = <1 13 0xff01>,
+		             <1 14 0xff01>;
+        clocks = <&oscclk7>;
+        clock-names="apb_pclk";
+	};
+
+
+	/** HACK : cortex-a9-twd-timer hack -- temporary fix */
+	/*timer@2c080000 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0 0x2c080000 0 0x20>;
+		interrupts = <1 13 0xf04>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};*/
+
+	pmu {
+		compatible = "arm,cortex-a15-pmu";
+		interrupts = <0 68 4>,
+			     <0 69 4>;
+	};
+
+	gem5_energy_ctrl@1c080000 {
+		compatible = "arm,gem5-energy-ctrl";
+		reg = <0 0x1c080000 0 0x1C>;
+	};
+
+	dcc {
+		compatible = "arm,vexpress,config-bus";
+		arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+		osc@0 {
+			/* CPU PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 0>;
+			freq-range = <50000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk0";
+		};
+
+		osc@4 {
+			/* Multiplexed AXI master clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 4>;
+			freq-range = <20000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk4";
+		};
+
+		oscclk5: osc@5 {
+			/* HDLCD PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 5>;
+//			freq-range = <23750000 165000000>;	// original
+			freq-range = <23750000 1000000000>;	// for gem5 extended
+								// resolution support
+			#clock-cells = <0>;
+			clock-output-names = "oscclk5";
+		};
+
+		smbclk: osc@6 {
+			/* SMB clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 6>;
+			freq-range = <20000000 50000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk6";
+		};
+
+		oscclk7: osc@7 {
+			/* SYS PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 7>;
+			freq-range = <20000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk7";
+		};
+
+		osc@8 {
+			/* DDR2 PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 8>;
+			freq-range = <40000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk8";
+		};
+
+		volt@0 {
+			/* CPU core voltage */
+			compatible = "arm,vexpress-volt";
+			arm,vexpress-sysreg,func = <2 0>;
+			regulator-name = "Cores";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <1050000>;
+			regulator-always-on;
+			label = "Cores";
+		};
+
+		amp@0 {
+			/* Total current for the two cores */
+			compatible = "arm,vexpress-amp";
+			arm,vexpress-sysreg,func = <3 0>;
+			label = "Cores";
+		};
+
+		temp@0 {
+			/* DCC internal temperature */
+			compatible = "arm,vexpress-temp";
+			arm,vexpress-sysreg,func = <4 0>;
+			label = "DCC";
+		};
+
+		power@0 {
+			/* Total power */
+			compatible = "arm,vexpress-power";
+			arm,vexpress-sysreg,func = <12 0>;
+			label = "Cores";
+		};
+
+		energy@0 {
+			/* Total energy */
+			compatible = "arm,vexpress-energy";
+			arm,vexpress-sysreg,func = <13 0>;
+			label = "Cores";
+		};
+	};
+
+	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/ "vexpress-v2m-rs1-gem5.dtsi"
+	};
+};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_4cpus.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_4cpus.dts
new file mode 100644
index 0000000..30a83ba
--- /dev/null
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_4cpus.dts
@@ -0,0 +1,371 @@
+/*
+ * ARM Ltd. Versatile Express
+ *
+ * CoreTile Express A15x2 (version with Test Chip 1)
+ * Cortex-A15 MPCore (V2P-CA15)
+ *
+ * HBI-0237A
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xbf000000 0x01000000;
+
+/ {
+	model = "V2P-CA15";
+	arm,hbi = <0x0>;
+	arm,vexpress,site = <0xf>;
+	compatible = "arm,vexpress,v2p-ca15,tc1", "arm,vexpress,v2p-ca15", "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;
+		i2c0 = &v2m_i2c_dvi;
+		i2c1 = &v2m_i2c_pcie;
+	};
+
+	clusters {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cluster0: cluster@0 {
+			reg = <0>;
+			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>;
+				};
+
+			};
+		};
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0>;
+			cluster = <&cluster0>;
+			core = <&core0>;
+			clock-frequency = <1000000000>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster0>;
+			core = <&core1>;
+			reg = <1>;
+		};
+
+		cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster0>;
+			core = <&core2>;
+			reg = <2>;
+		};
+
+		cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster0>;
+			core = <&core3>;
+			reg = <3>;
+		};
+
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x40000000>;
+	};
+
+	hdlcd@2b000000 {
+		compatible = "arm,hdlcd";
+		reg = <0 0x2b000000 0 0x1000>;
+		interrupts = <0 85 4>;
+		clocks = <&oscclk5>;
+		clock-names = "pxlclk";
+		mode = "1024x768-16@60";
+//		mode = "3840x2160M-16@60m";	// 4K mode string
+		framebuffer = <0 0xbf000000 0 0x01000000>;
+	};
+/*
+	memory-controller@2b0a0000 {
+		compatible = "arm,pl341", "arm,primecell";
+		reg = <0 0x2b0a0000 0 0x1000>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	wdt@2b060000 {
+		compatible = "arm,sp805", "arm,primecell";
+		status = "disabled";
+		reg = <0 0x2b060000 0 0x1000>;
+		interrupts = <0 98 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+	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>;
+	};
+/*
+	memory-controller@7ffd0000 {
+		compatible = "arm,pl354", "arm,primecell";
+		reg = <0 0x7ffd0000 0 0x1000>;
+		interrupts = <0 86 4>,
+			     <0 87 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	dma@7ffb0000 {
+		compatible = "arm,pl330", "arm,primecell";
+		reg = <0 0x7ffb0000 0 0x1000>;
+		interrupts = <0 92 4>,
+			     <0 88 4>,
+			     <0 89 4>,
+			     <0 90 4>,
+			     <0 91 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	timer {
+		compatible = "arm,armv7-timer";
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+*/
+
+
+	timer {
+		compatible = "arm,cortex-a15-timer",
+			     "arm,armv7-timer";
+		interrupts = <1 13 0xff01>,
+		             <1 14 0xff01>;
+        clocks = <&oscclk7>;
+        clock-names="apb_pclk";
+	};
+
+
+	/** HACK : cortex-a9-twd-timer hack -- temporary fix */
+	/*timer@2c080000 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0 0x2c080000 0 0x20>;
+		interrupts = <1 13 0xf04>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};*/
+
+	pmu {
+		compatible = "arm,cortex-a15-pmu";
+		interrupts = <0 68 4>,
+			     <0 69 4>;
+	};
+
+	gem5_energy_ctrl@1c080000 {
+		compatible = "arm,gem5-energy-ctrl";
+		reg = <0 0x1c080000 0 0x1C>;
+	};
+
+	dcc {
+		compatible = "arm,vexpress,config-bus";
+		arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+		osc@0 {
+			/* CPU PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 0>;
+			freq-range = <50000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk0";
+		};
+
+		osc@4 {
+			/* Multiplexed AXI master clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 4>;
+			freq-range = <20000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk4";
+		};
+
+		oscclk5: osc@5 {
+			/* HDLCD PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 5>;
+//			freq-range = <23750000 165000000>;	// original
+			freq-range = <23750000 1000000000>;	// for gem5 extended
+								// resolution support
+			#clock-cells = <0>;
+			clock-output-names = "oscclk5";
+		};
+
+		smbclk: osc@6 {
+			/* SMB clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 6>;
+			freq-range = <20000000 50000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk6";
+		};
+
+		oscclk7: osc@7 {
+			/* SYS PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 7>;
+			freq-range = <20000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk7";
+		};
+
+		osc@8 {
+			/* DDR2 PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 8>;
+			freq-range = <40000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk8";
+		};
+
+		volt@0 {
+			/* CPU core voltage */
+			compatible = "arm,vexpress-volt";
+			arm,vexpress-sysreg,func = <2 0>;
+			regulator-name = "Cores";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <1050000>;
+			regulator-always-on;
+			label = "Cores";
+		};
+
+		amp@0 {
+			/* Total current for the two cores */
+			compatible = "arm,vexpress-amp";
+			arm,vexpress-sysreg,func = <3 0>;
+			label = "Cores";
+		};
+
+		temp@0 {
+			/* DCC internal temperature */
+			compatible = "arm,vexpress-temp";
+			arm,vexpress-sysreg,func = <4 0>;
+			label = "DCC";
+		};
+
+		power@0 {
+			/* Total power */
+			compatible = "arm,vexpress-power";
+			arm,vexpress-sysreg,func = <12 0>;
+			label = "Cores";
+		};
+
+		energy@0 {
+			/* Total energy */
+			compatible = "arm,vexpress-energy";
+			arm,vexpress-sysreg,func = <13 0>;
+			label = "Cores";
+		};
+	};
+
+	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/ "vexpress-v2m-rs1-gem5.dtsi"
+	};
+};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_2cpus.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_2cpus.dts
new file mode 100644
index 0000000..6d68569
--- /dev/null
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_2cpus.dts
@@ -0,0 +1,354 @@
+/*
+ * ARM Ltd. Versatile Express
+ *
+ * CoreTile Express A15x2 (version with Test Chip 1)
+ * Cortex-A15 MPCore (V2P-CA15)
+ *
+ * HBI-0237A
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xbf000000 0x01000000;
+
+/ {
+	model = "V2P-CA15";
+	arm,hbi = <0x0>;
+	arm,vexpress,site = <0xf>;
+	compatible = "arm,vexpress,v2p-ca15,tc1", "arm,vexpress,v2p-ca15", "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;
+		i2c0 = &v2m_i2c_dvi;
+		i2c1 = &v2m_i2c_pcie;
+	};
+
+	clusters {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cluster0: cluster@0 {
+			reg = <0>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core0: core@0 {
+					reg = <0>;
+				};
+			};
+		};
+		cluster1: cluster@1 {
+			reg = <1>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core1: core@1 {
+					reg = <1>;
+				};
+			};
+		};
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0>;
+			cluster = <&cluster0>;
+			core = <&core0>;
+			clock-frequency = <1000000000>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster1>;
+			core = <&core1>;
+			reg = <0x101>;
+                        clock-frequency = <1000000000>;
+		};
+
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x40000000>;
+	};
+
+	hdlcd@2b000000 {
+		compatible = "arm,hdlcd";
+		reg = <0 0x2b000000 0 0x1000>;
+		interrupts = <0 85 4>;
+		clocks = <&oscclk5>;
+		clock-names = "pxlclk";
+		mode = "1024x768-16@60";
+//		mode = "3840x2160M-16@60m";	// 4K mode string
+		framebuffer = <0 0xbf000000 0 0x01000000>;
+	};
+/*
+	memory-controller@2b0a0000 {
+		compatible = "arm,pl341", "arm,primecell";
+		reg = <0 0x2b0a0000 0 0x1000>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	wdt@2b060000 {
+		compatible = "arm,sp805", "arm,primecell";
+		status = "disabled";
+		reg = <0 0x2b060000 0 0x1000>;
+		interrupts = <0 98 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+	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>;
+	};
+/*
+	memory-controller@7ffd0000 {
+		compatible = "arm,pl354", "arm,primecell";
+		reg = <0 0x7ffd0000 0 0x1000>;
+		interrupts = <0 86 4>,
+			     <0 87 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	dma@7ffb0000 {
+		compatible = "arm,pl330", "arm,primecell";
+		reg = <0 0x7ffb0000 0 0x1000>;
+		interrupts = <0 92 4>,
+			     <0 88 4>,
+			     <0 89 4>,
+			     <0 90 4>,
+			     <0 91 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	timer {
+		compatible = "arm,armv7-timer";
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+*/
+
+
+	timer {
+		compatible = "arm,cortex-a15-timer",
+			     "arm,armv7-timer";
+		interrupts = <1 13 0xff01>,
+		             <1 14 0xff01>;
+        clocks = <&oscclk7>;
+        clock-names="apb_pclk";
+	};
+
+
+	/** HACK : cortex-a9-twd-timer hack -- temporary fix */
+	/*timer@2c080000 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0 0x2c080000 0 0x20>;
+		interrupts = <1 13 0xf04>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};*/
+
+	pmu {
+		compatible = "arm,cortex-a15-pmu";
+		interrupts = <0 68 4>,
+			     <0 69 4>;
+	};
+
+	gem5_energy_ctrl@1c080000 {
+		compatible = "arm,gem5-energy-crtl";
+		reg = <0 0x1c080000 0 0x1C>;
+	};
+
+	dcc {
+		compatible = "arm,vexpress,config-bus";
+		arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+		osc@0 {
+			/* CPU PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 0>;
+			freq-range = <50000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk0";
+		};
+
+		osc@4 {
+			/* Multiplexed AXI master clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 4>;
+			freq-range = <20000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk4";
+		};
+
+		oscclk5: osc@5 {
+			/* HDLCD PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 5>;
+//			freq-range = <23750000 165000000>;	// original
+			freq-range = <23750000 1000000000>;	// for gem5 extended
+								// resolution support
+			#clock-cells = <0>;
+			clock-output-names = "oscclk5";
+		};
+
+		smbclk: osc@6 {
+			/* SMB clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 6>;
+			freq-range = <20000000 50000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk6";
+		};
+
+		oscclk7: osc@7 {
+			/* SYS PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 7>;
+			freq-range = <20000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk7";
+		};
+
+		osc@8 {
+			/* DDR2 PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 8>;
+			freq-range = <40000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk8";
+		};
+
+		volt@0 {
+			/* CPU core voltage */
+			compatible = "arm,vexpress-volt";
+			arm,vexpress-sysreg,func = <2 0>;
+			regulator-name = "Cores";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <1050000>;
+			regulator-always-on;
+			label = "Cores";
+		};
+
+		amp@0 {
+			/* Total current for the two cores */
+			compatible = "arm,vexpress-amp";
+			arm,vexpress-sysreg,func = <3 0>;
+			label = "Cores";
+		};
+
+		temp@0 {
+			/* DCC internal temperature */
+			compatible = "arm,vexpress-temp";
+			arm,vexpress-sysreg,func = <4 0>;
+			label = "DCC";
+		};
+
+		power@0 {
+			/* Total power */
+			compatible = "arm,vexpress-power";
+			arm,vexpress-sysreg,func = <12 0>;
+			label = "Cores";
+		};
+
+		energy@0 {
+			/* Total energy */
+			compatible = "arm,vexpress-energy";
+			arm,vexpress-sysreg,func = <13 0>;
+			label = "Cores";
+		};
+	};
+
+	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/ "vexpress-v2m-rs1-gem5.dtsi"
+	};
+};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_4cpus.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_4cpus.dts
new file mode 100644
index 0000000..36068c9
--- /dev/null
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1-gem5_dvfs_per_core_4cpus.dts
@@ -0,0 +1,393 @@
+/*
+ * ARM Ltd. Versatile Express
+ *
+ * CoreTile Express A15x2 (version with Test Chip 1)
+ * Cortex-A15 MPCore (V2P-CA15)
+ *
+ * HBI-0237A
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xbf000000 0x01000000;
+
+/ {
+	model = "V2P-CA15";
+	arm,hbi = <0x0>;
+	arm,vexpress,site = <0xf>;
+	compatible = "arm,vexpress,v2p-ca15,tc1", "arm,vexpress,v2p-ca15", "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;
+		i2c0 = &v2m_i2c_dvi;
+		i2c1 = &v2m_i2c_pcie;
+	};
+
+	clusters {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cluster0: cluster@0 {
+			reg = <0>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core0: core@0 {
+					reg = <0>;
+				};
+			};
+		};
+		cluster1: cluster@1 {
+			reg = <1>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core1: core@1 {
+					reg = <1>;
+				};
+			};
+		};
+		cluster2: cluster@2 {
+			reg = <2>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core2: core@2 {
+					reg = <2>;
+				};
+			};
+		};
+		cluster3: cluster@3 {
+			reg = <3>;
+			cores {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				core3: core@3 {
+					reg = <3>;
+				};
+			};
+		};
+        };
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0>;
+			cluster = <&cluster0>;
+			core = <&core0>;
+			clock-frequency = <1000000000>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster1>;
+			core = <&core1>;
+			reg = <0x101>;
+                        clock-frequency = <1000000000>;
+		};
+
+		cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster2>;
+			core = <&core2>;
+			reg = <0x202>;
+                        clock-frequency = <1000000000>;
+		};
+
+		cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			cluster = <&cluster3>;
+			core = <&core3>;
+			reg = <0x303>;
+                        clock-frequency = <1000000000>;
+		};
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x40000000>;
+	};
+
+	hdlcd@2b000000 {
+		compatible = "arm,hdlcd";
+		reg = <0 0x2b000000 0 0x1000>;
+		interrupts = <0 85 4>;
+		clocks = <&oscclk5>;
+		clock-names = "pxlclk";
+		mode = "1024x768-16@60";
+//		mode = "3840x2160M-16@60m";	// 4K mode string
+		framebuffer = <0 0xbf000000 0 0x01000000>;
+	};
+/*
+	memory-controller@2b0a0000 {
+		compatible = "arm,pl341", "arm,primecell";
+		reg = <0 0x2b0a0000 0 0x1000>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	wdt@2b060000 {
+		compatible = "arm,sp805", "arm,primecell";
+		status = "disabled";
+		reg = <0 0x2b060000 0 0x1000>;
+		interrupts = <0 98 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+	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>;
+	};
+/*
+	memory-controller@7ffd0000 {
+		compatible = "arm,pl354", "arm,primecell";
+		reg = <0 0x7ffd0000 0 0x1000>;
+		interrupts = <0 86 4>,
+			     <0 87 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	dma@7ffb0000 {
+		compatible = "arm,pl330", "arm,primecell";
+		reg = <0 0x7ffb0000 0 0x1000>;
+		interrupts = <0 92 4>,
+			     <0 88 4>,
+			     <0 89 4>,
+			     <0 90 4>,
+			     <0 91 4>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};
+*/
+/*
+	timer {
+		compatible = "arm,armv7-timer";
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+*/
+
+
+	timer {
+		compatible = "arm,cortex-a15-timer",
+			     "arm,armv7-timer";
+		interrupts = <1 13 0xff01>,
+		             <1 14 0xff01>;
+        clocks = <&oscclk7>;
+        clock-names="apb_pclk";
+	};
+
+
+	/** HACK : cortex-a9-twd-timer hack -- temporary fix */
+	/*timer@2c080000 {
+		compatible = "arm,cortex-a9-twd-timer";
+		reg = <0 0x2c080000 0 0x20>;
+		interrupts = <1 13 0xf04>;
+		clocks = <&oscclk7>;
+		clock-names = "apb_pclk";
+	};*/
+
+	pmu {
+		compatible = "arm,cortex-a15-pmu";
+		interrupts = <0 68 4>,
+			     <0 69 4>;
+	};
+
+	gem5_energy_ctrl@1c080000 {
+		compatible = "arm,gem5-energy-ctrl";
+		reg = <0 0x1c080000 0 0x1C>;
+	};
+
+	dcc {
+		compatible = "arm,vexpress,config-bus";
+		arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+		osc@0 {
+			/* CPU PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 0>;
+			freq-range = <50000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk0";
+		};
+
+		osc@4 {
+			/* Multiplexed AXI master clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 4>;
+			freq-range = <20000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk4";
+		};
+
+		oscclk5: osc@5 {
+			/* HDLCD PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 5>;
+//			freq-range = <23750000 165000000>;	// original
+			freq-range = <23750000 1000000000>;	// for gem5 extended
+								// resolution support
+			#clock-cells = <0>;
+			clock-output-names = "oscclk5";
+		};
+
+		smbclk: osc@6 {
+			/* SMB clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 6>;
+			freq-range = <20000000 50000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk6";
+		};
+
+		oscclk7: osc@7 {
+			/* SYS PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 7>;
+			freq-range = <20000000 60000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk7";
+		};
+
+		osc@8 {
+			/* DDR2 PLL reference clock */
+			compatible = "arm,vexpress-osc";
+			arm,vexpress-sysreg,func = <1 8>;
+			freq-range = <40000000 40000000>;
+			#clock-cells = <0>;
+			clock-output-names = "oscclk8";
+		};
+
+		volt@0 {
+			/* CPU core voltage */
+			compatible = "arm,vexpress-volt";
+			arm,vexpress-sysreg,func = <2 0>;
+			regulator-name = "Cores";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <1050000>;
+			regulator-always-on;
+			label = "Cores";
+		};
+
+		amp@0 {
+			/* Total current for the two cores */
+			compatible = "arm,vexpress-amp";
+			arm,vexpress-sysreg,func = <3 0>;
+			label = "Cores";
+		};
+
+		temp@0 {
+			/* DCC internal temperature */
+			compatible = "arm,vexpress-temp";
+			arm,vexpress-sysreg,func = <4 0>;
+			label = "DCC";
+		};
+
+		power@0 {
+			/* Total power */
+			compatible = "arm,vexpress-power";
+			arm,vexpress-sysreg,func = <12 0>;
+			label = "Cores";
+		};
+
+		energy@0 {
+			/* Total energy */
+			compatible = "arm,vexpress-energy";
+			arm,vexpress-sysreg,func = <13 0>;
+			label = "Cores";
+		};
+	};
+
+	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/ "vexpress-v2m-rs1-gem5.dtsi"
+	};
+};
diff --git a/arch/arm/configs/vexpress_gem5_dvfs_defconfig b/arch/arm/configs/vexpress_gem5_dvfs_defconfig
new file mode 100644
index 0000000..0975514
--- /dev/null
+++ b/arch/arm/configs/vexpress_gem5_dvfs_defconfig
@@ -0,0 +1,204 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SYSVIPC=y
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CGROUPS=y
+CONFIG_CPUSETS=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_PROFILING=y
+CONFIG_TRACEPOINTS=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_VEXPRESS_GEM5=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_MCPM=y
+CONFIG_BIG_LITTLE=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_SCHED_HRTICK=y
+CONFIG_AEABI=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+# CONFIG_ARM_CPU_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_NET_ACTIVITY_STATS is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_ARM_INTEGRATOR=y
+CONFIG_MTD_BLOCK2MTD=y
+CONFIG_MISC_DEVICES=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI_TGT=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_LOWLEVEL
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_HAVE_PATA_PLATFORM=y
+CONFIG_ATA=y
+CONFIG_SATA_PMP=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_SFF=y
+CONFIG_ATA_BMDMA=y
+CONFIG_ATA_PIIX=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMSC911X=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_MOUSE_PS2_TOUCHKIT=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_AMBAKMI=y
+# CONFIG_VT_CONSOLE_SLEEP is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_HWMON is not set
+CONFIG_DISPLAY_TIMING=y
+CONFIG_VIDEOMODE=y
+CONFIG_OF_DISPLAY_TIMING=y
+CONFIG_OF_VIDEOMODE=y
+CONFIG_FB=y
+CONFIG_FB_ARMCLCD=y
+CONFIG_FB_ARMHDLCD=y
+CONFIG_FRAMEBUFFER_CONSOLE=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_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+# CONFIG_SND_DRIVERS is not set
+CONFIG_SND_ARMAACI=y
+# CONFIG_SND_USB is not set
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_MON=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_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_GATOR=m
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_EXT4_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_CRAMFS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_ROOT_NFS=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_NOP_TRACER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_FTRACE=y
+CONFIG_DEBUG_USER=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_BINARY_PRINTF=y
+CONFIG_ARCH_GEM5_ENERGY_CTRL=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_ARM_GEM5_MULTI_CLUSTER_CPUFREQ=y
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index e9bbe99..0fb16ed 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -93,6 +93,16 @@
 	  between the dual cluster test-chip and the M3 microcontroller that
 	  carries out power management.
 
+config ARCH_GEM5_ENERGY_CTRL
+	bool "Gem5 Energy Controller"
+	depends on ARM
+	depends on !ARCH_VEXPRESS_SPC
+	select ARCH_HAS_CPUFREQ
+	select ARCH_HAS_OPP
+	select PM_OPP
+	help
+	  Select options avaiable for the gem5 energy controller related configs
+
 config ARCH_VEXPRESS_TC2_PM
 	bool "Versatile Express TC2 power management"
 	depends on MCPM
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 282134a..5367674 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -29,3 +29,4 @@
 obj-$(CONFIG_SMP)			+= platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)		+= hotplug.o
 obj-$(CONFIG_PCI)			+= pcie_gem5.o
+obj-$(CONFIG_ARCH_GEM5_ENERGY_CTRL)		+= gem5-energy-ctrl.o
diff --git a/arch/arm/mach-vexpress/gem5-energy-ctrl.c b/arch/arm/mach-vexpress/gem5-energy-ctrl.c
new file mode 100644
index 0000000..778ee81
--- /dev/null
+++ b/arch/arm/mach-vexpress/gem5-energy-ctrl.c
@@ -0,0 +1,382 @@
+/*
+ * Gem5 Energy Controller support
+ * (code adapted from vexpress-spc)
+ *
+ * Copyright (C) 2013-2014 ARM Ltd.
+ *
+ * Authors: Akash Bagdia <akash.bagdia@arm.com>
+ *          Vasileios Spiliopoulos <vasileios.spiliopoulos@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/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/gem5_energy_ctrl.h>
+
+// Register addresses
+#define DVFS_HANDLER_STATUS 0x00
+#define DVFS_NUM_DOMAINS 0x04
+#define DVFS_DOMAINID_AT_INDEX 0x08
+#define DVFS_HANDLER_TRANS_LATENCY 0x0C
+#define DOMAIN_ID  0x10
+#define PERF_LEVEL 0x14
+#define PERF_LEVEL_ACK 0x18
+#define NUM_OF_PERF_LEVELS 0x1C
+#define PERF_LEVEL_TO_READ 0x20
+#define FREQ_AT_PERF_LEVEL 0x24
+#define VOLT_AT_PERF_LEVEL 0x28
+
+#define TIME_OUT	100
+#define GEM5_MAX_NUM_DOMAINS	32
+
+struct gem5_energy_ctrl_drvdata {
+	void __iomem *baseaddr;
+	bool dvfs_handler_status;
+	u32 num_gem5_domains;
+	u32 *freqs[GEM5_MAX_NUM_DOMAINS];
+	u32 *voltages[GEM5_MAX_NUM_DOMAINS];
+	int opp_cnt[GEM5_MAX_NUM_DOMAINS];
+	u32 domain_ids[GEM5_MAX_NUM_DOMAINS];
+	spinlock_t lock;
+};
+
+static struct gem5_energy_ctrl_drvdata *info;
+
+static int gem5_energy_ctrl_load_result = -EAGAIN;
+
+static bool gem5_energy_ctrl_initialized(void)
+{
+	return gem5_energy_ctrl_load_result == 0;
+}
+
+static u32 index_of_domain_id(u32 domain_id)
+{
+	u32 i;
+
+	for(i = 0; i < info->num_gem5_domains; i++)
+		if(domain_id == info->domain_ids[i])
+			return i;
+	return GEM5_MAX_NUM_DOMAINS;
+}
+
+u32 gem5_energy_ctrl_get_trans_latency(void)
+{
+	u32 data;
+
+	if (!gem5_energy_ctrl_initialized() || !info->dvfs_handler_status)
+		return -EINVAL;
+
+	spin_lock(&info->lock);
+	data = readl(info->baseaddr + DVFS_HANDLER_TRANS_LATENCY);
+	spin_unlock(&info->lock);
+
+	return data;
+}
+EXPORT_SYMBOL_GPL(gem5_energy_ctrl_get_trans_latency);
+
+/**
+ * gem5_energy_ctrl_get_performance - get current performance level of domain
+ * @domain_id: mpidr[15:8] bitfield describing domain's affinity level
+ * @freq: pointer to the performance level to be assigned
+ *
+ * Return: 0 on success
+ *         < 0 on read error
+ */
+int gem5_energy_ctrl_get_performance(u32 domain_id, u32 *freq)
+{
+	int perf;
+	u32 domain_index;
+
+	if (!gem5_energy_ctrl_initialized() || !info->dvfs_handler_status)
+		return -EINVAL;
+
+	domain_index = index_of_domain_id(domain_id);
+	if(domain_index >= info->num_gem5_domains)
+		return -EINVAL;
+
+	spin_lock(&info->lock);
+	writel(domain_id, info->baseaddr + DOMAIN_ID);
+	perf = readl(info->baseaddr + PERF_LEVEL);
+	spin_unlock(&info->lock);
+	*freq = info->freqs[domain_index][perf];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gem5_energy_ctrl_get_performance);
+
+static int gem5_energy_ctrl_find_perf_index(u32 domain_index, u32 freq)
+{
+	int idx;
+
+	for (idx = 0; idx < info->opp_cnt[domain_index]; idx++)
+		if (info->freqs[domain_index][idx] == freq)
+			return idx;
+
+	return -EINVAL;
+}
+
+
+static inline int read_wait_to(void __iomem *reg, int status, int timeout)
+{
+	while (timeout-- && readl(reg) != status) {
+		cpu_relax();
+		udelay(2);
+	}
+	if (!timeout)
+		return -EAGAIN;
+	else
+		return 0;
+}
+
+/**
+ * gem5_energy_ctrl_set_performance - set current performance level of domain
+ *
+ * @domain_id: mpidr[15:8] bitfield describing domain's affinity level
+ * @freq: performance level to be programmed
+ *
+ * Returns: 0 on success
+ *          < 0 on write error
+ */
+int gem5_energy_ctrl_set_performance(u32 domain_id, u32 freq)
+{
+	int ret, perf;
+	u32 domain_index;
+
+	if (!gem5_energy_ctrl_initialized() || !info->dvfs_handler_status)
+		return -EINVAL;
+
+	domain_index = index_of_domain_id(domain_id);
+	if(domain_index >= info->num_gem5_domains)
+		return -EINVAL;
+
+	spin_lock(&info->lock);
+
+	writel(domain_id, info->baseaddr + DOMAIN_ID);
+
+	perf = gem5_energy_ctrl_find_perf_index(domain_index, freq);
+
+	if (perf < 0)
+		return -EINVAL;
+
+	writel(perf, info->baseaddr + PERF_LEVEL);
+
+	//Some logic to determine successful setting of perf level
+	if (read_wait_to(info->baseaddr + PERF_LEVEL_ACK, 1, TIME_OUT))
+		ret = -EAGAIN;
+
+	spin_unlock(&info->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gem5_energy_ctrl_set_performance);
+
+/**
+ * gem5_energy_ctrl_populate_opps() - initialize opp tables from energy ctrl
+ *
+ * @domain_id: mpidr[15:8] bitfield describing domain's affinity level
+ *
+ * Return: 0 on success
+ *         < 0 on error
+ */
+static int gem5_energy_ctrl_populate_opps(u32 domain_id)
+{
+	u32 data = 0, i;
+	u32 domain_index = index_of_domain_id(domain_id);
+
+	if (!info->dvfs_handler_status ||
+		WARN_ON_ONCE(domain_index >= info->num_gem5_domains))
+		return -EINVAL;
+
+	spin_lock(&info->lock);
+
+	writel(domain_id, info->baseaddr + DOMAIN_ID);
+
+	if (readl(info->baseaddr + DOMAIN_ID) != domain_id) {
+		spin_unlock(&info->lock);
+		return -EINVAL;
+	}
+
+	data = readl(info->baseaddr + NUM_OF_PERF_LEVELS);
+	info->opp_cnt[domain_index] = data;
+
+	info->freqs[domain_index] = kzalloc(sizeof(u32) *
+				info->opp_cnt[domain_index], GFP_KERNEL);
+
+	info->voltages[domain_index] = kzalloc(sizeof(u32) *
+				info->opp_cnt[domain_index], GFP_KERNEL);
+
+	for (i = 0; i < info->opp_cnt[domain_index] ; i++) {
+		writel(i, info->baseaddr + PERF_LEVEL_TO_READ);
+		data = readl(info->baseaddr + FREQ_AT_PERF_LEVEL);
+		info->freqs[domain_index][i] = data;
+		data = readl(info->baseaddr + VOLT_AT_PERF_LEVEL);
+		info->voltages[domain_index][i] = data;
+	}
+
+	spin_unlock(&info->lock);
+
+	return 0;
+}
+
+/**
+ * gem5_energy_ctrl_get_opp_table() - Retrieve a pointer to the frequency,
+ *                voltage tables for a given domain
+ *
+ * @domain_id: mpidr[15:8] bitfield describing domain's affinity level
+ * @fptr: pointer to be initialized
+ * Return: operating points count on success
+ *         -EINVAL on pointer error
+ */
+int gem5_energy_ctrl_get_opp_table(u32 domain_id, u32 **fptr, u32 **vptr)
+{
+	u32 domain_index;
+
+	if (!gem5_energy_ctrl_initialized() ||
+		WARN_ON_ONCE(!fptr || !info->dvfs_handler_status))
+		return -EINVAL;
+
+	domain_index = index_of_domain_id(domain_id);
+	if(domain_index >= info->num_gem5_domains)
+		return -EINVAL;
+
+	*fptr = info->freqs[domain_index];
+	*vptr = info->voltages[domain_index];
+	return info->opp_cnt[domain_index];
+}
+EXPORT_SYMBOL_GPL(gem5_energy_ctrl_get_opp_table);
+
+static const struct of_device_id gem5_energy_ctrl_ids[] __initconst = {
+	{ .compatible = "arm,gem5-energy-ctrl" },
+	{},
+};
+
+static int __init gem5_energy_ctrl_init(void)
+{
+	int ret;
+	u32 i, data;
+	struct device_node *node = of_find_matching_node(NULL,
+							 gem5_energy_ctrl_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->dvfs_handler_status = 0;
+	info->num_gem5_domains = 0;
+
+	info->baseaddr = of_iomap(node, 0);
+	if (WARN_ON(!info->baseaddr)) {
+		ret = -ENXIO;
+		goto mem_free;
+	}
+
+	spin_lock_init(&info->lock);
+
+	info->dvfs_handler_status = readl(info->baseaddr + DVFS_HANDLER_STATUS);
+
+	if (!info->dvfs_handler_status) {
+		pr_info("gem5 DVFS handler is disabled\n");
+	} else {
+		info->num_gem5_domains = readl(info->baseaddr +
+						DVFS_NUM_DOMAINS);
+		if (info->num_gem5_domains > GEM5_MAX_NUM_DOMAINS) {
+			pr_err("gem5 DVFS handler manages more domains than\
+				supported by the gem5 energy controller driver\n");
+			ret = -ENODEV;
+			goto unmap;
+		}
+		else {
+			/* Get domain ID information
+			* Populate operation table for all the domains(clusters)
+			* managed by the controller
+			*/
+			for(i = 0;i < info->num_gem5_domains; i++) {
+				spin_lock(&info->lock);
+				writel(i, info->baseaddr +
+					DVFS_DOMAINID_AT_INDEX);
+				data = readl(info->baseaddr +
+					DVFS_DOMAINID_AT_INDEX);
+				spin_unlock(&info->lock);
+				info->domain_ids[i] = data;
+				if(gem5_energy_ctrl_populate_opps(info->domain_ids[i]))
+				{
+					pr_err("failed to build OPP table for\
+						%d domain\n",
+					info->domain_ids[i]);
+					ret = -ENODEV;
+					goto unmap;
+				}
+			}
+		}
+	}
+	pr_info("gem5-energy-ctrl loaded at %p\n", info->baseaddr);
+	return 0;
+
+unmap:
+	pr_info("gem5-energy-ctrl unmapped at %p, possible error in syncing with "\
+		"the device\n", info->baseaddr);
+	iounmap(info->baseaddr);
+
+mem_free:
+	kfree(info);
+	return ret;
+}
+
+static bool __init __gem5_energy_ctrl_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 (*energy_ctrl_check_loaded)(void) __refdata = \
+     &__gem5_energy_ctrl_check_loaded;
+
+static bool __init __gem5_energy_ctrl_check_loaded(void)
+{
+	if (gem5_energy_ctrl_load_result == -EAGAIN)
+		gem5_energy_ctrl_load_result = gem5_energy_ctrl_init();
+	energy_ctrl_check_loaded = &gem5_energy_ctrl_initialized;
+	return gem5_energy_ctrl_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 gem5_energy_ctrl_check_loaded(void)
+{
+	return energy_ctrl_check_loaded();
+}
+EXPORT_SYMBOL_GPL(gem5_energy_ctrl_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");
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index f7caa2d..7c182d0 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -114,6 +114,12 @@
           Supports clock drivers for Keystone based SOCs. These SOCs have local
 	  a power sleep control module that gate the clock to the IPs and PLLs.
 
+config GEM5_CPUFREQ_CLK
+	bool "Clk driver for GEM5 Platform"
+	---help---
+	  Enable clk hardware for cpufreq in gem5 platforms
+
+
 source "drivers/clk/qcom/Kconfig"
 
 endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index fcfe62d..8953d40 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -31,6 +31,7 @@
 obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
 obj-$(CONFIG_ARCH_BCM_MOBILE)		+= bcm/
+obj-$(CONFIG_GEM5_CPUFREQ_CLK)		+= gem5/
 obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
 obj-$(CONFIG_COMMON_CLK_KEYSTONE)	+= keystone/
diff --git a/drivers/clk/gem5/Makefile b/drivers/clk/gem5/Makefile
new file mode 100644
index 0000000..0af147b
--- /dev/null
+++ b/drivers/clk/gem5/Makefile
@@ -0,0 +1,2 @@
+# Makefile for Gem5-specific clocks
+obj-y	+= clk-gem5-energy-ctrl.o
diff --git a/drivers/clk/gem5/clk-gem5-energy-ctrl.c b/drivers/clk/gem5/clk-gem5-energy-ctrl.c
new file mode 100644
index 0000000..1777de8
--- /dev/null
+++ b/drivers/clk/gem5/clk-gem5-energy-ctrl.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 -2014 ARM Limited
+ * Copyright (C) 2013 Linaro
+ *
+ * Authors: Akash Bagdia <Akash.bagdia@arm.com>
+ *          Vasileios Spiliopoulos <vasileios.spiliopoulos@arm.com>
+ * (code adapted from clk-vexpress-spc.c)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+/* PE Controller clock programming interface for Gem5 Platform cpus */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/gem5_energy_ctrl.h>
+
+struct clk_energy_ctrl {
+	struct clk_hw hw;
+	spinlock_t *lock;
+	int cluster;
+};
+
+#define to_clk_energy_ctrl(ec) container_of(ec, struct clk_energy_ctrl, hw)
+
+static unsigned long energy_ctrl_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_energy_ctrl *energy_ctrl = to_clk_energy_ctrl(hw);
+	u32 freq;
+
+	if (gem5_energy_ctrl_get_performance(energy_ctrl->cluster, &freq)) {
+		return -EIO;
+		pr_err("%s: Failed", __func__);
+	}
+
+	return freq * 1000;
+}
+
+static long energy_ctrl_round_rate(struct clk_hw *hw, unsigned long drate,
+		unsigned long *parent_rate)
+{
+	return drate;
+}
+
+static int energy_ctrl_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct clk_energy_ctrl *energy_ctrl = to_clk_energy_ctrl(hw);
+
+	return gem5_energy_ctrl_set_performance(energy_ctrl->cluster, rate / 1000);
+}
+
+static struct clk_ops clk_energy_ctrl_ops = {
+	.recalc_rate = energy_ctrl_recalc_rate,
+	.round_rate = energy_ctrl_round_rate,
+	.set_rate = energy_ctrl_set_rate,
+};
+
+struct clk *gem5_clk_register_energy_ctrl(const char *name, int cluster_id)
+{
+	struct clk_init_data init;
+	struct clk_energy_ctrl *energy_ctrl;
+	struct clk *clk;
+
+	if (!name) {
+		pr_err("Invalid name passed");
+		return ERR_PTR(-EINVAL);
+	}
+
+	energy_ctrl = kzalloc(sizeof(*energy_ctrl), GFP_KERNEL);
+	if (!energy_ctrl) {
+		pr_err("could not allocate energy_ctrl clk\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	energy_ctrl->hw.init = &init;
+	energy_ctrl->cluster = cluster_id;
+
+	init.name = name;
+	init.ops = &clk_energy_ctrl_ops;
+	init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
+	init.num_parents = 0;
+
+	clk = clk_register(NULL, &energy_ctrl->hw);
+	if (!IS_ERR_OR_NULL(clk))
+		return clk;
+
+	pr_err("clk register failed\n");
+	kfree(energy_ctrl);
+
+	return NULL;
+}
+
+void __init gem5_clk_of_register_energy_ctrl(void)
+{
+	char name[14] = "cpu-cluster.";
+	struct device_node *node = NULL;
+	struct clk *clk;
+	const u32 *val;
+	int cluster_id = 0, len;
+
+	if (!of_find_compatible_node(NULL, NULL, "arm,gem5-energy-ctrl")) {
+		pr_debug("%s: No EC found, Exiting!!\n", __func__);
+		return;
+	}
+
+	while ((node = of_find_node_by_name(node, "cluster"))) {
+		val = of_get_property(node, "reg", &len);
+		if (val && len == 4)
+			cluster_id = be32_to_cpup(val);
+
+		name[12] = cluster_id + '0';
+		clk = gem5_clk_register_energy_ctrl(name, cluster_id);
+		if (IS_ERR(clk))
+			return;
+
+		pr_debug("Registered clock '%s'\n", name);
+		clk_register_clkdev(clk, NULL, name);
+	}
+}
+CLK_OF_DECLARE(energy_ctrl, "arm,gem5-energy-ctrl", gem5_clk_of_register_energy_ctrl);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 3129749..0c2038f 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -16,6 +16,17 @@
 	  This enables probing via DT for Generic CPUfreq driver for ARM
 	  big.LITTLE platform. This gets frequency tables from DT.
 
+config ARM_GEM5_MULTI_CLUSTER_CPUFREQ
+	tristate "Generic ARM Multi-Cluster CPUfreq driver"
+	depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK
+	depends on !ARM_BIG_LITTLE_CPUFREQ
+	depends on ARCH_GEM5_ENERGY_CTRL
+	select CPU_FREQ_TABLE
+	select GEM5_CPUFREQ_CLK
+	help
+	  This enables the Generic CPUfreq driver for ARM gem5 multi-cluster
+	  platforms.
+
 config ARM_EXYNOS_CPUFREQ
 	bool
 
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 0fd80cb..ccfcd3f 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -48,6 +48,9 @@
 # LITTLE drivers, so that it is probed last.
 obj-$(CONFIG_ARM_DT_BL_CPUFREQ)		+= arm_big_little_dt.o
 
+# multi-cluster configuration support for a configurable gem5 platform.
+obj-$(CONFIG_ARM_GEM5_MULTI_CLUSTER_CPUFREQ)	+= arm_gem5_mc.o gem5_energy_ctrl_mc.o
+
 obj-$(CONFIG_ARCH_DAVINCI_DA850)	+= davinci-cpufreq.o
 obj-$(CONFIG_UX500_SOC_DB8500)		+= dbx500-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS_CPUFREQ)	+= exynos-cpufreq.o
diff --git a/drivers/cpufreq/arm_gem5_mc.c b/drivers/cpufreq/arm_gem5_mc.c
new file mode 100644
index 0000000..68b6764
--- /dev/null
+++ b/drivers/cpufreq/arm_gem5_mc.c
@@ -0,0 +1,385 @@
+/*
+ * ARM Multi-Cluster Platforms CPUFreq support for Gem5
+ *
+ * Copyright (C) 2013 -2014 ARM Ltd.
+ *
+ * Akash Bagdia <akash.bagdia@arm.com>
+ * Vasileios Spiliopoulos <vasileios.spiliopoulos@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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/topology.h>
+#include <linux/types.h>
+
+#include "arm_gem5_mc.h"
+
+static struct cpufreq_arm_mc_ops *arm_mc_ops;
+static struct clk *clk[MAX_CLUSTERS];
+static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
+static atomic_t cluster_usage[MAX_CLUSTERS + 1] = {ATOMIC_INIT(0),
+	ATOMIC_INIT(0)};
+
+static DEFINE_PER_CPU(unsigned int, physical_cluster);
+static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq);
+
+static struct mutex cluster_lock[MAX_CLUSTERS];
+
+static unsigned int find_cluster_maxfreq(int cluster)
+{
+	int j;
+	u32 max_freq = 0, cpu_freq;
+
+	for_each_online_cpu(j) {
+		cpu_freq = per_cpu(cpu_last_req_freq, j);
+
+		if ((cluster == per_cpu(physical_cluster, j)) &&
+				(max_freq < cpu_freq))
+			max_freq = cpu_freq;
+	}
+
+	pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster,
+			max_freq);
+
+	return max_freq;
+}
+
+static unsigned int clk_get_cpu_rate(unsigned int cpu)
+{
+	u32 cur_cluster = per_cpu(physical_cluster, cpu);
+	u32 rate = clk_get_rate(clk[cur_cluster]) / 1000;
+
+	pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu,
+			cur_cluster, rate);
+
+	return rate;
+}
+
+static unsigned int mc_cpufreq_get_rate(unsigned int cpu)
+{
+	return clk_get_cpu_rate(cpu);
+}
+
+static unsigned int
+mc_cpufreq_set_rate(u32 cpu, u32 new_cluster, u32 rate)
+{
+	u32 new_rate;
+	int ret;
+
+	mutex_lock(&cluster_lock[new_cluster]);
+
+	new_rate = rate;
+
+	pr_debug("%s: cpu: %d, new cluster: %d, freq: %d\n",
+			__func__, cpu, new_cluster, new_rate);
+
+	ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
+	if (WARN_ON(ret)) {
+		pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
+				new_cluster);
+
+		mutex_unlock(&cluster_lock[new_cluster]);
+
+		return ret;
+	}
+
+	mutex_unlock(&cluster_lock[new_cluster]);
+
+	return 0;
+}
+
+/* Validate policy frequency range */
+static int mc_cpufreq_verify_policy(struct cpufreq_policy *policy)
+{
+	u32 cur_cluster = cpu_to_cluster(policy->cpu);
+
+	return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]);
+}
+
+/* Set clock frequency */
+static int mc_cpufreq_set_target(struct cpufreq_policy *policy,
+		unsigned int target_freq, unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	u32 cpu = policy->cpu, freq_tab_idx, cur_cluster, new_cluster;
+	int ret = 0;
+
+	cur_cluster = cpu_to_cluster(cpu);
+	new_cluster = per_cpu(physical_cluster, cpu);
+
+	freqs.old = mc_cpufreq_get_rate(cpu);
+
+	/* Determine valid target frequency using freq_table */
+	cpufreq_frequency_table_target(policy, freq_table[cur_cluster],
+			target_freq, relation, &freq_tab_idx);
+	freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency;
+
+	pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n",
+			__func__, cpu, cur_cluster, freqs.old, target_freq,
+			freqs.new);
+
+	if (freqs.old == freqs.new)
+		return 0;
+
+	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+
+	ret = mc_cpufreq_set_rate(cpu, new_cluster, freqs.new);
+	if (ret)
+		freqs.new = freqs.old;
+
+	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+
+	return ret;
+}
+
+static inline u32 get_table_count(struct cpufreq_frequency_table *table)
+{
+	int count;
+
+	for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
+		;
+
+	return count;
+}
+
+/* get the minimum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_min(struct cpufreq_frequency_table *table)
+{
+	int i;
+	uint32_t min_freq = ~0;
+	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+		if (table[i].frequency < min_freq)
+			min_freq = table[i].frequency;
+	return min_freq;
+}
+
+/* get the maximum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_max(struct cpufreq_frequency_table *table)
+{
+	int i;
+	uint32_t max_freq = 0;
+	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+		if (table[i].frequency > max_freq)
+			max_freq = table[i].frequency;
+	return max_freq;
+}
+
+static void _put_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+	u32 cluster = cpu_to_cluster(cpu_dev->id);
+
+	if (!atomic_dec_return(&cluster_usage[cluster])) {
+		clk_put(clk[cluster]);
+		dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
+		dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
+	}
+}
+
+static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+	u32 cluster = cpu_to_cluster(cpu_dev->id);
+
+	if (cluster < MAX_CLUSTERS)
+		return _put_cluster_clk_and_freq_table(cpu_dev);
+}
+
+static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+	u32 cluster = cpu_to_cluster(cpu_dev->id);
+	char name[14] = "cpu-cluster.";
+	int ret;
+
+	if (atomic_inc_return(&cluster_usage[cluster]) != 1)
+		return 0;
+
+	ret = arm_mc_ops->init_opp_table(cpu_dev);
+	if (ret) {
+		dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
+				__func__, cpu_dev->id, ret);
+		goto atomic_dec;
+	}
+
+	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
+	if (ret) {
+		dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
+				__func__, cpu_dev->id, ret);
+		goto atomic_dec;
+	}
+
+	name[12] = cluster + '0';
+	clk[cluster] = clk_get_sys(name, NULL);
+	if (!IS_ERR(clk[cluster])) {
+		dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
+				__func__, clk[cluster], freq_table[cluster],
+				cluster);
+		return 0;
+	}
+
+	dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
+			__func__, cpu_dev->id, cluster);
+	ret = PTR_ERR(clk[cluster]);
+	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
+
+atomic_dec:
+	atomic_dec(&cluster_usage[cluster]);
+	dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
+			cluster);
+	return ret;
+}
+
+static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+	u32 cluster = cpu_to_cluster(cpu_dev->id);
+
+	if (cluster < MAX_CLUSTERS)
+		return _get_cluster_clk_and_freq_table(cpu_dev);
+
+	return 0;
+}
+
+/* Per-CPU initialization */
+static int mc_cpufreq_init(struct cpufreq_policy *policy)
+{
+	u32 cur_cluster = cpu_to_cluster(policy->cpu);
+	struct device *cpu_dev;
+	int ret;
+
+	cpu_dev = get_cpu_device(policy->cpu);
+	if (!cpu_dev) {
+		pr_err("%s: failed to get cpu%d device\n", __func__,
+				policy->cpu);
+		return -ENODEV;
+	}
+
+	ret = get_cluster_clk_and_freq_table(cpu_dev);
+	if (ret)
+		return ret;
+
+	ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
+	if (ret) {
+		dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
+				policy->cpu, cur_cluster);
+		put_cluster_clk_and_freq_table(cpu_dev);
+		return ret;
+	}
+
+	cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
+
+	if (cur_cluster < MAX_CLUSTERS) {
+		cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
+
+		per_cpu(physical_cluster, policy->cpu) = cur_cluster;
+	} else {
+		pr_err("Invalid current cluster %d\n", cur_cluster);
+		return -ENODEV;
+	}
+
+	if (arm_mc_ops->get_transition_latency)
+		policy->cpuinfo.transition_latency =
+			arm_mc_ops->get_transition_latency(cpu_dev);
+	else
+		policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+	policy->cur = clk_get_cpu_rate(policy->cpu);
+
+	dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
+	return 0;
+}
+
+/* Export freq_table to sysfs */
+static struct freq_attr *mc_cpufreq_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+static struct cpufreq_driver mc_cpufreq_driver = {
+	.name			= "arm-gem5-mc",
+	.flags			= CPUFREQ_STICKY |
+					CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
+	.verify			= mc_cpufreq_verify_policy,
+	.target			= mc_cpufreq_set_target,
+	.get			= mc_cpufreq_get_rate,
+	.init			= mc_cpufreq_init,
+	.attr			= mc_cpufreq_attr,
+};
+
+int mc_cpufreq_register(struct cpufreq_arm_mc_ops *ops)
+{
+	int ret, i;
+
+	if (arm_mc_ops) {
+		pr_debug("%s: Already registered: %s, exiting\n", __func__,
+				arm_mc_ops->name);
+		return -EBUSY;
+	}
+
+	if (!ops || !strlen(ops->name) || !ops->init_opp_table) {
+		pr_err("%s: Invalid arm_mc_ops, exiting\n", __func__);
+		return -ENODEV;
+	}
+
+	arm_mc_ops = ops;
+
+	for (i = 0; i < MAX_CLUSTERS; i++)
+		mutex_init(&cluster_lock[i]);
+
+	ret = cpufreq_register_driver(&mc_cpufreq_driver);
+	if (ret) {
+		pr_info("%s: Failed registering platform driver: %s, err: %d\n",
+				__func__, ops->name, ret);
+		arm_mc_ops = NULL;
+	} else {
+		pr_info("%s: Registered platform driver: %s\n",
+				__func__, ops->name);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mc_cpufreq_register);
+
+void mc_cpufreq_unregister(struct cpufreq_arm_mc_ops *ops)
+{
+	if (arm_mc_ops != ops) {
+		pr_err("%s: Registered with: %s, can't unregister, exiting\n",
+				__func__, arm_mc_ops->name);
+		return;
+	}
+
+	cpufreq_unregister_driver(&mc_cpufreq_driver);
+	pr_info("%s: Un-registered platform driver: %s\n", __func__,
+			arm_mc_ops->name);
+
+	int i;
+
+	for (i = 0; i < MAX_CLUSTERS; i++) {
+		struct device *cdev = get_cpu_device(i);
+		if (!cdev) {
+			pr_err("%s: failed to get cpu%d device\n",
+					__func__, i);
+			return;
+		}
+
+		put_cluster_clk_and_freq_table(cdev);
+	}
+
+	arm_mc_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(mc_cpufreq_unregister);
diff --git a/drivers/cpufreq/arm_gem5_mc.h b/drivers/cpufreq/arm_gem5_mc.h
new file mode 100644
index 0000000..77fa767
--- /dev/null
+++ b/drivers/cpufreq/arm_gem5_mc.h
@@ -0,0 +1,47 @@
+/*
+ * ARM GEM5 Multi-cluster platform's CPUFreq header file
+ *
+ * Copyright (C) 2013 - 2014 ARM Ltd.
+ *
+ * Authors: Akash Bagdia <Akash.bagdia@arm.com>
+ *          Vasileios Spiliopoulos <vasileios.spiliopoulos@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.
+ */
+#ifndef CPUFREQ_ARM_GEM5_MC_H
+#define CPUFREQ_ARM_GEM5_MC_H
+
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+/* Currently we support n=32 clusters */
+#define MAX_CLUSTERS	32
+
+struct cpufreq_arm_mc_ops {
+	char name[CPUFREQ_NAME_LEN];
+	int (*get_transition_latency)(struct device *cpu_dev);
+
+	/*
+	 * This must set opp table for cpu_dev in a similar way as done by
+	 * of_init_opp_table().
+	 */
+	int (*init_opp_table)(struct device *cpu_dev);
+};
+
+static inline int cpu_to_cluster(int cpu)
+{
+	return topology_physical_package_id(cpu);
+}
+
+int mc_cpufreq_register(struct cpufreq_arm_mc_ops *ops);
+void mc_cpufreq_unregister(struct cpufreq_arm_mc_ops *ops);
+
+#endif /* CPUFREQ_ARM_GEM5_MC_H */
diff --git a/drivers/cpufreq/gem5_energy_ctrl_mc.c b/drivers/cpufreq/gem5_energy_ctrl_mc.c
new file mode 100644
index 0000000..6f947ff
--- /dev/null
+++ b/drivers/cpufreq/gem5_energy_ctrl_mc.c
@@ -0,0 +1,95 @@
+/*
+ * Gem5 Multi-cluster CPUFreq Interface driver
+ * (adapted from vexpress_big_little.c)
+ *
+ * It provides necessary opp's to arm_gem5_mc.c cpufreq driver and gets
+ * frequency information from gem5 energy controller device.
+ *
+ * Copyright (C) 2013 - 2014 ARM Ltd.
+ * Authors: Akash Bagdia <Akash.bagdia@arm.com>
+ *          Vasileios Spiliopoulos <vasileios.spiliopoulos@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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpufreq.h>
+#include <linux/export.h>
+#include <linux/pm_opp.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/gem5_energy_ctrl.h>
+#include "arm_gem5_mc.h"
+
+static int gem5_init_opp_table(struct device *cpu_dev)
+{
+	int i = -1, count, cluster = cpu_to_cluster(cpu_dev->id);
+	u32 *freq_table;
+	u32 *volt_table; /* In micro volts */
+	int ret;
+
+	count = gem5_energy_ctrl_get_opp_table(cluster, &freq_table, &volt_table);
+	if (!freq_table || !count) {
+		pr_err("gem5 energy controller returned invalid freq table");
+		return -EINVAL;
+	}
+
+	if (!volt_table || !count) {
+		pr_err("gem5 energy controller returned invalid voltage table");
+		return -EINVAL;
+	}
+
+	while (++i < count) {
+		ret = dev_pm_opp_add(cpu_dev, freq_table[i] * 1000,
+			volt_table[i]);
+		if (ret) {
+			dev_warn(cpu_dev,
+				"%s: Failed to add OPP freq %d, u-voltage %d,\
+				 err: %d\n",
+				 __func__, freq_table[i] * 1000,
+				 volt_table[i], ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int gem5_get_transition_latency(struct device *cpu_dev)
+{
+	return gem5_energy_ctrl_get_trans_latency();
+}
+
+static struct cpufreq_arm_mc_ops gem5_mc_ops = {
+	.name	= "gem5-mc",
+	.get_transition_latency = gem5_get_transition_latency,
+	.init_opp_table = gem5_init_opp_table,
+};
+
+static int gem5_mc_init(void)
+{
+	if (!gem5_energy_ctrl_check_loaded()) {
+		pr_info("%s: No energy controller found\n", __func__);
+		return -ENOENT;
+	}
+
+	return mc_cpufreq_register(&gem5_mc_ops);
+}
+module_init(gem5_mc_init);
+
+static void gem5_mc_exit(void)
+{
+	return mc_cpufreq_unregister(&gem5_mc_ops);
+}
+module_exit(gem5_mc_exit);
+
+MODULE_DESCRIPTION("ARM gem5 multi-cluster cpufreq driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/gem5_energy_ctrl.h b/include/linux/gem5_energy_ctrl.h
new file mode 100644
index 0000000..1dc4943
--- /dev/null
+++ b/include/linux/gem5_energy_ctrl.h
@@ -0,0 +1,27 @@
+/*
+ * 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) 2013 - 2014 ARM Limited
+ * Authors: Akash Bagdia <Akash.bagdia@arm.com>
+ *          Vasileios Spiliopoulos <vasileios.spiliopoulos@arm.com>
+ */
+
+#ifndef _LINUX_GEM5_ENERGY_CTRL_H
+#define _LINUX_GEM5_ENERGY_CTRL_H
+
+/* Energy Controller */
+
+extern bool gem5_energy_ctrl_check_loaded(void);
+extern u32 gem5_energy_ctrl_get_trans_latency(void);
+extern int gem5_energy_ctrl_get_opp_table(u32, u32 **, u32 **);
+extern int gem5_energy_ctrl_get_performance(u32, u32 *);
+extern int gem5_energy_ctrl_set_performance(u32, u32);
+
+#endif