summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-01-15 07:58:30 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2020-01-15 07:58:30 +0100
commit6a1fca3a18ac01a183a787a3ccbd1d9c623391bc (patch)
treec70e6a205b3299a04fc7a1dc1c2737a693b87649
parent56c313fe1751756ba978824798f61b4abe6ac49c (diff)
parente76009b5953371b30005382f60863a385526d4d5 (diff)
downloadbarebox-next.tar.gz
Merge branch 'for-next/zynq'HEADnextmaster
-rw-r--r--arch/arm/boards/avnet-zedboard/lowlevel.c48
-rw-r--r--arch/arm/dts/zynq-7000.dtsi15
-rw-r--r--arch/arm/dts/zynq-zed.dts18
-rw-r--r--arch/arm/mach-zynq/Makefile3
-rw-r--r--arch/arm/mach-zynq/bootm-zynqimg.c49
-rw-r--r--arch/arm/mach-zynq/cpu_init.c13
-rw-r--r--arch/arm/mach-zynq/include/mach/init.h8
-rw-r--r--common/filetype.c4
-rw-r--r--drivers/clk/zynq/clkc.c3
-rw-r--r--drivers/firmware/zynqmp-fpga.c121
-rw-r--r--drivers/serial/serial_cadence.c41
-rw-r--r--drivers/spi/Kconfig8
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/zynq_qspi.c612
-rw-r--r--include/filetype.h1
-rw-r--r--include/serial/cadence.h92
16 files changed, 938 insertions, 99 deletions
diff --git a/arch/arm/boards/avnet-zedboard/lowlevel.c b/arch/arm/boards/avnet-zedboard/lowlevel.c
index 93e4da9..912eb11 100644
--- a/arch/arm/boards/avnet-zedboard/lowlevel.c
+++ b/arch/arm/boards/avnet-zedboard/lowlevel.c
@@ -20,7 +20,9 @@
#include <io.h>
#include <asm/barebox-arm.h>
#include <asm/barebox-arm-head.h>
+#include <mach/init.h>
#include <mach/zynq7000-regs.h>
+#include <serial/cadence.h>
#define DCI_DONE (1 << 13)
#define PLL_ARM_LOCK (1 << 0)
@@ -29,10 +31,17 @@
extern char __dtb_zynq_zed_start[];
-ENTRY_FUNCTION(start_avnet_zedboard, r0, r1, r2)
+static void avnet_zedboard_ps7_init(void)
{
-
- void *fdt = __dtb_zynq_zed_start + get_runtime_offset();
+ /*
+ * Read OCM mapping configuration, if only the upper 64 KByte are
+ * mapped to the high address, it's very likely that we just got control
+ * from the BootROM. If the mapping is changed something other than the
+ * BootROM was running before us. Skip PS7 init to avoid cutting the
+ * branch we are sitting on in that case.
+ */
+ if ((readl(0xf8000910) & 0xf) != 0x8)
+ return;
/* open sesame */
writel(0x0000DF0D, ZYNQ_SLCR_UNLOCK);
@@ -234,9 +243,18 @@ ENTRY_FUNCTION(start_avnet_zedboard, r0, r1, r2)
/* UART1 pinmux */
writel(0x000002E1, ZYNQ_MIO_BASE + 0xC8);
writel(0x000002E0, ZYNQ_MIO_BASE + 0xCC);
+ /* QSPI pinmux */
+ writel(0x00001602, ZYNQ_MIO_BASE + 0x04);
+ writel(0x00000702, ZYNQ_MIO_BASE + 0x08);
+ writel(0x00000702, ZYNQ_MIO_BASE + 0x0c);
+ writel(0x00000702, ZYNQ_MIO_BASE + 0x10);
+ writel(0x00000702, ZYNQ_MIO_BASE + 0x14);
+ writel(0x00000702, ZYNQ_MIO_BASE + 0x18);
+ writel(0x00000602, ZYNQ_MIO_BASE + 0x20);
/* poor mans clkctrl */
writel(0x00001403, ZYNQ_CLOCK_CTRL_BASE + ZYNQ_UART_CLK_CTRL);
+ writel(0x00000101, ZYNQ_CLOCK_CTRL_BASE + ZYNQ_LQSPI_CLK_CTRL);
/* GEM0 */
writel(0x00000001, 0xf8000138);
@@ -260,8 +278,32 @@ ENTRY_FUNCTION(start_avnet_zedboard, r0, r1, r2)
/* lock up. secure, secure */
writel(0x0000767B, ZYNQ_SLCR_LOCK);
+}
+
+static void avnet_zedboard_pbl_console_init(void)
+{
+ relocate_to_current_adr();
+ setup_c();
+ barrier();
+
+ cadence_uart_init((void *)ZYNQ_UART1_BASE_ADDR);
+ pbl_set_putc(cadence_uart_putc, (void *)ZYNQ_UART1_BASE_ADDR);
+
+ pr_debug("\nAvnet ZedBoard PBL\n");
+}
+
+ENTRY_FUNCTION(start_avnet_zedboard, r0, r1, r2)
+{
+
+ void *fdt = __dtb_zynq_zed_start + get_runtime_offset();
arm_cpu_lowlevel_init();
+ zynq_cpu_lowlevel_init();
+
+ avnet_zedboard_ps7_init();
+
+ if (IS_ENABLED(CONFIG_PBL_CONSOLE))
+ avnet_zedboard_pbl_console_init();
barebox_arm_entry(0, SZ_512M, fdt);
}
diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi
new file mode 100644
index 0000000..3791f74
--- /dev/null
+++ b/arch/arm/dts/zynq-7000.dtsi
@@ -0,0 +1,15 @@
+
+&amba {
+ qspi: spi@e000d000 {
+ compatible = "xlnx,zynq-qspi-1.0";
+ reg = <0xe000d000 0x1000>;
+ interrupt-parent = <&intc>;
+ interrupts = <0 19 4>;
+ clocks = <&clkc 10>, <&clkc 43>;
+ clock-names = "ref_clk", "pclk";
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+};
diff --git a/arch/arm/dts/zynq-zed.dts b/arch/arm/dts/zynq-zed.dts
index 52d6833..a6b1da8 100644
--- a/arch/arm/dts/zynq-zed.dts
+++ b/arch/arm/dts/zynq-zed.dts
@@ -1,7 +1,25 @@
#include <arm/zynq-zed.dts>
+#include "zynq-7000.dtsi"
/ {
chosen {
stdout-path = &uart1;
};
};
+
+&qspi {
+ status = "okay";
+ num-cs = <1>;
+
+ qspi_flash: flash@0 {
+ compatible = "spansion,s25fl256s1", "jedec,spi-nor";
+ 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>;
+ };
+};
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 3252247..06c2ce9 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -1 +1,2 @@
-obj-y += zynq.o
+obj-y += zynq.o bootm-zynqimg.o
+lwl-y += cpu_init.o
diff --git a/arch/arm/mach-zynq/bootm-zynqimg.c b/arch/arm/mach-zynq/bootm-zynqimg.c
new file mode 100644
index 0000000..e903ab6
--- /dev/null
+++ b/arch/arm/mach-zynq/bootm-zynqimg.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <bootm.h>
+#include <common.h>
+#include <init.h>
+#include <memory.h>
+
+static int do_bootm_zynqimage(struct image_data *data)
+{
+ resource_size_t start, end;
+ void (*barebox)(void);
+ u32 *header;
+ int ret;
+
+ ret = memory_bank_first_find_space(&start, &end);
+ if (ret)
+ return ret;
+
+ ret = bootm_load_os(data, start);
+ if (ret)
+ return ret;
+
+ header = (u32*)start;
+ barebox = (void*)start + header[12];
+
+ if (data->verbose)
+ printf("Loaded barebox image to 0x%08lx\n",
+ (unsigned long)barebox);
+
+ shutdown_barebox();
+
+ barebox();
+
+ return -EIO;
+}
+
+static struct image_handler zynq_image_handler = {
+ .name = "Zynq image",
+ .bootm = do_bootm_zynqimage,
+ .filetype = filetype_zynq_image,
+};
+
+static int zynq_register_image_handler(void)
+{
+ register_image_handler(&zynq_image_handler);
+
+ return 0;
+}
+late_initcall(zynq_register_image_handler);
diff --git a/arch/arm/mach-zynq/cpu_init.c b/arch/arm/mach-zynq/cpu_init.c
new file mode 100644
index 0000000..ca7c4b2
--- /dev/null
+++ b/arch/arm/mach-zynq/cpu_init.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <asm/barebox-arm-head.h>
+#include <asm/errata.h>
+#include <mach/init.h>
+
+void zynq_cpu_lowlevel_init(void)
+{
+ enable_arm_errata_761320_war();
+ enable_arm_errata_794072_war();
+ enable_arm_errata_845369_war();
+}
diff --git a/arch/arm/mach-zynq/include/mach/init.h b/arch/arm/mach-zynq/include/mach/init.h
new file mode 100644
index 0000000..c458f60
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/init.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __MACH_INIT_H
+#define __MACH_INIT_H
+
+void zynq_cpu_lowlevel_init(void);
+
+#endif
diff --git a/common/filetype.c b/common/filetype.c
index 39fea45..fd6e8e3 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -80,6 +80,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_ubootvar] = { "U-Boot environmemnt variable data",
"ubootvar" },
[filetype_stm32_image_v1] = { "STM32 image (v1)", "stm32-image-v1" },
+ [filetype_zynq_image] = { "Zynq image", "zynq-image" },
};
const char *file_type_to_string(enum filetype f)
@@ -392,6 +393,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
if (is_imx_flash_header_v2(_buf))
return filetype_imx_image_v2;
+ if (buf[8] == 0xAA995566 && buf[9] == 0x584C4E58)
+ return filetype_zynq_image;
+
return filetype_unknown;
}
diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c
index 30ca5a6..a6d8ba9 100644
--- a/drivers/clk/zynq/clkc.c
+++ b/drivers/clk/zynq/clkc.c
@@ -414,6 +414,9 @@ static int zynq_clock_probe(struct device_d *dev)
clks[ddrpll] = zynq_pll_clk(ZYNQ_PLL_DDR, "ddr_pll", clk_base + 0x4);
clks[iopll] = zynq_pll_clk(ZYNQ_PLL_IO, "io_pll", clk_base + 0x8);
+ zynq_periph_clk("lqspi_clk", clk_base + 0x4c);
+ clks[lqspi] = clk_gate("qspi0", "lqspi_clk", clk_base + 0x4c, 0, 0, 0);
+
zynq_periph_clk("sdio_clk", clk_base + 0x50);
clks[sdio0] = clk_gate("sdio0", "sdio_clk", clk_base + 0x50, 0, 0, 0);
clks[sdio1] = clk_gate("sdio1", "sdio_clk", clk_base + 0x50, 1, 0, 0);
diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c
index 1728e2a..e026673 100644
--- a/drivers/firmware/zynqmp-fpga.c
+++ b/drivers/firmware/zynqmp-fpga.c
@@ -24,11 +24,32 @@
#define ZYNQMP_PM_VERSION_1_1_FEATURES (ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL | \
ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED)
+/*
+ * Xilinx KU040 Bitstream Composition:
+ *
+ * Bitstream can be provided with an optinal header (`struct bs_header`).
+ * The true bitstream starts with the binary-header composed of 21 words:
+ *
+ * 0: 0xFFFFFFFF (Dummy pad word)
+ * ...
+ * 15: 0xFFFFFFFF (Dummy pad word)
+ * 16: 0x000000BB (Bus width auto detect word 1)
+ * 17: 0x11220044 (Bus width auto detect word 2)
+ * 18: 0xFFFFFFFF (Dummy pad word)
+ * 19: 0xFFFFFFFF (Dummy pad word)
+ * 20: 0xAA995566 (Sync word)
+ *
+ * See Xilinx UG570 (v1.11) September 30 2019, Chapter 9 "Configuration
+ * Details - Bitstream Composition" for further details.
+ */
#define DUMMY_WORD 0xFFFFFFFF
-#define BUS_WIDTH_WORD_1 0x000000BB
-#define BUS_WIDTH_WORD_2 0x11220044
+#define BUS_WIDTH_AUTO_DETECT1_OFFSET 16
+#define BUS_WIDTH_AUTO_DETECT1 0x000000BB
+#define BUS_WIDTH_AUTO_DETECT2_OFFSET 17
+#define BUS_WIDTH_AUTO_DETECT2 0x11220044
+#define SYNC_WORD_OFFSET 20
#define SYNC_WORD 0xAA995566
-#define SYNC_WORD_OFFS 20
+#define BIN_HEADER_LENGTH 21
enum xilinx_byte_order {
XILINX_BYTE_ORDER_BIT,
@@ -58,48 +79,6 @@ struct bs_header_entry {
char data[0];
} __attribute__ ((packed));
-/*
- * Xilinx KU040 Bitstream Composition:
- * Bitstream can be provided with an optinal header (`struct bs_header`).
- * The true bitstream starts with the binary-header composed of 21 words:
- *
- * 1: 0xFFFFFFFF (Dummy pad word)
- * ...
- * 16: 0xFFFFFFFF (Dummy pad word)
- * 17: 0x000000BB (Bus width auto detect word 1)
- * 18: 0x11220044 (Bus width auto detect word 2)
- * 19: 0xFFFFFFFF (Dummy pad word)
- * 20: 0xFFFFFFFF (Dummy pad word)
- * 21: 0xAA995566 (Sync word)
- *
- * Please refer to Xilinx UG570 (v1.11) September 30 2019,
- * Chapter 9 Configuration Details - Bitstream Composition
- * for further details!
- */
-static const u32 bin_format[] = {
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- DUMMY_WORD,
- BUS_WIDTH_WORD_1,
- BUS_WIDTH_WORD_2,
- DUMMY_WORD,
- DUMMY_WORD,
- SYNC_WORD,
-};
-
static void copy_words_swapped(u32 *dst, const u32 *src, size_t size)
{
int i;
@@ -111,36 +90,59 @@ static void copy_words_swapped(u32 *dst, const u32 *src, size_t size)
static int get_byte_order(const u32 *bin_header, size_t size,
enum xilinx_byte_order *byte_order)
{
- if (size < sizeof(bin_format))
+ if (size < BIN_HEADER_LENGTH * sizeof(*bin_header))
return -EINVAL;
- if (bin_header[SYNC_WORD_OFFS] == SYNC_WORD) {
+ if (bin_header[SYNC_WORD_OFFSET] == SYNC_WORD) {
*byte_order = XILINX_BYTE_ORDER_BIT;
return 0;
}
- if (bin_header[SYNC_WORD_OFFS] == __swab32(SYNC_WORD)) {
- *byte_order = XILINX_BYTE_ORDER_BIN;
+ if (bin_header[SYNC_WORD_OFFSET] == __swab32(SYNC_WORD)) {
+ *byte_order = XILINX_BYTE_ORDER_BIN;
return 0;
}
return -EINVAL;
}
-static int is_bin_header_valid(const u32 *bin_header, size_t size,
- enum xilinx_byte_order byte_order)
+static bool is_bin_header_valid(const u32 *bin_header, size_t size,
+ enum xilinx_byte_order byte_order)
{
- int i;
+ size_t i;
- if (size < ARRAY_SIZE(bin_format))
- return 0;
+ if (size < BIN_HEADER_LENGTH * sizeof(*bin_header))
+ return false;
+
+ for (i = 0; i < BIN_HEADER_LENGTH; i++, bin_header++) {
+ u32 current;
+ u32 expected;
+
+ if (byte_order == XILINX_BYTE_ORDER_BIT)
+ current = *bin_header;
+ else
+ current = __swab32(*bin_header);
- for (i = 0; i < ARRAY_SIZE(bin_format); i++)
- if (bin_header[i] != (byte_order == XILINX_BYTE_ORDER_BIT) ?
- bin_format[i] : __swab32(bin_format[i]))
- return 0;
+ switch (i) {
+ case BUS_WIDTH_AUTO_DETECT1_OFFSET:
+ expected = BUS_WIDTH_AUTO_DETECT1;
+ break;
+ case BUS_WIDTH_AUTO_DETECT2_OFFSET:
+ expected = BUS_WIDTH_AUTO_DETECT2;
+ break;
+ case SYNC_WORD_OFFSET:
+ expected = SYNC_WORD;
+ break;
+ default:
+ expected = DUMMY_WORD;
+ break;
+ }
+
+ if (current != expected)
+ return false;
+ }
- return 1;
+ return true;
}
static int get_header_length(const char *header, size_t size)
@@ -227,6 +229,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
goto err_free;
if (!is_bin_header_valid(body, body_length, byte_order)) {
+ dev_err(&mgr->dev, "Invalid bitstream header\n");
status = -EINVAL;
goto err_free;
}
diff --git a/drivers/serial/serial_cadence.c b/drivers/serial/serial_cadence.c
index 6454888..416800b 100644
--- a/drivers/serial/serial_cadence.c
+++ b/drivers/serial/serial_cadence.c
@@ -12,47 +12,16 @@
* GNU General Public License for more details.
*
*/
+
#include <common.h>
#include <driver.h>
#include <init.h>
-#include <malloc.h>
-#include <notifier.h>
#include <io.h>
-#include <linux/err.h>
#include <linux/clk.h>
-
-#define CADENCE_UART_CONTROL 0x00
-#define CADENCE_UART_MODE 0x04
-#define CADENCE_UART_BAUD_GEN 0x18
-#define CADENCE_UART_CHANNEL_STS 0x2C
-#define CADENCE_UART_RXTXFIFO 0x30
-#define CADENCE_UART_BAUD_DIV 0x34
-
-#define CADENCE_CTRL_RXRES (1 << 0)
-#define CADENCE_CTRL_TXRES (1 << 1)
-#define CADENCE_CTRL_RXEN (1 << 2)
-#define CADENCE_CTRL_RXDIS (1 << 3)
-#define CADENCE_CTRL_TXEN (1 << 4)
-#define CADENCE_CTRL_TXDIS (1 << 5)
-#define CADENCE_CTRL_RSTTO (1 << 6)
-#define CADENCE_CTRL_STTBRK (1 << 7)
-#define CADENCE_CTRL_STPBRK (1 << 8)
-
-#define CADENCE_MODE_CLK_REF (0 << 0)
-#define CADENCE_MODE_CLK_REF_DIV (1 << 0)
-#define CADENCE_MODE_CHRL_6 (3 << 1)
-#define CADENCE_MODE_CHRL_7 (2 << 1)
-#define CADENCE_MODE_CHRL_8 (0 << 1)
-#define CADENCE_MODE_PAR_EVEN (0 << 3)
-#define CADENCE_MODE_PAR_ODD (1 << 3)
-#define CADENCE_MODE_PAR_SPACE (2 << 3)
-#define CADENCE_MODE_PAR_MARK (3 << 3)
-#define CADENCE_MODE_PAR_NONE (4 << 3)
-
-#define CADENCE_STS_REMPTY (1 << 1)
-#define CADENCE_STS_RFUL (1 << 2)
-#define CADENCE_STS_TEMPTY (1 << 3)
-#define CADENCE_STS_TFUL (1 << 4)
+#include <linux/err.h>
+#include <malloc.h>
+#include <notifier.h>
+#include <serial/cadence.h>
/*
* create default values for different platforms
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 9d4b1cc..3758a0f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -88,6 +88,14 @@ config DRIVER_SPI_DSPI
This enables support for the Freescale DSPI controller in master
mode. VF610 platform uses the controller.
+config SPI_ZYNQ_QSPI
+ tristate "Xilinx Zynq QSPI controller"
+ depends on ARCH_ZYNQ
+ depends on SPI_MEM
+ help
+ This enables support for the Zynq Quad SPI controller in master mode.
+ This controller only supports SPI memory interface.
+
endif
endmenu
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index c5d2de7..75a89ef 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_DRIVER_SPI_OMAP3) += omap3_spi.o
obj-$(CONFIG_DRIVER_SPI_DSPI) += dspi_spi.o
+obj-$(CONFIG_SPI_ZYNQ_QSPI) += zynq_qspi.o
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
new file mode 100644
index 0000000..d01e4a8
--- /dev/null
+++ b/drivers/spi/zynq_qspi.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Xilinx, Inc.
+ *
+ * Author: Naga Sureshkumar Relli <nagasure@xilinx.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/spi/spi-mem.h>
+#include <spi/spi.h>
+
+/* Register offset definitions */
+#define ZYNQ_QSPI_CONFIG_OFFSET 0x00 /* Configuration Register, RW */
+#define ZYNQ_QSPI_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO */
+#define ZYNQ_QSPI_IEN_OFFSET 0x08 /* Interrupt Enable Register, WO */
+#define ZYNQ_QSPI_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO */
+#define ZYNQ_QSPI_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask Reg,RO */
+#define ZYNQ_QSPI_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW */
+#define ZYNQ_QSPI_DELAY_OFFSET 0x18 /* Delay Register, RW */
+#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */
+#define ZYNQ_QSPI_RXD_OFFSET 0x20 /* Data Receive Register, RO */
+#define ZYNQ_QSPI_SIC_OFFSET 0x24 /* Slave Idle Count Register, RW */
+#define ZYNQ_QSPI_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_GPIO_OFFSET 0x30 /* GPIO Register, RW */
+#define ZYNQ_QSPI_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, RW */
+#define ZYNQ_QSPI_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */
+
+/*
+ * QSPI Configuration Register bit Masks
+ *
+ * This register contains various control bits that effect the operation
+ * of the QSPI controller
+ */
+#define ZYNQ_QSPI_CONFIG_IFMODE_MASK BIT(31) /* Flash Memory Interface */
+#define ZYNQ_QSPI_CONFIG_MANSRT_MASK BIT(16) /* Manual TX Start */
+#define ZYNQ_QSPI_CONFIG_MANSRTEN_MASK BIT(15) /* Enable Manual TX Mode */
+#define ZYNQ_QSPI_CONFIG_SSFORCE_MASK BIT(14) /* Manual Chip Select */
+#define ZYNQ_QSPI_CONFIG_BDRATE_MASK GENMASK(5, 3) /* Baud Rate Mask */
+#define ZYNQ_QSPI_CONFIG_CPHA_MASK BIT(2) /* Clock Phase Control */
+#define ZYNQ_QSPI_CONFIG_CPOL_MASK BIT(1) /* Clock Polarity Control */
+#define ZYNQ_QSPI_CONFIG_SSCTRL_MASK BIT(10) /* Slave Select Mask */
+#define ZYNQ_QSPI_CONFIG_FWIDTH_MASK GENMASK(7, 6) /* FIFO width */
+#define ZYNQ_QSPI_CONFIG_MSTREN_MASK BIT(0) /* Master Mode */
+
+/*
+ * QSPI Configuration Register - Baud rate and slave select
+ *
+ * These are the values used in the calculation of baud rate divisor and
+ * setting the slave select.
+ */
+#define ZYNQ_QSPI_BAUD_DIV_MAX GENMASK(2, 0) /* Baud rate maximum */
+#define ZYNQ_QSPI_BAUD_DIV_SHIFT 3 /* Baud rate divisor shift in CR */
+#define ZYNQ_QSPI_SS_SHIFT 10 /* Slave Select field shift in CR */
+
+/*
+ * QSPI Interrupt Registers bit Masks
+ *
+ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
+ * bit definitions.
+ */
+#define ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK BIT(0) /* QSPI RX FIFO Overflow */
+#define ZYNQ_QSPI_IXR_TXNFULL_MASK BIT(2) /* QSPI TX FIFO Overflow */
+#define ZYNQ_QSPI_IXR_TXFULL_MASK BIT(3) /* QSPI TX FIFO is full */
+#define ZYNQ_QSPI_IXR_RXNEMTY_MASK BIT(4) /* QSPI RX FIFO Not Empty */
+#define ZYNQ_QSPI_IXR_RXF_FULL_MASK BIT(5) /* QSPI RX FIFO is full */
+#define ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK BIT(6) /* QSPI TX FIFO Underflow */
+#define ZYNQ_QSPI_IXR_ALL_MASK (ZYNQ_QSPI_IXR_RX_OVERFLOW_MASK | \
+ ZYNQ_QSPI_IXR_TXNFULL_MASK | \
+ ZYNQ_QSPI_IXR_TXFULL_MASK | \
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK | \
+ ZYNQ_QSPI_IXR_RXF_FULL_MASK | \
+ ZYNQ_QSPI_IXR_TXF_UNDRFLOW_MASK)
+#define ZYNQ_QSPI_IXR_RXTX_MASK (ZYNQ_QSPI_IXR_TXNFULL_MASK | \
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+
+/*
+ * QSPI Enable Register bit Masks
+ *
+ * This register is used to enable or disable the QSPI controller
+ */
+#define ZYNQ_QSPI_ENABLE_ENABLE_MASK BIT(0) /* QSPI Enable Bit Mask */
+
+/*
+ * QSPI Linear Configuration Register
+ *
+ * It is named Linear Configuration but it controls other modes when not in
+ * linear mode also.
+ */
+#define ZYNQ_QSPI_LCFG_TWO_MEM_MASK BIT(30) /* LQSPI Two memories Mask */
+#define ZYNQ_QSPI_LCFG_SEP_BUS_MASK BIT(29) /* LQSPI Separate bus Mask */
+#define ZYNQ_QSPI_LCFG_U_PAGE_MASK BIT(28) /* LQSPI Upper Page Mask */
+
+#define ZYNQ_QSPI_LCFG_DUMMY_SHIFT 8
+
+#define ZYNQ_QSPI_FAST_READ_QOUT_CODE 0x6B /* read instruction code */
+#define ZYNQ_QSPI_FIFO_DEPTH 63 /* FIFO depth in words */
+#define ZYNQ_QSPI_RX_THRESHOLD 32 /* Rx FIFO threshold level */
+#define ZYNQ_QSPI_TX_THRESHOLD 1 /* Tx FIFO threshold level */
+
+/*
+ * The modebits configurable by the driver to make the SPI support different
+ * data formats
+ */
+#define ZYNQ_QSPI_MODEBITS (SPI_CPOL | SPI_CPHA)
+
+/**
+ * struct zynq_qspi - Defines qspi driver instance
+ * @regs: Virtual address of the QSPI controller registers
+ * @refclk: Pointer to the peripheral clock
+ * @pclk: Pointer to the APB clock
+ * @irq: IRQ number
+ * @txbuf: Pointer to the TX buffer
+ * @rxbuf: Pointer to the RX buffer
+ * @tx_bytes: Number of bytes left to transfer
+ * @rx_bytes: Number of bytes left to receive
+ * @data_completion: completion structure
+ */
+struct zynq_qspi {
+ struct spi_controller ctlr;
+ struct device_d *dev;
+ void __iomem *regs;
+ struct clk *refclk;
+ struct clk *pclk;
+ int tx_bytes;
+ int rx_bytes;
+ u8 *txbuf;
+ u8 *rxbuf;
+};
+
+/*
+ * Inline functions for the QSPI controller read/write
+ */
+static inline u32 zynq_qspi_read(struct zynq_qspi *xqspi, u32 offset)
+{
+ return readl(xqspi->regs + offset);
+}
+
+static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset, u32 val)
+{
+ writel(val, xqspi->regs + offset);
+}
+
+/**
+ * zynq_qspi_init_hw - Initialize the hardware
+ * @xqspi: Pointer to the zynq_qspi structure
+ *
+ * The default settings of the QSPI controller's configurable parameters on
+ * reset are
+ * - Master mode
+ * - Baud rate divisor is set to 2
+ * - Tx threshold set to 1l Rx threshold set to 32
+ * - Flash memory interface mode enabled
+ * - Size of the word to be transferred as 8 bit
+ * This function performs the following actions
+ * - Disable and clear all the interrupts
+ * - Enable manual slave select
+ * - Enable manual start
+ * - Deselect all the chip select lines
+ * - Set the size of the word to be transferred as 32 bit
+ * - Set the little endian mode of TX FIFO and
+ * - Enable the QSPI controller
+ */
+static void zynq_qspi_init_hw(struct zynq_qspi *xqspi)
+{
+ u32 config_reg;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
+
+ /* Disable linear mode as the boot loader may have used it */
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, 0);
+
+ /* Clear the RX FIFO */
+ while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) &
+ ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+ zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+ config_reg &= ~(ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+ ZYNQ_QSPI_CONFIG_CPOL_MASK |
+ ZYNQ_QSPI_CONFIG_CPHA_MASK |
+ ZYNQ_QSPI_CONFIG_BDRATE_MASK |
+ ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+ ZYNQ_QSPI_CONFIG_MANSRTEN_MASK |
+ ZYNQ_QSPI_CONFIG_MANSRT_MASK);
+ config_reg |= (ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+ ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+ ZYNQ_QSPI_CONFIG_FWIDTH_MASK |
+ ZYNQ_QSPI_CONFIG_IFMODE_MASK);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_RX_THRESH_OFFSET,
+ ZYNQ_QSPI_RX_THRESHOLD);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TX_THRESH_OFFSET,
+ ZYNQ_QSPI_TX_THRESHOLD);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+ ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+}
+
+/**
+ * zynq_qspi_rxfifo_op - Read 1..4 bytes from RxFIFO to RX buffer
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @size: Number of bytes to be read (1..4)
+ */
+static void zynq_qspi_rxfifo_op(struct zynq_qspi *xqspi, unsigned int size)
+{
+ u32 data;
+
+ data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+ if (xqspi->rxbuf) {
+ memcpy(xqspi->rxbuf, ((u8 *)&data) + 4 - size, size);
+ xqspi->rxbuf += size;
+ }
+
+ xqspi->rx_bytes -= size;
+ if (xqspi->rx_bytes < 0)
+ xqspi->rx_bytes = 0;
+}
+
+/**
+ * zynq_qspi_txfifo_op - Write 1..4 bytes from TX buffer to TxFIFO
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @size: Number of bytes to be written (1..4)
+ */
+static void zynq_qspi_txfifo_op(struct zynq_qspi *xqspi, unsigned int size)
+{
+ static const unsigned int offset[4] = {
+ ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET,
+ ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET };
+ u32 data;
+
+ if (xqspi->txbuf) {
+ data = 0xffffffff;
+ memcpy(&data, xqspi->txbuf, size);
+ xqspi->txbuf += size;
+ } else {
+ data = 0;
+ }
+
+ xqspi->tx_bytes -= size;
+ zynq_qspi_write(xqspi, offset[size - 1], data);
+}
+
+/**
+ * zynq_qspi_chipselect - Select or deselect the chip select line
+ * @spi: Pointer to the spi_device structure
+ * @assert: 1 for select or 0 for deselect the chip select line
+ */
+static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
+{
+ struct spi_controller *ctrl = spi->master;
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(ctrl);
+ u32 config_reg;
+
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+ if (assert) {
+ /* Select the slave */
+ config_reg &= ~ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+ config_reg |= (((~(BIT(spi->chip_select))) <<
+ ZYNQ_QSPI_SS_SHIFT) &
+ ZYNQ_QSPI_CONFIG_SSCTRL_MASK);
+ } else {
+ config_reg |= ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+ }
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+}
+
+/**
+ * zynq_qspi_config_op - Configure QSPI controller for specified transfer
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @qspi: Pointer to the spi_device structure
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer and
+ * sets the requested clock frequency.
+ *
+ * Return: 0 on success and -EINVAL on invalid input parameter
+ *
+ * Note: If the requested frequency is not an exact match with what can be
+ * obtained using the prescalar value, the driver sets the clock frequency which
+ * is lower than the requested frequency (maximum lower) for the transfer. If
+ * the requested frequency is higher or lower than that is supported by the QSPI
+ * controller the driver will set the highest or lowest frequency supported by
+ * controller.
+ */
+static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
+{
+ u32 config_reg, baud_rate_val = 0;
+
+ /*
+ * Set the clock frequency
+ * The baud rate divisor is not a direct mapping to the value written
+ * into the configuration register (config_reg[5:3])
+ * i.e. 000 - divide by 2
+ * 001 - divide by 4
+ * ----------------
+ * 111 - divide by 256
+ */
+ while ((baud_rate_val < ZYNQ_QSPI_BAUD_DIV_MAX) &&
+ (clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) >
+ spi->max_speed_hz)
+ baud_rate_val++;
+
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+
+ /* Set the QSPI clock phase and clock polarity */
+ config_reg &= (~ZYNQ_QSPI_CONFIG_CPHA_MASK) &
+ (~ZYNQ_QSPI_CONFIG_CPOL_MASK);
+ if (spi->mode & SPI_CPHA)
+ config_reg |= ZYNQ_QSPI_CONFIG_CPHA_MASK;
+ if (spi->mode & SPI_CPOL)
+ config_reg |= ZYNQ_QSPI_CONFIG_CPOL_MASK;
+
+ config_reg &= ~ZYNQ_QSPI_CONFIG_BDRATE_MASK;
+ config_reg |= (baud_rate_val << ZYNQ_QSPI_BAUD_DIV_SHIFT);
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+ return 0;
+}
+
+static int zynq_qspi_setup_op(struct spi_device *spi)
+{
+ struct spi_controller *ctrl = spi->master;
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(ctrl);
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+ ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+
+ return 0;
+}
+
+/**
+ * zynq_qspi_write_op - Fills the TX FIFO with as many bytes as possible
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @txcount: Maximum number of words to write
+ * @txempty: Indicates that TxFIFO is empty
+ */
+static void zynq_qspi_write_op(struct zynq_qspi *xqspi, int txcount,
+ bool txempty)
+{
+ int count, len, k;
+
+ len = xqspi->tx_bytes;
+ if (len && len < sizeof(u32)) {
+ /*
+ * We must empty the TxFIFO between accesses to TXD0,
+ * TXD1, TXD2, TXD3.
+ */
+ if (txempty)
+ zynq_qspi_txfifo_op(xqspi, len);
+
+ return;
+ }
+
+ count = len / sizeof(u32);
+ if (count > txcount)
+ count = txcount;
+
+ if (xqspi->txbuf) {
+ u32 *buf = (u32 *)xqspi->txbuf;
+ for (k = 0; k < count; k++, buf++)
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TXD_00_00_OFFSET, *buf);
+ xqspi->txbuf += count * sizeof(u32);
+ } else {
+ for (k = 0; k < count; k++)
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_TXD_00_00_OFFSET, 0);
+ }
+
+ xqspi->tx_bytes -= count * sizeof(u32);
+}
+
+/**
+ * zynq_qspi_read_op - Drains the RX FIFO by as many bytes as possible
+ * @xqspi: Pointer to the zynq_qspi structure
+ * @rxcount: Maximum number of words to read
+ */
+static void zynq_qspi_read_op(struct zynq_qspi *xqspi, int rxcount)
+{
+ int count, len, k;
+
+ len = xqspi->rx_bytes - xqspi->tx_bytes;
+ count = len / sizeof(u32);
+ if (count > rxcount)
+ count = rxcount;
+ if (xqspi->rxbuf) {
+ u32 *buf = (u32 *)xqspi->rxbuf;
+ for (k = 0; k < count; k++, buf++)
+ *buf = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+ xqspi->rxbuf += count * sizeof(u32);
+ } else {
+ for (k = 0; k < count; k++)
+ zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+ }
+ xqspi->rx_bytes -= count * sizeof(u32);
+ len -= count * sizeof(u32);
+
+ if (len && len < 4 && count < rxcount)
+ zynq_qspi_rxfifo_op(xqspi, len);
+}
+
+static int zynq_qspi_poll_irq(struct zynq_qspi *xqspi)
+{
+ u32 intr_status;
+ bool txempty;
+ int ret;
+
+ for (;;) {
+ ret = readl_poll_timeout(xqspi->regs + ZYNQ_QSPI_STATUS_OFFSET,
+ intr_status, intr_status,
+ 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, intr_status);
+
+ if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) ||
+ (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) {
+ /*
+ * This bit is set when Tx FIFO has < THRESHOLD entries.
+ * We have the THRESHOLD value set to 1,
+ * so this bit indicates Tx FIFO is empty.
+ */
+ txempty = !!(intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK);
+ /* Read out the data from the RX FIFO */
+ zynq_qspi_read_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD);
+ if (xqspi->tx_bytes) {
+ /* There is more data to send */
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_RX_THRESHOLD,
+ txempty);
+ } else if (!xqspi->rx_bytes){
+ /* No more RX or TX bytes -> transfer done */
+ return 0;
+ }
+ }
+ }
+}
+
+/**
+ * zynq_qspi_exec_mem_op() - Initiates the QSPI transfer
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first selects the chip and starts the memory operation.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master);
+ int ret, i;
+ u8 *tmpbuf;
+
+ dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
+ op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+ op->dummy.buswidth, op->data.buswidth);
+
+ zynq_qspi_chipselect(mem->spi, true);
+ zynq_qspi_config_op(xqspi, mem->spi);
+
+ if (op->cmd.opcode) {
+ xqspi->txbuf = (u8 *)&op->cmd.opcode;
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = sizeof(op->cmd.opcode);
+ xqspi->rx_bytes = sizeof(op->cmd.opcode);
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++) {
+ xqspi->txbuf[i] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+ }
+
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = op->addr.nbytes;
+ xqspi->rx_bytes = op->addr.nbytes;
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ if (op->dummy.nbytes) {
+ tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL);
+ memset(tmpbuf, 0xff, op->dummy.nbytes);
+ xqspi->txbuf = tmpbuf;
+ xqspi->rxbuf = NULL;
+ xqspi->tx_bytes = op->dummy.nbytes;
+ xqspi->rx_bytes = op->dummy.nbytes;
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ kfree(tmpbuf);
+ if (ret)
+ return ret;
+ }
+
+ if (op->data.nbytes) {
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ xqspi->txbuf = (u8 *)op->data.buf.out;
+ xqspi->tx_bytes = op->data.nbytes;
+ xqspi->rxbuf = NULL;
+ xqspi->rx_bytes = op->data.nbytes;
+ } else {
+ xqspi->txbuf = NULL;
+ xqspi->rxbuf = (u8 *)op->data.buf.in;
+ xqspi->rx_bytes = op->data.nbytes;
+ xqspi->tx_bytes = op->data.nbytes;
+ }
+
+ zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+ ret = zynq_qspi_poll_irq(xqspi);
+ if (ret)
+ return ret;
+ }
+
+ zynq_qspi_chipselect(mem->spi, false);
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
+ .exec_op = zynq_qspi_exec_mem_op,
+};
+
+static int zynq_qspi_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct spi_controller *ctlr;
+ struct zynq_qspi *xqspi;
+ struct resource *iores;
+ u32 num_cs;
+ int ret;
+
+ xqspi = xzalloc(sizeof(*xqspi));
+
+ ctlr = &xqspi->ctlr;
+ xqspi->dev = dev;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ xqspi->regs = IOMEM(iores->start);
+
+ xqspi->pclk = clk_get(dev, "pclk");
+ if (IS_ERR_OR_NULL(xqspi->pclk)) {
+ dev_err(dev, "pclk clock not found.\n");
+ return PTR_ERR(xqspi->pclk);
+ }
+
+ xqspi->refclk = clk_get(dev, "ref_clk");
+ if (IS_ERR_OR_NULL(xqspi->refclk)) {
+ dev_err(dev, "ref_clk clock not found.\n");
+ return PTR_ERR(xqspi->refclk);
+ }
+
+ ret = clk_enable(xqspi->pclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable APB clock.\n");
+ return ret;
+ }
+
+ ret = clk_enable(xqspi->refclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable device clock.\n");
+ return ret;
+ }
+
+ /* QSPI controller initializations */
+ zynq_qspi_init_hw(xqspi);
+
+ if (of_property_read_u32(np, "num-cs", &num_cs))
+ ctlr->num_chipselect = 1;
+ else
+ ctlr->num_chipselect = num_cs;
+
+ ctlr->dev = dev;
+ ctlr->mem_ops = &zynq_qspi_mem_ops;
+ ctlr->setup = zynq_qspi_setup_op;
+
+ spi_controller_set_devdata(ctlr, xqspi);
+
+ return spi_register_controller(ctlr);
+}
+
+static const struct of_device_id zynq_qspi_of_match[] = {
+ { .compatible = "xlnx,zynq-qspi-1.0", },
+ { /* end of table */ }
+};
+
+static struct driver_d zynq_qspi_driver = {
+ .name = "zynq-qspi",
+ .probe = zynq_qspi_probe,
+ .of_compatible = DRV_OF_COMPAT(zynq_qspi_of_match),
+};
+device_platform_driver(zynq_qspi_driver);
diff --git a/include/filetype.h b/include/filetype.h
index 90a03de..db95fda 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -49,6 +49,7 @@ enum filetype {
filetype_layerscape_qspi_image,
filetype_ubootvar,
filetype_stm32_image_v1,
+ filetype_zynq_image,
filetype_max,
};
diff --git a/include/serial/cadence.h b/include/serial/cadence.h
new file mode 100644
index 0000000..f08b5b0
--- /dev/null
+++ b/include/serial/cadence.h
@@ -0,0 +1,92 @@
+#ifndef __CADENCE_UART_H__
+#define __CADENCE_UART_H__
+
+#define CADENCE_UART_CONTROL 0x00
+#define CADENCE_UART_MODE 0x04
+#define CADENCE_UART_BAUD_GEN 0x18
+#define CADENCE_UART_CHANNEL_STS 0x2C
+#define CADENCE_UART_RXTXFIFO 0x30
+#define CADENCE_UART_BAUD_DIV 0x34
+
+#define CADENCE_CTRL_RXRES (1 << 0)
+#define CADENCE_CTRL_TXRES (1 << 1)
+#define CADENCE_CTRL_RXEN (1 << 2)
+#define CADENCE_CTRL_RXDIS (1 << 3)
+#define CADENCE_CTRL_TXEN (1 << 4)
+#define CADENCE_CTRL_TXDIS (1 << 5)
+#define CADENCE_CTRL_RSTTO (1 << 6)
+#define CADENCE_CTRL_STTBRK (1 << 7)
+#define CADENCE_CTRL_STPBRK (1 << 8)
+
+#define CADENCE_MODE_CLK_REF (0 << 0)
+#define CADENCE_MODE_CLK_REF_DIV (1 << 0)
+#define CADENCE_MODE_CHRL_6 (3 << 1)
+#define CADENCE_MODE_CHRL_7 (2 << 1)
+#define CADENCE_MODE_CHRL_8 (0 << 1)
+#define CADENCE_MODE_PAR_EVEN (0 << 3)
+#define CADENCE_MODE_PAR_ODD (1 << 3)
+#define CADENCE_MODE_PAR_SPACE (2 << 3)
+#define CADENCE_MODE_PAR_MARK (3 << 3)
+#define CADENCE_MODE_PAR_NONE (4 << 3)
+
+#define CADENCE_STS_REMPTY (1 << 1)
+#define CADENCE_STS_RFUL (1 << 2)
+#define CADENCE_STS_TEMPTY (1 << 3)
+#define CADENCE_STS_TFUL (1 << 4)
+
+static inline void cadence_uart_init(void __iomem *uartbase)
+{
+ int baudrate = CONFIG_BAUDRATE;
+ unsigned int clk = 49999995;
+ unsigned int gen, div;
+
+ /* disable transmitter and receiver */
+ writel(0, uartbase + CADENCE_UART_CONTROL);
+
+ /* calculate and set baud clock generator parameters */
+ for (div = 4; div < 256; div++) {
+ int calc_rate, error;
+
+ gen = clk / (baudrate * (div + 1));
+
+ if (gen < 1 || gen > 65535)
+ continue;
+
+ calc_rate = clk / (gen * (div + 1));
+ error = baudrate - calc_rate;
+ if (error < 0)
+ error *= -1;
+ if (((error * 100) / baudrate) < 3)
+ break;
+ }
+
+ writel(gen, uartbase + CADENCE_UART_BAUD_GEN);
+ writel(div, uartbase + CADENCE_UART_BAUD_DIV);
+
+ /* soft-reset tx/rx paths */
+ writel(CADENCE_CTRL_RXRES | CADENCE_CTRL_TXRES,
+ uartbase + CADENCE_UART_CONTROL);
+
+ while (readl(uartbase + CADENCE_UART_CONTROL) &
+ (CADENCE_CTRL_RXRES | CADENCE_CTRL_TXRES))
+ ;
+
+ /* enable UART */
+ writel(CADENCE_MODE_CLK_REF | CADENCE_MODE_CHRL_8 |
+ CADENCE_MODE_PAR_NONE, uartbase + CADENCE_UART_MODE);
+ writel(CADENCE_CTRL_RXEN | CADENCE_CTRL_TXEN,
+ uartbase + CADENCE_UART_CONTROL);
+}
+
+static inline void cadence_uart_putc(void *base, int c)
+{
+ if (!(readl(base + CADENCE_UART_CONTROL) & CADENCE_CTRL_TXEN))
+ return;
+
+ while ((readl(base + CADENCE_UART_CHANNEL_STS) & CADENCE_STS_TFUL))
+ ;
+
+ writel(c, base + CADENCE_UART_RXTXFIFO);
+}
+
+#endif /* __CADENCE_UART_H__ */