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 #include #include +#include #include +#include #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 @@ /* 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 @@ /* 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 +#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 +#include +#include +#include + +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 +#include +#include +#include + +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 @@ [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 @@ 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 @@ 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 @@ 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 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 < ARRAY_SIZE(bin_format); i++) - if (bin_header[i] != (byte_order == XILINX_BYTE_ORDER_BIT) ? - bin_format[i] : __swab32(bin_format[i])) - return 0; + for (i = 0; i < BIN_HEADER_LENGTH; i++, bin_header++) { + u32 current; + u32 expected; - return 1; + if (byte_order == XILINX_BYTE_ORDER_BIT) + current = *bin_header; + else + current = __swab32(*bin_header); + + 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 true; } static int get_header_length(const char *header, size_t size) @@ -227,6 +229,7 @@ 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 #include #include +#include +#include +#include #include #include -#include -#include -#include - -#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 /* * 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 @@ 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_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 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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 @@ 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__ */