- distro boot support for SPI flash
- sifive spi flash driver
This commit is contained in:
Tom Rini 2020-04-30 18:05:15 -04:00
commit b641dd3ec8
19 changed files with 1072 additions and 1189 deletions

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (C) 2019 Jagan Teki <jagan@amarulasolutions.com>
*/
/ {
aliases {
spi0 = &qspi0;
spi2 = &qspi2;
};
};

View file

@ -26,6 +26,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
imply CMD_FS_GENERIC
imply CMD_NET
imply CMD_PING
imply CMD_SF
imply CLK_SIFIVE
imply CLK_SIFIVE_FU540_PRCI
imply DOS_PARTITION
@ -40,6 +41,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
imply SIFIVE_SERIAL
imply SPI
imply SPI_SIFIVE
imply SPI_FLASH
imply SPI_FLASH_ISSI
imply MMC
imply MMC_SPI
imply MMC_BROKEN_CD

View file

@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o gigadevice.o macronix.o micron.o winbond.o
spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View file

@ -835,6 +835,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&toshiba_spinand_manufacturer,
&winbond_spinand_manufacturer,
};

View file

@ -0,0 +1,201 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 exceet electronics GmbH
* Copyright (c) 2018 Kontron Electronics GmbH
*
* Author: Frieder Schrempf <frieder.schrempf@kontron.de>
*/
#ifndef __UBOOT__
#include <malloc.h>
#include <linux/device.h>
#include <linux/kernel.h>
#endif
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_TOSHIBA 0x98
#define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD(false, 0, NULL, 0));
static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 0)
return -ERANGE;
region->offset = mtd->oobsize / 2;
region->length = mtd->oobsize / 2;
return 0;
}
static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 0)
return -ERANGE;
/* 2 bytes reserved for BBM */
region->offset = 2;
region->length = (mtd->oobsize / 2) - 2;
return 0;
}
static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = {
.ecc = tc58cxgxsx_ooblayout_ecc,
.rfree = tc58cxgxsx_ooblayout_free,
};
static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
return 0;
case STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
case STATUS_ECC_HAS_BITFLIPS:
case TOSH_STATUS_ECC_HAS_BITFLIPS_T:
/*
* Let's try to retrieve the real maximum number of bitflips
* in order to avoid forcing the wear-leveling layer to move
* data around if it's not necessary.
*/
if (spi_mem_exec_op(spinand->slave, &op))
return nand->eccreq.strength;
mbf >>= 4;
if (WARN_ON(mbf > nand->eccreq.strength || !mbf))
return nand->eccreq.strength;
return mbf;
default:
break;
}
return -EINVAL;
}
static const struct spinand_info toshiba_spinand_table[] = {
/* 3.3V 1Gb */
SPINAND_INFO("TC58CVG0S3", 0xC2,
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 3.3V 2Gb */
SPINAND_INFO("TC58CVG1S3", 0xCB,
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 3.3V 4Gb */
SPINAND_INFO("TC58CVG2S0", 0xCD,
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 3.3V 4Gb */
SPINAND_INFO("TC58CVG2S0", 0xED,
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 1.8V 1Gb */
SPINAND_INFO("TC58CYG0S3", 0xB2,
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 1.8V 2Gb */
SPINAND_INFO("TC58CYG1S3", 0xBB,
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
/* 1.8V 4Gb */
SPINAND_INFO("TC58CYG2S0", 0xBD,
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
tc58cxgxsx_ecc_get_status)),
};
static int toshiba_spinand_detect(struct spinand_device *spinand)
{
u8 *id = spinand->id.data;
int ret;
/*
* Toshiba SPI NAND read ID needs a dummy byte,
* so the first byte in id is garbage.
*/
if (id[1] != SPINAND_MFR_TOSHIBA)
return 0;
ret = spinand_match_and_init(spinand, toshiba_spinand_table,
ARRAY_SIZE(toshiba_spinand_table),
id[2]);
if (ret)
return ret;
return 1;
}
static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
.detect = toshiba_spinand_detect,
};
const struct spinand_manufacturer toshiba_spinand_manufacturer = {
.id = SPINAND_MFR_TOSHIBA,
.name = "Toshiba",
.ops = &toshiba_spinand_manuf_ops,
};

View file

@ -325,6 +325,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
case SNOR_MFR_ISSI:
case SNOR_MFR_MACRONIX:
case SNOR_MFR_WINBOND:
if (need_wren)
@ -1246,11 +1247,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
* If page_size is a power of two, the offset can be quickly
* calculated with an AND operation. On the other cases we
* need to do a modulus operation (more expensive).
* Power of two numbers have only one bit set and we can use
* the instruction hweight32 to detect if we need to do a
* modulus (do_div()) or not.
*/
if (hweight32(nor->page_size) == 1) {
if (is_power_of_2(nor->page_size)) {
page_offset = addr & (nor->page_size - 1);
} else {
u64 aux = addr;

View file

@ -135,7 +135,8 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("is25wp128", 0x9d7018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("is25wp256", 0x9d7019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_4B_OPCODES) },
#endif
#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */
/* Macronix */
@ -183,8 +184,8 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("n25q00", 0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_4B_OPCODES) },
{ INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_4B_OPCODES) },
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
#endif
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
/* Spansion/Cypress -- single (large) sector size only, at least
@ -192,9 +193,10 @@ const struct flash_info spi_nor_ids[] = {
*/
{ INFO("s25sl032p", 0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("s25sl064p", 0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) },
{ INFO("s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO6("s25fl512s", 0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO6("s25fl512s", 0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO6("s25fs512s", 0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl512s_256k", 0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl512s_64k", 0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl512s_512k", 0x010220, 0x4f00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },

View file

@ -166,11 +166,28 @@ static int cadence_spi_probe(struct udevice *bus)
{
struct cadence_spi_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus);
struct clk clk;
int ret;
priv->regbase = plat->regbase;
priv->ahbbase = plat->ahbbase;
if (plat->ref_clk_hz == 0) {
ret = clk_get_by_index(bus, 0, &clk);
if (ret) {
#ifdef CONFIG_CQSPI_REF_CLK
plat->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
#else
return ret;
#endif
} else {
plat->ref_clk_hz = clk_get_rate(&clk);
clk_free(&clk);
if (IS_ERR_VALUE(plat->ref_clk_hz))
return plat->ref_clk_hz;
}
}
ret = reset_get_bulk(bus, &priv->resets);
if (ret)
dev_warn(bus, "Can't get reset: %d\n", ret);
@ -268,8 +285,6 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
{
struct cadence_spi_platdata *plat = bus->platdata;
ofnode subnode;
struct clk clk;
int ret;
plat->regbase = (void *)devfdt_get_addr_index(bus, 0);
plat->ahbbase = (void *)devfdt_get_addr_size_index(bus, 1,
@ -305,20 +320,6 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20);
ret = clk_get_by_index(bus, 0, &clk);
if (ret) {
#ifdef CONFIG_CQSPI_REF_CLK
plat->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
#else
return ret;
#endif
} else {
plat->ref_clk_hz = clk_get_rate(&clk);
clk_free(&clk);
if (IS_ERR_VALUE(plat->ref_clk_hz))
return plat->ref_clk_hz;
}
debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
__func__, plat->regbase, plat->ahbbase, plat->max_hz,
plat->page_size);

File diff suppressed because it is too large Load diff

View file

@ -1,145 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2013-2014 Freescale Semiconductor, Inc.
*
* Register definitions for Freescale QSPI
*/
#ifndef _FSL_QSPI_H_
#define _FSL_QSPI_H_
struct fsl_qspi_regs {
u32 mcr;
u32 rsvd0[1];
u32 ipcr;
u32 flshcr;
u32 buf0cr;
u32 buf1cr;
u32 buf2cr;
u32 buf3cr;
u32 bfgencr;
u32 soccr;
u32 rsvd1[2];
u32 buf0ind;
u32 buf1ind;
u32 buf2ind;
u32 rsvd2[49];
u32 sfar;
u32 rsvd3[1];
u32 smpr;
u32 rbsr;
u32 rbct;
u32 rsvd4[15];
u32 tbsr;
u32 tbdr;
u32 rsvd5[1];
u32 sr;
u32 fr;
u32 rser;
u32 spndst;
u32 sptrclr;
u32 rsvd6[4];
u32 sfa1ad;
u32 sfa2ad;
u32 sfb1ad;
u32 sfb2ad;
u32 rsvd7[28];
u32 rbdr[32];
u32 rsvd8[32];
u32 lutkey;
u32 lckcr;
u32 rsvd9[2];
u32 lut[64];
};
#define QSPI_IPCR_SEQID_SHIFT 24
#define QSPI_IPCR_SEQID_MASK (0xf << QSPI_IPCR_SEQID_SHIFT)
#define QSPI_MCR_END_CFD_SHIFT 2
#define QSPI_MCR_END_CFD_MASK (3 << QSPI_MCR_END_CFD_SHIFT)
#ifdef CONFIG_SYS_FSL_QSPI_AHB
/* AHB needs 64bit operation */
#define QSPI_MCR_END_CFD_LE (3 << QSPI_MCR_END_CFD_SHIFT)
#else
#define QSPI_MCR_END_CFD_LE (1 << QSPI_MCR_END_CFD_SHIFT)
#endif
#define QSPI_MCR_DDR_EN_SHIFT 7
#define QSPI_MCR_DDR_EN_MASK (1 << QSPI_MCR_DDR_EN_SHIFT)
#define QSPI_MCR_CLR_RXF_SHIFT 10
#define QSPI_MCR_CLR_RXF_MASK (1 << QSPI_MCR_CLR_RXF_SHIFT)
#define QSPI_MCR_CLR_TXF_SHIFT 11
#define QSPI_MCR_CLR_TXF_MASK (1 << QSPI_MCR_CLR_TXF_SHIFT)
#define QSPI_MCR_MDIS_SHIFT 14
#define QSPI_MCR_MDIS_MASK (1 << QSPI_MCR_MDIS_SHIFT)
#define QSPI_MCR_RESERVED_SHIFT 16
#define QSPI_MCR_RESERVED_MASK (0xf << QSPI_MCR_RESERVED_SHIFT)
#define QSPI_MCR_SWRSTHD_SHIFT 1
#define QSPI_MCR_SWRSTHD_MASK (1 << QSPI_MCR_SWRSTHD_SHIFT)
#define QSPI_MCR_SWRSTSD_SHIFT 0
#define QSPI_MCR_SWRSTSD_MASK (1 << QSPI_MCR_SWRSTSD_SHIFT)
#define QSPI_SMPR_HSENA_SHIFT 0
#define QSPI_SMPR_HSENA_MASK (1 << QSPI_SMPR_HSENA_SHIFT)
#define QSPI_SMPR_FSPHS_SHIFT 5
#define QSPI_SMPR_FSPHS_MASK (1 << QSPI_SMPR_FSPHS_SHIFT)
#define QSPI_SMPR_FSDLY_SHIFT 6
#define QSPI_SMPR_FSDLY_MASK (1 << QSPI_SMPR_FSDLY_SHIFT)
#define QSPI_SMPR_DDRSMP_SHIFT 16
#define QSPI_SMPR_DDRSMP_MASK (7 << QSPI_SMPR_DDRSMP_SHIFT)
#define QSPI_BUFXCR_INVALID_MSTRID 0xe
#define QSPI_BUF3CR_ALLMST_SHIFT 31
#define QSPI_BUF3CR_ALLMST_MASK (1 << QSPI_BUF3CR_ALLMST_SHIFT)
#define QSPI_BUF3CR_ADATSZ_SHIFT 8
#define QSPI_BUF3CR_ADATSZ_MASK (0xFF << QSPI_BUF3CR_ADATSZ_SHIFT)
#define QSPI_BFGENCR_SEQID_SHIFT 12
#define QSPI_BFGENCR_SEQID_MASK (0xf << QSPI_BFGENCR_SEQID_SHIFT)
#define QSPI_BFGENCR_PAR_EN_SHIFT 16
#define QSPI_BFGENCR_PAR_EN_MASK (1 << QSPI_BFGENCR_PAR_EN_SHIFT)
#define QSPI_RBSR_RDBFL_SHIFT 8
#define QSPI_RBSR_RDBFL_MASK (0x3f << QSPI_RBSR_RDBFL_SHIFT)
#define QSPI_RBCT_RXBRD_SHIFT 8
#define QSPI_RBCT_RXBRD_USEIPS (1 << QSPI_RBCT_RXBRD_SHIFT)
#define QSPI_SR_AHB_ACC_SHIFT 2
#define QSPI_SR_AHB_ACC_MASK (1 << QSPI_SR_AHB_ACC_SHIFT)
#define QSPI_SR_IP_ACC_SHIFT 1
#define QSPI_SR_IP_ACC_MASK (1 << QSPI_SR_IP_ACC_SHIFT)
#define QSPI_SR_BUSY_SHIFT 0
#define QSPI_SR_BUSY_MASK (1 << QSPI_SR_BUSY_SHIFT)
#define QSPI_LCKCR_LOCK 0x1
#define QSPI_LCKCR_UNLOCK 0x2
#define LUT_KEY_VALUE 0x5af05af0
#define OPRND0_SHIFT 0
#define OPRND0(x) ((x) << OPRND0_SHIFT)
#define PAD0_SHIFT 8
#define PAD0(x) ((x) << PAD0_SHIFT)
#define INSTR0_SHIFT 10
#define INSTR0(x) ((x) << INSTR0_SHIFT)
#define OPRND1_SHIFT 16
#define OPRND1(x) ((x) << OPRND1_SHIFT)
#define PAD1_SHIFT 24
#define PAD1(x) ((x) << PAD1_SHIFT)
#define INSTR1_SHIFT 26
#define INSTR1(x) ((x) << INSTR1_SHIFT)
#define LUT_CMD 1
#define LUT_ADDR 2
#define LUT_DUMMY 3
#define LUT_READ 7
#define LUT_WRITE 8
#define LUT_PAD1 0
#define LUT_PAD2 1
#define LUT_PAD4 2
#define ADDR24BIT 0x18
#define ADDR32BIT 0x20
#endif /* _FSL_QSPI_H_ */

View file

@ -153,7 +153,7 @@ bool spi_mem_default_supports_op(struct spi_slave *slave,
spi_check_buswidth_req(slave, op->dummy.buswidth, true))
return false;
if (op->data.nbytes &&
if (op->data.dir != SPI_MEM_NO_DATA &&
spi_check_buswidth_req(slave, op->data.buswidth,
op->data.dir == SPI_MEM_DATA_OUT))
return false;

View file

@ -8,8 +8,10 @@
#include <common.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <malloc.h>
#include <spi.h>
#include <spi-mem.h>
#include <wait_bit.h>
#include <asm/io.h>
#include <linux/log2.h>
#include <clk.h>
@ -85,6 +87,11 @@
#define SIFIVE_SPI_IP_TXWM BIT(0)
#define SIFIVE_SPI_IP_RXWM BIT(1)
/* format protocol */
#define SIFIVE_SPI_PROTO_QUAD 4 /* 4 lines I/O protocol transfer */
#define SIFIVE_SPI_PROTO_DUAL 2 /* 2 lines I/O protocol transfer */
#define SIFIVE_SPI_PROTO_SINGLE 1 /* 1 line I/O protocol transfer */
struct sifive_spi {
void *regs; /* base address of the registers */
u32 fifo_depth;
@ -92,28 +99,29 @@ struct sifive_spi {
u32 cs_inactive; /* Level of the CS pins when inactive*/
u32 freq;
u32 num_cs;
u8 fmt_proto;
};
static void sifive_spi_prep_device(struct sifive_spi *spi,
struct dm_spi_slave_platdata *slave)
struct dm_spi_slave_platdata *slave_plat)
{
/* Update the chip select polarity */
if (slave->mode & SPI_CS_HIGH)
spi->cs_inactive &= ~BIT(slave->cs);
if (slave_plat->mode & SPI_CS_HIGH)
spi->cs_inactive &= ~BIT(slave_plat->cs);
else
spi->cs_inactive |= BIT(slave->cs);
spi->cs_inactive |= BIT(slave_plat->cs);
writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
/* Select the correct device */
writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID);
writel(slave_plat->cs, spi->regs + SIFIVE_SPI_REG_CSID);
}
static int sifive_spi_set_cs(struct sifive_spi *spi,
struct dm_spi_slave_platdata *slave)
struct dm_spi_slave_platdata *slave_plat)
{
u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;
if (slave->mode & SPI_CS_HIGH)
if (slave_plat->mode & SPI_CS_HIGH)
cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);
@ -127,8 +135,8 @@ static void sifive_spi_clear_cs(struct sifive_spi *spi)
}
static void sifive_spi_prep_transfer(struct sifive_spi *spi,
bool is_rx_xfer,
struct dm_spi_slave_platdata *slave)
struct dm_spi_slave_platdata *slave_plat,
u8 *rx_ptr)
{
u32 cr;
@ -141,21 +149,26 @@ static void sifive_spi_prep_transfer(struct sifive_spi *spi,
/* LSB first? */
cr &= ~SIFIVE_SPI_FMT_ENDIAN;
if (slave->mode & SPI_LSB_FIRST)
if (slave_plat->mode & SPI_LSB_FIRST)
cr |= SIFIVE_SPI_FMT_ENDIAN;
/* Number of wires ? */
cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
if ((slave->mode & SPI_TX_QUAD) || (slave->mode & SPI_RX_QUAD))
switch (spi->fmt_proto) {
case SIFIVE_SPI_PROTO_QUAD:
cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
else if ((slave->mode & SPI_TX_DUAL) || (slave->mode & SPI_RX_DUAL))
break;
case SIFIVE_SPI_PROTO_DUAL:
cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
else
break;
default:
cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
break;
}
/* SPI direction in/out ? */
cr &= ~SIFIVE_SPI_FMT_DIR;
if (!is_rx_xfer)
if (!rx_ptr)
cr |= SIFIVE_SPI_FMT_DIR;
writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
@ -186,50 +199,62 @@ static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
}
static int sifive_spi_wait(struct sifive_spi *spi, u32 bit)
{
return wait_for_bit_le32(spi->regs + SIFIVE_SPI_REG_IP,
bit, true, 100, false);
}
static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct sifive_spi *spi = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
const unsigned char *tx_ptr = dout;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
const u8 *tx_ptr = dout;
u8 *rx_ptr = din;
u32 remaining_len;
int ret;
if (flags & SPI_XFER_BEGIN) {
sifive_spi_prep_device(spi, slave);
sifive_spi_prep_device(spi, slave_plat);
ret = sifive_spi_set_cs(spi, slave);
ret = sifive_spi_set_cs(spi, slave_plat);
if (ret)
return ret;
}
sifive_spi_prep_transfer(spi, true, slave);
sifive_spi_prep_transfer(spi, slave_plat, rx_ptr);
remaining_len = bitlen / 8;
while (remaining_len) {
int n_words, tx_words, rx_words;
n_words = min(remaining_len, spi->fifo_depth);
unsigned int n_words = min(remaining_len, spi->fifo_depth);
unsigned int tx_words, rx_words;
/* Enqueue n_words for transmission */
if (tx_ptr) {
for (tx_words = 0; tx_words < n_words; ++tx_words) {
sifive_spi_tx(spi, tx_ptr);
sifive_spi_rx(spi, NULL);
tx_ptr++;
}
for (tx_words = 0; tx_words < n_words; tx_words++) {
if (!tx_ptr)
sifive_spi_tx(spi, NULL);
else
sifive_spi_tx(spi, tx_ptr++);
}
/* Read out all the data from the RX FIFO */
if (rx_ptr) {
for (rx_words = 0; rx_words < n_words; ++rx_words) {
sifive_spi_tx(spi, NULL);
sifive_spi_rx(spi, rx_ptr);
rx_ptr++;
}
/* Wait for transmission + reception to complete */
writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
if (ret)
return ret;
/* Read out all the data from the RX FIFO */
for (rx_words = 0; rx_words < n_words; rx_words++)
sifive_spi_rx(spi, rx_ptr++);
} else {
/* Wait for transmission to complete */
ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
if (ret)
return ret;
}
remaining_len -= n_words;
@ -241,6 +266,80 @@ static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
return 0;
}
static int sifive_spi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
struct udevice *dev = slave->dev;
struct sifive_spi *spi = dev_get_priv(dev->parent);
unsigned long flags = SPI_XFER_BEGIN;
u8 opcode = op->cmd.opcode;
unsigned int pos = 0;
const void *tx_buf = NULL;
void *rx_buf = NULL;
int op_len, i;
int ret;
if (!op->addr.nbytes && !op->dummy.nbytes && !op->data.nbytes)
flags |= SPI_XFER_END;
spi->fmt_proto = op->cmd.buswidth;
/* send the opcode */
ret = sifive_spi_xfer(dev, 8, (void *)&opcode, NULL, flags);
if (ret < 0) {
dev_err(dev, "failed to xfer opcode\n");
return ret;
}
op_len = op->addr.nbytes + op->dummy.nbytes;
u8 op_buf[op_len];
/* send the addr + dummy */
if (op->addr.nbytes) {
/* fill address */
for (i = 0; i < op->addr.nbytes; i++)
op_buf[pos + i] = op->addr.val >>
(8 * (op->addr.nbytes - i - 1));
pos += op->addr.nbytes;
/* fill dummy */
if (op->dummy.nbytes)
memset(op_buf + pos, 0xff, op->dummy.nbytes);
/* make sure to set end flag, if no data bytes */
if (!op->data.nbytes)
flags |= SPI_XFER_END;
spi->fmt_proto = op->addr.buswidth;
ret = sifive_spi_xfer(dev, op_len * 8, op_buf, NULL, flags);
if (ret < 0) {
dev_err(dev, "failed to xfer addr + dummy\n");
return ret;
}
}
/* send/received the data */
if (op->data.nbytes) {
if (op->data.dir == SPI_MEM_DATA_IN)
rx_buf = op->data.buf.in;
else
tx_buf = op->data.buf.out;
spi->fmt_proto = op->data.buswidth;
ret = sifive_spi_xfer(dev, op->data.nbytes * 8,
tx_buf, rx_buf, SPI_XFER_END);
if (ret) {
dev_err(dev, "failed to xfer data\n");
return ret;
}
}
return 0;
}
static int sifive_spi_set_speed(struct udevice *bus, uint speed)
{
struct sifive_spi *spi = dev_get_priv(bus);
@ -309,6 +408,10 @@ static void sifive_spi_init_hw(struct sifive_spi *spi)
/* Watermark interrupts are disabled by default */
writel(0, spi->regs + SIFIVE_SPI_REG_IE);
/* Default watermark FIFO threshold values */
writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);
/* Set CS/SCK Delays and Inactive Time to defaults */
writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
spi->regs + SIFIVE_SPI_REG_DELAY0);
@ -348,11 +451,16 @@ static int sifive_spi_probe(struct udevice *bus)
return 0;
}
static const struct spi_controller_mem_ops sifive_spi_mem_ops = {
.exec_op = sifive_spi_exec_op,
};
static const struct dm_spi_ops sifive_spi_ops = {
.xfer = sifive_spi_xfer,
.set_speed = sifive_spi_set_speed,
.set_mode = sifive_spi_set_mode,
.cs_info = sifive_spi_cs_info,
.mem_ops = &sifive_spi_mem_ops,
};
static const struct udevice_id sifive_spi_ids[] = {

View file

@ -49,6 +49,7 @@ config ULP_WATCHDOG
config DESIGNWARE_WATCHDOG
bool "Designware watchdog timer support"
select HW_WATCHDOG if !WDT
default y if WDT && ROCKCHIP_RK3399
help
Enable this to support Designware Watchdog Timer IP, present e.g.
on Altera SoCFPGA SoCs.

View file

@ -48,6 +48,8 @@
#define ENV_MEM_LAYOUT_SETTINGS \
"scriptaddr=0x00500000\0" \
"script_offset_f=0xffe000\0" \
"script_size_f=0x2000\0" \
"pxefile_addr_r=0x00600000\0" \
"fdt_addr_r=0x01f00000\0" \
"kernel_addr_r=0x02080000\0" \
@ -58,6 +60,7 @@
#endif
#include <config_distro_bootcmd.h>
#include <environment/distro/sf.h>
#define CONFIG_EXTRA_ENV_SETTINGS \
ENV_MEM_LAYOUT_SETTINGS \
"fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \

View file

@ -41,11 +41,26 @@
#define BOOT_TARGET_DHCP(func)
#endif
#if CONFIG_IS_ENABLED(CMD_SF)
#define BOOT_TARGET_SF(func) func(SF, sf, 0)
#else
#define BOOT_TARGET_SF(func)
#endif
#ifdef CONFIG_ROCKCHIP_RK3399
#define BOOT_TARGET_DEVICES(func) \
BOOT_TARGET_MMC(func) \
BOOT_TARGET_USB(func) \
BOOT_TARGET_PXE(func) \
BOOT_TARGET_DHCP(func) \
BOOT_TARGET_SF(func)
#else
#define BOOT_TARGET_DEVICES(func) \
BOOT_TARGET_MMC(func) \
BOOT_TARGET_USB(func) \
BOOT_TARGET_PXE(func) \
BOOT_TARGET_DHCP(func)
#endif
#ifdef CONFIG_ARM64
#define ROOT_UUID "B921B045-1DF0-41C3-AF44-4C6F280D3FAE;\0"

View file

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2020 Amarula Solutions(India)
*
* SF distro configurations.
*/
#ifndef __DISTRO_SF_CONFIG_H
#define __DISTRO_SF_CONFIG_H
#if CONFIG_IS_ENABLED(CMD_SF)
#define BOOTENV_SHARED_SF(devtypel) \
#devtypel "_boot=" \
"if " #devtypel " probe ${busnum}; then " \
"devtype=" #devtypel "; " \
"run scan_sf_for_scripts; " \
"fi\0"
#define BOOTENV_DEV_SF(devtypeu, devtypel, instance) \
"bootcmd_" #devtypel #instance "=" \
"busnum=" #instance "; " \
"run " #devtypel "_boot\0"
#define BOOTENV_DEV_NAME_SF(devtypeu, devtypel, instance) \
#devtypel #instance " "
#else
#define BOOTENV_SHARED_SF(devtypel)
#define BOOTENV_DEV_SF \
BOOT_TARGET_DEVICES_references_SF_without_CONFIG_CMD_SF
#define BOOTENV_DEV_NAME_SF \
BOOT_TARGET_DEVICES_references_SF_without_CONFIG_CMD_SF
#endif /* CONFIG_CMD_SF */
#define BOOTENV_SF \
BOOTENV_SHARED_SF(sf) \
"scan_sf_for_scripts=" \
"${devtype} read ${scriptaddr} " \
"${script_offset_f} ${script_size_f}; " \
"source ${scriptaddr}; " \
"echo SCRIPT FAILED: continuing...\0"
#endif /* __DISTRO_SF_CONFIG_H */

View file

@ -22,6 +22,7 @@
#define SNOR_MFR_INTEL CFI_MFR_INTEL
#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro <--> Micron */
#define SNOR_MFR_MICRON CFI_MFR_MICRON /* ST Micro <--> Micron */
#define SNOR_MFR_ISSI CFI_MFR_PMC
#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX
#define SNOR_MFR_SPANSION CFI_MFR_AMD
#define SNOR_MFR_SST CFI_MFR_SST

View file

@ -204,6 +204,7 @@ struct spinand_manufacturer {
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
/**

View file

@ -60,10 +60,12 @@
/**
* enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective
* @SPI_MEM_NO_DATA: no data transferred
* @SPI_MEM_DATA_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory
*/
enum spi_mem_data_dir {
SPI_MEM_NO_DATA,
SPI_MEM_DATA_IN,
SPI_MEM_DATA_OUT,
};