Merge branch 'master' of git://git.denx.de/u-boot-spi

This is the PR for SPI-NAND changes along with few spi changes.

[trini: Re-sync changes for ls1012afrwy_qspi*_defconfig]
Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
Tom Rini
2018-10-02 13:02:22 -04:00
104 changed files with 6002 additions and 1044 deletions

View File

@@ -240,7 +240,7 @@
/* LB refresh timer prescal, 266MHz/32 */
#define CONFIG_SYS_LBC_MRTPR 0x20000000 /*TODO */
/* drivers/mtd/nand/nand.c */
/* drivers/mtd/nand/raw/nand.c */
#if defined(CONFIG_NAND) && defined(CONFIG_SPL_BUILD)
#define CONFIG_SYS_NAND_BASE 0xFFF00000
#else

View File

@@ -15,9 +15,12 @@
#define MTD_DEV_TYPE_NOR 0x0001
#define MTD_DEV_TYPE_NAND 0x0002
#define MTD_DEV_TYPE_ONENAND 0x0004
#define MTD_DEV_TYPE_SPINAND 0x0008
#define MTD_DEV_TYPE(type) ((type == MTD_DEV_TYPE_NAND) ? "nand" : \
(type == MTD_DEV_TYPE_ONENAND) ? "onenand" : "nor")
#define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" : \
(type == MTD_DEV_TYPE_NOR ? "nor" : \
(type == MTD_DEV_TYPE_ONENAND ? "onenand" : \
"spi-nand"))) \
struct mtd_device {
struct list_head link;

View File

@@ -20,7 +20,11 @@
#include <linux/compat.h>
#include <mtd/mtd-abi.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <div64.h>
#if IS_ENABLED(CONFIG_DM)
#include <dm/device.h>
#endif
#define MAX_MTD_DEVICES 32
#endif
@@ -304,8 +308,64 @@ struct mtd_info {
struct udevice *dev;
#endif
int usecount;
/* MTD devices do not have any parent. MTD partitions do. */
struct mtd_info *parent;
/*
* Offset of the partition relatively to the parent offset.
* Is 0 for real MTD devices (ie. not partitions).
*/
u64 offset;
/*
* List node used to add an MTD partition to the parent
* partition list.
*/
struct list_head node;
/*
* List of partitions attached to this MTD device (the parent
* MTD device can itself be a partition).
*/
struct list_head partitions;
};
#if IS_ENABLED(CONFIG_DM)
static inline void mtd_set_of_node(struct mtd_info *mtd,
const struct device_node *np)
{
mtd->dev->node.np = np;
}
static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd)
{
return mtd->dev->node.np;
}
#else
struct device_node;
static inline void mtd_set_of_node(struct mtd_info *mtd,
const struct device_node *np)
{
}
static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd)
{
return NULL;
}
#endif
static inline bool mtd_is_partition(const struct mtd_info *mtd)
{
return mtd->parent;
}
static inline bool mtd_has_partitions(const struct mtd_info *mtd)
{
return !list_empty(&mtd->partitions);
}
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc);
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
@@ -351,17 +411,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf);
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
ops->retlen = ops->oobretlen = 0;
if (!mtd->_write_oob)
return -EOPNOTSUPP;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
return mtd->_write_oob(mtd, to, ops);
}
int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);
int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
struct otp_info *buf);
@@ -515,6 +565,12 @@ int del_mtd_device(struct mtd_info *mtd);
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *);
struct mtd_info *__mtd_next_device(int i);
#define mtd_for_each_device(mtd) \
for ((mtd) = __mtd_next_device(0); \
(mtd) != NULL; \
(mtd) = __mtd_next_device(mtd->index + 1))
int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
loff_t *maxsize, int devtype, uint64_t chipsize);
int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
@@ -525,5 +581,10 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off,
void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
const uint64_t length, uint64_t *len_incl_bad,
int *truncated);
/* drivers/mtd/mtd_uboot.c */
int mtd_search_alternate_name(const char *mtdname, char *altname,
unsigned int max_len);
#endif
#endif /* __MTD_MTD_H__ */

734
include/linux/mtd/nand.h Normal file
View File

@@ -0,0 +1,734 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2017 - Free Electrons
*
* Authors:
* Boris Brezillon <boris.brezillon@free-electrons.com>
* Peter Pan <peterpandong@micron.com>
*/
#ifndef __LINUX_MTD_NAND_H
#define __LINUX_MTD_NAND_H
#include <linux/mtd/mtd.h>
/**
* struct nand_memory_organization - Memory organization structure
* @bits_per_cell: number of bits per NAND cell
* @pagesize: page size
* @oobsize: OOB area size
* @pages_per_eraseblock: number of pages per eraseblock
* @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number)
* @planes_per_lun: number of planes per LUN
* @luns_per_target: number of LUN per target (target is a synonym for die)
* @ntargets: total number of targets exposed by the NAND device
*/
struct nand_memory_organization {
unsigned int bits_per_cell;
unsigned int pagesize;
unsigned int oobsize;
unsigned int pages_per_eraseblock;
unsigned int eraseblocks_per_lun;
unsigned int planes_per_lun;
unsigned int luns_per_target;
unsigned int ntargets;
};
#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \
{ \
.bits_per_cell = (bpc), \
.pagesize = (ps), \
.oobsize = (os), \
.pages_per_eraseblock = (ppe), \
.eraseblocks_per_lun = (epl), \
.planes_per_lun = (ppl), \
.luns_per_target = (lpt), \
.ntargets = (nt), \
}
/**
* struct nand_row_converter - Information needed to convert an absolute offset
* into a row address
* @lun_addr_shift: position of the LUN identifier in the row address
* @eraseblock_addr_shift: position of the eraseblock identifier in the row
* address
*/
struct nand_row_converter {
unsigned int lun_addr_shift;
unsigned int eraseblock_addr_shift;
};
/**
* struct nand_pos - NAND position object
* @target: the NAND target/die
* @lun: the LUN identifier
* @plane: the plane within the LUN
* @eraseblock: the eraseblock within the LUN
* @page: the page within the LUN
*
* These information are usually used by specific sub-layers to select the
* appropriate target/die and generate a row address to pass to the device.
*/
struct nand_pos {
unsigned int target;
unsigned int lun;
unsigned int plane;
unsigned int eraseblock;
unsigned int page;
};
/**
* struct nand_page_io_req - NAND I/O request object
* @pos: the position this I/O request is targeting
* @dataoffs: the offset within the page
* @datalen: number of data bytes to read from/write to this page
* @databuf: buffer to store data in or get data from
* @ooboffs: the OOB offset within the page
* @ooblen: the number of OOB bytes to read from/write to this page
* @oobbuf: buffer to store OOB data in or get OOB data from
* @mode: one of the %MTD_OPS_XXX mode
*
* This object is used to pass per-page I/O requests to NAND sub-layers. This
* way all useful information are already formatted in a useful way and
* specific NAND layers can focus on translating these information into
* specific commands/operations.
*/
struct nand_page_io_req {
struct nand_pos pos;
unsigned int dataoffs;
unsigned int datalen;
union {
const void *out;
void *in;
} databuf;
unsigned int ooboffs;
unsigned int ooblen;
union {
const void *out;
void *in;
} oobbuf;
int mode;
};
/**
* struct nand_ecc_req - NAND ECC requirements
* @strength: ECC strength
* @step_size: ECC step/block size
*/
struct nand_ecc_req {
unsigned int strength;
unsigned int step_size;
};
#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) }
/**
* struct nand_bbt - bad block table object
* @cache: in memory BBT cache
*/
struct nand_bbt {
unsigned long *cache;
};
struct nand_device;
/**
* struct nand_ops - NAND operations
* @erase: erase a specific block. No need to check if the block is bad before
* erasing, this has been taken care of by the generic NAND layer
* @markbad: mark a specific block bad. No need to check if the block is
* already marked bad, this has been taken care of by the generic
* NAND layer. This method should just write the BBM (Bad Block
* Marker) so that future call to struct_nand_ops->isbad() return
* true
* @isbad: check whether a block is bad or not. This method should just read
* the BBM and return whether the block is bad or not based on what it
* reads
*
* These are all low level operations that should be implemented by specialized
* NAND layers (SPI NAND, raw NAND, ...).
*/
struct nand_ops {
int (*erase)(struct nand_device *nand, const struct nand_pos *pos);
int (*markbad)(struct nand_device *nand, const struct nand_pos *pos);
bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos);
};
/**
* struct nand_device - NAND device
* @mtd: MTD instance attached to the NAND device
* @memorg: memory layout
* @eccreq: ECC requirements
* @rowconv: position to row address converter
* @bbt: bad block table info
* @ops: NAND operations attached to the NAND device
*
* Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND)
* should declare their own NAND object embedding a nand_device struct (that's
* how inheritance is done).
* struct_nand_device->memorg and struct_nand_device->eccreq should be filled
* at device detection time to reflect the NAND device
* capabilities/requirements. Once this is done nanddev_init() can be called.
* It will take care of converting NAND information into MTD ones, which means
* the specialized NAND layers should never manually tweak
* struct_nand_device->mtd except for the ->_read/write() hooks.
*/
struct nand_device {
struct mtd_info *mtd;
struct nand_memory_organization memorg;
struct nand_ecc_req eccreq;
struct nand_row_converter rowconv;
struct nand_bbt bbt;
const struct nand_ops *ops;
};
/**
* struct nand_io_iter - NAND I/O iterator
* @req: current I/O request
* @oobbytes_per_page: maximum number of OOB bytes per page
* @dataleft: remaining number of data bytes to read/write
* @oobleft: remaining number of OOB bytes to read/write
*
* Can be used by specialized NAND layers to iterate over all pages covered
* by an MTD I/O request, which should greatly simplifies the boiler-plate
* code needed to read/write data from/to a NAND device.
*/
struct nand_io_iter {
struct nand_page_io_req req;
unsigned int oobbytes_per_page;
unsigned int dataleft;
unsigned int oobleft;
};
/**
* mtd_to_nanddev() - Get the NAND device attached to the MTD instance
* @mtd: MTD instance
*
* Return: the NAND device embedding @mtd.
*/
static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd)
{
return mtd->priv;
}
/**
* nanddev_to_mtd() - Get the MTD device attached to a NAND device
* @nand: NAND device
*
* Return: the MTD device embedded in @nand.
*/
static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand)
{
return nand->mtd;
}
/*
* nanddev_bits_per_cell() - Get the number of bits per cell
* @nand: NAND device
*
* Return: the number of bits per cell.
*/
static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand)
{
return nand->memorg.bits_per_cell;
}
/**
* nanddev_page_size() - Get NAND page size
* @nand: NAND device
*
* Return: the page size.
*/
static inline size_t nanddev_page_size(const struct nand_device *nand)
{
return nand->memorg.pagesize;
}
/**
* nanddev_per_page_oobsize() - Get NAND OOB size
* @nand: NAND device
*
* Return: the OOB size.
*/
static inline unsigned int
nanddev_per_page_oobsize(const struct nand_device *nand)
{
return nand->memorg.oobsize;
}
/**
* nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock
* @nand: NAND device
*
* Return: the number of pages per eraseblock.
*/
static inline unsigned int
nanddev_pages_per_eraseblock(const struct nand_device *nand)
{
return nand->memorg.pages_per_eraseblock;
}
/**
* nanddev_per_page_oobsize() - Get NAND erase block size
* @nand: NAND device
*
* Return: the eraseblock size.
*/
static inline size_t nanddev_eraseblock_size(const struct nand_device *nand)
{
return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock;
}
/**
* nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN
* @nand: NAND device
*
* Return: the number of eraseblocks per LUN.
*/
static inline unsigned int
nanddev_eraseblocks_per_lun(const struct nand_device *nand)
{
return nand->memorg.eraseblocks_per_lun;
}
/**
* nanddev_target_size() - Get the total size provided by a single target/die
* @nand: NAND device
*
* Return: the total size exposed by a single target/die in bytes.
*/
static inline u64 nanddev_target_size(const struct nand_device *nand)
{
return (u64)nand->memorg.luns_per_target *
nand->memorg.eraseblocks_per_lun *
nand->memorg.pages_per_eraseblock *
nand->memorg.pagesize;
}
/**
* nanddev_ntarget() - Get the total of targets
* @nand: NAND device
*
* Return: the number of targets/dies exposed by @nand.
*/
static inline unsigned int nanddev_ntargets(const struct nand_device *nand)
{
return nand->memorg.ntargets;
}
/**
* nanddev_neraseblocks() - Get the total number of erasablocks
* @nand: NAND device
*
* Return: the total number of eraseblocks exposed by @nand.
*/
static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand)
{
return (u64)nand->memorg.luns_per_target *
nand->memorg.eraseblocks_per_lun *
nand->memorg.pages_per_eraseblock;
}
/**
* nanddev_size() - Get NAND size
* @nand: NAND device
*
* Return: the total size (in bytes) exposed by @nand.
*/
static inline u64 nanddev_size(const struct nand_device *nand)
{
return nanddev_target_size(nand) * nanddev_ntargets(nand);
}
/**
* nanddev_get_memorg() - Extract memory organization info from a NAND device
* @nand: NAND device
*
* This can be used by the upper layer to fill the memorg info before calling
* nanddev_init().
*
* Return: the memorg object embedded in the NAND device.
*/
static inline struct nand_memory_organization *
nanddev_get_memorg(struct nand_device *nand)
{
return &nand->memorg;
}
int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
struct module *owner);
void nanddev_cleanup(struct nand_device *nand);
/**
* nanddev_register() - Register a NAND device
* @nand: NAND device
*
* Register a NAND device.
* This function is just a wrapper around mtd_device_register()
* registering the MTD device embedded in @nand.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
static inline int nanddev_register(struct nand_device *nand)
{
return mtd_device_register(nand->mtd, NULL, 0);
}
/**
* nanddev_unregister() - Unregister a NAND device
* @nand: NAND device
*
* Unregister a NAND device.
* This function is just a wrapper around mtd_device_unregister()
* unregistering the MTD device embedded in @nand.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
static inline int nanddev_unregister(struct nand_device *nand)
{
return mtd_device_unregister(nand->mtd);
}
/**
* nanddev_set_of_node() - Attach a DT node to a NAND device
* @nand: NAND device
* @np: DT node
*
* Attach a DT node to a NAND device.
*/
static inline void nanddev_set_of_node(struct nand_device *nand,
const struct device_node *np)
{
mtd_set_of_node(nand->mtd, np);
}
/**
* nanddev_get_of_node() - Retrieve the DT node attached to a NAND device
* @nand: NAND device
*
* Return: the DT node attached to @nand.
*/
static inline const struct device_node *nanddev_get_of_node(struct nand_device *nand)
{
return mtd_get_of_node(nand->mtd);
}
/**
* nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position
* @nand: NAND device
* @offs: absolute NAND offset (usually passed by the MTD layer)
* @pos: a NAND position object to fill in
*
* Converts @offs into a nand_pos representation.
*
* Return: the offset within the NAND page pointed by @pos.
*/
static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand,
loff_t offs,
struct nand_pos *pos)
{
unsigned int pageoffs;
u64 tmp = offs;
pageoffs = do_div(tmp, nand->memorg.pagesize);
pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock);
pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun);
pos->plane = pos->eraseblock % nand->memorg.planes_per_lun;
pos->lun = do_div(tmp, nand->memorg.luns_per_target);
pos->target = tmp;
return pageoffs;
}
/**
* nanddev_pos_cmp() - Compare two NAND positions
* @a: First NAND position
* @b: Second NAND position
*
* Compares two NAND positions.
*
* Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b.
*/
static inline int nanddev_pos_cmp(const struct nand_pos *a,
const struct nand_pos *b)
{
if (a->target != b->target)
return a->target < b->target ? -1 : 1;
if (a->lun != b->lun)
return a->lun < b->lun ? -1 : 1;
if (a->eraseblock != b->eraseblock)
return a->eraseblock < b->eraseblock ? -1 : 1;
if (a->page != b->page)
return a->page < b->page ? -1 : 1;
return 0;
}
/**
* nanddev_pos_to_offs() - Convert a NAND position into an absolute offset
* @nand: NAND device
* @pos: the NAND position to convert
*
* Converts @pos NAND position into an absolute offset.
*
* Return: the absolute offset. Note that @pos points to the beginning of a
* page, if one wants to point to a specific offset within this page
* the returned offset has to be adjusted manually.
*/
static inline loff_t nanddev_pos_to_offs(struct nand_device *nand,
const struct nand_pos *pos)
{
unsigned int npages;
npages = pos->page +
((pos->eraseblock +
(pos->lun +
(pos->target * nand->memorg.luns_per_target)) *
nand->memorg.eraseblocks_per_lun) *
nand->memorg.pages_per_eraseblock);
return (loff_t)npages * nand->memorg.pagesize;
}
/**
* nanddev_pos_to_row() - Extract a row address from a NAND position
* @nand: NAND device
* @pos: the position to convert
*
* Converts a NAND position into a row address that can then be passed to the
* device.
*
* Return: the row address extracted from @pos.
*/
static inline unsigned int nanddev_pos_to_row(struct nand_device *nand,
const struct nand_pos *pos)
{
return (pos->lun << nand->rowconv.lun_addr_shift) |
(pos->eraseblock << nand->rowconv.eraseblock_addr_shift) |
pos->page;
}
/**
* nanddev_pos_next_target() - Move a position to the next target/die
* @nand: NAND device
* @pos: the position to update
*
* Updates @pos to point to the start of the next target/die. Useful when you
* want to iterate over all targets/dies of a NAND device.
*/
static inline void nanddev_pos_next_target(struct nand_device *nand,
struct nand_pos *pos)
{
pos->page = 0;
pos->plane = 0;
pos->eraseblock = 0;
pos->lun = 0;
pos->target++;
}
/**
* nanddev_pos_next_lun() - Move a position to the next LUN
* @nand: NAND device
* @pos: the position to update
*
* Updates @pos to point to the start of the next LUN. Useful when you want to
* iterate over all LUNs of a NAND device.
*/
static inline void nanddev_pos_next_lun(struct nand_device *nand,
struct nand_pos *pos)
{
if (pos->lun >= nand->memorg.luns_per_target - 1)
return nanddev_pos_next_target(nand, pos);
pos->lun++;
pos->page = 0;
pos->plane = 0;
pos->eraseblock = 0;
}
/**
* nanddev_pos_next_eraseblock() - Move a position to the next eraseblock
* @nand: NAND device
* @pos: the position to update
*
* Updates @pos to point to the start of the next eraseblock. Useful when you
* want to iterate over all eraseblocks of a NAND device.
*/
static inline void nanddev_pos_next_eraseblock(struct nand_device *nand,
struct nand_pos *pos)
{
if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1)
return nanddev_pos_next_lun(nand, pos);
pos->eraseblock++;
pos->page = 0;
pos->plane = pos->eraseblock % nand->memorg.planes_per_lun;
}
/**
* nanddev_pos_next_eraseblock() - Move a position to the next page
* @nand: NAND device
* @pos: the position to update
*
* Updates @pos to point to the start of the next page. Useful when you want to
* iterate over all pages of a NAND device.
*/
static inline void nanddev_pos_next_page(struct nand_device *nand,
struct nand_pos *pos)
{
if (pos->page >= nand->memorg.pages_per_eraseblock - 1)
return nanddev_pos_next_eraseblock(nand, pos);
pos->page++;
}
/**
* nand_io_iter_init - Initialize a NAND I/O iterator
* @nand: NAND device
* @offs: absolute offset
* @req: MTD request
* @iter: NAND I/O iterator
*
* Initializes a NAND iterator based on the information passed by the MTD
* layer.
*/
static inline void nanddev_io_iter_init(struct nand_device *nand,
loff_t offs, struct mtd_oob_ops *req,
struct nand_io_iter *iter)
{
struct mtd_info *mtd = nanddev_to_mtd(nand);
iter->req.mode = req->mode;
iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos);
iter->req.ooboffs = req->ooboffs;
iter->oobbytes_per_page = mtd_oobavail(mtd, req);
iter->dataleft = req->len;
iter->oobleft = req->ooblen;
iter->req.databuf.in = req->datbuf;
iter->req.datalen = min_t(unsigned int,
nand->memorg.pagesize - iter->req.dataoffs,
iter->dataleft);
iter->req.oobbuf.in = req->oobbuf;
iter->req.ooblen = min_t(unsigned int,
iter->oobbytes_per_page - iter->req.ooboffs,
iter->oobleft);
}
/**
* nand_io_iter_next_page - Move to the next page
* @nand: NAND device
* @iter: NAND I/O iterator
*
* Updates the @iter to point to the next page.
*/
static inline void nanddev_io_iter_next_page(struct nand_device *nand,
struct nand_io_iter *iter)
{
nanddev_pos_next_page(nand, &iter->req.pos);
iter->dataleft -= iter->req.datalen;
iter->req.databuf.in += iter->req.datalen;
iter->oobleft -= iter->req.ooblen;
iter->req.oobbuf.in += iter->req.ooblen;
iter->req.dataoffs = 0;
iter->req.ooboffs = 0;
iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize,
iter->dataleft);
iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page,
iter->oobleft);
}
/**
* nand_io_iter_end - Should end iteration or not
* @nand: NAND device
* @iter: NAND I/O iterator
*
* Check whether @iter has reached the end of the NAND portion it was asked to
* iterate on or not.
*
* Return: true if @iter has reached the end of the iteration request, false
* otherwise.
*/
static inline bool nanddev_io_iter_end(struct nand_device *nand,
const struct nand_io_iter *iter)
{
if (iter->dataleft || iter->oobleft)
return false;
return true;
}
/**
* nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O
* request
* @nand: NAND device
* @start: start address to read/write from
* @req: MTD I/O request
* @iter: NAND I/O iterator
*
* Should be used for iterate over pages that are contained in an MTD request.
*/
#define nanddev_io_for_each_page(nand, start, req, iter) \
for (nanddev_io_iter_init(nand, start, req, iter); \
!nanddev_io_iter_end(nand, iter); \
nanddev_io_iter_next_page(nand, iter))
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos);
bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
/* BBT related functions */
enum nand_bbt_block_status {
NAND_BBT_BLOCK_STATUS_UNKNOWN,
NAND_BBT_BLOCK_GOOD,
NAND_BBT_BLOCK_WORN,
NAND_BBT_BLOCK_RESERVED,
NAND_BBT_BLOCK_FACTORY_BAD,
NAND_BBT_BLOCK_NUM_STATUS,
};
int nanddev_bbt_init(struct nand_device *nand);
void nanddev_bbt_cleanup(struct nand_device *nand);
int nanddev_bbt_update(struct nand_device *nand);
int nanddev_bbt_get_block_status(const struct nand_device *nand,
unsigned int entry);
int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
enum nand_bbt_block_status status);
int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block);
/**
* nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry
* @nand: NAND device
* @pos: the NAND position we want to get BBT entry for
*
* Return the BBT entry used to store information about the eraseblock pointed
* by @pos.
*
* Return: the BBT entry storing information about eraseblock pointed by @pos.
*/
static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand,
const struct nand_pos *pos)
{
return pos->eraseblock +
((pos->lun + (pos->target * nand->memorg.luns_per_target)) *
nand->memorg.eraseblocks_per_lun);
}
/**
* nanddev_bbt_is_initialized() - Check if the BBT has been initialized
* @nand: NAND device
*
* Return: true if the BBT has been initialized, false otherwise.
*/
static inline bool nanddev_bbt_is_initialized(struct nand_device *nand)
{
return !!nand->bbt.cache;
}
/* MTD -> NAND helper functions. */
int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo);
#endif /* __LINUX_MTD_NAND_H */

View File

@@ -81,10 +81,30 @@ extern void register_mtd_parser(struct mtd_part_parser *parser);
extern void deregister_mtd_parser(struct mtd_part_parser *parser);
#endif
int mtd_is_partition(const struct mtd_info *mtd);
int mtd_add_partition(struct mtd_info *master, const char *name,
long long offset, long long length);
int mtd_del_partition(struct mtd_info *master, int partno);
uint64_t mtd_get_device_size(const struct mtd_info *mtd);
#if defined(CONFIG_MTD_PARTITIONS)
int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
struct mtd_partition **_parts, int *_nparts);
void mtd_free_parsed_partitions(struct mtd_partition *parts,
unsigned int nparts);
#else
static inline int
mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
struct mtd_partition **_parts, int *_nparts)
{
*_nparts = 0;
return 0;
}
static inline void
mtd_free_parsed_partitions(struct mtd_partition *parts, unsigned int nparts)
{
return;
}
#endif /* defined(MTD_PARTITIONS) */
#endif

432
include/linux/mtd/spinand.h Normal file
View File

@@ -0,0 +1,432 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2016-2017 Micron Technology, Inc.
*
* Authors:
* Peter Pan <peterpandong@micron.com>
*/
#ifndef __LINUX_MTD_SPINAND_H
#define __LINUX_MTD_SPINAND_H
#ifndef __UBOOT__
#include <linux/mutex.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#else
#include <common.h>
#include <spi.h>
#include <spi-mem.h>
#include <linux/mtd/nand.h>
#endif
/**
* Standard SPI NAND flash operations
*/
#define SPINAND_RESET_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_WR_EN_DIS_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_READID_OP(ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1))
#define SPINAND_SET_FEATURE_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \
SPI_MEM_OP_ADDR(1, reg, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, valptr, 1))
#define SPINAND_GET_FEATURE_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \
SPI_MEM_OP_ADDR(1, reg, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, valptr, 1))
#define SPINAND_BLK_ERASE_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1))
#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
SPI_MEM_OP_ADDR(2, addr, 2), \
SPI_MEM_OP_DUMMY(ndummy, 2), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
SPI_MEM_OP_ADDR(2, addr, 4), \
SPI_MEM_OP_DUMMY(ndummy, 4), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
#define SPINAND_PROG_EXEC_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PROG_LOAD(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 1))
#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 4))
/**
* Standard SPI NAND flash commands
*/
#define SPINAND_CMD_PROG_LOAD_X4 0x32
#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34
/* feature register */
#define REG_BLOCK_LOCK 0xa0
#define BL_ALL_UNLOCKED 0x00
/* configuration register */
#define REG_CFG 0xb0
#define CFG_OTP_ENABLE BIT(6)
#define CFG_ECC_ENABLE BIT(4)
#define CFG_QUAD_ENABLE BIT(0)
/* status register */
#define REG_STATUS 0xc0
#define STATUS_BUSY BIT(0)
#define STATUS_ERASE_FAILED BIT(2)
#define STATUS_PROG_FAILED BIT(3)
#define STATUS_ECC_MASK GENMASK(5, 4)
#define STATUS_ECC_NO_BITFLIPS (0 << 4)
#define STATUS_ECC_HAS_BITFLIPS (1 << 4)
#define STATUS_ECC_UNCOR_ERROR (2 << 4)
struct spinand_op;
struct spinand_device;
#define SPINAND_MAX_ID_LEN 4
/**
* struct spinand_id - SPI NAND id structure
* @data: buffer containing the id bytes. Currently 4 bytes large, but can
* be extended if required
* @len: ID length
*
* struct_spinand_id->data contains all bytes returned after a READ_ID command,
* including dummy bytes if the chip does not emit ID bytes right after the
* READ_ID command. The responsibility to extract real ID bytes is left to
* struct_manufacurer_ops->detect().
*/
struct spinand_id {
u8 data[SPINAND_MAX_ID_LEN];
int len;
};
/**
* struct manufacurer_ops - SPI NAND manufacturer specific operations
* @detect: detect a SPI NAND device. Every time a SPI NAND device is probed
* the core calls the struct_manufacurer_ops->detect() hook of each
* registered manufacturer until one of them return 1. Note that
* the first thing to check in this hook is that the manufacturer ID
* in struct_spinand_device->id matches the manufacturer whose
* ->detect() hook has been called. Should return 1 if there's a
* match, 0 if the manufacturer ID does not match and a negative
* error code otherwise. When true is returned, the core assumes
* that properties of the NAND chip (spinand->base.memorg and
* spinand->base.eccreq) have been filled
* @init: initialize a SPI NAND device
* @cleanup: cleanup a SPI NAND device
*
* Each SPI NAND manufacturer driver should implement this interface so that
* NAND chips coming from this vendor can be detected and initialized properly.
*/
struct spinand_manufacturer_ops {
int (*detect)(struct spinand_device *spinand);
int (*init)(struct spinand_device *spinand);
void (*cleanup)(struct spinand_device *spinand);
};
/**
* struct spinand_manufacturer - SPI NAND manufacturer instance
* @id: manufacturer ID
* @name: manufacturer name
* @ops: manufacturer operations
*/
struct spinand_manufacturer {
u8 id;
char *name;
const struct spinand_manufacturer_ops *ops;
};
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
/**
* struct spinand_op_variants - SPI NAND operation variants
* @ops: the list of variants for a given operation
* @nops: the number of variants
*
* Some operations like read-from-cache/write-to-cache have several variants
* depending on the number of IO lines you use to transfer data or address
* cycles. This structure is a way to describe the different variants supported
* by a chip and let the core pick the best one based on the SPI mem controller
* capabilities.
*/
struct spinand_op_variants {
const struct spi_mem_op *ops;
unsigned int nops;
};
#define SPINAND_OP_VARIANTS(name, ...) \
const struct spinand_op_variants name = { \
.ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \
.nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \
sizeof(struct spi_mem_op), \
}
/**
* spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND
* chip
* @get_status: get the ECC status. Should return a positive number encoding
* the number of corrected bitflips if correction was possible or
* -EBADMSG if there are uncorrectable errors. I can also return
* other negative error codes if the error is not caused by
* uncorrectable bitflips
* @ooblayout: the OOB layout used by the on-die ECC implementation
*/
struct spinand_ecc_info {
int (*get_status)(struct spinand_device *spinand, u8 status);
const struct mtd_ooblayout_ops *ooblayout;
};
#define SPINAND_HAS_QE_BIT BIT(0)
/**
* struct spinand_info - Structure used to describe SPI NAND chips
* @model: model name
* @devid: device ID
* @flags: OR-ing of the SPINAND_XXX flags
* @memorg: memory organization
* @eccreq: ECC requirements
* @eccinfo: on-die ECC info
* @op_variants: operations variants
* @op_variants.read_cache: variants of the read-cache operation
* @op_variants.write_cache: variants of the write-cache operation
* @op_variants.update_cache: variants of the update-cache operation
* @select_target: function used to select a target/die. Required only for
* multi-die chips
*
* Each SPI NAND manufacturer driver should have a spinand_info table
* describing all the chips supported by the driver.
*/
struct spinand_info {
const char *model;
u8 devid;
u32 flags;
struct nand_memory_organization memorg;
struct nand_ecc_req eccreq;
struct spinand_ecc_info eccinfo;
struct {
const struct spinand_op_variants *read_cache;
const struct spinand_op_variants *write_cache;
const struct spinand_op_variants *update_cache;
} op_variants;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
};
#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \
{ \
.read_cache = __read, \
.write_cache = __write, \
.update_cache = __update, \
}
#define SPINAND_ECCINFO(__ooblayout, __get_status) \
.eccinfo = { \
.ooblayout = __ooblayout, \
.get_status = __get_status, \
}
#define SPINAND_SELECT_TARGET(__func) \
.select_target = __func,
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
__flags, ...) \
{ \
.model = __model, \
.devid = __id, \
.memorg = __memorg, \
.eccreq = __eccreq, \
.op_variants = __op_variants, \
.flags = __flags, \
__VA_ARGS__ \
}
/**
* struct spinand_device - SPI NAND device instance
* @base: NAND device instance
* @slave: pointer to the SPI slave object
* @lock: lock used to serialize accesses to the NAND
* @id: NAND ID as returned by READ_ID
* @flags: NAND flags
* @op_templates: various SPI mem op templates
* @op_templates.read_cache: read cache op template
* @op_templates.write_cache: write cache op template
* @op_templates.update_cache: update cache op template
* @select_target: select a specific target/die. Usually called before sending
* a command addressing a page or an eraseblock embedded in
* this die. Only required if your chip exposes several dies
* @cur_target: currently selected target/die
* @eccinfo: on-die ECC information
* @cfg_cache: config register cache. One entry per die
* @databuf: bounce buffer for data
* @oobbuf: bounce buffer for OOB data
* @scratchbuf: buffer used for everything but page accesses. This is needed
* because the spi-mem interface explicitly requests that buffers
* passed in spi_mem_op be DMA-able, so we can't based the bufs on
* the stack
* @manufacturer: SPI NAND manufacturer information
* @priv: manufacturer private data
*/
struct spinand_device {
struct nand_device base;
#ifndef __UBOOT__
struct spi_mem *spimem;
struct mutex lock;
#else
struct spi_slave *slave;
#endif
struct spinand_id id;
u32 flags;
struct {
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
} op_templates;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
unsigned int cur_target;
struct spinand_ecc_info eccinfo;
u8 *cfg_cache;
u8 *databuf;
u8 *oobbuf;
u8 *scratchbuf;
const struct spinand_manufacturer *manufacturer;
void *priv;
};
/**
* mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance
* @mtd: MTD instance
*
* Return: the SPI NAND device attached to @mtd.
*/
static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
{
return container_of(mtd_to_nanddev(mtd), struct spinand_device, base);
}
/**
* spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device
* @spinand: SPI NAND device
*
* Return: the MTD device embedded in @spinand.
*/
static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand)
{
return nanddev_to_mtd(&spinand->base);
}
/**
* nand_to_spinand() - Get the SPI NAND device embedding an NAND object
* @nand: NAND object
*
* Return: the SPI NAND device embedding @nand.
*/
static inline struct spinand_device *nand_to_spinand(struct nand_device *nand)
{
return container_of(nand, struct spinand_device, base);
}
/**
* spinand_to_nand() - Get the NAND device embedded in a SPI NAND object
* @spinand: SPI NAND device
*
* Return: the NAND device embedded in @spinand.
*/
static inline struct nand_device *
spinand_to_nand(struct spinand_device *spinand)
{
return &spinand->base;
}
/**
* spinand_set_of_node - Attach a DT node to a SPI NAND device
* @spinand: SPI NAND device
* @np: DT node
*
* Attach a DT node to a SPI NAND device.
*/
static inline void spinand_set_of_node(struct spinand_device *spinand,
const struct device_node *np)
{
nanddev_set_of_node(&spinand->base, np);
}
int spinand_match_and_init(struct spinand_device *dev,
const struct spinand_info *table,
unsigned int table_size, u8 devid);
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
int spinand_select_target(struct spinand_device *spinand, unsigned int target);
#endif /* __LINUX_MTD_SPINAND_H */

View File

@@ -8,15 +8,7 @@
#include <linux/mtd/mtd.h>
/*
* Get mtd_info structure of the dev, which is stored as uclass private.
*
* @dev: The MTD device
* @return: pointer to mtd_info, NULL on error
*/
static inline struct mtd_info *mtd_get_info(struct udevice *dev)
{
return dev_get_uclass_priv(dev);
}
int mtd_probe(struct udevice *dev);
int mtd_probe_devices(void);
#endif /* _MTD_H_ */

258
include/spi-mem.h Normal file
View File

@@ -0,0 +1,258 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
*
* Author:
* Peter Pan <peterpandong@micron.com>
* Boris Brezillon <boris.brezillon@bootlin.com>
*/
#ifndef __UBOOT_SPI_MEM_H
#define __UBOOT_SPI_MEM_H
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <spi.h>
#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
{ \
.buswidth = __buswidth, \
.opcode = __opcode, \
}
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
{ \
.nbytes = __nbytes, \
.val = __val, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_ADDR { }
#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \
{ \
.nbytes = __nbytes, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_DUMMY { }
#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \
{ \
.dir = SPI_MEM_DATA_IN, \
.nbytes = __nbytes, \
.buf.in = __buf, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
{ \
.dir = SPI_MEM_DATA_OUT, \
.nbytes = __nbytes, \
.buf.out = __buf, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_DATA { }
/**
* enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective
* @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_DATA_IN,
SPI_MEM_DATA_OUT,
};
/**
* struct spi_mem_op - describes a SPI memory operation
* @cmd.buswidth: number of IO lines used to transmit the command
* @cmd.opcode: operation opcode
* @addr.nbytes: number of address bytes to send. Can be zero if the operation
* does not need to send an address
* @addr.buswidth: number of IO lines used to transmit the address cycles
* @addr.val: address value. This value is always sent MSB first on the bus.
* Note that only @addr.nbytes are taken into account in this
* address value, so users should make sure the value fits in the
* assigned number of bytes.
* @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
* be zero if the operation does not require dummy bytes
* @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
* @data.buswidth: number of IO lanes used to send/receive the data
* @data.dir: direction of the transfer
* @data.buf.in: input buffer
* @data.buf.out: output buffer
*/
struct spi_mem_op {
struct {
u8 buswidth;
u8 opcode;
} cmd;
struct {
u8 nbytes;
u8 buswidth;
u64 val;
} addr;
struct {
u8 nbytes;
u8 buswidth;
} dummy;
struct {
u8 buswidth;
enum spi_mem_data_dir dir;
unsigned int nbytes;
/* buf.{in,out} must be DMA-able. */
union {
void *in;
const void *out;
} buf;
} data;
};
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \
{ \
.cmd = __cmd, \
.addr = __addr, \
.dummy = __dummy, \
.data = __data, \
}
#ifndef __UBOOT__
/**
* struct spi_mem - describes a SPI memory device
* @spi: the underlying SPI device
* @drvpriv: spi_mem_driver private data
*
* Extra information that describe the SPI memory device and may be needed by
* the controller to properly handle this device should be placed here.
*
* One example would be the device size since some controller expose their SPI
* mem devices through a io-mapped region.
*/
struct spi_mem {
struct udevice *dev;
void *drvpriv;
};
/**
* struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
* device
* @mem: memory device
* @data: data to attach to the memory device
*/
static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
{
mem->drvpriv = data;
}
/**
* struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
* device
* @mem: memory device
*
* Return: the data attached to the mem device.
*/
static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
{
return mem->drvpriv;
}
#endif /* __UBOOT__ */
/**
* struct spi_controller_mem_ops - SPI memory operations
* @adjust_op_size: shrink the data xfer of an operation to match controller's
* limitations (can be alignment of max RX/TX size
* limitations)
* @supports_op: check if an operation is supported by the controller
* @exec_op: execute a SPI memory operation
*
* This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the
* case for QSPI controllers.
*/
struct spi_controller_mem_ops {
int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op);
bool (*supports_op)(struct spi_slave *slave,
const struct spi_mem_op *op);
int (*exec_op)(struct spi_slave *slave,
const struct spi_mem_op *op);
};
#ifndef __UBOOT__
/**
* struct spi_mem_driver - SPI memory driver
* @spidrv: inherit from a SPI driver
* @probe: probe a SPI memory. Usually where detection/initialization takes
* place
* @remove: remove a SPI memory
* @shutdown: take appropriate action when the system is shutdown
*
* This is just a thin wrapper around a spi_driver. The core takes care of
* allocating the spi_mem object and forwarding the probe/remove/shutdown
* request to the spi_mem_driver. The reason we use this wrapper is because
* we might have to stuff more information into the spi_mem struct to let
* SPI controllers know more about the SPI memory they interact with, and
* having this intermediate layer allows us to do that without adding more
* useless fields to the spi_device object.
*/
struct spi_mem_driver {
struct spi_driver spidrv;
int (*probe)(struct spi_mem *mem);
int (*remove)(struct spi_mem *mem);
void (*shutdown)(struct spi_mem *mem);
};
#if IS_ENABLED(CONFIG_SPI_MEM)
int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg);
void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg);
#else
static inline int
spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg)
{
return -ENOTSUPP;
}
static inline void
spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg)
{
}
#endif /* CONFIG_SPI_MEM */
#endif /* __UBOOT__ */
int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op);
bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op);
int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
#ifndef __UBOOT__
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner);
void spi_mem_driver_unregister(struct spi_mem_driver *drv);
#define spi_mem_driver_register(__drv) \
spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
#define module_spi_mem_driver(__drv) \
module_driver(__drv, spi_mem_driver_register, \
spi_mem_driver_unregister)
#endif
#endif /* __LINUX_SPI_MEM_H */

View File

@@ -9,6 +9,8 @@
#ifndef _SPI_H_
#define _SPI_H_
#include <common.h>
/* SPI mode flags */
#define SPI_CPHA BIT(0) /* clock phase */
#define SPI_CPOL BIT(1) /* clock polarity */
@@ -402,6 +404,15 @@ struct dm_spi_ops {
int (*xfer)(struct udevice *dev, unsigned int bitlen, const void *dout,
void *din, unsigned long flags);
/**
* Optimized handlers for SPI memory-like operations.
*
* Optimized/dedicated operations for interactions with SPI memory. This
* field is optional and should only be implemented if the controller
* has native support for memory like operations.
*/
const struct spi_controller_mem_ops *mem_ops;
/**
* Set transfer speed.
* This sets a new speed to be applied for next spi_xfer().