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>
This commit is contained in:
Mukesh Savaliya 2018-05-11 07:41:33 -07:00 committed by Patrick Georgi
parent 03f05cff2f
commit b02452b490
9 changed files with 452 additions and 31 deletions

View File

@ -6,10 +6,11 @@ if BOARD_GOOGLE_CHEZA_COMMON
config BOARD_SPECIFIC_OPTIONS config BOARD_SPECIFIC_OPTIONS
def_bool y def_bool y
select BOARD_ROMSIZE_KB_8192 select BOARD_ROMSIZE_KB_16384
select COMMON_CBFS_SPI_WRAPPER select COMMON_CBFS_SPI_WRAPPER
select SOC_QUALCOMM_SDM845 select SOC_QUALCOMM_SDM845
select SPI_FLASH select SPI_FLASH
select SPI_FLASH_WINBOND
select MAINBOARD_HAS_CHROMEOS select MAINBOARD_HAS_CHROMEOS
select MISSING_BOARD_RESET select MISSING_BOARD_RESET

View File

@ -19,4 +19,14 @@ config VBOOT
select VBOOT_RETURN_FROM_VERSTAGE select VBOOT_RETURN_FROM_VERSTAGE
select VBOOT_OPROM_MATTERS select VBOOT_OPROM_MATTERS
select VBOOT_STARTS_IN_BOOTBLOCK 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 endif

View File

@ -7,14 +7,15 @@ bootblock-y += spi.c
bootblock-y += mmu.c bootblock-y += mmu.c
bootblock-y += timer.c bootblock-y += timer.c
bootblock-y += gpio.c bootblock-y += gpio.c
bootblock-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
bootblock-y += clock.c bootblock-y += clock.c
bootblock-$(CONFIG_SDM845_QSPI) += qspi.c
################################################################################ ################################################################################
verstage-y += spi.c verstage-y += spi.c
verstage-y += timer.c verstage-y += timer.c
verstage-y += gpio.c verstage-y += gpio.c
verstage-y += clock.c verstage-y += clock.c
verstage-$(CONFIG_SDM845_QSPI) += qspi.c
################################################################################ ################################################################################
romstage-y += spi.c romstage-y += spi.c
@ -22,6 +23,7 @@ romstage-y += cbmem.c
romstage-y += timer.c romstage-y += timer.c
romstage-y += gpio.c romstage-y += gpio.c
romstage-y += clock.c romstage-y += clock.c
romstage-$(CONFIG_SDM845_QSPI) += qspi.c
################################################################################ ################################################################################
ramstage-y += soc.c ramstage-y += soc.c
@ -30,6 +32,7 @@ ramstage-y += cbmem.c
ramstage-y += timer.c ramstage-y += timer.c
ramstage-y += gpio.c ramstage-y += gpio.c
ramstage-y += clock.c ramstage-y += clock.c
ramstage-$(CONFIG_SDM845_QSPI) += qspi.c
################################################################################ ################################################################################

View File

@ -16,9 +16,11 @@
#include <bootblock_common.h> #include <bootblock_common.h>
#include <soc/mmu.h> #include <soc/mmu.h>
#include <soc/clock.h> #include <soc/clock.h>
#include <soc/qspi.h>
void bootblock_soc_init(void) void bootblock_soc_init(void)
{ {
clock_init(); clock_init();
sdm845_mmu_init(); sdm845_mmu_init();
quadspi_init(25 * MHz);
} }

View File

@ -27,8 +27,8 @@
SECTIONS SECTIONS
{ {
SSRAM_START(0x14680000) SSRAM_START(0x14680000)
OVERLAP_VERSTAGE_ROMSTAGE(0x14680000, 128K) OVERLAP_VERSTAGE_ROMSTAGE(0x14680000, 100K)
REGION(fw_reserved1, 0x146A0000, 0x20000, 4096) DMA_COHERENT(0x14699000, 0x2000)
SSRAM_END(0x146C0000) SSRAM_END(0x146C0000)
BSRAM_START(0x14800000) BSRAM_START(0x14800000)

View File

@ -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__ */

View File

@ -19,14 +19,19 @@
#include <soc/mmu.h> #include <soc/mmu.h>
#include <soc/symbols.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_init();
mmu_config_range((void *)(4 * KiB), ((4UL * GiB) - (4 * KiB)), mmu_config_range((void *)(4 * KiB), ((4UL * GiB) - (4 * KiB)), DEV_MEM);
MA_DEV | MA_S | MA_RW); mmu_config_range((void *)_ssram, _ssram_size, CACHED_RAM);
mmu_config_range((void *)_ssram, _ssram_size, MA_MEM | MA_S | MA_RW); mmu_config_range((void *)_bsram, _bsram_size, CACHED_RAM);
mmu_config_range((void *)_bsram, _bsram_size, MA_MEM | MA_S | MA_RW); mmu_config_range((void *)_dma_coherent, _dma_coherent_size,
UNCACHED_RAM);
mmu_enable(); mmu_enable();
} }

View File

@ -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;
}

View File

@ -15,35 +15,20 @@
#include <spi-generic.h> #include <spi-generic.h>
#include <spi_flash.h> #include <spi_flash.h>
#include <soc/qspi.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;
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.claim_bus = spi_ctrlr_claim_bus, .claim_bus = sdm845_claim_bus,
.release_bus = spi_ctrlr_release_bus, .release_bus = sdm845_release_bus,
.xfer = spi_ctrlr_xfer, .xfer = sdm845_xfer,
.max_xfer_size = 65535, .max_xfer_size = QSPI_MAX_PACKET_COUNT,
}; };
const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
{ {
.ctrlr = &spi_ctrlr, .ctrlr = &spi_ctrlr,
.bus_start = 0, .bus_start = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
.bus_end = 0, .bus_end = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS,
}, },
}; };