sdm845: Add SPI-NOR flash driver

TEST=build & run

Change-Id: Ie404faf37617d2ad792310709ca2063f9a372076
Signed-off-by: Mukesh Savaliya <msavaliy@codeaurora.org>
Reviewed-on: https://review.coreboot.org/c/25392
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/src/mainboard/google/cheza/Kconfig b/src/mainboard/google/cheza/Kconfig
index 5f952f2..14ddc81 100644
--- a/src/mainboard/google/cheza/Kconfig
+++ b/src/mainboard/google/cheza/Kconfig
@@ -6,10 +6,11 @@
 
 config BOARD_SPECIFIC_OPTIONS
 	def_bool y
-	select BOARD_ROMSIZE_KB_8192
+	select BOARD_ROMSIZE_KB_16384
 	select COMMON_CBFS_SPI_WRAPPER
 	select SOC_QUALCOMM_SDM845
 	select SPI_FLASH
+	select SPI_FLASH_WINBOND
 	select MAINBOARD_HAS_CHROMEOS
 	select MISSING_BOARD_RESET
 
diff --git a/src/soc/qualcomm/sdm845/Kconfig b/src/soc/qualcomm/sdm845/Kconfig
index 6399ad1..9af1954 100644
--- a/src/soc/qualcomm/sdm845/Kconfig
+++ b/src/soc/qualcomm/sdm845/Kconfig
@@ -19,4 +19,14 @@
 	select VBOOT_RETURN_FROM_VERSTAGE
 	select VBOOT_OPROM_MATTERS
 	select VBOOT_STARTS_IN_BOOTBLOCK
+
+config SDM845_QSPI
+	bool
+	default y if COMMON_CBFS_SPI_WRAPPER
+	prompt "Build Flash Using SPI-NOR"
+
+config BOOT_DEVICE_SPI_FLASH_BUS
+	int
+	default 16
+
 endif
diff --git a/src/soc/qualcomm/sdm845/Makefile.inc b/src/soc/qualcomm/sdm845/Makefile.inc
index fc8edd5..896ae58 100644
--- a/src/soc/qualcomm/sdm845/Makefile.inc
+++ b/src/soc/qualcomm/sdm845/Makefile.inc
@@ -7,14 +7,15 @@
 bootblock-y += mmu.c
 bootblock-y += timer.c
 bootblock-y += gpio.c
-bootblock-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
 bootblock-y += clock.c
+bootblock-$(CONFIG_SDM845_QSPI) += qspi.c
 
 ################################################################################
 verstage-y += spi.c
 verstage-y += timer.c
 verstage-y += gpio.c
 verstage-y += clock.c
+verstage-$(CONFIG_SDM845_QSPI) += qspi.c
 
 ################################################################################
 romstage-y += spi.c
@@ -22,6 +23,7 @@
 romstage-y += timer.c
 romstage-y += gpio.c
 romstage-y += clock.c
+romstage-$(CONFIG_SDM845_QSPI) += qspi.c
 
 ################################################################################
 ramstage-y += soc.c
@@ -30,6 +32,7 @@
 ramstage-y += timer.c
 ramstage-y += gpio.c
 ramstage-y += clock.c
+ramstage-$(CONFIG_SDM845_QSPI) += qspi.c
 
 ################################################################################
 
diff --git a/src/soc/qualcomm/sdm845/bootblock.c b/src/soc/qualcomm/sdm845/bootblock.c
index eb4289b..589865e 100644
--- a/src/soc/qualcomm/sdm845/bootblock.c
+++ b/src/soc/qualcomm/sdm845/bootblock.c
@@ -16,9 +16,11 @@
 #include <bootblock_common.h>
 #include <soc/mmu.h>
 #include <soc/clock.h>
+#include <soc/qspi.h>
 
 void bootblock_soc_init(void)
 {
 	clock_init();
 	sdm845_mmu_init();
+	quadspi_init(25 * MHz);
 }
diff --git a/src/soc/qualcomm/sdm845/include/soc/memlayout.ld b/src/soc/qualcomm/sdm845/include/soc/memlayout.ld
index 2abe74e..5d99a8e 100644
--- a/src/soc/qualcomm/sdm845/include/soc/memlayout.ld
+++ b/src/soc/qualcomm/sdm845/include/soc/memlayout.ld
@@ -27,8 +27,8 @@
 SECTIONS
 {
 	SSRAM_START(0x14680000)
-	OVERLAP_VERSTAGE_ROMSTAGE(0x14680000, 128K)
-	REGION(fw_reserved1, 0x146A0000, 0x20000, 4096)
+	OVERLAP_VERSTAGE_ROMSTAGE(0x14680000, 100K)
+	DMA_COHERENT(0x14699000, 0x2000)
 	SSRAM_END(0x146C0000)
 
 	BSRAM_START(0x14800000)
diff --git a/src/soc/qualcomm/sdm845/include/soc/qspi.h b/src/soc/qualcomm/sdm845/include/soc/qspi.h
new file mode 100644
index 0000000..ac9fd50
--- /dev/null
+++ b/src/soc/qualcomm/sdm845/include/soc/qspi.h
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Qualcomm Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <types.h>
+#include <soc/addressmap.h>
+#include <spi-generic.h>
+
+#ifndef __SOC_QUALCOMM_SDM845_QSPI_H__
+#define __SOC_QUALCOMM_SDM845_QSPI_H__
+
+struct sdm845_qspi_regs {
+	u32 mstr_cfg;
+	u32 ahb_mstr_cfg;
+	u32 reserve_0;
+	u32 mstr_int_en;
+	u32 mstr_int_sts;
+	u32 pio_xfer_ctrl;
+	u32 pio_xfer_cfg;
+	u32 pio_xfer_sts;
+	u32 pio_dataout_1byte;
+	u32 pio_dataout_4byte;
+	u32 rd_fifo_cfg;
+	u32 rd_fifo_sts;
+	u32 rd_fifo_rst;
+	u32 reserve_1[3];
+	u32 next_dma_desc_addr;
+	u32 current_dma_desc_addr;
+	u32 current_mem_addr;
+	u32 hw_version;
+	u32 rd_fifo[16];
+};
+
+check_member(sdm845_qspi_regs, rd_fifo, 0x50);
+static struct sdm845_qspi_regs * const sdm845_qspi = (void *) QSPI_BASE;
+
+// MSTR_CONFIG register
+
+#define TX_DATA_OE_DELAY_SHIFT 24
+#define TX_DATA_OE_DELAY_MASK (0x3 << TX_DATA_OE_DELAY_SHIFT)
+#define TX_CS_N_DELAY_SHIFT 22
+#define TX_CS_N_DELAY_MASK (0x3 << TX_CS_N_DELAY_SHIFT)
+#define TX_CLK_DELAY_SHIFT 20
+#define TX_CLK_DELAY_MASK (0x3 << TX_CLK_DELAY_SHIFT)
+#define TX_DATA_DELAY_SHIFT 18
+#define TX_DATA_DELAY_MASK (0x3 << TX_DATA_DELAY_SHIFT)
+#define LPA_BASE_SHIFT 14
+#define LPA_BASE_MASK (0xF << LPA_BASE_SHIFT)
+#define SBL_EN BIT(13)
+#define CHIP_SELECT_NUM BIT(12)
+#define SPI_MODE_SHIFT 10
+#define SPI_MODE_MASK (0x3 << SPI_MODE_SHIFT)
+#define BIG_ENDIAN_MODE BIT(9)
+#define DMA_ENABLE BIT(8)
+#define PIN_WPN BIT(7)
+#define PIN_HOLDN BIT(6)
+#define FB_CLK_EN BIT(4)
+#define FULL_CYCLE_MODE BIT(3)
+
+// MSTR_INT_ENABLE and MSTR_INT_STATUS register
+
+#define DMA_CHAIN_DONE BIT(31)
+#define TRANSACTION_DONE BIT(16)
+#define WRITE_FIFO_OVERRUN BIT(11)
+#define WRITE_FIFO_FULL BIT(10)
+#define HRESP_FROM_NOC_ERR BIT(3)
+#define RESP_FIFO_RDY BIT(2)
+#define RESP_FIFO_NOT_EMPTY BIT(1)
+#define RESP_FIFO_UNDERRUN BIT(0)
+
+// PIO_TRANSFER_CONFIG register
+
+#define TRANSFER_FRAGMENT BIT(8)
+#define MULTI_IO_MODE_SHIFT 1
+#define MULTI_IO_MODE_MASK (0x7 << MULTI_IO_MODE_SHIFT)
+#define TRANSFER_DIRECTION BIT(0)
+
+// PIO_TRANSFER_STATUS register
+
+#define WR_FIFO_BYTES_SHIFT 16
+#define WR_FIFO_BYTES_MASK (0xFFFF << WR_FIFO_BYTES_SHIFT)
+
+// RD_FIFO_CONFIG register
+
+#define CONTINUOUS_MODE BIT(0)
+
+// RD_FIFO_STATUS register
+
+#define FIFO_EMPTY BIT(11)
+#define WR_CNTS_SHIFT 4
+#define WR_CNTS_MASK (0x7F << WR_CNTS_SHIFT)
+#define RDY_64BYTE BIT(3)
+#define RDY_32BYTE BIT(2)
+#define RDY_16BYTE BIT(1)
+#define FIFO_RDY BIT(0)
+
+// RD_FIFO_RESET register
+
+#define RESET_FIFO BIT(0)
+
+#define QSPI_MAX_PACKET_COUNT 0xFFC0
+
+void quadspi_init(uint32_t hz);
+int sdm845_claim_bus(const struct spi_slave *slave);
+int sdm845_setup_bus(const struct spi_slave *slave);
+void sdm845_release_bus(const struct spi_slave *slave);
+int sdm845_xfer(const struct spi_slave *slave, const void *dout,
+		size_t out_bytes, void *din, size_t in_bytes);
+#endif /* __SOC_QUALCOMM_SDM845_QSPI_H__ */
diff --git a/src/soc/qualcomm/sdm845/mmu.c b/src/soc/qualcomm/sdm845/mmu.c
index 12219e8..52e7733 100644
--- a/src/soc/qualcomm/sdm845/mmu.c
+++ b/src/soc/qualcomm/sdm845/mmu.c
@@ -19,14 +19,19 @@
 #include <soc/mmu.h>
 #include <soc/symbols.h>
 
-void sdm845_mmu_init()
+#define   CACHED_RAM (MA_MEM | MA_S | MA_RW)
+#define UNCACHED_RAM (MA_MEM | MA_S | MA_RW | MA_MEM_NC)
+#define      DEV_MEM (MA_DEV | MA_S | MA_RW)
+
+void sdm845_mmu_init(void)
 {
 	mmu_init();
 
-	mmu_config_range((void *)(4 * KiB), ((4UL * GiB) - (4 * KiB)),
-			MA_DEV | MA_S | MA_RW);
-	mmu_config_range((void *)_ssram, _ssram_size, MA_MEM | MA_S | MA_RW);
-	mmu_config_range((void *)_bsram, _bsram_size, MA_MEM | MA_S | MA_RW);
+	mmu_config_range((void *)(4 * KiB), ((4UL * GiB) - (4 * KiB)), DEV_MEM);
+	mmu_config_range((void *)_ssram, _ssram_size, CACHED_RAM);
+	mmu_config_range((void *)_bsram, _bsram_size, CACHED_RAM);
+	mmu_config_range((void *)_dma_coherent, _dma_coherent_size,
+			 UNCACHED_RAM);
 
 	mmu_enable();
 }
diff --git a/src/soc/qualcomm/sdm845/qspi.c b/src/soc/qualcomm/sdm845/qspi.c
new file mode 100644
index 0000000..8f1cfc1
--- /dev/null
+++ b/src/soc/qualcomm/sdm845/qspi.c
@@ -0,0 +1,296 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2018, The Linux Foundation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <spi-generic.h>
+#include <spi_flash.h>
+#include <arch/cache.h>
+#include <arch/io.h>
+#include <soc/addressmap.h>
+#include <soc/qspi.h>
+#include <soc/gpio.h>
+#include <soc/clock.h>
+#include <symbols.h>
+#include <assert.h>
+#include <gpio.h>
+#include <string.h>
+
+#define CACHE_LINE_SIZE	64
+
+static int curr_desc_idx = -1;
+
+struct cmd_desc {
+	uint32_t data_address;
+	uint32_t next_descriptor;
+	uint32_t direction:1;
+	uint32_t multi_io_mode:3;
+	uint32_t reserved1:4;
+	uint32_t fragment:1;
+	uint32_t reserved2:7;
+	uint32_t length:16;
+	//------------------------//
+	uint32_t bounce_src;
+	uint32_t bounce_dst;
+	uint32_t bounce_length;
+	uint64_t padding[5];
+};
+
+enum qspi_mode {
+	SDR_1BIT = 1,
+	SDR_2BIT = 2,
+	SDR_4BIT = 3,
+	DDR_1BIT = 5,
+	DDR_2BIT = 6,
+	DDR_4BIT = 7,
+};
+
+enum cs_state {
+	CS_DEASSERT,
+	CS_ASSERT
+};
+
+struct xfer_cfg {
+	enum qspi_mode mode;
+};
+
+enum bus_xfer_direction {
+	MASTER_READ = 0,
+	MASTER_WRITE = 1,
+};
+
+struct {
+	struct cmd_desc descriptors[3];
+	uint8_t buffers[3][CACHE_LINE_SIZE];
+} *dma = (void *)_dma_coherent;
+
+static void dma_transfer_chain(struct cmd_desc *chain)
+{
+	uint32_t mstr_int_status;
+
+	write32(&sdm845_qspi->mstr_int_sts, 0xFFFFFFFF);
+	write32(&sdm845_qspi->next_dma_desc_addr, (uint32_t)(uintptr_t) chain);
+
+	while (1) {
+		mstr_int_status = read32(&sdm845_qspi->mstr_int_sts);
+		if (mstr_int_status & DMA_CHAIN_DONE)
+			break;
+	}
+}
+
+static void flush_chain(void)
+{
+	struct cmd_desc *desc = &dma->descriptors[0];
+	uint8_t *src;
+	uint8_t *dst;
+
+	dma_transfer_chain(desc);
+
+	while (desc) {
+		if (desc->direction == MASTER_READ) {
+			if (desc->bounce_length == 0)
+				dcache_invalidate_by_mva(
+					(void *)(uintptr_t) desc->data_address,
+					desc->length);
+			else {
+				src = (void *)(uintptr_t) desc->bounce_src;
+				dst = (void *)(uintptr_t) desc->bounce_dst;
+				memcpy(dst, src, desc->bounce_length);
+			}
+		}
+		desc = (void *)(uintptr_t) desc->next_descriptor;
+	}
+	curr_desc_idx = -1;
+}
+
+static struct cmd_desc *allocate_descriptor(void)
+{
+	struct cmd_desc *current;
+	struct cmd_desc *next;
+	uint8_t index;
+
+	current = (curr_desc_idx == -1) ?
+		NULL : &dma->descriptors[curr_desc_idx];
+
+	index = ++curr_desc_idx;
+	next = &dma->descriptors[index];
+
+	next->data_address = (uint32_t) (uintptr_t) dma->buffers[index];
+
+	next->next_descriptor = 0;
+	next->direction = MASTER_READ;
+	next->multi_io_mode = 0;
+	next->reserved1 = 0;
+	next->fragment = 0;
+	next->reserved2 = 0;
+	next->length = 0;
+	next->bounce_src = 0;
+	next->bounce_dst = 0;
+	next->bounce_length = 0;
+
+	if (current) {
+		current->next_descriptor = (uint32_t)(uintptr_t) next;
+		current->fragment = 1;
+	}
+
+	return next;
+}
+
+static void cs_change(enum cs_state state)
+{
+	gpio_set(GPIO(90), state == CS_DEASSERT);
+}
+
+static void configure_gpios(void)
+{
+	gpio_output(GPIO(90), 1);
+
+	gpio_configure(GPIO(91), GPIO91_FUNC_QSPI_DATA,
+		GPIO_NO_PULL, GPIO_2MA, GPIO_ENABLE);
+
+	gpio_configure(GPIO(92), GPIO92_FUNC_QSPI_DATA,
+		GPIO_NO_PULL, GPIO_2MA, GPIO_ENABLE);
+
+	gpio_configure(GPIO(95), GPIO95_FUNC_QSPI_CLK,
+		GPIO_NO_PULL, GPIO_2MA, GPIO_ENABLE);
+}
+
+static void queue_bounce_data(uint8_t *data, uint32_t data_bytes,
+			      enum qspi_mode data_mode, bool write)
+{
+	struct cmd_desc *desc;
+	uint8_t *ptr;
+
+	desc = allocate_descriptor();
+	desc->direction = write;
+	desc->multi_io_mode = data_mode;
+	ptr = (void *)(uintptr_t) desc->data_address;
+
+	if (write) {
+		memcpy(ptr, data, data_bytes);
+	} else {
+		desc->bounce_src = (uint32_t)(uintptr_t) ptr;
+		desc->bounce_dst = (uint32_t)(uintptr_t) data;
+		desc->bounce_length = data_bytes;
+	}
+
+	desc->length = data_bytes;
+}
+
+static void queue_direct_data(uint8_t *data, uint32_t data_bytes,
+			      enum qspi_mode data_mode, bool write)
+{
+	struct cmd_desc *desc;
+
+	desc = allocate_descriptor();
+	desc->direction = write;
+	desc->multi_io_mode = data_mode;
+	desc->data_address = (uint32_t)(uintptr_t) data;
+	desc->length = data_bytes;
+
+	if (write)
+		dcache_clean_by_mva(data, data_bytes);
+	else
+		dcache_invalidate_by_mva(data, data_bytes);
+}
+
+static void queue_data(uint8_t *data, uint32_t data_bytes,
+	enum qspi_mode data_mode, bool write)
+{
+	uint8_t *aligned_ptr;
+	uint8_t *epilog_ptr;
+	uint32_t prolog_bytes, aligned_bytes, epilog_bytes;
+
+	if (data_bytes == 0)
+		return;
+
+	aligned_ptr =
+		(uint8_t *)ALIGN_UP((uintptr_t)data, CACHE_LINE_SIZE);
+
+	prolog_bytes = MIN(data_bytes, aligned_ptr - data);
+	aligned_bytes = ALIGN_DOWN(data_bytes - prolog_bytes, CACHE_LINE_SIZE);
+	epilog_bytes = data_bytes - prolog_bytes - aligned_bytes;
+
+	epilog_ptr = data + prolog_bytes + aligned_bytes;
+
+	if (prolog_bytes)
+		queue_bounce_data(data, prolog_bytes, data_mode, write);
+	if (aligned_bytes)
+		queue_direct_data(aligned_ptr, aligned_bytes, data_mode, write);
+	if (epilog_bytes)
+		queue_bounce_data(epilog_ptr, epilog_bytes, data_mode, write);
+}
+
+static void reg_init(void)
+{
+	uint32_t spi_mode;
+	uint32_t tx_data_oe_delay, tx_data_delay;
+	uint32_t mstr_config;
+
+	spi_mode = 0;
+
+	tx_data_oe_delay = 0;
+	tx_data_delay = 0;
+
+	mstr_config = (tx_data_oe_delay << TX_DATA_OE_DELAY_SHIFT) |
+		(tx_data_delay << TX_DATA_DELAY_SHIFT) | (SBL_EN) |
+		(spi_mode << SPI_MODE_SHIFT) |
+		(PIN_HOLDN) |
+		(FB_CLK_EN) |
+		(DMA_ENABLE) |
+		(FULL_CYCLE_MODE);
+
+	write32(&sdm845_qspi->mstr_cfg, mstr_config);
+	write32(&sdm845_qspi->ahb_mstr_cfg, 0xA42);
+	write32(&sdm845_qspi->mstr_int_en, 0x0);
+	write32(&sdm845_qspi->mstr_int_sts, 0xFFFFFFFF);
+	write32(&sdm845_qspi->rd_fifo_cfg, 0x0);
+	write32(&sdm845_qspi->rd_fifo_rst, RESET_FIFO);
+}
+
+void quadspi_init(uint32_t hz)
+{
+	assert(dcache_line_bytes() == CACHE_LINE_SIZE);
+	clock_configure_qspi(hz * 4);
+	configure_gpios();
+	reg_init();
+}
+
+int sdm845_claim_bus(const struct spi_slave *slave)
+{
+	cs_change(CS_ASSERT);
+	return 0;
+}
+
+void sdm845_release_bus(const struct spi_slave *slave)
+{
+	cs_change(CS_DEASSERT);
+}
+
+int sdm845_xfer(const struct spi_slave *slave, const void *dout,
+		       size_t out_bytes, void *din, size_t in_bytes)
+{
+	enum qspi_mode mode = SDR_1BIT;
+
+	if ((out_bytes && !dout) || (in_bytes && !din) ||
+		(in_bytes && out_bytes)) {
+		return -1;
+	}
+
+	queue_data((uint8_t *) (out_bytes ? dout : din),
+		in_bytes | out_bytes, mode, !!out_bytes);
+
+	flush_chain();
+
+	return 0;
+}
diff --git a/src/soc/qualcomm/sdm845/spi.c b/src/soc/qualcomm/sdm845/spi.c
index c04b15d..e276e1d 100644
--- a/src/soc/qualcomm/sdm845/spi.c
+++ b/src/soc/qualcomm/sdm845/spi.c
@@ -15,35 +15,20 @@
 
 #include <spi-generic.h>
 #include <spi_flash.h>
-
-static int spi_ctrlr_claim_bus(const struct spi_slave *slave)
-{
-	return 0;
-}
-
-static void spi_ctrlr_release_bus(const struct spi_slave *slave)
-{
-
-}
-
-static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
-			  size_t bytes_out, void *din, size_t bytes_in)
-{
-	return 0;
-}
+#include <soc/qspi.h>
 
 static const struct spi_ctrlr spi_ctrlr = {
-	.claim_bus = spi_ctrlr_claim_bus,
-	.release_bus = spi_ctrlr_release_bus,
-	.xfer = spi_ctrlr_xfer,
-	.max_xfer_size = 65535,
+	.claim_bus = sdm845_claim_bus,
+	.release_bus = sdm845_release_bus,
+	.xfer = sdm845_xfer,
+	.max_xfer_size = QSPI_MAX_PACKET_COUNT,
 };
 
 const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
 	{
 		.ctrlr = &spi_ctrlr,
-		.bus_start = 0,
-		.bus_end = 0,
+		.bus_start = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
+		.bus_end = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
 	},
 };