aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/boot/dts/zynq-7000.dtsi221
-rw-r--r--arch/arm/boot/dts/zynq-cc108.dts41
-rw-r--r--arch/arm/boot/dts/zynq-zc702.dts71
-rw-r--r--arch/arm/boot/dts/zynq-zc706.dts54
-rw-r--r--arch/arm/boot/dts/zynq-zc770-xm010.dts38
-rw-r--r--arch/arm/boot/dts/zynq-zc770-xm011.dts42
-rw-r--r--arch/arm/boot/dts/zynq-zc770-xm012.dts42
-rw-r--r--arch/arm/boot/dts/zynq-zc770-xm013.dts39
-rw-r--r--arch/arm/boot/dts/zynq-zed.dts51
-rw-r--r--arch/arm/boot/dts/zynq-zturn.dts2
-rw-r--r--arch/arm/boot/dts/zynq-zybo.dts12
-rw-r--r--arch/arm/configs/xilinx_zynq_defconfig242
-rw-r--r--arch/arm/include/asm/hardirq.h2
-rw-r--r--arch/arm/include/asm/smp.h3
-rw-r--r--arch/arm/kernel/smp.c168
-rw-r--r--arch/arm/mach-zynq/Kconfig16
-rw-r--r--arch/arm/mach-zynq/Makefile6
-rw-r--r--arch/arm/mach-zynq/common.c9
-rw-r--r--arch/arm/mach-zynq/common.h26
-rw-r--r--arch/arm/mach-zynq/efuse.c75
-rw-r--r--arch/arm/mach-zynq/platsmp.c10
-rw-r--r--arch/arm/mach-zynq/pm.c170
-rw-r--r--arch/arm/mach-zynq/slcr.c47
-rw-r--r--arch/arm/mach-zynq/suspend.S185
-rw-r--r--arch/arm/mach-zynq/zynq_ocm.c245
25 files changed, 1713 insertions, 104 deletions
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index ca6425ad794c..5602f4f3ad1c 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -1,6 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2011 - 2015 Xilinx
*/
/ {
@@ -60,6 +60,7 @@
};
amba: amba {
+ u-boot,dm-pre-reloc;
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
@@ -155,6 +156,13 @@
reg = <0xf8006000 0x1000>;
};
+ ocmc: ocmc@f800c000 {
+ compatible = "xlnx,zynq-ocmc-1.0";
+ interrupt-parent = <&intc>;
+ interrupts = <0 3 4>;
+ reg = <0xf800c000 0x1000>;
+ };
+
uart0: serial@e0000000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
@@ -197,6 +205,45 @@
#size-cells = <0>;
};
+ qspi: spi@e000d000 {
+ clock-names = "ref_clk", "pclk";
+ clocks = <&clkc 10>, <&clkc 43>;
+ compatible = "xlnx,zynq-qspi-1.0";
+ status = "disabled";
+ interrupt-parent = <&intc>;
+ interrupts = <0 19 4>;
+ reg = <0xe000d000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ smcc: memory-controller@e000e000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+ clock-names = "memclk", "apb_pclk";
+ clocks = <&clkc 11>, <&clkc 44>;
+ compatible = "arm,pl353-smc-r2p1", "arm,primecell";
+ interrupt-parent = <&intc>;
+ interrupts = <0 18 4>;
+ ranges ;
+ reg = <0xe000e000 0x1000>;
+ nand0: flash@e1000000 {
+ status = "disabled";
+ compatible = "arm,pl353-nand-r2p1";
+ reg = <0xe1000000 0x1000000>;
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ };
+ nor0: flash@e2000000 {
+ status = "disabled";
+ compatible = "cfi-flash";
+ reg = <0xe2000000 0x2000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+ };
+
gem0: ethernet@e000b000 {
compatible = "cdns,zynq-gem", "cdns,gem";
reg = <0xe000b000 0x1000>;
@@ -240,15 +287,17 @@
};
slcr: slcr@f8000000 {
+ u-boot,dm-pre-reloc;
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd";
reg = <0xF8000000 0x1000>;
ranges;
clkc: clkc@100 {
+ u-boot,dm-pre-reloc;
#clock-cells = <1>;
compatible = "xlnx,ps7-clkc";
- fclk-enable = <0>;
+ fclk-enable = <0xf>;
clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
"cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
"dci", "lqspi", "smc", "pcap", "gem0", "gem1",
@@ -297,14 +346,19 @@
devcfg: devcfg@f8007000 {
compatible = "xlnx,zynq-devcfg-1.0";
- reg = <0xf8007000 0x100>;
interrupt-parent = <&intc>;
interrupts = <0 8 4>;
- clocks = <&clkc 12>;
- clock-names = "ref_clk";
+ reg = <0xf8007000 0x100>;
+ clocks = <&clkc 12>, <&clkc 15>, <&clkc 16>, <&clkc 17>, <&clkc 18>;
+ clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
syscon = <&slcr>;
};
+ efuse: efuse@f800d000 {
+ compatible = "xlnx,zynq-efuse";
+ reg = <0xf800d000 0x20>;
+ };
+
global_timer: timer@f8f00200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0xf8f00200 0x20>;
@@ -365,5 +419,160 @@
reg = <0xf8005000 0x1000>;
timeout-sec = <10>;
};
+
+ etb@f8801000 {
+ compatible = "arm,coresight-etb10", "arm,primecell";
+ reg = <0xf8801000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+
+ port {
+ etb_in_port: endpoint {
+ remote-endpoint = <&replicator_out_port1>;
+ };
+ };
+ };
+
+ tpiu@f8803000 {
+ compatible = "arm,coresight-tpiu", "arm,primecell";
+ reg = <0xf8803000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+
+ port {
+ tpiu_in_port: endpoint {
+ slave-mode;
+ remote-endpoint = <&replicator_out_port0>;
+ };
+ };
+ };
+
+ funnel@0,f8804000 {
+ compatible = "arm,coresight-funnel", "arm,primecell";
+ reg = <0xf8804000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+
+ /* funnel output ports */
+ out-ports {
+ port {
+ funnel_out_port: endpoint {
+ remote-endpoint =
+ <&replicator_in_port0>;
+ };
+ };
+ };
+
+ in-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* funnel input ports */
+ port@0 {
+ reg = <0>;
+ funnel0_in_port0: endpoint {
+ slave-mode;
+ remote-endpoint = <&ptm0_out_port>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ funnel0_in_port1: endpoint {
+ slave-mode;
+ remote-endpoint = <&ptm1_out_port>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ funnel0_in_port2: endpoint {
+ slave-mode;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ funnel0_in_port3: endpoint {
+ slave-mode;
+ remote-endpoint = <&itm_out_port>;
+ };
+ };
+ /*The other input ports are not connect to anything */
+ };
+ };
+
+ replicator {
+ compatible = "arm,coresight-replicator";
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+
+ out-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* replicator output ports */
+ port@0 {
+ reg = <0>;
+ replicator_out_port0: endpoint {
+ remote-endpoint = <&tpiu_in_port>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ replicator_out_port1: endpoint {
+ remote-endpoint = <&etb_in_port>;
+ };
+ };
+ };
+ in-ports {
+ /* replicator input port */
+ port {
+ replicator_in_port0: endpoint {
+ slave-mode;
+ remote-endpoint = <&funnel_out_port>;
+ };
+ };
+ };
+ };
+
+ itm@0,f8805000 {
+ compatible = "arm,coresight-etm3x", "arm,primecell";
+ reg = <0xf8805000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+
+ port {
+ itm_out_port: endpoint {
+ remote-endpoint = <&funnel0_in_port3>;
+ };
+ };
+ };
+
+ ptm@0,f889c000 {
+ compatible = "arm,coresight-etm3x", "arm,primecell";
+ reg = <0xf889c000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+ cpu = <&cpu0>;
+ port {
+ ptm0_out_port: endpoint {
+ remote-endpoint = <&funnel0_in_port0>;
+ };
+ };
+ };
+
+ ptm@0,f889d000 {
+ compatible = "arm,coresight-etm3x", "arm,primecell";
+ reg = <0xf889d000 0x1000>;
+ clocks = <&clkc 27>, <&clkc 46>, <&clkc 47>;
+ clock-names = "apb_pclk", "dbg_trc", "dbg_apb";
+ cpu = <&cpu1>;
+ port {
+ ptm1_out_port: endpoint {
+ remote-endpoint = <&funnel0_in_port1>;
+ };
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/zynq-cc108.dts b/arch/arm/boot/dts/zynq-cc108.dts
index 8b9ab9bba23b..64d73ecbc592 100644
--- a/arch/arm/boot/dts/zynq-cc108.dts
+++ b/arch/arm/boot/dts/zynq-cc108.dts
@@ -18,6 +18,7 @@
aliases {
ethernet0 = &gem0;
serial0 = &uart0;
+ spi0 = &qspi;
};
chosen {
@@ -52,6 +53,45 @@
};
};
+&qspi {
+ status = "okay";
+ is-dual = <0>;
+ num-cs = <1>;
+ flash@0 { /* 16 MB */
+ compatible = "n25q128a11";
+ reg = <0x0>;
+ spi-max-frequency = <50000000>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "qspi-fsbl-uboot-bs";
+ reg = <0x0 0x400000>; /* 4MB */
+ };
+ partition@400000 {
+ label = "qspi-linux";
+ reg = <0x400000 0x400000>; /* 4MB */
+ };
+ partition@800000 {
+ label = "qspi-rootfs";
+ reg = <0x800000 0x400000>; /* 4MB */
+ };
+ partition@c00000 {
+ label = "qspi-devicetree";
+ reg = <0xc00000 0x100000>; /* 1MB */
+ };
+ partition@d00000 {
+ label = "qspi-scratch";
+ reg = <0xd00000 0x200000>; /* 2MB */
+ };
+ partition@f00000 {
+ label = "qspi-uboot-env";
+ reg = <0xf00000 0x100000>; /* 1MB */
+ };
+ };
+};
+
&sdhci1 {
status = "okay";
broken-cd ;
@@ -59,6 +99,7 @@
};
&uart0 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts
index 27cd6cb52f1b..c9940fb366ce 100644
--- a/arch/arm/boot/dts/zynq-zc702.dts
+++ b/arch/arm/boot/dts/zynq-zc702.dts
@@ -1,6 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2011 - 2015 Xilinx
* Copyright (C) 2012 National Instruments Corp.
*/
/dts-v1/;
@@ -14,7 +14,9 @@
ethernet0 = &gem0;
i2c0 = &i2c0;
serial0 = &uart1;
+ spi0 = &qspi;
mmc0 = &sdhci0;
+ usb0 = &usb0;
};
memory@0 {
@@ -56,9 +58,12 @@
};
};
- usb_phy0: phy0 {
- compatible = "usb-nop-xceiv";
+ usb_phy0: phy0@e0002000 {
+ compatible = "ulpi-phy";
#phy-cells = <0>;
+ reg = <0xe0002000 0x1000>;
+ view-port = <0x0170>;
+ drv-vbus;
};
};
@@ -85,6 +90,8 @@
phy-handle = <&ethernet_phy>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gem0_default>;
+ phy-reset-gpio = <&gpio0 11 0>;
+ phy-reset-active-low;
ethernet_phy: ethernet-phy@7 {
reg = <7>;
@@ -100,8 +107,11 @@
&i2c0 {
status = "okay";
clock-frequency = <400000>;
- pinctrl-names = "default";
+ pinctrl-names = "default", "gpio";
pinctrl-0 = <&pinctrl_i2c0_default>;
+ pinctrl-1 = <&pinctrl_i2c0_gpio>;
+ scl-gpios = <&gpio0 50 0>;
+ sda-gpios = <&gpio0 51 0>;
i2c-mux@74 {
compatible = "nxp,pca9548";
@@ -292,6 +302,19 @@
};
};
+ pinctrl_i2c0_gpio: i2c0-gpio {
+ mux {
+ groups = "gpio0_50_grp", "gpio0_51_grp";
+ function = "gpio0";
+ };
+
+ conf {
+ groups = "gpio0_50_grp", "gpio0_51_grp";
+ slew-rate = <0>;
+ io-standard = <1>;
+ };
+ };
+
pinctrl_sdhci0_default: sdhci0-default {
mux {
groups = "sdio0_2_grp";
@@ -380,13 +403,51 @@
};
};
+&qspi {
+ u-boot,dm-pre-reloc;
+ status = "okay";
+ is-dual = <0>;
+ num-cs = <1>;
+ flash@0 {
+ compatible = "n25q128a11";
+ reg = <0x0>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@qspi-fsbl-uboot {
+ label = "qspi-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@qspi-linux {
+ label = "qspi-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@qspi-device-tree {
+ label = "qspi-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@qspi-rootfs {
+ label = "qspi-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@qspi-bitstream {
+ label = "qspi-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+ };
+};
+
&sdhci0 {
+ u-boot,dm-pre-reloc;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdhci0_default>;
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1_default>;
diff --git a/arch/arm/boot/dts/zynq-zc706.dts b/arch/arm/boot/dts/zynq-zc706.dts
index 77943c16d33f..1a1b03a4223d 100644
--- a/arch/arm/boot/dts/zynq-zc706.dts
+++ b/arch/arm/boot/dts/zynq-zc706.dts
@@ -1,6 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2011 - 2015 Xilinx
* Copyright (C) 2012 National Instruments Corp.
*/
/dts-v1/;
@@ -14,6 +14,7 @@
ethernet0 = &gem0;
i2c0 = &i2c0;
serial0 = &uart1;
+ spi0 = &qspi;
mmc0 = &sdhci0;
};
@@ -27,9 +28,12 @@
stdout-path = "serial0:115200n8";
};
- usb_phy0: phy0 {
- compatible = "usb-nop-xceiv";
+ usb_phy0: phy0@e0002000 {
+ compatible = "ulpi-phy";
#phy-cells = <0>;
+ reg = <0xe0002000 0x1000>;
+ view-port = <0x0170>;
+ drv-vbus;
};
};
@@ -303,13 +307,51 @@
};
};
+&qspi {
+ u-boot,dm-pre-reloc;
+ status = "okay";
+ is-dual = <1>;
+ num-cs = <1>;
+ flash@0 {
+ compatible = "n25q128a11";
+ reg = <0x0>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@qspi-fsbl-uboot {
+ label = "qspi-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@qspi-linux {
+ label = "qspi-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@qspi-device-tree {
+ label = "qspi-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@qspi-rootfs {
+ label = "qspi-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@qspi-bitstream {
+ label = "qspi-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+ };
+};
+
&sdhci0 {
+ u-boot,dm-pre-reloc;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdhci0_default>;
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1_default>;
@@ -322,3 +364,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb0_default>;
};
+
+&watchdog0 {
+ reset-on-timeout;
+};
diff --git a/arch/arm/boot/dts/zynq-zc770-xm010.dts b/arch/arm/boot/dts/zynq-zc770-xm010.dts
index 0dd352289a45..8596b4ce8c91 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm010.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm010.dts
@@ -15,6 +15,7 @@
ethernet0 = &gem0;
i2c0 = &i2c0;
serial0 = &uart1;
+ spi0 = &qspi;
spi1 = &spi1;
};
@@ -57,7 +58,41 @@
compatible = "atmel,24c02";
reg = <0x52>;
};
+};
+&qspi {
+ status = "okay";
+ is-dual = <0>;
+ num-cs = <1>;
+ flash@0 {
+ compatible = "n25q128a11";
+ reg = <0x0>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@qspi-fsbl-uboot {
+ label = "qspi-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@qspi-linux {
+ label = "qspi-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@qspi-device-tree {
+ label = "qspi-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@qspi-rootfs {
+ label = "qspi-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@qspi-bitstream {
+ label = "qspi-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+ };
};
&sdhci0 {
@@ -68,7 +103,7 @@
status = "okay";
num-cs = <4>;
is-decoded-cs = <0>;
- flash@1 {
+ flash@0 {
compatible = "sst25wf080", "jedec,spi-nor";
reg = <1>;
spi-max-frequency = <1000000>;
@@ -85,6 +120,7 @@
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zc770-xm011.dts b/arch/arm/boot/dts/zynq-zc770-xm011.dts
index b7f65862c022..142e7263a177 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm011.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm011.dts
@@ -47,6 +47,47 @@
};
};
+&nand0 {
+ status = "okay";
+ arm,nand-cycle-t0 = <0x4>;
+ arm,nand-cycle-t1 = <0x4>;
+ arm,nand-cycle-t2 = <0x1>;
+ arm,nand-cycle-t3 = <0x2>;
+ arm,nand-cycle-t4 = <0x2>;
+ arm,nand-cycle-t5 = <0x2>;
+ arm,nand-cycle-t6 = <0x4>;
+
+ partition@nand-fsbl-uboot {
+ label = "nand-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@nand-linux {
+ label = "nand-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@nand-device-tree {
+ label = "nand-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@nand-rootfs {
+ label = "nand-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@nand-bitstream {
+ label = "nand-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+};
+
+&smcc {
+ status = "okay";
+ arm,addr25 = <0x0>;
+ arm,nor-chip-sel0 = <0x0>;
+ arm,nor-chip-sel1 = <0x0>;
+ arm,sram-chip-sel0 = <0x0>;
+ arm,sram-chip-sel1 = <0x0>;
+};
+
&spi0 {
status = "okay";
num-cs = <4>;
@@ -54,6 +95,7 @@
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zc770-xm012.dts b/arch/arm/boot/dts/zynq-zc770-xm012.dts
index d2359b789eb8..e0e5980200cb 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm012.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm012.dts
@@ -53,6 +53,47 @@
};
};
+&nor0 {
+ status = "okay";
+ bank-width = <1>;
+ xlnx,sram-cycle-t0 = <0xb>;
+ xlnx,sram-cycle-t1 = <0xb>;
+ xlnx,sram-cycle-t2 = <0x4>;
+ xlnx,sram-cycle-t3 = <0x4>;
+ xlnx,sram-cycle-t4 = <0x3>;
+ xlnx,sram-cycle-t5 = <0x3>;
+ xlnx,sram-cycle-t6 = <0x2>;
+ partition@nor-fsbl-uboot {
+ label = "nor-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@nor-linux {
+ label = "nor-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@nor-device-tree {
+ label = "nor-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@nor-rootfs {
+ label = "nor-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@nor-bitstream {
+ label = "nor-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+};
+
+&smcc {
+ status = "okay";
+ arm,addr25 = <0x1>;
+ arm,nor-chip-sel0 = <0x1>;
+ arm,nor-chip-sel1 = <0x0>;
+ arm,sram-chip-sel0 = <0x0>;
+ arm,sram-chip-sel1 = <0x0>;
+};
+
&spi1 {
status = "okay";
num-cs = <4>;
@@ -60,5 +101,6 @@
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zc770-xm013.dts b/arch/arm/boot/dts/zynq-zc770-xm013.dts
index 4ae2c85df3a0..d91330aab9b9 100644
--- a/arch/arm/boot/dts/zynq-zc770-xm013.dts
+++ b/arch/arm/boot/dts/zynq-zc770-xm013.dts
@@ -15,6 +15,7 @@
ethernet0 = &gem1;
i2c0 = &i2c1;
serial0 = &uart0;
+ spi0 = &qspi;
spi1 = &spi0;
};
@@ -58,11 +59,46 @@
};
};
+&qspi {
+ status = "okay";
+ is-dual = <1>;
+ num-cs = <1>;
+ flash@0 {
+ compatible = "n25q128a11";
+ reg = <0x0>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@qspi-fsbl-uboot {
+ label = "qspi-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@qspi-linux {
+ label = "qspi-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@qspi-device-tree {
+ label = "qspi-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@qspi-rootfs {
+ label = "qspi-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@qspi-bitstream {
+ label = "qspi-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+ };
+};
+
&spi0 {
status = "okay";
num-cs = <4>;
is-decoded-cs = <0>;
- eeprom: eeprom@2 {
+ eeprom: eeprom@0 {
at25,byte-len = <8192>;
at25,addr-mode = <2>;
at25,page-size = <32>;
@@ -74,5 +110,6 @@
};
&uart0 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zed.dts b/arch/arm/boot/dts/zynq-zed.dts
index 6a5a93aa6552..849240fbd076 100644
--- a/arch/arm/boot/dts/zynq-zed.dts
+++ b/arch/arm/boot/dts/zynq-zed.dts
@@ -1,6 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2011 - 2015 Xilinx
* Copyright (C) 2012 National Instruments Corp.
*/
/dts-v1/;
@@ -13,6 +13,7 @@
aliases {
ethernet0 = &gem0;
serial0 = &uart1;
+ spi0 = &qspi;
mmc0 = &sdhci0;
};
@@ -26,9 +27,12 @@
stdout-path = "serial0:115200n8";
};
- usb_phy0: phy0 {
- compatible = "usb-nop-xceiv";
+ usb_phy0: phy0@e0002000 {
+ compatible = "ulpi-phy";
#phy-cells = <0>;
+ reg = <0xe0002000 0x1000>;
+ view-port = <0x0170>;
+ drv-vbus;
};
};
@@ -47,11 +51,50 @@
};
};
+&qspi {
+ u-boot,dm-pre-reloc;
+ status = "okay";
+ is-dual = <0>;
+ num-cs = <1>;
+ flash@0 {
+ compatible = "spansion,s25fl256s", "spi-flash";
+ reg = <0>;
+ spi-tx-bus-width = <1>;
+ spi-rx-bus-width = <4>;
+ spi-max-frequency = <50000000>;
+ m25p,fast-read;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@qspi-fsbl-uboot {
+ label = "qspi-fsbl-uboot";
+ reg = <0x0 0x100000>;
+ };
+ partition@qspi-linux {
+ label = "qspi-linux";
+ reg = <0x100000 0x500000>;
+ };
+ partition@qspi-device-tree {
+ label = "qspi-device-tree";
+ reg = <0x600000 0x20000>;
+ };
+ partition@qspi-rootfs {
+ label = "qspi-rootfs";
+ reg = <0x620000 0x5E0000>;
+ };
+ partition@qspi-bitstream {
+ label = "qspi-bitstream";
+ reg = <0xC00000 0x400000>;
+ };
+ };
+};
+
&sdhci0 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/boot/dts/zynq-zturn.dts b/arch/arm/boot/dts/zynq-zturn.dts
index 5ec616ebca08..b38704657960 100644
--- a/arch/arm/boot/dts/zynq-zturn.dts
+++ b/arch/arm/boot/dts/zynq-zturn.dts
@@ -54,7 +54,7 @@
label = "K1";
gpios = <&gpio0 0x32 0x1>;
linux,code = <0x66>;
- wakeup-source;
+ gpio-key,wakeup;
autorepeat;
};
};
diff --git a/arch/arm/boot/dts/zynq-zybo.dts b/arch/arm/boot/dts/zynq-zybo.dts
index 755f6f109d5a..0ac54ebbdc8b 100644
--- a/arch/arm/boot/dts/zynq-zybo.dts
+++ b/arch/arm/boot/dts/zynq-zybo.dts
@@ -1,6 +1,6 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2011 - 2015 Xilinx
* Copyright (C) 2012 National Instruments Corp.
*/
/dts-v1/;
@@ -13,6 +13,7 @@
aliases {
ethernet0 = &gem0;
serial0 = &uart1;
+ spi0 = &qspi;
mmc0 = &sdhci0;
};
@@ -48,11 +49,18 @@
};
};
+&qspi {
+ u-boot,dm-pre-reloc;
+ status = "okay";
+};
+
&sdhci0 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
&uart1 {
+ u-boot,dm-pre-reloc;
status = "okay";
};
diff --git a/arch/arm/configs/xilinx_zynq_defconfig b/arch/arm/configs/xilinx_zynq_defconfig
new file mode 100644
index 000000000000..031778caf47e
--- /dev/null
+++ b/arch/arm/configs/xilinx_zynq_defconfig
@@ -0,0 +1,242 @@
+CONFIG_LOCALVERSION="-xilinx"
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_BUG is not set
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_ZYNQ=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_754327=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_XILINX=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_ZYNQ_CPUIDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_COMPACTION is not set
+CONFIG_CMA=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_CAN=y
+CONFIG_CAN_XILINXCAN=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_PL353=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_OF_OVERLAY=y
+CONFIG_OF_CONFIGFS=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_XILINX_TRAFGEN=y
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_NETDEVICES=y
+CONFIG_MACB=y
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+CONFIG_E1000E=y
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+CONFIG_R8169=y
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_XILINX_EMACLITE=y
+CONFIG_XILINX_AXI_EMAC=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MARVELL_PHY=y
+CONFIG_VITESSE_PHY=y
+CONFIG_INPUT_SPARSEKMAP=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_GPIO_POLLED=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_XILINX_PS_UART=y
+CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_CADENCE=y
+CONFIG_SPI=y
+CONFIG_SPI_CADENCE=y
+CONFIG_SPI_XILINX=y
+CONFIG_SPI_ZYNQ_QSPI=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_XILINX=y
+CONFIG_GPIO_ZYNQ=y
+CONFIG_PMBUS=y
+CONFIG_SENSORS_UCD9000=y
+CONFIG_SENSORS_UCD9200=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_XILINX_WATCHDOG=y
+CONFIG_CADENCE_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_XILINX=y
+CONFIG_VIDEO_XILINX_CFA=y
+CONFIG_VIDEO_XILINX_CRESAMPLE=y
+CONFIG_VIDEO_XILINX_REMAPPER=y
+CONFIG_VIDEO_XILINX_RGB2YUV=y
+CONFIG_VIDEO_XILINX_SCALER=y
+CONFIG_VIDEO_XILINX_SWITCH=y
+CONFIG_VIDEO_XILINX_TPG=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7604=y
+CONFIG_DRM=y
+CONFIG_DRM_XILINX=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_ADI=y
+CONFIG_SND_SOC_ADI_AXI_I2S=y
+CONFIG_SND_SOC_ADI_AXI_SPDIF=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_XILINX=y
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_ZERO=m
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_ARASAN=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=y
+CONFIG_LEDS_TRIGGER_CAMERA=y
+CONFIG_EDAC=y
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PCF8563=y
+CONFIG_DMADEVICES=y
+CONFIG_PL330_DMA=y
+CONFIG_XILINX_DMA_ENGINES=y
+CONFIG_XILINX_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_PDRV_GENIRQ=y
+CONFIG_UIO_XILINX_APM=y
+CONFIG_COMMON_CLK_SI570=y
+CONFIG_REMOTEPROC=y
+CONFIG_ZYNQ_REMOTEPROC=m
+CONFIG_MEMORY=y
+CONFIG_IIO=y
+CONFIG_XILINX_XADC=y
+CONFIG_RAS=y
+CONFIG_FPGA=y
+CONFIG_FPGA_MGR_ZYNQ_FPGA=y
+CONFIG_FPGA_MGR_ZYNQ_AFI_FPGA=y
+CONFIG_FPGA_BRIDGE=y
+CONFIG_XILINX_PR_DECOUPLER=y
+CONFIG_FPGA_REGION=y
+CONFIG_OF_FPGA_REGION=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_FTRACE is not set
diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h
index 7a88f160b1fb..136a9506f1ed 100644
--- a/arch/arm/include/asm/hardirq.h
+++ b/arch/arm/include/asm/hardirq.h
@@ -7,7 +7,7 @@
#include <asm/irq.h>
/* number of IPIS _not_ including IPI_CPU_BACKTRACE */
-#define NR_IPI 7
+#define NR_IPI 16
typedef struct {
unsigned int __softirq_pending;
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index a91f21e3c5b5..bbdfd74ff98a 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -120,4 +120,7 @@ struct of_cpu_method {
*/
extern void smp_set_ops(const struct smp_operations *);
+extern int set_ipi_handler(int ipinr, void *handler, char *desc);
+extern void clear_ipi_handler(int ipinr);
+
#endif /* ifndef __ASM_ARM_SMP_H */
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index a137608cd197..8003ab884f30 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -507,20 +507,59 @@ void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
__smp_cross_call = fn;
}
-static const char *ipi_types[NR_IPI] __tracepoint_string = {
-#define S(x,s) [x] = s
- S(IPI_WAKEUP, "CPU wakeup interrupts"),
- S(IPI_TIMER, "Timer broadcast interrupts"),
- S(IPI_RESCHEDULE, "Rescheduling interrupts"),
- S(IPI_CALL_FUNC, "Function call interrupts"),
- S(IPI_CPU_STOP, "CPU stop interrupts"),
- S(IPI_IRQ_WORK, "IRQ work interrupts"),
- S(IPI_COMPLETION, "completion interrupts"),
+struct ipi {
+ const char *desc;
+ void (*handler)(void);
+};
+
+static void ipi_cpu_stop(void);
+static void ipi_complete(void);
+
+#define IPI_DESC_STRING_IPI_WAKEUP "CPU wakeup interrupts"
+#define IPI_DESC_STRING_IPI_TIMER "Timer broadcast interrupts"
+#define IPI_DESC_STRING_IPI_RESCHEDULE "Rescheduling interrupts"
+#define IPI_DESC_STRING_IPI_CALL_FUNC "Function call interrupts"
+#define IPI_DESC_STRING_IPI_CPU_STOP "CPU stop interrupts"
+#define IPI_DESC_STRING_IPI_IRQ_WORK "IRQ work interrupts"
+#define IPI_DESC_STRING_IPI_COMPLETION "completion interrupts"
+
+#define IPI_DESC_STR(x) IPI_DESC_STRING_ ## x
+
+static const char* ipi_desc_strings[] __tracepoint_string =
+ {
+ [IPI_WAKEUP] = IPI_DESC_STR(IPI_WAKEUP),
+ [IPI_TIMER] = IPI_DESC_STR(IPI_TIMER),
+ [IPI_RESCHEDULE] = IPI_DESC_STR(IPI_RESCHEDULE),
+ [IPI_CALL_FUNC] = IPI_DESC_STR(IPI_CALL_FUNC),
+ [IPI_CPU_STOP] = IPI_DESC_STR(IPI_CPU_STOP),
+ [IPI_IRQ_WORK] = IPI_DESC_STR(IPI_IRQ_WORK),
+ [IPI_COMPLETION] = IPI_DESC_STR(IPI_COMPLETION),
+ };
+
+
+static void tick_receive_broadcast_local(void)
+{
+ tick_receive_broadcast();
+}
+
+static struct ipi ipi_types[NR_IPI] = {
+#define S(x, f) [x].desc = IPI_DESC_STR(x), [x].handler = f
+ S(IPI_WAKEUP, NULL),
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+ S(IPI_TIMER, tick_receive_broadcast_local),
+#endif
+ S(IPI_RESCHEDULE, scheduler_ipi),
+ S(IPI_CALL_FUNC, generic_smp_call_function_interrupt),
+ S(IPI_CPU_STOP, ipi_cpu_stop),
+#ifdef CONFIG_IRQ_WORK
+ S(IPI_IRQ_WORK, irq_work_run),
+#endif
+ S(IPI_COMPLETION, ipi_complete),
};
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
- trace_ipi_raise_rcuidle(target, ipi_types[ipinr]);
+ trace_ipi_raise_rcuidle(target, ipi_desc_strings[ipinr]);
__smp_cross_call(target, ipinr);
}
@@ -529,13 +568,13 @@ void show_ipi_list(struct seq_file *p, int prec)
unsigned int cpu, i;
for (i = 0; i < NR_IPI; i++) {
- seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
-
- for_each_online_cpu(cpu)
- seq_printf(p, "%10u ",
- __get_irq_stat(cpu, ipi_irqs[i]));
-
- seq_printf(p, " %s\n", ipi_types[i]);
+ if (ipi_types[i].handler) {
+ seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);
+ for_each_present_cpu(cpu)
+ seq_printf(p, "%10u ",
+ __get_irq_stat(cpu, ipi_irqs[i]));
+ seq_printf(p, " %s\n", ipi_types[i].desc);
+ }
}
}
@@ -585,8 +624,10 @@ static DEFINE_RAW_SPINLOCK(stop_lock);
/*
* ipi_cpu_stop - handle IPI from smp_send_stop()
*/
-static void ipi_cpu_stop(unsigned int cpu)
+static void ipi_cpu_stop(void)
{
+ unsigned int cpu = smp_processor_id();
+
if (system_state <= SYSTEM_RUNNING) {
raw_spin_lock(&stop_lock);
pr_crit("CPU%u: stopping\n", cpu);
@@ -613,8 +654,10 @@ int register_ipi_completion(struct completion *completion, int cpu)
return IPI_COMPLETION;
}
-static void ipi_complete(unsigned int cpu)
+static void ipi_complete(void)
{
+ unsigned int cpu = smp_processor_id();
+
complete(per_cpu(cpu_completion, cpu));
}
@@ -631,71 +674,48 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
unsigned int cpu = smp_processor_id();
struct pt_regs *old_regs = set_irq_regs(regs);
- if ((unsigned)ipinr < NR_IPI) {
- trace_ipi_entry_rcuidle(ipi_types[ipinr]);
+ if (ipi_types[ipinr].handler) {
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
- }
-
- switch (ipinr) {
- case IPI_WAKEUP:
- break;
-
-#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
- case IPI_TIMER:
- irq_enter();
- tick_receive_broadcast();
- irq_exit();
- break;
-#endif
-
- case IPI_RESCHEDULE:
- scheduler_ipi();
- break;
-
- case IPI_CALL_FUNC:
irq_enter();
- generic_smp_call_function_interrupt();
+ (*ipi_types[ipinr].handler)();
irq_exit();
- break;
+ } else
+ pr_debug("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
- case IPI_CPU_STOP:
- irq_enter();
- ipi_cpu_stop(cpu);
- irq_exit();
- break;
+ set_irq_regs(old_regs);
+}
-#ifdef CONFIG_IRQ_WORK
- case IPI_IRQ_WORK:
- irq_enter();
- irq_work_run();
- irq_exit();
- break;
-#endif
+/*
+ * set_ipi_handler:
+ * Interface provided for a kernel module to specify an IPI handler function.
+ */
+int set_ipi_handler(int ipinr, void *handler, char *desc)
+{
+ unsigned int cpu = smp_processor_id();
- case IPI_COMPLETION:
- irq_enter();
- ipi_complete(cpu);
- irq_exit();
- break;
+ if (ipi_types[ipinr].handler) {
+ pr_crit("CPU%u: IPI handler 0x%x already registered to %pf\n",
+ cpu, ipinr, ipi_types[ipinr].handler);
+ return -1;
+ }
- case IPI_CPU_BACKTRACE:
- printk_nmi_enter();
- irq_enter();
- nmi_cpu_backtrace(regs);
- irq_exit();
- printk_nmi_exit();
- break;
+ ipi_types[ipinr].handler = handler;
+ ipi_types[ipinr].desc = desc;
- default:
- pr_crit("CPU%u: Unknown IPI message 0x%x\n",
- cpu, ipinr);
- break;
- }
+ return 0;
+}
+EXPORT_SYMBOL(set_ipi_handler);
- if ((unsigned)ipinr < NR_IPI)
- trace_ipi_exit_rcuidle(ipi_types[ipinr]);
- set_irq_regs(old_regs);
+/*
+ * clear_ipi_handler:
+ * Interface provided for a kernel module to clear an IPI handler function.
+ */
+void clear_ipi_handler(int ipinr)
+{
+ ipi_types[ipinr].handler = NULL;
+ ipi_types[ipinr].desc = NULL;
}
+EXPORT_SYMBOL(clear_ipi_handler);
void smp_send_reschedule(int cpu)
{
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index 1ca633e3d024..557bfe794d29 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -17,3 +17,19 @@ config ARCH_ZYNQ
select SOC_BUS
help
Support for Xilinx Zynq ARM Cortex A9 Platform
+
+if ARCH_ZYNQ
+
+menu "Xilinx Specific Options"
+
+config XILINX_PREFETCH
+ bool "Cache Prefetch"
+ default y
+ help
+ This option turns on L1 & L2 cache prefetching to get the best performance
+ in many cases. This may not always be the best performance depending on
+ the usage.
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 9df74cd85fd0..dbb75be53deb 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -4,5 +4,9 @@
#
# Common support
-obj-y := common.o slcr.o pm.o
+obj-y := common.o efuse.o slcr.o zynq_ocm.o pm.o
+
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
+ORIG_AFLAGS := $(KBUILD_AFLAGS)
+KBUILD_AFLAGS = $(subst -march=armv6k,,$(ORIG_AFLAGS))
+obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 3a4248fd7962..0a86e6fc8fb6 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -95,6 +95,7 @@ static void __init zynq_init_late(void)
{
zynq_core_pm_init();
zynq_pm_late_init();
+ zynq_prefetch_init();
}
/**
@@ -175,6 +176,7 @@ static void __init zynq_map_io(void)
static void __init zynq_irq_init(void)
{
+ zynq_early_efuse_init();
zynq_early_slcr_init();
irqchip_init();
}
@@ -186,8 +188,13 @@ static const char * const zynq_dt_match[] = {
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
/* 64KB way size, 8-way associativity, parity disabled */
- .l2c_aux_val = 0x00400000,
+#ifdef CONFIG_XILINX_PREFETCH
+ .l2c_aux_val = 0x30400000,
+ .l2c_aux_mask = 0xcfbfffff,
+#else
+ .l2c_aux_val = 0x00400000,
.l2c_aux_mask = 0xffbfffff,
+#endif
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = zynq_irq_init,
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 60e662324699..5816d57e5a5d 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -15,8 +15,12 @@ extern void zynq_slcr_cpu_stop(int cpu);
extern void zynq_slcr_cpu_start(int cpu);
extern bool zynq_slcr_cpu_state_read(int cpu);
extern void zynq_slcr_cpu_state_write(int cpu, bool die);
+extern u32 zynq_slcr_get_ocm_config(void);
extern u32 zynq_slcr_get_device_id(void);
+extern bool zynq_efuse_cpu_state(int cpu);
+extern int zynq_early_efuse_init(void);
+
#ifdef CONFIG_SMP
extern char zynq_secondary_trampoline;
extern char zynq_secondary_trampoline_jump;
@@ -25,9 +29,31 @@ extern int zynq_cpun_start(u32 address, int cpu);
extern const struct smp_operations zynq_smp_ops;
#endif
+extern void zynq_slcr_init_preload_fpga(void);
+extern void zynq_slcr_init_postload_fpga(void);
+
+extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
void zynq_pm_late_init(void);
+extern unsigned int zynq_sys_suspend_sz;
+int zynq_sys_suspend(void __iomem *ddrc_base, void __iomem *slcr_base);
+
+static inline void zynq_prefetch_init(void)
+{
+ /*
+ * Enable prefetching in aux control register. L2 prefetch must
+ * only be enabled if the slave supports it (PL310 does)
+ */
+ asm volatile ("mrc p15, 0, r1, c1, c0, 1\n"
+#ifdef CONFIG_XILINX_PREFETCH
+ "orr r1, r1, #6\n"
+#else
+ "bic r1, r1, #6\n"
+#endif
+ "mcr p15, 0, r1, c1, c0, 1\n"
+ : : : "r1");
+}
static inline void zynq_core_pm_init(void)
{
diff --git a/arch/arm/mach-zynq/efuse.c b/arch/arm/mach-zynq/efuse.c
new file mode 100644
index 000000000000..d31a5822ec65
--- /dev/null
+++ b/arch/arm/mach-zynq/efuse.c
@@ -0,0 +1,75 @@
+/*
+ * Xilinx EFUSE driver
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include "common.h"
+
+#define EFUSE_STATUS_OFFSET 0x10
+
+/* 0 means cpu1 is working, 1 means cpu1 is broken */
+#define EFUSE_STATUS_CPU_BIT BIT(7)
+
+void __iomem *zynq_efuse_base;
+
+/**
+ * zynq_efuse_cpu_state - Read/write cpu state
+ * @cpu: cpu number
+ *
+ * Return: true if cpu is running, false if cpu is broken
+ */
+bool zynq_efuse_cpu_state(int cpu)
+{
+ u32 state;
+
+ if (!cpu)
+ return true;
+
+ state = readl(zynq_efuse_base + EFUSE_STATUS_OFFSET);
+ state &= EFUSE_STATUS_CPU_BIT;
+
+ if (!state)
+ return true;
+
+ return false;
+}
+
+/**
+ * zynq_early_efuse_init - Early efuse init function
+ *
+ * Return: 0 on success, negative errno otherwise.
+ *
+ * Called very early during boot from platform code.
+ */
+int __init zynq_early_efuse_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-efuse");
+ if (!np) {
+ pr_err("%s: no efuse node found\n", __func__);
+ BUG();
+ }
+
+ zynq_efuse_base = of_iomap(np, 0);
+ if (!zynq_efuse_base) {
+ pr_err("%s: Unable to map I/O memory\n", __func__);
+ BUG();
+ }
+
+ np->data = (__force void *)zynq_efuse_base;
+
+ pr_info("%s mapped to %p\n", np->name, zynq_efuse_base);
+
+ of_node_put(np);
+
+ return 0;
+}
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
index e65ee8180c35..5d76546fd8b2 100644
--- a/arch/arm/mach-zynq/platsmp.c
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include <linux/irqchip/arm-gic.h>
#include "common.h"
@@ -30,6 +31,7 @@ int zynq_cpun_start(u32 address, int cpu)
{
u32 trampoline_code_size = &zynq_secondary_trampoline_end -
&zynq_secondary_trampoline;
+ u32 phy_cpuid = cpu_logical_map(cpu);
/* MS: Expectation that SLCR are directly map and accessible */
/* Not possible to jump to non aligned address */
@@ -39,7 +41,7 @@ int zynq_cpun_start(u32 address, int cpu)
u32 trampoline_size = &zynq_secondary_trampoline_jump -
&zynq_secondary_trampoline;
- zynq_slcr_cpu_stop(cpu);
+ zynq_slcr_cpu_stop(phy_cpuid);
if (address) {
if (__pa(PAGE_OFFSET)) {
zero = ioremap(0, trampoline_code_size);
@@ -68,7 +70,7 @@ int zynq_cpun_start(u32 address, int cpu)
if (__pa(PAGE_OFFSET))
iounmap(zero);
}
- zynq_slcr_cpu_start(cpu);
+ zynq_slcr_cpu_start(phy_cpuid);
return 0;
}
@@ -81,6 +83,9 @@ EXPORT_SYMBOL(zynq_cpun_start);
static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
+ if (!zynq_efuse_cpu_state(cpu))
+ return -1;
+
return zynq_cpun_start(__pa_symbol(secondary_startup), cpu);
}
@@ -113,6 +118,7 @@ static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
static void zynq_secondary_init(unsigned int cpu)
{
zynq_core_pm_init();
+ zynq_prefetch_init();
}
#ifdef CONFIG_HOTPLUG_CPU
diff --git a/arch/arm/mach-zynq/pm.c b/arch/arm/mach-zynq/pm.c
index 8ba450ab559c..b9445a654b59 100644
--- a/arch/arm/mach-zynq/pm.c
+++ b/arch/arm/mach-zynq/pm.c
@@ -7,6 +7,14 @@
* Sören Brinkmann <soren.brinkmann@xilinx.com>
*/
+#include <linux/clk/zynq.h>
+#include <linux/genalloc.h>
+#include <linux/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/fncpy.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/map.h>
+#include <asm/suspend.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -22,6 +30,165 @@
static void __iomem *ddrc_base;
+#ifdef CONFIG_SUSPEND
+static int (*zynq_suspend_ptr)(void __iomem *, void __iomem *);
+
+static int zynq_pm_prepare_late(void)
+{
+ return zynq_clk_suspend_early();
+}
+
+static void zynq_pm_wake(void)
+{
+ zynq_clk_resume_late();
+}
+
+static int zynq_pm_suspend(unsigned long arg)
+{
+ u32 reg;
+ int do_ddrpll_bypass = 1;
+
+ /* Topswitch clock stop disable */
+ zynq_clk_topswitch_disable();
+
+ if (!zynq_suspend_ptr || !ddrc_base) {
+ do_ddrpll_bypass = 0;
+ } else {
+ /* enable DDRC self-refresh mode */
+ reg = readl(ddrc_base + DDRC_CTRL_REG1_OFFS);
+ reg |= DDRC_SELFREFRESH_MASK;
+ writel(reg, ddrc_base + DDRC_CTRL_REG1_OFFS);
+ }
+
+ if (do_ddrpll_bypass) {
+ /*
+ * Going this way will turn off DDR related clocks and the DDR
+ * PLL. I.e. We might brake sub systems relying on any of this
+ * clocks. And even worse: If there are any other masters in the
+ * system (e.g. in the PL) accessing DDR they are screwed.
+ */
+ flush_cache_all();
+ if (zynq_suspend_ptr(ddrc_base, zynq_slcr_base))
+ pr_warn("DDR self refresh failed.\n");
+ } else {
+ WARN_ONCE(1, "DRAM self-refresh not available\n");
+ cpu_do_idle();
+ }
+
+ /* disable DDRC self-refresh mode */
+ if (do_ddrpll_bypass) {
+ reg = readl(ddrc_base + DDRC_CTRL_REG1_OFFS);
+ reg &= ~DDRC_SELFREFRESH_MASK;
+ writel(reg, ddrc_base + DDRC_CTRL_REG1_OFFS);
+ }
+
+ /* Topswitch clock stop enable */
+ zynq_clk_topswitch_enable();
+
+ return 0;
+}
+
+static int zynq_pm_enter(suspend_state_t suspend_state)
+{
+ switch (suspend_state) {
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ cpu_suspend(0, zynq_pm_suspend);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct platform_suspend_ops zynq_pm_ops = {
+ .prepare_late = zynq_pm_prepare_late,
+ .enter = zynq_pm_enter,
+ .wake = zynq_pm_wake,
+ .valid = suspend_valid_only_mem,
+};
+
+/**
+ * zynq_pm_remap_ocm() - Remap OCM
+ * Returns a pointer to the mapped memory or NULL.
+ *
+ * Remap the OCM.
+ */
+static void __iomem *zynq_pm_remap_ocm(void)
+{
+ struct device_node *np;
+ const char *comp = "xlnx,zynq-ocmc-1.0";
+ void __iomem *base = NULL;
+
+ np = of_find_compatible_node(NULL, NULL, comp);
+ if (np) {
+ struct device *dev;
+ unsigned long pool_addr;
+ unsigned long pool_addr_virt;
+ struct gen_pool *pool;
+
+ of_node_put(np);
+
+ dev = &(of_find_device_by_node(np)->dev);
+
+ /* Get OCM pool from device tree or platform data */
+ pool = gen_pool_get(dev, NULL);
+ if (!pool) {
+ pr_warn("%s: OCM pool is not available\n", __func__);
+ return NULL;
+ }
+
+ pool_addr_virt = gen_pool_alloc(pool, zynq_sys_suspend_sz);
+ if (!pool_addr_virt) {
+ pr_warn("%s: Can't get OCM poll\n", __func__);
+ return NULL;
+ }
+ pool_addr = gen_pool_virt_to_phys(pool, pool_addr_virt);
+ if (!pool_addr) {
+ pr_warn("%s: Can't get physical address of OCM pool\n",
+ __func__);
+ return NULL;
+ }
+ base = __arm_ioremap_exec(pool_addr, zynq_sys_suspend_sz,
+ MT_MEMORY_RWX);
+ if (!base) {
+ pr_warn("%s: IOremap OCM pool failed\n", __func__);
+ return NULL;
+ }
+ pr_debug("%s: Remap OCM %s from %lx to %lx\n", __func__, comp,
+ pool_addr_virt, (unsigned long)base);
+ } else {
+ pr_warn("%s: no compatible node found for '%s'\n", __func__,
+ comp);
+ }
+
+ return base;
+}
+
+static void zynq_pm_suspend_init(void)
+{
+ void __iomem *ocm_base = zynq_pm_remap_ocm();
+
+ if (!ocm_base) {
+ pr_warn("%s: Unable to map OCM.\n", __func__);
+ } else {
+ /*
+ * Copy code to suspend system into OCM. The suspend code
+ * needs to run from OCM as DRAM may no longer be available
+ * when the PLL is stopped.
+ */
+ zynq_suspend_ptr = fncpy((__force void *)ocm_base,
+ (__force void *)&zynq_sys_suspend,
+ zynq_sys_suspend_sz);
+ }
+
+ suspend_set_ops(&zynq_pm_ops);
+}
+#else /* CONFIG_SUSPEND */
+static void zynq_pm_suspend_init(void) { };
+#endif /* CONFIG_SUSPEND */
+
/**
* zynq_pm_ioremap() - Create IO mappings
* @comp: DT compatible string
@@ -68,4 +235,7 @@ void __init zynq_pm_late_init(void)
reg |= DDRC_CLOCKSTOP_MASK;
writel(reg, ddrc_base + DDRC_DRAM_PARAM_REG3_OFFS);
}
+
+ /* set up suspend */
+ zynq_pm_suspend_init();
}
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
index 37707614885a..18a36c48db2e 100644
--- a/arch/arm/mach-zynq/slcr.c
+++ b/arch/arm/mach-zynq/slcr.c
@@ -16,10 +16,13 @@
/* register offsets */
#define SLCR_UNLOCK_OFFSET 0x8 /* SCLR unlock register */
#define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */
+#define SLCR_FPGA_RST_CTRL_OFFSET 0x240 /* FPGA Software Reset Control */
#define SLCR_A9_CPU_RST_CTRL_OFFSET 0x244 /* CPU Software Reset Control */
#define SLCR_REBOOT_STATUS_OFFSET 0x258 /* PS Reboot Status */
#define SLCR_PSS_IDCODE 0x530 /* PS IDCODE */
#define SLCR_L2C_RAM 0xA1C /* L2C_RAM in AR#54190 */
+#define SLCR_LVL_SHFTR_EN_OFFSET 0x900 /* Level Shifters Enable */
+#define SLCR_OCM_CFG_OFFSET 0x910 /* OCM Address Mapping */
#define SLCR_UNLOCK_MAGIC 0xDF0D
#define SLCR_A9_CPU_CLKSTOP 0x10
@@ -27,7 +30,7 @@
#define SLCR_PSS_IDCODE_DEVICE_SHIFT 12
#define SLCR_PSS_IDCODE_DEVICE_MASK 0x1F
-static void __iomem *zynq_slcr_base;
+void __iomem *zynq_slcr_base;
static struct regmap *zynq_slcr_regmap;
/**
@@ -116,6 +119,48 @@ static struct notifier_block zynq_slcr_restart_nb = {
};
/**
+ * zynq_slcr_get_ocm_config - Get SLCR OCM config
+ *
+ * return: OCM config bits
+ */
+u32 zynq_slcr_get_ocm_config(void)
+{
+ u32 ret;
+
+ zynq_slcr_read(&ret, SLCR_OCM_CFG_OFFSET);
+ return ret;
+}
+
+/**
+ * zynq_slcr_init_preload_fpga - Disable communication from the PL to PS.
+ */
+void zynq_slcr_init_preload_fpga(void)
+{
+ /* Assert FPGA top level output resets */
+ zynq_slcr_write(0xF, SLCR_FPGA_RST_CTRL_OFFSET);
+
+ /* Disable level shifters */
+ zynq_slcr_write(0, SLCR_LVL_SHFTR_EN_OFFSET);
+
+ /* Enable output level shifters */
+ zynq_slcr_write(0xA, SLCR_LVL_SHFTR_EN_OFFSET);
+}
+EXPORT_SYMBOL(zynq_slcr_init_preload_fpga);
+
+/**
+ * zynq_slcr_init_postload_fpga - Re-enable communication from the PL to PS.
+ */
+void zynq_slcr_init_postload_fpga(void)
+{
+ /* Enable level shifters */
+ zynq_slcr_write(0xf, SLCR_LVL_SHFTR_EN_OFFSET);
+
+ /* Deassert AXI interface resets */
+ zynq_slcr_write(0, SLCR_FPGA_RST_CTRL_OFFSET);
+}
+EXPORT_SYMBOL(zynq_slcr_init_postload_fpga);
+
+/**
* zynq_slcr_cpu_start - Start cpu
* @cpu: cpu number
*/
diff --git a/arch/arm/mach-zynq/suspend.S b/arch/arm/mach-zynq/suspend.S
new file mode 100644
index 000000000000..f3f8440e8018
--- /dev/null
+++ b/arch/arm/mach-zynq/suspend.S
@@ -0,0 +1,185 @@
+/*
+ * Suspend support for Zynq
+ *
+ * Copyright (C) 2012 Xilinx
+ *
+ * Soren Brinkmann <soren.brinkmann@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/linkage.h>
+
+#define ARMPLL_CTRL_OFFS 0x100
+#define DDRPLL_CTRL_OFFS 0x104
+#define PLLSTATUS_OFFS 0x10c
+#define DDR_CLK_CTRL_OFFS 0x124
+#define DCI_CLK_CTRL_OFFS 0x128
+#define MODE_STS_OFFS 0x54
+
+#define PLL_RESET_MASK 1
+#define PLL_PWRDWN_MASK (1 << 1)
+#define PLL_BYPASS_MASK (1 << 4)
+#define DCICLK_ENABLE_MASK 1
+#define DDRCLK_ENABLE_MASK 3
+#define ARM_LOCK_MASK (1 << 0)
+#define DDR_LOCK_MASK (1 << 1)
+#define DDRC_STATUS_MASK 7
+
+#define DDRC_OPMODE_SR 3
+#define MAXTRIES 100
+
+ .text
+ .align 3
+
+/**
+ * zynq_sys_suspend - Enter suspend
+ * @ddrc_base: Base address of the DDRC
+ * @slcr_base: Base address of the SLCR
+ * Returns -1 if DRAM subsystem is not gated off, 0 otherwise.
+ *
+ * This function is moved into OCM and finishes the suspend operation. I.e. DDR
+ * related clocks are gated off and the DDR PLL is bypassed.
+ */
+ENTRY(zynq_sys_suspend)
+ push {r4 - r7}
+
+ /* Check DDRC is in self-refresh mode */
+ ldr r2, [r0, #MODE_STS_OFFS]
+ and r2, #DDRC_STATUS_MASK
+ cmp r2, #DDRC_OPMODE_SR
+ movweq r3, #0xff00
+ bne suspend
+
+ mov r3, #MAXTRIES
+ movw r4, #0xfff0
+ movt r4, #0x1f
+ /* Wait for command queue empty */
+1: subs r3, #1
+ movweq r3, #0xff00
+ beq suspend
+ dsb sy
+ ldr r2, [r0, #MODE_STS_OFFS]
+ ands r2, r4
+ bne 1b
+
+ dsb sy
+
+ /*
+ * Wait for DDRC pipeline/queues to drain.
+ * We should wait ~40 DDR cycles. DDR is still at full speed while the
+ * CPU might already run in PLL bypass mode. The fastest speed the CPU
+ * runs at is ~1 GHz ~ 2 * DDR speed.
+ */
+ mov r3, #160
+1: nop
+ subs r3, #1
+ bne 1b
+
+ dsb sy
+
+ /* read back CAM status once more */
+ ldr r2, [r0, #MODE_STS_OFFS]
+ ands r2, r4
+ movwne r3, #0xff00
+ bne suspend
+
+ /* Stop DDR clocks */
+ ldr r2, [r1, #DDR_CLK_CTRL_OFFS]
+ bic r2, #DDRCLK_ENABLE_MASK
+ str r2, [r1, #DDR_CLK_CTRL_OFFS]
+
+ dmb st
+
+ ldr r2, [r1, #DCI_CLK_CTRL_OFFS]
+ bic r2, #DCICLK_ENABLE_MASK
+ str r2, [r1, #DCI_CLK_CTRL_OFFS]
+
+ dmb st
+
+ /* Bypass and powerdown DDR PLL */
+ ldr r2, [r1, #DDRPLL_CTRL_OFFS]
+ orr r2, #PLL_BYPASS_MASK
+ str r2, [r1, #DDRPLL_CTRL_OFFS]
+ orr r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
+ str r2, [r1, #DDRPLL_CTRL_OFFS]
+
+ /* Bypass and powerdown ARM PLL */
+ ldr r2, [r1, #ARMPLL_CTRL_OFFS]
+ orr r2, #PLL_BYPASS_MASK
+ str r2, [r1, #ARMPLL_CTRL_OFFS]
+ orr r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
+ str r2, [r1, #ARMPLL_CTRL_OFFS]
+
+suspend:
+ dsb sy
+ wfi
+ dsb sy
+ cmp r3, #0xff00
+ moveq r0, #-1
+ beq exit
+
+ /* Power up ARM PLL */
+ ldr r2, [r1, #ARMPLL_CTRL_OFFS]
+ bic r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
+ str r2, [r1, #ARMPLL_CTRL_OFFS]
+ /* wait for lock */
+1: ldr r2, [r1, #PLLSTATUS_OFFS]
+ ands r2, #ARM_LOCK_MASK
+ beq 1b
+
+ dsb sy
+
+ /* Disable ARM PLL bypass */
+ ldr r2, [r1, #ARMPLL_CTRL_OFFS]
+ bic r2, #PLL_BYPASS_MASK
+ str r2, [r1, #ARMPLL_CTRL_OFFS]
+
+ dmb st
+
+ /* Power up DDR PLL */
+ ldr r2, [r1, #DDRPLL_CTRL_OFFS]
+ bic r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
+ str r2, [r1, #DDRPLL_CTRL_OFFS]
+ /* wait for lock */
+1: ldr r2, [r1, #PLLSTATUS_OFFS]
+ ands r2, #DDR_LOCK_MASK
+ beq 1b
+
+ dsb sy
+
+ /* Disable DDR PLL bypass */
+ ldr r2, [r1, #DDRPLL_CTRL_OFFS]
+ bic r2, #PLL_BYPASS_MASK
+ str r2, [r1, #DDRPLL_CTRL_OFFS]
+
+ dmb st
+
+ /* Start DDR clocks */
+ ldr r2, [r1, #DCI_CLK_CTRL_OFFS]
+ orr r2, #DCICLK_ENABLE_MASK
+ str r2, [r1, #DCI_CLK_CTRL_OFFS]
+
+ dmb st
+
+ ldr r2, [r1, #DDR_CLK_CTRL_OFFS]
+ orr r2, #DDRCLK_ENABLE_MASK
+ str r2, [r1, #DDR_CLK_CTRL_OFFS]
+
+ dsb sy
+
+ mov r0, #0
+exit: pop {r4 - r7}
+ bx lr
+
+ENTRY(zynq_sys_suspend_sz)
+ .word . - zynq_sys_suspend
+
+ ENDPROC(zynq_sys_suspend)
diff --git a/arch/arm/mach-zynq/zynq_ocm.c b/arch/arm/mach-zynq/zynq_ocm.c
new file mode 100644
index 000000000000..324b7c125bf5
--- /dev/null
+++ b/arch/arm/mach-zynq/zynq_ocm.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 Xilinx
+ *
+ * Based on "Generic on-chip SRAM allocation driver"
+ *
+ * Copyright (C) 2012 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/genalloc.h>
+
+#include "common.h"
+
+#define ZYNQ_OCM_HIGHADDR 0xfffc0000
+#define ZYNQ_OCM_LOWADDR 0x0
+#define ZYNQ_OCM_BLOCK_SIZE 0x10000
+#define ZYNQ_OCM_BLOCKS 4
+#define ZYNQ_OCM_GRANULARITY 32
+
+#define ZYNQ_OCM_PARITY_CTRL 0x0
+#define ZYNQ_OCM_PARITY_ENABLE 0x1e
+
+#define ZYNQ_OCM_PARITY_ERRADDRESS 0x4
+
+#define ZYNQ_OCM_IRQ_STS 0x8
+#define ZYNQ_OCM_IRQ_STS_ERR_MASK 0x7
+
+struct zynq_ocm_dev {
+ void __iomem *base;
+ int irq;
+ struct gen_pool *pool;
+ struct resource res[ZYNQ_OCM_BLOCKS];
+};
+
+/**
+ * zynq_ocm_irq_handler - Interrupt service routine of the OCM controller
+ * @irq: IRQ number
+ * @data: Pointer to the zynq_ocm_dev structure
+ *
+ * Return: IRQ_HANDLED when handled; IRQ_NONE otherwise.
+ */
+static irqreturn_t zynq_ocm_irq_handler(int irq, void *data)
+{
+ u32 sts;
+ u32 err_addr;
+ struct zynq_ocm_dev *zynq_ocm = data;
+
+ /* check status */
+ sts = readl(zynq_ocm->base + ZYNQ_OCM_IRQ_STS);
+ if (sts & ZYNQ_OCM_IRQ_STS_ERR_MASK) {
+ /* check error address */
+ err_addr = readl(zynq_ocm->base + ZYNQ_OCM_PARITY_ERRADDRESS);
+ pr_err("%s: OCM err intr generated at 0x%04x (stat: 0x%08x).",
+ __func__, err_addr, sts & ZYNQ_OCM_IRQ_STS_ERR_MASK);
+ return IRQ_HANDLED;
+ }
+ pr_warn("%s: Interrupt generated by OCM, but no error is found.",
+ __func__);
+
+ return IRQ_NONE;
+}
+
+/**
+ * zynq_ocm_probe - Probe method for the OCM driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * Return: 0 on success and error value on failure
+ */
+static int zynq_ocm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct zynq_ocm_dev *zynq_ocm;
+ u32 i, ocm_config, curr;
+ struct resource *res;
+
+ ocm_config = zynq_slcr_get_ocm_config();
+
+ zynq_ocm = devm_kzalloc(&pdev->dev, sizeof(*zynq_ocm), GFP_KERNEL);
+ if (!zynq_ocm)
+ return -ENOMEM;
+
+ zynq_ocm->pool = devm_gen_pool_create(&pdev->dev,
+ ilog2(ZYNQ_OCM_GRANULARITY),
+ NUMA_NO_NODE, NULL);
+ if (!zynq_ocm->pool)
+ return -ENOMEM;
+
+ curr = 0; /* For storing current struct resource for OCM */
+ for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) {
+ u32 base, start, end;
+
+ /* Setup base address for 64kB OCM block */
+ if (ocm_config & BIT(i))
+ base = ZYNQ_OCM_HIGHADDR;
+ else
+ base = ZYNQ_OCM_LOWADDR;
+
+ /* Calculate start and end block addresses */
+ start = i * ZYNQ_OCM_BLOCK_SIZE + base;
+ end = start + (ZYNQ_OCM_BLOCK_SIZE - 1);
+
+ /* Concatenate OCM blocks together to get bigger pool */
+ if (i > 0 && start == (zynq_ocm->res[curr - 1].end + 1)) {
+ zynq_ocm->res[curr - 1].end = end;
+ } else {
+#ifdef CONFIG_SMP
+ /*
+ * OCM block if placed at 0x0 has special meaning
+ * for SMP because jump trampoline is added there.
+ * Ensure that this address won't be allocated.
+ */
+ if (!base) {
+ u32 trampoline_code_size =
+ &zynq_secondary_trampoline_end -
+ &zynq_secondary_trampoline;
+ dev_dbg(&pdev->dev,
+ "Allocate reset vector table %dB\n",
+ trampoline_code_size);
+ /* postpone start offset */
+ start += trampoline_code_size;
+ }
+#endif
+ /* First resource is always initialized */
+ zynq_ocm->res[curr].start = start;
+ zynq_ocm->res[curr].end = end;
+ zynq_ocm->res[curr].flags = IORESOURCE_MEM;
+ curr++; /* Increment curr value */
+ }
+ dev_dbg(&pdev->dev, "OCM block %d, start %x, end %x\n",
+ i, start, end);
+ }
+
+ /*
+ * Separate pool allocation from OCM block detection to ensure
+ * the biggest possible pool.
+ */
+ for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) {
+ unsigned long size;
+ void __iomem *virt_base;
+
+ /* Skip all zero size resources */
+ if (zynq_ocm->res[i].end == 0)
+ break;
+ dev_dbg(&pdev->dev, "OCM resources %d, start %x, end %x\n",
+ i, zynq_ocm->res[i].start, zynq_ocm->res[i].end);
+ size = resource_size(&zynq_ocm->res[i]);
+ virt_base = devm_ioremap_resource(&pdev->dev,
+ &zynq_ocm->res[i]);
+ if (IS_ERR(virt_base))
+ return PTR_ERR(virt_base);
+
+ ret = gen_pool_add_virt(zynq_ocm->pool,
+ (unsigned long)virt_base,
+ zynq_ocm->res[i].start, size, -1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Gen pool failed\n");
+ return ret;
+ }
+ dev_info(&pdev->dev, "ZYNQ OCM pool: %ld KiB @ 0x%p\n",
+ size / 1024, virt_base);
+ }
+
+ /* Get OCM config space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ zynq_ocm->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(zynq_ocm->base))
+ return PTR_ERR(zynq_ocm->base);
+
+ /* Allocate OCM parity IRQ */
+ zynq_ocm->irq = platform_get_irq(pdev, 0);
+ if (zynq_ocm->irq < 0) {
+ dev_err(&pdev->dev, "irq resource not found\n");
+ return zynq_ocm->irq;
+ }
+ ret = devm_request_irq(&pdev->dev, zynq_ocm->irq, zynq_ocm_irq_handler,
+ 0, pdev->name, zynq_ocm);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ return ret;
+ }
+
+ /* Enable parity errors */
+ writel(ZYNQ_OCM_PARITY_ENABLE, zynq_ocm->base + ZYNQ_OCM_PARITY_CTRL);
+
+ platform_set_drvdata(pdev, zynq_ocm);
+
+ return 0;
+}
+
+/**
+ * zynq_ocm_remove - Remove method for the OCM driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees all resources allocated to
+ * the device.
+ *
+ * Return: 0 on success and error value on failure
+ */
+static int zynq_ocm_remove(struct platform_device *pdev)
+{
+ struct zynq_ocm_dev *zynq_ocm = platform_get_drvdata(pdev);
+
+ if (gen_pool_avail(zynq_ocm->pool) < gen_pool_size(zynq_ocm->pool))
+ dev_dbg(&pdev->dev, "removed while SRAM allocated\n");
+
+ return 0;
+}
+
+static struct of_device_id zynq_ocm_dt_ids[] = {
+ { .compatible = "xlnx,zynq-ocmc-1.0" },
+ { /* end of table */ }
+};
+
+static struct platform_driver zynq_ocm_driver = {
+ .driver = {
+ .name = "zynq-ocm",
+ .of_match_table = zynq_ocm_dt_ids,
+ },
+ .probe = zynq_ocm_probe,
+ .remove = zynq_ocm_remove,
+};
+
+static int __init zynq_ocm_init(void)
+{
+ return platform_driver_register(&zynq_ocm_driver);
+}
+
+arch_initcall(zynq_ocm_init);