summaryrefslogtreecommitdiffstats
path: root/src/soc/qualcomm/common
diff options
context:
space:
mode:
authorRavi Kumar Bokka <rbokka@codeaurora.org>2021-07-16 16:59:25 +0530
committerShelley Chen <shchen@google.com>2021-09-03 16:59:06 +0000
commit423945876dd632598ee842894cac50203c93d4ea (patch)
tree001b9f300894cee8f2f0cd06a45e2d1c8bef5858 /src/soc/qualcomm/common
parent65af8bbe72125eeaf970e5e57102cb802d00fc69 (diff)
downloadcoreboot-423945876dd632598ee842894cac50203c93d4ea.tar.gz
coreboot-423945876dd632598ee842894cac50203c93d4ea.tar.bz2
coreboot-423945876dd632598ee842894cac50203c93d4ea.zip
soc/qualcomm/common/qspi: Add support for common QSPI driver
copy existing QSPI driver from /soc/qualcomm/sc7180 to common folder. This common QSPI driver works in master mode and provides read/write operation for the slave devices like flash. BUG=b:182963902 TEST=Validated on qualcomm sc7180 and sc7280 development board Signed-off-by: Rajesh Patil <rajpat@codeaurora.org> Change-Id: I5b3816b823e14db1dd13f1eb4a6761c7a61604b9 Reviewed-on: https://review.coreboot.org/c/coreboot/+/55323 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Shelley Chen <shchen@google.com>
Diffstat (limited to 'src/soc/qualcomm/common')
-rw-r--r--src/soc/qualcomm/common/include/soc/qspi_common.h108
-rw-r--r--src/soc/qualcomm/common/qspi.c296
-rw-r--r--src/soc/qualcomm/common/spi.c23
3 files changed, 427 insertions, 0 deletions
diff --git a/src/soc/qualcomm/common/include/soc/qspi_common.h b/src/soc/qualcomm/common/include/soc/qspi_common.h
new file mode 100644
index 000000000000..a0f612c04f0c
--- /dev/null
+++ b/src/soc/qualcomm/common/include/soc/qspi_common.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <types.h>
+#include <soc/addressmap.h>
+#include <spi-generic.h>
+
+#ifndef __SOC_QUALCOMM_QSPI_H__
+#define __SOC_QUALCOMM_QSPI_H__
+
+struct qcom_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(qcom_qspi_regs, rd_fifo, 0x50);
+static struct qcom_qspi_regs * const qcom_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 qspi_claim_bus(const struct spi_slave *slave);
+int qspi_setup_bus(const struct spi_slave *slave);
+void qspi_release_bus(const struct spi_slave *slave);
+int qspi_xfer(const struct spi_slave *slave, const void *dout,
+ size_t out_bytes, void *din, size_t in_bytes);
+int qspi_xfer_dual(const struct spi_slave *slave, const void *dout,
+ size_t out_bytes, void *din, size_t in_bytes);
+#endif /* __SOC_QUALCOMM_QSPI_H__ */
diff --git a/src/soc/qualcomm/common/qspi.c b/src/soc/qualcomm/common/qspi.c
new file mode 100644
index 000000000000..b6d2b2d5b390
--- /dev/null
+++ b/src/soc/qualcomm/common/qspi.c
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <spi-generic.h>
+#include <spi_flash.h>
+#include <arch/cache.h>
+#include <device/mmio.h>
+#include <soc/addressmap.h>
+#include <soc/qspi_common.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(&qcom_qspi->mstr_int_sts, 0xFFFFFFFF);
+ write32(&qcom_qspi->next_dma_desc_addr, (uint32_t)(uintptr_t) chain);
+
+ while (1) {
+ mstr_int_status = read32(&qcom_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;
+ /*
+ * QSPI controller doesn't support transfer starts with read segment.
+ * So to support read transfers that are not preceded by write, set
+ * transfer fragment bit = 1
+ */
+ next->fragment = 1;
+ 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;
+
+ return next;
+}
+
+static void cs_change(enum cs_state state)
+{
+ gpio_set(QSPI_CS, state == CS_DEASSERT);
+}
+
+static void configure_gpios(void)
+{
+ gpio_output(QSPI_CS, 1);
+
+ gpio_configure(QSPI_DATA_0, GPIO_FUNC_QSPI_DATA_0,
+ GPIO_NO_PULL, GPIO_2MA, GPIO_OUTPUT);
+
+ gpio_configure(QSPI_DATA_1, GPIO_FUNC_QSPI_DATA_1,
+ GPIO_NO_PULL, GPIO_2MA, GPIO_OUTPUT);
+
+ gpio_configure(QSPI_CLK, GPIO_FUNC_QSPI_CLK,
+ GPIO_NO_PULL, GPIO_8MA, GPIO_OUTPUT);
+}
+
+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(&qcom_qspi->mstr_cfg, mstr_config);
+ write32(&qcom_qspi->ahb_mstr_cfg, 0xA42);
+ write32(&qcom_qspi->mstr_int_en, 0x0);
+ write32(&qcom_qspi->mstr_int_sts, 0xFFFFFFFF);
+ write32(&qcom_qspi->rd_fifo_cfg, 0x0);
+ write32(&qcom_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 qspi_claim_bus(const struct spi_slave *slave)
+{
+ cs_change(CS_ASSERT);
+ return 0;
+}
+
+void qspi_release_bus(const struct spi_slave *slave)
+{
+ cs_change(CS_DEASSERT);
+}
+
+static int xfer(enum qspi_mode mode, const void *dout, size_t out_bytes,
+ void *din, size_t in_bytes)
+{
+ 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;
+}
+
+int qspi_xfer(const struct spi_slave *slave, const void *dout,
+ size_t out_bytes, void *din, size_t in_bytes)
+{
+ return xfer(SDR_1BIT, dout, out_bytes, din, in_bytes);
+}
+
+int qspi_xfer_dual(const struct spi_slave *slave, const void *dout,
+ size_t out_bytes, void *din, size_t in_bytes)
+{
+ return xfer(SDR_2BIT, dout, out_bytes, din, in_bytes);
+}
diff --git a/src/soc/qualcomm/common/spi.c b/src/soc/qualcomm/common/spi.c
new file mode 100644
index 000000000000..b4db3e589c51
--- /dev/null
+++ b/src/soc/qualcomm/common/spi.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <spi-generic.h>
+#include <spi_flash.h>
+#include <soc/qspi_common.h>
+
+static const struct spi_ctrlr qspi_ctrlr = {
+ .claim_bus = qspi_claim_bus,
+ .release_bus = qspi_release_bus,
+ .xfer = qspi_xfer,
+ .xfer_dual = qspi_xfer_dual,
+ .max_xfer_size = QSPI_MAX_PACKET_COUNT,
+};
+
+const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
+ {
+ .ctrlr = &qspi_ctrlr,
+ .bus_start = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
+ .bus_end = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
+ },
+};
+
+const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);