- 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_FS_GENERIC
imply CMD_NET imply CMD_NET
imply CMD_PING imply CMD_PING
imply CMD_SF
imply CLK_SIFIVE imply CLK_SIFIVE
imply CLK_SIFIVE_FU540_PRCI imply CLK_SIFIVE_FU540_PRCI
imply DOS_PARTITION imply DOS_PARTITION
@ -40,6 +41,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
imply SIFIVE_SERIAL imply SIFIVE_SERIAL
imply SPI imply SPI
imply SPI_SIFIVE imply SPI_SIFIVE
imply SPI_FLASH
imply SPI_FLASH_ISSI
imply MMC imply MMC
imply MMC_SPI imply MMC_SPI
imply MMC_BROKEN_CD imply MMC_BROKEN_CD

View File

@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0 # 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 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View File

@ -835,6 +835,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&gigadevice_spinand_manufacturer, &gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer, &macronix_spinand_manufacturer,
&micron_spinand_manufacturer, &micron_spinand_manufacturer,
&toshiba_spinand_manufacturer,
&winbond_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: case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */ /* Some Micron need WREN command; all will accept it */
need_wren = true; need_wren = true;
case SNOR_MFR_ISSI:
case SNOR_MFR_MACRONIX: case SNOR_MFR_MACRONIX:
case SNOR_MFR_WINBOND: case SNOR_MFR_WINBOND:
if (need_wren) 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 * If page_size is a power of two, the offset can be quickly
* calculated with an AND operation. On the other cases we * calculated with an AND operation. On the other cases we
* need to do a modulus operation (more expensive). * 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); page_offset = addr & (nor->page_size - 1);
} else { } else {
u64 aux = addr; u64 aux = addr;

View File

@ -135,7 +135,8 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("is25wp128", 0x9d7018, 0, 64 * 1024, 256, { INFO("is25wp128", 0x9d7018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("is25wp256", 0x9d7019, 0, 64 * 1024, 512, { 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 #endif
#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ #ifdef CONFIG_SPI_FLASH_MACRONIX /* 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("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("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("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("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_4B_OPCODES) }, { INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
#endif #endif
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
/* Spansion/Cypress -- single (large) sector size only, at least /* 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("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("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) }, { 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_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_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) }, { 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_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus); struct cadence_spi_priv *priv = dev_get_priv(bus);
struct clk clk;
int ret; int ret;
priv->regbase = plat->regbase; priv->regbase = plat->regbase;
priv->ahbbase = plat->ahbbase; 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); ret = reset_get_bulk(bus, &priv->resets);
if (ret) if (ret)
dev_warn(bus, "Can't get reset: %d\n", 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; struct cadence_spi_platdata *plat = bus->platdata;
ofnode subnode; ofnode subnode;
struct clk clk;
int ret;
plat->regbase = (void *)devfdt_get_addr_index(bus, 0); plat->regbase = (void *)devfdt_get_addr_index(bus, 0);
plat->ahbbase = (void *)devfdt_get_addr_size_index(bus, 1, 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->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-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", debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
__func__, plat->regbase, plat->ahbbase, plat->max_hz, __func__, plat->regbase, plat->ahbbase, plat->max_hz,
plat->page_size); 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)) spi_check_buswidth_req(slave, op->dummy.buswidth, true))
return false; return false;
if (op->data.nbytes && if (op->data.dir != SPI_MEM_NO_DATA &&
spi_check_buswidth_req(slave, op->data.buswidth, spi_check_buswidth_req(slave, op->data.buswidth,
op->data.dir == SPI_MEM_DATA_OUT)) op->data.dir == SPI_MEM_DATA_OUT))
return false; return false;

View File

@ -8,8 +8,10 @@
#include <common.h> #include <common.h>
#include <dm.h> #include <dm.h>
#include <dm/device_compat.h>
#include <malloc.h> #include <malloc.h>
#include <spi.h> #include <spi-mem.h>
#include <wait_bit.h>
#include <asm/io.h> #include <asm/io.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <clk.h> #include <clk.h>
@ -85,6 +87,11 @@
#define SIFIVE_SPI_IP_TXWM BIT(0) #define SIFIVE_SPI_IP_TXWM BIT(0)
#define SIFIVE_SPI_IP_RXWM BIT(1) #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 { struct sifive_spi {
void *regs; /* base address of the registers */ void *regs; /* base address of the registers */
u32 fifo_depth; u32 fifo_depth;
@ -92,28 +99,29 @@ struct sifive_spi {
u32 cs_inactive; /* Level of the CS pins when inactive*/ u32 cs_inactive; /* Level of the CS pins when inactive*/
u32 freq; u32 freq;
u32 num_cs; u32 num_cs;
u8 fmt_proto;
}; };
static void sifive_spi_prep_device(struct sifive_spi *spi, 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 */ /* Update the chip select polarity */
if (slave->mode & SPI_CS_HIGH) if (slave_plat->mode & SPI_CS_HIGH)
spi->cs_inactive &= ~BIT(slave->cs); spi->cs_inactive &= ~BIT(slave_plat->cs);
else else
spi->cs_inactive |= BIT(slave->cs); spi->cs_inactive |= BIT(slave_plat->cs);
writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF); writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
/* Select the correct device */ /* 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, 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; 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; cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE); 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, static void sifive_spi_prep_transfer(struct sifive_spi *spi,
bool is_rx_xfer, struct dm_spi_slave_platdata *slave_plat,
struct dm_spi_slave_platdata *slave) u8 *rx_ptr)
{ {
u32 cr; u32 cr;
@ -141,21 +149,26 @@ static void sifive_spi_prep_transfer(struct sifive_spi *spi,
/* LSB first? */ /* LSB first? */
cr &= ~SIFIVE_SPI_FMT_ENDIAN; cr &= ~SIFIVE_SPI_FMT_ENDIAN;
if (slave->mode & SPI_LSB_FIRST) if (slave_plat->mode & SPI_LSB_FIRST)
cr |= SIFIVE_SPI_FMT_ENDIAN; cr |= SIFIVE_SPI_FMT_ENDIAN;
/* Number of wires ? */ /* Number of wires ? */
cr &= ~SIFIVE_SPI_FMT_PROTO_MASK; 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; 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; cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
else break;
default:
cr |= SIFIVE_SPI_FMT_PROTO_SINGLE; cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
break;
}
/* SPI direction in/out ? */ /* SPI direction in/out ? */
cr &= ~SIFIVE_SPI_FMT_DIR; cr &= ~SIFIVE_SPI_FMT_DIR;
if (!is_rx_xfer) if (!rx_ptr)
cr |= SIFIVE_SPI_FMT_DIR; cr |= SIFIVE_SPI_FMT_DIR;
writel(cr, spi->regs + SIFIVE_SPI_REG_FMT); 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); 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, static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags) const void *dout, void *din, unsigned long flags)
{ {
struct udevice *bus = dev->parent; struct udevice *bus = dev->parent;
struct sifive_spi *spi = dev_get_priv(bus); struct sifive_spi *spi = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev); struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
const unsigned char *tx_ptr = dout; const u8 *tx_ptr = dout;
u8 *rx_ptr = din; u8 *rx_ptr = din;
u32 remaining_len; u32 remaining_len;
int ret; int ret;
if (flags & SPI_XFER_BEGIN) { 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) if (ret)
return ret; return ret;
} }
sifive_spi_prep_transfer(spi, true, slave); sifive_spi_prep_transfer(spi, slave_plat, rx_ptr);
remaining_len = bitlen / 8; remaining_len = bitlen / 8;
while (remaining_len) { while (remaining_len) {
int n_words, tx_words, rx_words; unsigned int n_words = min(remaining_len, spi->fifo_depth);
unsigned int tx_words, rx_words;
n_words = min(remaining_len, spi->fifo_depth);
/* Enqueue n_words for transmission */ /* Enqueue n_words for transmission */
if (tx_ptr) { for (tx_words = 0; tx_words < n_words; tx_words++) {
for (tx_words = 0; tx_words < n_words; ++tx_words) { if (!tx_ptr)
sifive_spi_tx(spi, tx_ptr); sifive_spi_tx(spi, NULL);
sifive_spi_rx(spi, NULL); else
tx_ptr++; sifive_spi_tx(spi, tx_ptr++);
}
} }
/* Read out all the data from the RX FIFO */
if (rx_ptr) { if (rx_ptr) {
for (rx_words = 0; rx_words < n_words; ++rx_words) { /* Wait for transmission + reception to complete */
sifive_spi_tx(spi, NULL); writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
sifive_spi_rx(spi, rx_ptr); ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
rx_ptr++; 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; remaining_len -= n_words;
@ -241,6 +266,80 @@ static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
return 0; 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) static int sifive_spi_set_speed(struct udevice *bus, uint speed)
{ {
struct sifive_spi *spi = dev_get_priv(bus); 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 */ /* Watermark interrupts are disabled by default */
writel(0, spi->regs + SIFIVE_SPI_REG_IE); 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 */ /* Set CS/SCK Delays and Inactive Time to defaults */
writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1), writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
spi->regs + SIFIVE_SPI_REG_DELAY0); spi->regs + SIFIVE_SPI_REG_DELAY0);
@ -348,11 +451,16 @@ static int sifive_spi_probe(struct udevice *bus)
return 0; 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 = { static const struct dm_spi_ops sifive_spi_ops = {
.xfer = sifive_spi_xfer, .xfer = sifive_spi_xfer,
.set_speed = sifive_spi_set_speed, .set_speed = sifive_spi_set_speed,
.set_mode = sifive_spi_set_mode, .set_mode = sifive_spi_set_mode,
.cs_info = sifive_spi_cs_info, .cs_info = sifive_spi_cs_info,
.mem_ops = &sifive_spi_mem_ops,
}; };
static const struct udevice_id sifive_spi_ids[] = { static const struct udevice_id sifive_spi_ids[] = {

View File

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

View File

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

View File

@ -41,11 +41,26 @@
#define BOOT_TARGET_DHCP(func) #define BOOT_TARGET_DHCP(func)
#endif #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) \ #define BOOT_TARGET_DEVICES(func) \
BOOT_TARGET_MMC(func) \ BOOT_TARGET_MMC(func) \
BOOT_TARGET_USB(func) \ BOOT_TARGET_USB(func) \
BOOT_TARGET_PXE(func) \ BOOT_TARGET_PXE(func) \
BOOT_TARGET_DHCP(func) BOOT_TARGET_DHCP(func)
#endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
#define ROOT_UUID "B921B045-1DF0-41C3-AF44-4C6F280D3FAE;\0" #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_INTEL CFI_MFR_INTEL
#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro <--> Micron */ #define SNOR_MFR_ST CFI_MFR_ST /* ST Micro <--> Micron */
#define SNOR_MFR_MICRON CFI_MFR_MICRON /* 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_MACRONIX CFI_MFR_MACRONIX
#define SNOR_MFR_SPANSION CFI_MFR_AMD #define SNOR_MFR_SPANSION CFI_MFR_AMD
#define SNOR_MFR_SST CFI_MFR_SST #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 gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_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; 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 * enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective * 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_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory * @SPI_MEM_DATA_OUT: data sent the SPI memory
*/ */
enum spi_mem_data_dir { enum spi_mem_data_dir {
SPI_MEM_NO_DATA,
SPI_MEM_DATA_IN, SPI_MEM_DATA_IN,
SPI_MEM_DATA_OUT, SPI_MEM_DATA_OUT,
}; };