diff options
Diffstat (limited to 'arch/arm')
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 = <ðernet_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); |