[RFC/PATCH v2] ARM: PRIMA2: initial support for SiRFmarco dual-core SoC

Barry Song Barry.Song at csr.com
Mon Sep 10 23:42:57 EDT 2012


SiRFmarco is a dual-core cortex-A9 SoC. This patch adds initial support for it.
It is only a RFC for early review from community. I'd like to split it into
multiple patches later.
change list:
1. Marco has different OS timer hardware with Prima2, so add a new timer-marco
timer0 and timer1 of Marco work as the local timer of CPU0 and CPU1
2. add platsmp.c, headsmp.S and hotplug.c for MPcore support
3. some hardwares have changed, like rstc, so use of_compatible to branch Prima2 and Marco
4. add initial .dtsi for Marco SoC and initial .dts for the EVB
5. some hardware support both Prima2 and Marco, add marco stuff to of_match tables
6. use GIC for Marco instead of Prima2's IRQ controller
7. add DEBUG_LL uart ports for Prima2 and Marco debug ports
8. make uniprocess PrimaII work on SMP kernel(add handle_irq entry for prima2 machine),
then all SiRF SoCs can use same defconfig and zImage

Signed-off-by: Barry Song <Barry.Song at csr.com>
---
 -v2: 
 make uniprocess prima2 work on SMP kernel(add handle_irq entry in old irqchip driver);
 add LOCAL_TIMER for CPU1 to make both cores has NO_HZ and HIGHRES_TIMER;
 use the only prima2_defconfig for both prima2 and marco;
 fix windows to 16MB for Marco's iobrg address ranges;
 set "disable" status for ports in .dtsi and "okay" for enabled ports in dts; 
 some other minor cleanup.
 other issues:
 smp_ops is not used yet(want to handle after 3.6);
 timer drivers not moved to drivers/clocksource/(want to handle after 3.6) 

 arch/arm/Kconfig                                 |    3 +-
 arch/arm/Kconfig.debug                           |   14 +
 arch/arm/Makefile                                |    2 +-
 arch/arm/boot/dts/marco-evb.dts                  |   28 ++
 arch/arm/boot/dts/marco.dtsi                     |  514 ++++++++++++++++++++++
 arch/arm/configs/prima2_defconfig                |    2 +
 arch/arm/mach-prima2/Kconfig                     |   14 +
 arch/arm/mach-prima2/Makefile                    |    5 +-
 arch/arm/mach-prima2/Makefile.boot               |    1 +
 arch/arm/mach-prima2/common.c                    |   47 ++-
 arch/arm/mach-prima2/common.h                    |   11 +-
 arch/arm/mach-prima2/headsmp.S                   |   79 ++++
 arch/arm/mach-prima2/hotplug.c                   |   57 +++
 arch/arm/mach-prima2/include/mach/uart.h         |    6 +
 arch/arm/mach-prima2/include/mach/uncompress.h   |    3 +
 arch/arm/mach-prima2/irq.c                       |   16 +-
 arch/arm/mach-prima2/l2x0.c                      |   19 +-
 arch/arm/mach-prima2/platsmp.c                   |  154 +++++++
 arch/arm/mach-prima2/pm.c                        |    1 +
 arch/arm/mach-prima2/rstc.c                      |   38 ++-
 arch/arm/mach-prima2/rtciobrg.c                  |    1 +
 arch/arm/mach-prima2/timer-marco.c               |  374 ++++++++++++++++
 arch/arm/mach-prima2/{timer.c => timer-prima2.c} |    6 +-
 23 files changed, 1369 insertions(+), 26 deletions(-)
 create mode 100644 arch/arm/boot/dts/marco-evb.dts
 create mode 100644 arch/arm/boot/dts/marco.dtsi
 create mode 100644 arch/arm/mach-prima2/headsmp.S
 create mode 100644 arch/arm/mach-prima2/hotplug.c
 create mode 100644 arch/arm/mach-prima2/platsmp.c
 create mode 100644 arch/arm/mach-prima2/timer-marco.c
 rename arch/arm/mach-prima2/{timer.c => timer-prima2.c} (98%)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a2be386..6a9cd14 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -418,8 +418,9 @@ config ARCH_SIRF
 	select PINCTRL
 	select PINCTRL_SIRF
 	select USE_OF
+	select AUTO_ZRELADDR
 	help
-	  Support for CSR SiRFprimaII/Marco/Polo platforms
+	 Support for CSR SiRFprimaII/Marco/Polo platforms
 
 config ARCH_EBSA110
 	bool "EBSA-110"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index f15f82b..2e63c78 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -310,6 +310,20 @@ choice
 		  The uncompressor code port configuration is now handled
 		  by CONFIG_S3C_LOWLEVEL_UART_PORT.
 
+	config DEBUG_SIRFPRIMA2_UART1
+		bool "Kernel low-level debugging messages via SiRFprimaII UART1"
+		depends on ARCH_PRIMA2
+		help
+		  Say Y here if you want the debug print routines to direct
+		  their output to the uart1 port on SiRFprimaII devices.
+
+	config DEBUG_SIRFMARCO_UART1
+		bool "Kernel low-level debugging messages via SiRFmarco UART1"
+		depends on ARCH_MARCO
+		help
+		  Say Y here if you want the debug print routines to direct
+		  their output to the uart1 port on SiRFmarco devices.
+
 	config DEBUG_VEXPRESS_UART0_DETECT
 		bool "Autodetect UART0 on Versatile Express Cortex-A core tiles"
 		depends on ARCH_VEXPRESS && CPU_CP15_MMU
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 30eae87..6fbec07 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -168,7 +168,6 @@ machine-$(CONFIG_ARCH_OMAP2PLUS)	:= omap2
 machine-$(CONFIG_ARCH_ORION5X)		:= orion5x
 machine-$(CONFIG_ARCH_PICOXCELL)	:= picoxcell
 machine-$(CONFIG_ARCH_PNX4008)		:= pnx4008
-machine-$(CONFIG_ARCH_PRIMA2)		:= prima2
 machine-$(CONFIG_ARCH_PXA)		:= pxa
 machine-$(CONFIG_ARCH_REALVIEW)		:= realview
 machine-$(CONFIG_ARCH_RPC)		:= rpc
@@ -182,6 +181,7 @@ machine-$(CONFIG_ARCH_EXYNOS5)		:= exynos
 machine-$(CONFIG_ARCH_SA1100)		:= sa1100
 machine-$(CONFIG_ARCH_SHARK)		:= shark
 machine-$(CONFIG_ARCH_SHMOBILE) 	:= shmobile
+machine-$(CONFIG_ARCH_SIRF)		:= prima2
 machine-$(CONFIG_ARCH_TEGRA)		:= tegra
 machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
diff --git a/arch/arm/boot/dts/marco-evb.dts b/arch/arm/boot/dts/marco-evb.dts
new file mode 100644
index 0000000..29644d3
--- /dev/null
+++ b/arch/arm/boot/dts/marco-evb.dts
@@ -0,0 +1,28 @@
+/*
+ * DTS file for CSR SiRFmarco Evaluation Board
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/dts-v1/;
+
+/include/ "marco.dtsi"
+
+/ {
+	model = "CSR SiRFmarco Evaluation Board";
+	compatible = "sirf,marco", "sirf,marco-cb";
+
+	memory {
+		reg = <0x40000000 0x60000000>;
+	};
+
+	axi {
+		peri-iobg {
+		uart1: uart at cc060000 {
+				status = "okay";
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/marco.dtsi b/arch/arm/boot/dts/marco.dtsi
new file mode 100644
index 0000000..ac8d7d2
--- /dev/null
+++ b/arch/arm/boot/dts/marco.dtsi
@@ -0,0 +1,514 @@
+/*
+ * DTS file for CSR SiRFmarco SoC
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/include/ "skeleton.dtsi"
+/ {
+	compatible = "sirf,marco";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&gic>;
+
+	cpus {
+		cpu at 0 {
+			compatible = "arm,cortex-a9";
+		};
+		cpu at 1 {
+			compatible = "arm,cortex-a9";
+		};
+	};
+
+	axi {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x40000000 0x40000000 0xa0000000>;
+
+		l2-cache-controller at c0030000 {
+			compatible = "arm,pl310-cache", "sirf,marco-pl310-cache";
+			reg = <0xc0030000 0x1000>;
+			interrupts = <0 59 0>;
+			arm,tag-latency = <1 1 1>;
+			arm,data-latency = <1 1 1>;
+			arm,filter-ranges = <0x40000000 0xc0000000>;
+		};
+
+                gic: interrupt-controller at c0011000 {
+			compatible = "arm,cortex-a9-gic";
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			reg = <0xc0011000 0x1000>,
+			      <0xc0010100 0x0100>;
+                };
+
+		 rstc-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc2000000 0xc2000000 0x1000000>;
+
+			reset-controller at c2000000 {
+				compatible = "sirf,marco-rstc";
+				reg = <0xc2000000 0x10000>;
+			};
+		};
+
+		sys-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc3000000 0xc3000000 0x1000000>;
+
+			clock-controller at c3000000 {
+				compatible = "sirf,marco-clkc";
+				reg = <0xc3000000 0x1000>;
+				interrupts = <0 3 0>;
+			};
+
+			rsc-controller at c3010000 {
+				compatible = "sirf,marco-rsc";
+				reg = <0xc3010000 0x1000>;
+			};
+		};
+
+		mem-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc4000000 0xc4000000 0x1000000>;
+
+			memory-controller at c4000000 {
+				compatible = "sirf,marco-memc";
+				reg = <0xc4000000 0x10000>;
+				interrupts = <0 27 0>;
+			};
+		};
+
+		disp-iobg0 {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc5000000 0xc5000000 0x1000000>;
+
+			display0 at c5000000 {
+				compatible = "sirf,marco-lcd";
+				reg = <0xc5000000 0x10000>;
+				interrupts = <0 30 0>;
+			};
+
+			vpp0 at c5010000 {
+				compatible = "sirf,marco-vpp";
+				reg = <0xc5010000 0x10000>;
+				interrupts = <0 31 0>;
+			};
+		};
+
+		disp-iobg1 {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc6000000 0xc6000000 0x1000000>;
+
+			display1 at c6000000 {
+				compatible = "sirf,marco-lcd";
+				reg = <0xc6000000 0x10000>;
+				interrupts = <0 62 0>;
+			};
+
+			vpp1 at c6010000 {
+				compatible = "sirf,marco-vpp";
+				reg = <0xc6010000 0x10000>;
+				interrupts = <0 63 0>;
+			};
+		};
+
+		graphics-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc8000000 0xc8000000 0x1000000>;
+
+			graphics at c8000000 {
+				compatible = "powervr,sgx540";
+				reg = <0xc8000000 0x1000000>;
+				interrupts = <0 6 0>;
+			};
+		};
+
+		multimedia-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xc9000000 0xc9000000 0x1000000>;
+
+			multimedia at a0000000 {
+				compatible = "sirf,marco-video-codec";
+				reg = <0xc9000000 0x1000000>;
+				interrupts = <0 5 0>;
+			};
+		};
+
+		dsp-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xca000000 0xca000000 0x2000000>;
+
+			dspif at ca000000 {
+				compatible = "sirf,marco-dspif";
+				reg = <0xca000000 0x10000>;
+				interrupts = <0 9 0>;
+			};
+
+			gps at ca010000 {
+				compatible = "sirf,marco-gps";
+				reg = <0xca010000 0x10000>;
+				interrupts = <0 7 0>;
+			};
+
+			dsp at cb000000 {
+				compatible = "sirf,marco-dsp";
+				reg = <0xcb000000 0x1000000>;
+				interrupts = <0 8 0>;
+			};
+		};
+
+		peri-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xcc000000 0xcc000000 0x1000000>;
+
+			timer at cc020000 {
+				compatible = "sirf,marco-tick";
+				reg = <0xcc020000 0x1000>;
+				interrupts = <0 0 0
+					      0 1 0
+					      0 2 0
+					      0 49 0
+					      0 50 0
+					      0 51 0>;
+			};
+
+			nand at cc030000 {
+				compatible = "sirf,marco-nand";
+				reg = <0xcc030000 0x10000>;
+				interrupts = <0 41 0>;
+			};
+
+			audio at cc040000 {
+				compatible = "sirf,marco-audio";
+				reg = <0xcc040000 0x10000>;
+				interrupts = <0 35 0>;
+			};
+
+			uart0: uart at cc050000 {
+				cell-index = <0>;
+				compatible = "sirf,marco-uart";
+				reg = <0xcc050000 0x1000>;
+				interrupts = <0 17 0>;
+				fifosize = <128>;
+				status = "disabled";
+			};
+
+			uart1: uart at cc060000 {
+				cell-index = <1>;
+				compatible = "sirf,marco-uart";
+				reg = <0xcc060000 0x1000>;
+				interrupts = <0 18 0>;
+				fifosize = <32>;
+				status = "disabled";
+			};
+
+			uart2: uart at cc070000 {
+				cell-index = <2>;
+				compatible = "sirf,marco-uart";
+				reg = <0xcc070000 0x1000>;
+				interrupts = <0 19 0>;
+				fifosize = <128>;
+				status = "disabled";
+			};
+
+			uart3: uart at cc190000 {
+				cell-index = <3>;
+				compatible = "sirf,marco-uart";
+				reg = <0xcc190000 0x1000>;
+				interrupts = <0 66 0>;
+				fifosize = <128>;
+				status = "disabled";
+			};
+
+			uart4: uart at cc1a0000 {
+				cell-index = <4>;
+				compatible = "sirf,marco-uart";
+				reg = <0xcc1a0000 0x1000>;
+				interrupts = <0 69 0>;
+				fifosize = <128>;
+				status = "disabled";
+			};
+
+			usp0: usp at cc080000 {
+				cell-index = <0>;
+				compatible = "sirf,marco-usp";
+				reg = <0xcc080000 0x10000>;
+				interrupts = <0 20 0>;
+				status = "disabled";
+			};
+
+			usp1: usp at cc090000 {
+				cell-index = <1>;
+				compatible = "sirf,marco-usp";
+				reg = <0xcc090000 0x10000>;
+				interrupts = <0 21 0>;
+				status = "disabled";
+			};
+
+			usp2: usp at cc0a0000 {
+				cell-index = <2>;
+				compatible = "sirf,marco-usp";
+				reg = <0xcc0a0000 0x10000>;
+				interrupts = <0 22 0>;
+				status = "disabled";
+			};
+
+			dmac0: dma-controller at cc0b0000 {
+				cell-index = <0>;
+				compatible = "sirf,marco-dmac";
+				reg = <0xcc0b0000 0x10000>;
+				interrupts = <0 12 0>;
+			};
+
+			dmac1: dma-controller at cc160000 {
+				cell-index = <1>;
+				compatible = "sirf,marco-dmac";
+				reg = <0xcc160000 0x10000>;
+				interrupts = <0 13 0>;
+			};
+
+			vip at cc0c0000 {
+				compatible = "sirf,marco-vip";
+				reg = <0xcc0c0000 0x10000>;
+			};
+
+			spi0: spi at cc0d0000 {
+				cell-index = <0>;
+				compatible = "sirf,marco-spi";
+				reg = <0xcc0d0000 0x10000>;
+				interrupts = <0 15 0>;
+				sirf,spi-num-chipselects = <1>;
+				cs-gpios = <&gpio 0 0>;
+				status = "disabled";
+			};
+
+			spi1: spi at cc170000 {
+				cell-index = <1>;
+				compatible = "sirf,marco-spi";
+				reg = <0xcc170000 0x10000>;
+				interrupts = <0 16 0>;
+				sirf,spi-num-chipselects = <1>;
+				cs-gpios = <&gpio 0 0>;
+				status = "disabled";
+			};
+
+			i2c0: i2c at cc0e0000 {
+				cell-index = <0>;
+				compatible = "sirf,marco-i2c";
+				reg = <0xcc0e0000 0x10000>;
+				interrupts = <0 24 0>;
+				status = "disabled";
+			};
+
+			i2c1: i2c at cc0f0000 {
+				cell-index = <1>;
+				compatible = "sirf,marco-i2c";
+				reg = <0xcc0f0000 0x10000>;
+				interrupts = <0 25 0>;
+				status = "disabled";
+			};
+
+			tsc at cc110000 {
+				compatible = "sirf,marco-tsc";
+				reg = <0xcc110000 0x10000>;
+				interrupts = <0 33 0>;
+			};
+
+			gpio: gpio-controller at cc120000 {
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				compatible = "sirf,marco-gpio-pinmux";
+				reg = <0xcc120000 0x10000>;
+				interrupts = <0 43 0>,
+					   <0 44 0>,
+					   <0 45 0>,
+					   <0 46 0>,
+					   <0 47 0>;
+				gpio-controller;
+				interrupt-controller;
+			};
+
+			pwm at cc130000 {
+				compatible = "sirf,marco-pwm";
+				reg = <0xcc130000 0x10000>;
+			};
+
+			efusesys at cc140000 {
+				compatible = "sirf,marco-efuse";
+				reg = <0xcc140000 0x10000>;
+			};
+
+			pulsec at cc150000 {
+				compatible = "sirf,marco-pulsec";
+				reg = <0xcc150000 0x10000>;
+				interrupts = <0 48 0>;
+			};
+
+			pci-iobg {
+				compatible = "sirf,marco-pciiobg", "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0xcd000000 0xcd000000 0x1000000>;
+
+				sd0: sdhci at cd000000 {
+					cell-index = <0>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd000000 0x100000>;
+					interrupts = <0 38 0>;
+				};
+
+				sd1: sdhci at cd100000 {
+					cell-index = <1>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd100000 0x100000>;
+					interrupts = <0 38 0>;
+				};
+
+				sd2: sdhci at cd200000 {
+					cell-index = <2>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd200000 0x100000>;
+					interrupts = <0 23 0>;
+				};
+
+				sd3: sdhci at cd300000 {
+					cell-index = <3>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd300000 0x100000>;
+					interrupts = <0 23 0>;
+				};
+
+				sd4: sdhci at cd400000 {
+					cell-index = <4>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd400000 0x100000>;
+					interrupts = <0 39 0>;
+				};
+
+				sd5: sdhci at cd500000 {
+					cell-index = <5>;
+					compatible = "sirf,marco-sdhc";
+					reg = <0xcd500000 0x100000>;
+					interrupts = <0 39 0>;
+				};
+
+				pci-copy at cd900000 {
+					compatible = "sirf,marco-pcicp";
+					reg = <0xcd900000 0x100000>;
+					interrupts = <0 40 0>;
+				};
+
+				rom-interface at cda00000 {
+					compatible = "sirf,marco-romif";
+					reg = <0xcda00000 0x100000>;
+				};
+			};
+		};
+
+		rtc-iobg {
+			compatible = "sirf,marco-rtciobg", "sirf-marco-rtciobg-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			reg = <0xc1000000 0x10000>;
+
+			gpsrtc at 1000 {
+				compatible = "sirf,marco-gpsrtc";
+				reg = <0x1000 0x1000>;
+				interrupts = <0 55 0>,
+					   <0 56 0>,
+					   <0 57 0>;
+			};
+
+			sysrtc at 2000 {
+				compatible = "sirf,marco-sysrtc";
+				reg = <0x2000 0x1000>;
+				interrupts = <0 52 0>,
+					   <0 53 0>,
+					   <0 54 0>;
+			};
+
+			pwrc at 3000 {
+				compatible = "sirf,marco-pwrc";
+				reg = <0x3000 0x1000>;
+				interrupts = <0 32 0>;
+			};
+		};
+
+		uus-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xce000000 0xce000000 0x1000000>;
+
+			usb0: usb at ce000000 {
+				compatible = "chipidea,ci13611a-marco";
+				reg = <0xce000000 0x10000>;
+				interrupts = <0 10 0>;
+			};
+
+			usb1: usb at ce010000 {
+				compatible = "chipidea,ci13611a-marco";
+				reg = <0xce010000 0x10000>;
+				interrupts = <0 11 0>;
+			};
+
+			security at ce020000 {
+				compatible = "sirf,marco-security";
+				reg = <0xce020000 0x10000>;
+				interrupts = <0 42 0>;
+			};
+		};
+
+		can-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xd0000000 0xd0000000 0x1000000>;
+
+			can0: can at d0000000 {
+				compatible = "sirf,marco-can";
+				reg = <0xd0000000 0x10000>;
+			};
+
+			can1: can at d0010000 {
+				compatible = "sirf,marco-can";
+				reg = <0xd0010000 0x10000>;
+			};
+		};
+
+		lvds-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xd1000000 0xd1000000 0x1000000>;
+
+			lvds at d1000000 {
+				compatible = "sirf,marco-lvds";
+				reg = <0xd1000000 0x10000>;
+				interrupts = <0 64 0>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig
index 807d4e2..8fd8a56 100644
--- a/arch/arm/configs/prima2_defconfig
+++ b/arch/arm/configs/prima2_defconfig
@@ -11,6 +11,8 @@ CONFIG_PARTITION_ADVANCED=y
 CONFIG_BSD_DISKLABEL=y
 CONFIG_SOLARIS_X86_PARTITION=y
 CONFIG_ARCH_SIRF=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_SMP=y
 CONFIG_PREEMPT=y
 CONFIG_AEABI=y
 CONFIG_KEXEC=y
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index 41fc853..1ad4dd3 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -11,9 +11,23 @@ config ARCH_PRIMA2
 	help
           Support for CSR SiRFSoC ARM Cortex A9 Platform
 
+config ARCH_MARCO
+	bool "CSR SiRFSoC MARCO ARM Cortex A9 Platform"
+	default y
+	select CPU_V7
+	select HAVE_SMP
+	select SMP_ON_UP
+	select ARM_GIC
+	select SIRFMARCO_FPGA
+	help
+          Support for CSR SiRFSoC ARM Cortex A9 Platform
+
 endmenu
 
 config SIRF_IRQ
 	bool
 
+config SIRFMARCO_FPGA
+	bool "CSR SiRFmarco FPGA with 26Mhz io clock"
+
 endif
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile
index fc9ce22..bfe360c 100644
--- a/arch/arm/mach-prima2/Makefile
+++ b/arch/arm/mach-prima2/Makefile
@@ -1,4 +1,3 @@
-obj-y := timer.o
 obj-y += rstc.o
 obj-y += common.o
 obj-y += rtciobrg.o
@@ -6,3 +5,7 @@ obj-$(CONFIG_DEBUG_LL) += lluart.o
 obj-$(CONFIG_CACHE_L2X0) += l2x0.o
 obj-$(CONFIG_SUSPEND) += pm.o sleep.o
 obj-$(CONFIG_SIRF_IRQ) += irq.o
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_HOTPLUG_CPU)  += hotplug.o
+obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
+obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
diff --git a/arch/arm/mach-prima2/Makefile.boot b/arch/arm/mach-prima2/Makefile.boot
index 98167da..8443f58 100644
--- a/arch/arm/mach-prima2/Makefile.boot
+++ b/arch/arm/mach-prima2/Makefile.boot
@@ -3,3 +3,4 @@ params_phys-y		:= 0x00000100
 initrd_phys-y		:= 0x00800000
 
 dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
+dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c
index f25a541..9053410 100644
--- a/arch/arm/mach-prima2/common.c
+++ b/arch/arm/mach-prima2/common.c
@@ -8,9 +8,11 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/of_irq.h>
 #include <asm/sizes.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
+#include <asm/hardware/gic.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include "common.h"
@@ -30,17 +32,24 @@ void __init sirfsoc_init_late(void)
 	sirfsoc_pm_init();
 }
 
+static __init void sirfsoc_map_io(void)
+{
+	sirfsoc_map_lluart();
+	sirfsoc_map_scu();
+}
+
 #ifdef CONFIG_ARCH_PRIMA2
 static const char *prima2_dt_match[] __initdata = {
-       "sirf,prima2",
-       NULL
+	"sirf,prima2",
+	NULL
 };
 
 DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
 	/* Maintainer: Barry Song <baohua.song at csr.com> */
-	.map_io         = sirfsoc_map_lluart,
+	.map_io         = sirfsoc_map_io,
 	.init_irq	= sirfsoc_of_irq_init,
-	.timer		= &sirfsoc_timer,
+	.handle_irq	= sirfsoc_handle_irq,
+	.timer		= &sirfsoc_prima2_timer,
 	.dma_zone_size	= SZ_256M,
 	.init_machine	= sirfsoc_mach_init,
 	.init_late	= sirfsoc_init_late,
@@ -48,3 +57,33 @@ DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
 	.restart	= sirfsoc_restart,
 MACHINE_END
 #endif
+
+#ifdef CONFIG_ARCH_MARCO
+static const struct of_device_id marco_irq_match[] __initconst = {
+	{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
+	{ /* sentinel */ }
+};
+
+static void __init marco_init_irq(void)
+{
+	of_irq_init(marco_irq_match);
+}
+
+static const char *marco_dt_match[] __initdata = {
+	"sirf,marco",
+	NULL
+};
+
+DT_MACHINE_START(MARCO_DT, "Generic MARCO (Flattened Device Tree)")
+	/* Maintainer: Barry Song <baohua.song at csr.com> */
+	.map_io         = sirfsoc_map_io,
+	.init_irq	= marco_init_irq,
+	.handle_irq     = gic_handle_irq,
+	.timer		= &sirfsoc_marco_timer,
+	.init_machine	= sirfsoc_mach_init,
+	.init_late	= sirfsoc_init_late,
+	.dt_compat      = marco_dt_match,
+	.restart	= sirfsoc_restart,
+MACHINE_END
+#endif
+
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
index 60d826f..39ef2b9 100644
--- a/arch/arm/mach-prima2/common.h
+++ b/arch/arm/mach-prima2/common.h
@@ -11,12 +11,17 @@
 
 #include <linux/init.h>
 #include <asm/mach/time.h>
+#include <asm/exception.h>
 
-extern struct sys_timer sirfsoc_timer;
+extern struct sys_timer sirfsoc_prima2_timer;
+extern struct sys_timer sirfsoc_marco_timer;
 
 extern void __init sirfsoc_of_irq_init(void);
-extern void __init sirfsoc_of_clk_init(void);
+extern void __init sirfsoc_prima2_of_clk_init(void);
+extern void __init sirfsoc_marco_of_clk_init(void);
 extern void sirfsoc_restart(char, const char *);
+extern void sirfsoc_secondary_startup(void);
+extern asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs);
 
 #ifndef CONFIG_DEBUG_LL
 static inline void sirfsoc_map_lluart(void)  {}
@@ -24,6 +29,8 @@ static inline void sirfsoc_map_lluart(void)  {}
 extern void __init sirfsoc_map_lluart(void);
 #endif
 
+extern void sirfsoc_map_scu(void);
+
 #ifdef CONFIG_SUSPEND
 extern int sirfsoc_pm_init(void);
 #else
diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S
new file mode 100644
index 0000000..6ec19d5
--- /dev/null
+++ b/arch/arm/mach-prima2/headsmp.S
@@ -0,0 +1,79 @@
+/*
+ * Entry of the second core for CSR Marco dual-core SMP SoCs
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+	__INIT
+/*
+ * Cold boot and hardware reset show different behaviour,
+ * system will be always panic if we warm-reset the board
+ * Here we invalidate L1 of CPU1 to make sure there isn't
+ * uninitialized data written into memory later
+ */
+ENTRY(v7_invalidate_l1)
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c5, 0	@ invalidate I cache
+	mcr	p15, 2, r0, c0, c0, 0
+	mrc	p15, 1, r0, c0, c0, 0
+
+	ldr	r1, =0x7fff
+	and	r2, r1, r0, lsr #13
+
+	ldr	r1, =0x3ff
+
+	and	r3, r1, r0, lsr #3	@ NumWays - 1
+	add	r2, r2, #1		@ NumSets
+
+	and	r0, r0, #0x7
+	add	r0, r0, #4	@ SetShift
+
+	clz	r1, r3		@ WayShift
+	add	r4, r3, #1	@ NumWays
+1:	sub	r2, r2, #1	@ NumSets--
+	mov	r3, r4		@ Temp = NumWays
+2:	subs	r3, r3, #1	@ Temp--
+	mov	r5, r3, lsl r1
+	mov	r6, r2, lsl r0
+	orr	r5, r5, r6	@ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+	mcr	p15, 0, r5, c7, c6, 2
+	bgt	2b
+	cmp	r2, #0
+	bgt	1b
+	dsb
+	isb
+	mov	pc, lr
+ENDPROC(v7_invalidate_l1)
+
+/*
+ * SIRFSOC specific entry point for secondary CPUs.  This provides
+ * a "holding pen" into which all secondary cores are held until we're
+ * ready for them to initialise.
+ */
+ENTRY(sirfsoc_secondary_startup)
+	bl v7_invalidate_l1
+        mrc     p15, 0, r0, c0, c0, 5
+        and     r0, r0, #15
+        adr     r4, 1f
+        ldmia   r4, {r5, r6}
+        sub     r4, r4, r5
+        add     r6, r6, r4
+pen:    ldr     r7, [r6]
+        cmp     r7, r0
+        bne     pen
+
+        /*
+         * we've been released from the holding pen: secondary_stack
+         * should now contain the SVC stack for this core
+         */
+        b       secondary_startup
+ENDPROC(sirfsoc_secondary_startup)
+
+        .align
+1:      .long   .
+        .long   pen_release
diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c
new file mode 100644
index 0000000..7d5febe
--- /dev/null
+++ b/arch/arm/mach-prima2/hotplug.c
@@ -0,0 +1,57 @@
+/*
+ * CPU hotplug support for CSR Marco dual-core SMP SoCs
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+
+extern volatile int pen_release;
+
+static inline void platform_do_lowpower(unsigned int cpu)
+{
+	flush_cache_all();
+
+	/* we put the platform to just WFI */
+	for (;;) {
+		__asm__ __volatile__("dsb\n\t" "wfi\n\t"
+			: : : "memory");
+		if (pen_release == cpu_logical_map(cpu)) {
+			/*
+			 * OK, proper wakeup, we're done
+			 */
+			break;
+		}
+	}
+}
+
+int platform_cpu_kill(unsigned int cpu)
+{
+	return 1;
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void platform_cpu_die(unsigned int cpu)
+{
+	platform_do_lowpower(cpu);
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+	/*
+	 * we don't allow CPU 0 to be shutdown (it is still too special
+	 * e.g. clock tick interrupts)
+	 */
+	return cpu == 0 ? -EPERM : 0;
+}
diff --git a/arch/arm/mach-prima2/include/mach/uart.h b/arch/arm/mach-prima2/include/mach/uart.h
index c98b4d5..c10510d 100644
--- a/arch/arm/mach-prima2/include/mach/uart.h
+++ b/arch/arm/mach-prima2/include/mach/uart.h
@@ -10,7 +10,13 @@
 #define __MACH_PRIMA2_SIRFSOC_UART_H
 
 /* UART-1: used as serial debug port */
+#if defined(CONFIG_DEBUG_SIRFPRIMA2_UART1)
 #define SIRFSOC_UART1_PA_BASE          0xb0060000
+#elif defined(CONFIG_DEBUG_SIRFMARCO_UART1)
+#define SIRFSOC_UART1_PA_BASE          0xcc060000
+#else
+#define SIRFSOC_UART1_PA_BASE          0
+#endif
 #define SIRFSOC_UART1_VA_BASE          SIRFSOC_VA(0x060000)
 #define SIRFSOC_UART1_SIZE		SZ_4K
 
diff --git a/arch/arm/mach-prima2/include/mach/uncompress.h b/arch/arm/mach-prima2/include/mach/uncompress.h
index 83125c6..9f1d0a7 100644
--- a/arch/arm/mach-prima2/include/mach/uncompress.h
+++ b/arch/arm/mach-prima2/include/mach/uncompress.h
@@ -25,6 +25,9 @@ static __inline__ void putc(char c)
 	 * during kernel decompression, all mappings are flat:
 	 *  virt_addr == phys_addr
 	 */
+	if (!SIRFSOC_UART1_PA_BASE)
+		return;
+
 	while (__raw_readl(SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS)
 		& SIRFSOC_UART1_TXFIFO_FULL)
 		barrier();
diff --git a/arch/arm/mach-prima2/irq.c b/arch/arm/mach-prima2/irq.c
index 7dee917..6c0f3e9 100644
--- a/arch/arm/mach-prima2/irq.c
+++ b/arch/arm/mach-prima2/irq.c
@@ -9,17 +9,19 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/irq.h>
-#include <mach/hardware.h>
-#include <asm/mach/irq.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/irqdomain.h>
 #include <linux/syscore_ops.h>
+#include <asm/mach/irq.h>
+#include <asm/exception.h>
+#include <mach/hardware.h>
 
 #define SIRFSOC_INT_RISC_MASK0          0x0018
 #define SIRFSOC_INT_RISC_MASK1          0x001C
 #define SIRFSOC_INT_RISC_LEVEL0         0x0020
 #define SIRFSOC_INT_RISC_LEVEL1         0x0024
+#define SIRFSOC_INIT_IRQ_ID		0x0038
 
 void __iomem *sirfsoc_intc_base;
 
@@ -52,6 +54,16 @@ static __init void sirfsoc_irq_init(void)
 	writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1);
 }
 
+asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
+{
+	u32 irqstat, irqnr;
+
+	irqstat = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INIT_IRQ_ID);
+	irqnr = irqstat & 0xff;
+
+	handle_IRQ(irqnr, regs);
+}
+
 static struct of_device_id intc_ids[]  = {
 	{ .compatible = "sirf,prima2-intc" },
 	{},
diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c
index c998377..35a2973 100644
--- a/arch/arm/mach-prima2/l2x0.c
+++ b/arch/arm/mach-prima2/l2x0.c
@@ -11,21 +11,38 @@
 #include <linux/of.h>
 #include <asm/hardware/cache-l2x0.h>
 
+/*
+ * fixme: make aux_val and aux_mask the private data
+ * of of_device_id after we get right aux_val/mask
+ */
 static struct of_device_id prima2_l2x0_ids[]  = {
 	{ .compatible = "sirf,prima2-pl310-cache" },
 	{},
 };
 
+static struct of_device_id marco_l2x0_ids[]  = {
+	{ .compatible = "sirf,marco-pl310-cache" },
+	{},
+};
+
 static int __init sirfsoc_l2x0_init(void)
 {
 	struct device_node *np;
-
 	np = of_find_matching_node(NULL, prima2_l2x0_ids);
 	if (np) {
 		pr_info("Initializing prima2 L2 cache\n");
 		return l2x0_of_init(0x40000, 0);
 	}
 
+	np = of_find_matching_node(NULL, marco_l2x0_ids);
+	if (np) {
+		pr_info("Initializing marco L2 cache\n");
+		/*
+		 * fixme: set the right aux_val and aux_mask
+		 * return l2x0_of_init(0x10000, 0);
+		 */
+	}
+
 	return 0;
 }
 early_initcall(sirfsoc_l2x0_init);
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c
new file mode 100644
index 0000000..e990a79
--- /dev/null
+++ b/arch/arm/mach-prima2/platsmp.c
@@ -0,0 +1,154 @@
+/*
+ * plat smp support for CSR Marco dual-core SMP SoCs
+ *
+ * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <asm/page.h>
+#include <asm/mach/map.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/hardware/gic.h>
+#include <mach/map.h>
+
+#include "common.h"
+
+static void __iomem *scu_base;
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen".
+ */
+volatile int pen_release = -1;
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static struct map_desc scu_io_desc __initdata = {
+	.length		= SZ_4K,
+	.type		= MT_DEVICE,
+};
+
+void __init sirfsoc_map_scu(void)
+{
+	unsigned long base;
+
+	/* Get SCU base */
+	asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
+
+	scu_io_desc.virtual = SIRFSOC_VA(base);
+	scu_io_desc.pfn = __phys_to_pfn(base);
+	iotable_init(&scu_io_desc, 1);
+
+	scu_base = (void __iomem *)SIRFSOC_VA(base);
+}
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+	/*
+	 * if any interrupts are already enabled for the primary
+	 * core (e.g. timer irq), then they will not have been enabled
+	 * for us: do so
+	 */
+	gic_secondary_init(0);
+
+	/*
+	 * let the primary processor know we're out of the
+	 * pen, then head off into the C entry point
+	 */
+	pen_release = -1;
+	smp_wmb();
+
+	/*
+	 * Synchronise with the boot thread.
+	 */
+	spin_lock(&boot_lock);
+	spin_unlock(&boot_lock);
+}
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	unsigned long timeout;
+	static void __iomem *sram_base;
+
+	/* for the moment in FPGA we use DSP SRAM */
+#define SIRFSOC_DSP_SRAM_BASE 0xCA008000UL
+	sram_base = ioremap_nocache(SIRFSOC_DSP_SRAM_BASE, SZ_4K);
+
+	/*
+	 * write the address of secondary startup into the sram register
+	 * at offset 0x34, then write the magic number 0x12345678 to the
+	 * sram register at offset 0x30, which is what boot rom code is
+	 * waiting for. This would wake up the secondary core from WFI
+	 */
+#define SIRFSOC_CPU1_JUMPADDR_OFFSET 0x34
+	__raw_writel(virt_to_phys(sirfsoc_secondary_startup),
+		sram_base + SIRFSOC_CPU1_JUMPADDR_OFFSET);
+
+#define SIRFSOC_CPU1_WAKEMAGIC_OFFSET 0x30
+	__raw_writel(0x12345678,
+		sram_base + SIRFSOC_CPU1_WAKEMAGIC_OFFSET);
+
+	/* make sure write buffer is drained */
+	mb();
+
+	spin_lock(&boot_lock);
+
+	/*
+	 * The secondary processor is waiting to be released from
+	 * the holding pen - release it, then wait for it to flag
+	 * that it has been released by resetting pen_release.
+	 *
+	 * Note that "pen_release" is the hardware CPU ID, whereas
+	 * "cpu" is Linux's internal ID.
+	 */
+	pen_release = cpu_logical_map(cpu);
+	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+
+	/*
+	 * Send the secondary CPU a soft interrupt ID15, thereby causing
+	 * the boot monitor to read the JUMPADDR and WAKEMAGIC, and branch
+	 * to the address found there.
+	 */
+	gic_raise_softirq(cpumask_of(cpu), 15);
+
+	timeout = jiffies + (1 * HZ);
+	while (time_before(jiffies, timeout)) {
+		smp_rmb();
+		if (pen_release == -1)
+			break;
+
+		udelay(10);
+	}
+
+	/*
+	 * now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return pen_release != -1 ? -ENOSYS : 0;
+}
+
+void __init smp_init_cpus(void)
+{
+	int i, ncores;
+
+	ncores = scu_get_core_count(scu_base);
+
+	for (i = 0; i < ncores; i++)
+		set_cpu_possible(i, true);
+
+	set_smp_cross_call(gic_raise_softirq);
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+	scu_enable(scu_base);
+}
diff --git a/arch/arm/mach-prima2/pm.c b/arch/arm/mach-prima2/pm.c
index fb5a791..a16dec7 100644
--- a/arch/arm/mach-prima2/pm.c
+++ b/arch/arm/mach-prima2/pm.c
@@ -93,6 +93,7 @@ int __init sirfsoc_pm_init(void)
 
 static const struct of_device_id pwrc_ids[] = {
 	{ .compatible = "sirf,prima2-pwrc" },
+	{ .compatible = "sirf,marco-pwrc" },
 	{}
 };
 
diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c
index 762adb7..50a9155 100644
--- a/arch/arm/mach-prima2/rstc.c
+++ b/arch/arm/mach-prima2/rstc.c
@@ -19,6 +19,7 @@ static DEFINE_MUTEX(rstc_lock);
 
 static struct of_device_id rstc_ids[]  = {
 	{ .compatible = "sirf,prima2-rstc" },
+	{ .compatible = "sirf,marco-rstc" },
 	{},
 };
 
@@ -52,17 +53,32 @@ int sirfsoc_reset_device(struct device *dev)
 
 	mutex_lock(&rstc_lock);
 
-	/*
-	 * Writing 1 to this bit resets corresponding block. Writing 0 to this
-	 * bit de-asserts reset signal of the corresponding block.
-	 * datasheet doesn't require explicit delay between the set and clear
-	 * of reset bit. it could be shorter if tests pass.
-	 */
-	writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | reset_bit,
-		sirfsoc_rstc_base + (reset_bit / 32) * 4);
-	msleep(10);
-	writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
-		sirfsoc_rstc_base + (reset_bit / 32) * 4);
+	if (of_device_is_compatible(dev->of_node, "sirf,prima2-rstc")) {
+		/*
+		 * Writing 1 to this bit resets corresponding block. Writing 0 to this
+		 * bit de-asserts reset signal of the corresponding block.
+		 * datasheet doesn't require explicit delay between the set and clear
+		 * of reset bit. it could be shorter if tests pass.
+		 */
+		writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | reset_bit,
+			sirfsoc_rstc_base + (reset_bit / 32) * 4);
+		msleep(10);
+		writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
+			sirfsoc_rstc_base + (reset_bit / 32) * 4);
+	} else {
+		/*
+		 * For MARCO and POLO
+		 * Writing 1 to SET register resets corresponding block. Writing 1 to CLEAR
+		 * register de-asserts reset signal of the corresponding block.
+		 * datasheet doesn't require explicit delay between the set and clear
+		 * of reset bit. it could be shorter if tests pass.
+		 */
+		writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 8) | reset_bit,
+			sirfsoc_rstc_base + (reset_bit / 32) * 8);
+		msleep(10);
+		writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 8) | reset_bit,
+			sirfsoc_rstc_base + (reset_bit / 32) * 8 + 4);
+	}
 
 	mutex_unlock(&rstc_lock);
 
diff --git a/arch/arm/mach-prima2/rtciobrg.c b/arch/arm/mach-prima2/rtciobrg.c
index 9d80f1e..fc6c4ab 100644
--- a/arch/arm/mach-prima2/rtciobrg.c
+++ b/arch/arm/mach-prima2/rtciobrg.c
@@ -104,6 +104,7 @@ EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_writel);
 
 static const struct of_device_id rtciobrg_ids[] = {
 	{ .compatible = "sirf,prima2-rtciobg" },
+	{ .compatible = "sirf,marco-rtciobg" },
 	{}
 };
 
diff --git a/arch/arm/mach-prima2/timer-marco.c b/arch/arm/mach-prima2/timer-marco.c
new file mode 100644
index 0000000..0977792
--- /dev/null
+++ b/arch/arm/mach-prima2/timer-marco.c
@@ -0,0 +1,374 @@
+/*
+ * System timer for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <asm/sched_clock.h>
+#include <asm/localtimer.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+#define SIRFSOC_TIMER_32COUNTER_0_CTRL			0x0000
+#define SIRFSOC_TIMER_32COUNTER_1_CTRL			0x0004
+#define SIRFSOC_TIMER_MATCH_0				0x0018
+#define SIRFSOC_TIMER_MATCH_1				0x001c
+#define SIRFSOC_TIMER_COUNTER_0				0x0048
+#define SIRFSOC_TIMER_COUNTER_1				0x004c
+#define SIRFSOC_TIMER_INTR_STATUS			0x0060
+#define SIRFSOC_TIMER_WATCHDOG_EN			0x0064
+#define SIRFSOC_TIMER_64COUNTER_CTRL			0x0068
+#define SIRFSOC_TIMER_64COUNTER_LO			0x006c
+#define SIRFSOC_TIMER_64COUNTER_HI			0x0070
+#define SIRFSOC_TIMER_64COUNTER_LOAD_LO			0x0074
+#define SIRFSOC_TIMER_64COUNTER_LOAD_HI			0x0078
+#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO		0x007c
+#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI		0x0080
+
+#define SIRFSOC_TIMER_REG_CNT 6
+
+static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
+	SIRFSOC_TIMER_WATCHDOG_EN,
+	SIRFSOC_TIMER_32COUNTER_0_CTRL,
+	SIRFSOC_TIMER_32COUNTER_1_CTRL,
+	SIRFSOC_TIMER_64COUNTER_CTRL,
+	SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
+	SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
+};
+
+static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
+
+static void __iomem *sirfsoc_timer_base;
+static void __init sirfsoc_of_timer_map(void);
+
+/* disable count and interrupt */
+static inline void sirfsoc_timer_count_disable(int idx)
+{
+	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
+		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
+}
+
+/* enable count and interrupt */
+static inline void sirfsoc_timer_count_enable(int idx)
+{
+	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
+		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
+}
+
+/* timer0 interrupt handler */
+static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *ce = dev_id;
+
+	/* clear timer0 interrupt */
+	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
+
+	if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
+		sirfsoc_timer_count_disable(0);
+
+	ce->event_handler(ce);
+
+	return IRQ_HANDLED;
+}
+
+/* read 64-bit timer counter */
+static cycle_t sirfsoc_timer_read(struct clocksource *cs)
+{
+	u64 cycles;
+
+	writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+			BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+
+	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
+	cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
+
+	return cycles;
+}
+
+static int sirfsoc_timer_set_next_event(unsigned long delta,
+	struct clock_event_device *ce)
+{
+
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
+#if 0
+	writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
+#else
+	/* Fix FPGA: divider of 32counter_0 doesn't work */
+	writel_relaxed(delta * 13, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
+#endif
+
+	/* enable the tick */
+	sirfsoc_timer_count_enable(0);
+
+	return 0;
+}
+
+static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
+	struct clock_event_device *ce)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_ONESHOT:
+		/* enable in set_next_event */
+		break;
+	default:
+		break;
+	}
+
+	sirfsoc_timer_count_disable(0);
+}
+
+static void sirfsoc_clocksource_suspend(struct clocksource *cs)
+{
+	int i;
+
+	for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
+		sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+}
+
+static void sirfsoc_clocksource_resume(struct clocksource *cs)
+{
+	int i;
+
+	for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
+		writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+
+	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
+		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
+	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
+		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
+
+	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+}
+
+static struct clock_event_device sirfsoc_clockevent = {
+	.name = "sirfsoc_clockevent",
+	.rating = 200,
+	.features = CLOCK_EVT_FEAT_ONESHOT,
+	.set_mode = sirfsoc_timer_set_mode,
+	.set_next_event = sirfsoc_timer_set_next_event,
+};
+
+static struct clocksource sirfsoc_clocksource = {
+	.name = "sirfsoc_clocksource",
+	.rating = 200,
+	.mask = CLOCKSOURCE_MASK(64),
+	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+	.read = sirfsoc_timer_read,
+	.suspend = sirfsoc_clocksource_suspend,
+	.resume = sirfsoc_clocksource_resume,
+};
+
+static struct irqaction sirfsoc_timer_irq = {
+	.name = "sirfsoc_timer0",
+	.flags = IRQF_TIMER | IRQF_NOBALANCING,
+	.handler = sirfsoc_timer_interrupt,
+	.dev_id = &sirfsoc_clockevent,
+};
+
+#ifdef CONFIG_LOCAL_TIMERS
+
+/* timer1 interrupt handler */
+static irqreturn_t sirfsoc_timer1_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *ce = dev_id;
+
+	/* clear timer1 interrupt */
+	writel_relaxed(BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
+
+	if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
+		sirfsoc_timer_count_disable(1);
+
+	ce->event_handler(ce);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction sirfsoc_timer1_irq = {
+	.name = "sirfsoc_timer1",
+	.flags = IRQF_TIMER | IRQF_NOBALANCING,
+	.handler = sirfsoc_timer1_interrupt,
+};
+
+static int sirfsoc_timer1_set_next_event(unsigned long delta,
+	struct clock_event_device *ce)
+{
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
+#if 0
+	writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_1);
+#else
+	/* Fix FPGA: divider of 32counter_0 doesn't work */
+	writel_relaxed(delta * 13, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_1);
+#endif
+
+	/* enable the tick */
+	sirfsoc_timer_count_enable(1);
+
+	return 0;
+}
+
+static void sirfsoc_timer1_set_mode(enum clock_event_mode mode,
+	struct clock_event_device *ce)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_ONESHOT:
+		/* enable in set_next_event */
+		break;
+	default:
+		break;
+	}
+
+	sirfsoc_timer_count_disable(1);
+}
+
+static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
+{
+	/* Use existing clock_event for cpu 0 */
+	if (!smp_processor_id())
+		return 0;
+
+	ce->irq = sirfsoc_timer1_irq.irq;
+	ce->name = "local_timer";
+	ce->features = sirfsoc_clockevent.features;
+	ce->rating = sirfsoc_clockevent.rating;
+	ce->cpumask = cpumask_of(1);
+	ce->set_mode = sirfsoc_timer1_set_mode;
+	ce->set_next_event = sirfsoc_timer1_set_next_event;
+	ce->shift = sirfsoc_clockevent.shift;
+	ce->mult = sirfsoc_clockevent.mult;
+	ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
+	ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
+
+	sirfsoc_timer1_irq.dev_id = ce;
+	BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
+	irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
+
+	clockevents_register_device(ce);
+	return 0;
+}
+
+static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
+{
+	sirfsoc_timer_count_disable(1);
+
+	remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
+}
+
+static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
+	.setup	= sirfsoc_local_timer_setup,
+	.stop	= sirfsoc_local_timer_stop,
+};
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static void __init sirfsoc_clockevent_init(void)
+{
+	clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
+
+	sirfsoc_clockevent.max_delta_ns =
+		clockevent_delta2ns(-2, &sirfsoc_clockevent);
+	sirfsoc_clockevent.min_delta_ns =
+		clockevent_delta2ns(2, &sirfsoc_clockevent);
+
+	sirfsoc_clockevent.cpumask = cpumask_of(0);
+	clockevents_register_device(&sirfsoc_clockevent);
+#ifdef CONFIG_LOCAL_TIMERS
+	local_timer_register(&sirfsoc_local_timer_ops);
+#endif
+}
+
+/* initialize the kernel jiffy timer source */
+static void __init sirfsoc_timer_init(void)
+{
+	unsigned long rate;
+	u32 timer_div;
+	struct clk *clk;
+
+	/* initialize clocking early, we want to set the OS timer */
+	sirfsoc_marco_of_clk_init();
+
+	/* timer's input clock is io clock */
+	clk = clk_get_sys("io", NULL);
+
+	BUG_ON(IS_ERR(clk));
+#if defined(CONFIG_SIRFMARCO_FPGA)
+	/* For FPGA, the io clk is fixed to 26Mhz */
+	rate = 26000000;
+#else
+	rate = clk_get_rate(clk);
+#endif
+
+	BUG_ON(rate < CLOCK_TICK_RATE);
+	BUG_ON(rate % CLOCK_TICK_RATE);
+
+	sirfsoc_of_timer_map();
+
+	/* Initialize the timer dividers */
+	timer_div = rate / CLOCK_TICK_RATE / 2 - 1;
+	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
+	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
+
+	/* Initialize timer counters to 0 */
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
+	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
+		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
+
+	/* Clear all interrupts */
+	writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
+
+	BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+
+	BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
+
+	sirfsoc_clockevent_init();
+}
+
+static struct of_device_id timer_ids[] = {
+	{ .compatible = "sirf,marco-tick" },
+	{},
+};
+
+static void __init sirfsoc_of_timer_map(void)
+{
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, timer_ids);
+	if (!np)
+		return;
+	sirfsoc_timer_base = of_iomap(np, 0);
+	if (!sirfsoc_timer_base)
+		panic("unable to map timer cpu registers\n");
+
+	sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
+	if (!sirfsoc_timer_irq.irq)
+		panic("No irq passed for timer0 via DT\n");
+
+#ifdef CONFIG_LOCAL_TIMERS
+	sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
+	if (!sirfsoc_timer1_irq.irq)
+		panic("No irq passed for timer1 via DT\n");
+#endif
+
+	of_node_put(np);
+}
+
+struct sys_timer sirfsoc_marco_timer = {
+	.init = sirfsoc_timer_init,
+};
diff --git a/arch/arm/mach-prima2/timer.c b/arch/arm/mach-prima2/timer-prima2.c
similarity index 98%
rename from arch/arm/mach-prima2/timer.c
rename to arch/arm/mach-prima2/timer-prima2.c
index d95bf25..f536867 100644
--- a/arch/arm/mach-prima2/timer.c
+++ b/arch/arm/mach-prima2/timer-prima2.c
@@ -193,7 +193,7 @@ static void __init sirfsoc_timer_init(void)
 	struct clk *clk;
 
 	/* initialize clocking early, we want to set the OS timer */
-	sirfsoc_of_clk_init();
+	sirfsoc_prima2_of_clk_init();
 
 	/* timer's input clock is io clock */
 	clk = clk_get_sys("io", NULL);
@@ -233,7 +233,7 @@ static void __init sirfsoc_of_timer_map(void)
 
 	np = of_find_matching_node(NULL, timer_ids);
 	if (!np)
-		panic("unable to find compatible timer node in dtb\n");
+		return;
 	sirfsoc_timer_base = of_iomap(np, 0);
 	if (!sirfsoc_timer_base)
 		panic("unable to map timer cpu registers\n");
@@ -246,6 +246,6 @@ static void __init sirfsoc_of_timer_map(void)
 	of_node_put(np);
 }
 
-struct sys_timer sirfsoc_timer = {
+struct sys_timer sirfsoc_prima2_timer = {
 	.init = sirfsoc_timer_init,
 };
-- 
1.7.0.4



Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog



More information about the linux-arm-kernel mailing list