diff options
Diffstat (limited to 'include/linux/mtd')
| -rw-r--r-- | include/linux/mtd/mtd.h | 8 | ||||
| -rw-r--r-- | include/linux/mtd/rawnand.h | 126 | ||||
| -rw-r--r-- | include/linux/mtd/spi-nor.h | 1 | ||||
| -rw-r--r-- | include/linux/mtd/spinand.h | 421 | 
4 files changed, 504 insertions, 52 deletions
| diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a86c4fa93115..cd0be91bdefa 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -67,9 +67,11 @@ struct mtd_erase_region_info {   * @datbuf:	data buffer - if NULL only oob data are read/written   * @oobbuf:	oob data buffer   * - * Note, it is allowed to read more than one OOB area at one go, but not write. - * The interface assumes that the OOB write requests program only one page's - * OOB area. + * Note, some MTD drivers do not allow you to write more than one OOB area at + * one go. If you try to do that on such an MTD device, -EINVAL will be + * returned. If you want to make your implementation portable on all kind of MTD + * devices you should split the write request into several sub-requests when the + * request crosses a page boundary.   */  struct mtd_oob_ops {  	unsigned int	mode; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3e8ec3b8a39c..efb2345359bb 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -21,11 +21,10 @@  #include <linux/mtd/mtd.h>  #include <linux/mtd/flashchip.h>  #include <linux/mtd/bbm.h> +#include <linux/of.h>  #include <linux/types.h> -struct mtd_info;  struct nand_flash_dev; -struct device_node;  /* Scan and identify a NAND device */  int nand_scan_with_ids(struct mtd_info *mtd, int max_chips, @@ -36,17 +35,6 @@ static inline int nand_scan(struct mtd_info *mtd, int max_chips)  	return nand_scan_with_ids(mtd, max_chips, NULL);  } -/* - * Separate phases of nand_scan(), allowing board driver to intervene - * and override command or ECC setup according to flash type. - */ -int nand_scan_ident(struct mtd_info *mtd, int max_chips, -			   struct nand_flash_dev *table); -int nand_scan_tail(struct mtd_info *mtd); - -/* Unregister the MTD device and free resources held by the NAND device */ -void nand_release(struct mtd_info *mtd); -  /* Internal helper for board drivers which need to override command function */  void nand_wait_ready(struct mtd_info *mtd); @@ -121,6 +109,7 @@ enum nand_ecc_algo {  	NAND_ECC_UNKNOWN,  	NAND_ECC_HAMMING,  	NAND_ECC_BCH, +	NAND_ECC_RS,  };  /* @@ -218,6 +207,12 @@ enum nand_ecc_algo {   */  #define NAND_WAIT_TCCS		0x00200000 +/* + * Whether the NAND chip is a boot medium. Drivers might use this information + * to select ECC algorithms supported by the boot ROM or similar restrictions. + */ +#define NAND_IS_BOOT_MEDIUM	0x00400000 +  /* Options set by nand scan */  /* Nand scan has allocated controller struct */  #define NAND_CONTROLLER_ALLOC	0x80000000 @@ -230,6 +225,17 @@ enum nand_ecc_algo {  /* Keep gcc happy */  struct nand_chip; +/* ONFI version bits */ +#define ONFI_VERSION_1_0		BIT(1) +#define ONFI_VERSION_2_0		BIT(2) +#define ONFI_VERSION_2_1		BIT(3) +#define ONFI_VERSION_2_2		BIT(4) +#define ONFI_VERSION_2_3		BIT(5) +#define ONFI_VERSION_3_0		BIT(6) +#define ONFI_VERSION_3_1		BIT(7) +#define ONFI_VERSION_3_2		BIT(8) +#define ONFI_VERSION_4_0		BIT(9) +  /* ONFI features */  #define ONFI_FEATURE_16_BIT_BUS		(1 << 0)  #define ONFI_FEATURE_EXT_PARAM_PAGE	(1 << 7) @@ -470,13 +476,13 @@ struct onfi_params {   */  struct nand_parameters {  	/* Generic parameters */ -	char model[100]; +	const char *model;  	bool supports_set_get_features;  	DECLARE_BITMAP(set_feature_list, ONFI_FEATURE_NUMBER);  	DECLARE_BITMAP(get_feature_list, ONFI_FEATURE_NUMBER);  	/* ONFI parameters */ -	struct onfi_params onfi; +	struct onfi_params *onfi;  };  /* The maximum expected count of bytes in the NAND ID sequence */ @@ -493,20 +499,42 @@ struct nand_id {  };  /** - * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices + * struct nand_controller_ops - Controller operations + * + * @attach_chip: this method is called after the NAND detection phase after + *		 flash ID and MTD fields such as erase size, page size and OOB + *		 size have been set up. ECC requirements are available if + *		 provided by the NAND chip or device tree. Typically used to + *		 choose the appropriate ECC configuration and allocate + *		 associated resources. + *		 This hook is optional. + * @detach_chip: free all resources allocated/claimed in + *		 nand_controller_ops->attach_chip(). + *		 This hook is optional. + */ +struct nand_controller_ops { +	int (*attach_chip)(struct nand_chip *chip); +	void (*detach_chip)(struct nand_chip *chip); +}; + +/** + * struct nand_controller - Structure used to describe a NAND controller + *   * @lock:               protection lock   * @active:		the mtd device which holds the controller currently   * @wq:			wait queue to sleep on if a NAND operation is in   *			progress used instead of the per chip wait queue   *			when a hw controller is available. + * @ops:		NAND controller operations.   */ -struct nand_hw_control { +struct nand_controller {  	spinlock_t lock;  	struct nand_chip *active;  	wait_queue_head_t wq; +	const struct nand_controller_ops *ops;  }; -static inline void nand_hw_control_init(struct nand_hw_control *nfc) +static inline void nand_controller_init(struct nand_controller *nfc)  {  	nfc->active = NULL;  	spin_lock_init(&nfc->lock); @@ -778,11 +806,15 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)   *	  implementation) if any.   * @cleanup: the ->init() function may have allocated resources, ->cleanup()   *	     is here to let vendor specific code release those resources. + * @fixup_onfi_param_page: apply vendor specific fixups to the ONFI parameter + *			   page. This is called after the checksum is verified.   */  struct nand_manufacturer_ops {  	void (*detect)(struct nand_chip *chip);  	int (*init)(struct nand_chip *chip);  	void (*cleanup)(struct nand_chip *chip); +	void (*fixup_onfi_param_page)(struct nand_chip *chip, +				      struct nand_onfi_params *p);  };  /** @@ -986,14 +1018,14 @@ struct nand_subop {  	unsigned int last_instr_end_off;  }; -int nand_subop_get_addr_start_off(const struct nand_subop *subop, -				  unsigned int op_id); -int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, -				unsigned int op_id); -int nand_subop_get_data_start_off(const struct nand_subop *subop, -				  unsigned int op_id); -int nand_subop_get_data_len(const struct nand_subop *subop, -			    unsigned int op_id); +unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, +					   unsigned int op_id); +unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, +					 unsigned int op_id); +unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, +					   unsigned int op_id); +unsigned int nand_subop_get_data_len(const struct nand_subop *subop, +				     unsigned int op_id);  /**   * struct nand_op_parser_addr_constraints - Constraints for address instructions @@ -1176,9 +1208,9 @@ int nand_op_parser_exec_op(struct nand_chip *chip,   *			setting the read-retry mode. Mostly needed for MLC NAND.   * @ecc:		[BOARDSPECIFIC] ECC control structure   * @buf_align:		minimum buffer alignment required by a platform - * @hwcontrol:		platform-specific hardware control structure + * @dummy_controller:	dummy controller implementation for drivers that can + *			only control a single chip   * @erase:		[REPLACEABLE] erase function - * @scan_bbt:		[REPLACEABLE] function to scan bad block table   * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transferring   *			data from array to read regs (tR).   * @state:		[INTERN] the current state of the NAND device @@ -1271,7 +1303,6 @@ struct nand_chip {  		       const struct nand_operation *op,  		       bool check_only);  	int (*erase)(struct mtd_info *mtd, int page); -	int (*scan_bbt)(struct mtd_info *mtd);  	int (*set_features)(struct mtd_info *mtd, struct nand_chip *chip,  			    int feature_addr, uint8_t *subfeature_para);  	int (*get_features)(struct mtd_info *mtd, struct nand_chip *chip, @@ -1314,11 +1345,11 @@ struct nand_chip {  	flstate_t state;  	uint8_t *oob_poi; -	struct nand_hw_control *controller; +	struct nand_controller *controller;  	struct nand_ecc_ctrl ecc;  	unsigned long buf_align; -	struct nand_hw_control hwcontrol; +	struct nand_controller dummy_controller;  	uint8_t *bbt;  	struct nand_bbt_descr *bbt_td; @@ -1517,14 +1548,12 @@ extern const struct nand_manufacturer_ops micron_nand_manuf_ops;  extern const struct nand_manufacturer_ops amd_nand_manuf_ops;  extern const struct nand_manufacturer_ops macronix_nand_manuf_ops; -int nand_default_bbt(struct mtd_info *mtd); +int nand_create_bbt(struct nand_chip *chip);  int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);  int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);  int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);  int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  		    int allowbbt); -int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, -		 size_t *retlen, uint8_t *buf);  /**   * struct platform_nand_chip - chip level device structure @@ -1555,14 +1584,12 @@ struct platform_device;   * struct platform_nand_ctrl - controller level device structure   * @probe:		platform specific function to probe/setup hardware   * @remove:		platform specific function to remove/teardown hardware - * @hwcontrol:		platform specific hardware control structure   * @dev_ready:		platform specific function to read ready/busy pin   * @select_chip:	platform specific chip select function   * @cmd_ctrl:		platform specific function for controlling   *			ALE/CLE/nCE. Also used to write command and address   * @write_buf:		platform specific function for write buffer   * @read_buf:		platform specific function for read buffer - * @read_byte:		platform specific function to read one byte from chip   * @priv:		private data to transport driver specific settings   *   * All fields are optional and depend on the hardware driver requirements @@ -1570,13 +1597,11 @@ struct platform_device;  struct platform_nand_ctrl {  	int (*probe)(struct platform_device *pdev);  	void (*remove)(struct platform_device *pdev); -	void (*hwcontrol)(struct mtd_info *mtd, int cmd);  	int (*dev_ready)(struct mtd_info *mtd);  	void (*select_chip)(struct mtd_info *mtd, int chip);  	void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);  	void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);  	void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); -	unsigned char (*read_byte)(struct mtd_info *mtd);  	void *priv;  }; @@ -1593,10 +1618,10 @@ struct platform_nand_data {  /* return the supported asynchronous timing mode. */  static inline int onfi_get_async_timing_mode(struct nand_chip *chip)  { -	if (!chip->parameters.onfi.version) +	if (!chip->parameters.onfi)  		return ONFI_TIMING_MODE_UNKNOWN; -	return chip->parameters.onfi.async_timing_mode; +	return chip->parameters.onfi->async_timing_mode;  }  int onfi_fill_data_interface(struct nand_chip *chip, @@ -1641,14 +1666,8 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,  				void *extraoob, int extraooblen,  				int threshold); -int nand_check_ecc_caps(struct nand_chip *chip, -			const struct nand_ecc_caps *caps, int oobavail); - -int nand_match_ecc_req(struct nand_chip *chip, -		       const struct nand_ecc_caps *caps,  int oobavail); - -int nand_maximize_ecc(struct nand_chip *chip, -		      const struct nand_ecc_caps *caps, int oobavail); +int nand_ecc_choose_conf(struct nand_chip *chip, +			 const struct nand_ecc_caps *caps, int oobavail);  /* Default write_oob implementation */  int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); @@ -1674,10 +1693,14 @@ int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip,  /* Default read_page_raw implementation */  int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,  		       uint8_t *buf, int oob_required, int page); +int nand_read_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, +			       u8 *buf, int oob_required, int page);  /* Default write_page_raw implementation */  int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,  			const uint8_t *buf, int oob_required, int page); +int nand_write_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, +				const u8 *buf, int oob_required, int page);  /* Reset and initialize a NAND device */  int nand_reset(struct nand_chip *chip, int chipnr); @@ -1711,8 +1734,13 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,  int nand_write_data_op(struct nand_chip *chip, const void *buf,  		       unsigned int len, bool force_8bit); -/* Free resources held by the NAND device */ +/* + * Free resources held by the NAND device, must be called on error after a + * sucessful nand_scan(). + */  void nand_cleanup(struct nand_chip *chip); +/* Unregister the MTD device and calls nand_cleanup() */ +void nand_release(struct mtd_info *mtd);  /* Default extended ID decoding function */  void nand_decode_ext_id(struct nand_chip *chip); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e60da0d34cc1..c922e97f205a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -235,6 +235,7 @@ enum spi_nor_option_flags {  	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),  	SNOR_F_READY_XSR_RDY	= BIT(4),  	SNOR_F_USE_CLSR		= BIT(5), +	SNOR_F_BROKEN_RESET	= BIT(6),  };  /** diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h new file mode 100644 index 000000000000..088ff96c3eb6 --- /dev/null +++ b/include/linux/mtd/spinand.h @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + *  Authors: + *	Peter Pan <[email protected]> + */ +#ifndef __LINUX_MTD_SPINAND_H +#define __LINUX_MTD_SPINAND_H + +#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> + +/** + * 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 + * @spimem: pointer to the SPI mem 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; +	struct spi_mem *spimem; +	struct mutex lock; +	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, +				       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 */ |