mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	MTD updates for 3.16:
- refactor m25p80.c driver for use as a general SPI NOR framework for other drivers which may speak to SPI NOR flash without providing full SPI support (i.e., not part of drivers/spi/) - new Freescale QuadSPI driver (utilizing new SPI NOR framework) - updates for the STMicro "FSM" SPI NOR driver - fix sync/flush behavior on mtd_blkdevs - fixup subpage write support on a few NAND drivers - correct the MTD OOB test for odd-sized OOB areas - add BCH-16 support for OMAP NAND - fix warnings and trivial refactoring - utilize new ECC DT bindings in pxa3xx NAND driver - new LPDDR NVM driver - address a few assorted bugs caught by Coverity - add new imx6sx support for GPMI NAND - use a bounce buffer for NAND when non-DMA-able buffers are used -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTl/9oAAoJEFySrpd9RFgtfnUP+wURZfF1Xnek/3yNZP0Pt90x yToXPDgsK5oteBAAWMUwtnJImDzsUMD8BgLNLU1jvPKuvMo9lwNMaCF+l1wUrTeC F1VgYWq4tub3tk104Dthlguk0Jhj66k61LbvFvKXhkGEYGD9iPFeTPWyARUZTYOv R4eRybuU+l2ZTDd+vNStXx9oWyqzWXekwrhMi10YWoxF694kBMI4C0rZQ2CexjVl B5K0oL2P8JU/yNLgtMgPOfkh8rHZEoXECA3vaQscZzsOnc0evDndKSTkTX1Ls61Y eWFgXV6qyhL+5VKTiHNzi6/J0NeNaTquOs9HoBuWr1DwaS8aoWEhBjeuNrXGYs/s 6++CRoDDcdWunAXBH8hqFSu6IweYB5TQ+QMUa7Z69C9n/fZg82dF4i2RSnp4Y2rs qI19LrPIpdyL1ril5ndp0U2JRYXdxOpX3+jf2anG6u3vYjzI8P8tXEGKUz/uNpnK fpEn2zKpeHAq62eqScuhGsO7MO2bIg7yNKMdSoeeeT9dgbah6fkrQnaDNbtjC+Y1 rXMhgLiVebmm8BVe6w5XSFqCw+76RxmO04TAj/Vy3WVPQ2KNn+OuLc0yVlsqAO9n 7Y19QvHeMZZW4O/w5RQ/OniJpysXN0ESj2cE93DHdgUPQ5aedIN0r5eQA0M1e8c6 W2MQFS5nJPiCxUYia4KP =6UIq -----END PGP SIGNATURE----- Merge tag 'for-linus-20140610' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: - refactor m25p80.c driver for use as a general SPI NOR framework for other drivers which may speak to SPI NOR flash without providing full SPI support (i.e., not part of drivers/spi/) - new Freescale QuadSPI driver (utilizing new SPI NOR framework) - updates for the STMicro "FSM" SPI NOR driver - fix sync/flush behavior on mtd_blkdevs - fixup subpage write support on a few NAND drivers - correct the MTD OOB test for odd-sized OOB areas - add BCH-16 support for OMAP NAND - fix warnings and trivial refactoring - utilize new ECC DT bindings in pxa3xx NAND driver - new LPDDR NVM driver - address a few assorted bugs caught by Coverity - add new imx6sx support for GPMI NAND - use a bounce buffer for NAND when non-DMA-able buffers are used * tag 'for-linus-20140610' of git://git.infradead.org/linux-mtd: (77 commits) mtd: gpmi: add gpmi support for imx6sx mtd: maps: remove check for CONFIG_MTD_SUPERH_RESERVE mtd: bf5xx_nand: use the managed version of kzalloc mtd: pxa3xx_nand: make the driver work on big-endian systems mtd: nand: omap: fix omap_calculate_ecc_bch() for-loop error mtd: nand: r852: correct write_buf loop bounds mtd: nand_bbt: handle error case for nand_create_badblock_pattern() mtd: nand_bbt: remove unused variable mtd: maps: sc520cdp: fix warnings mtd: slram: fix unused variable warning mtd: pfow: remove unused variable mtd: lpddr: fix Kconfig dependency, for I/O accessors mtd: nand: pxa3xx: Add supported ECC strength and step size to the DT binding mtd: nand: pxa3xx: Use ECC strength and step size devicetree binding mtd: nand: pxa3xx: Clean pxa_ecc_init() error handling mtd: nand: Warn the user if the selected ECC strength is too weak mtd: nand: omap: Documentation: How to select correct ECC scheme for your device ? mtd: nand: omap: add support for BCH16_ECC - NAND driver updates mtd: nand: omap: add support for BCH16_ECC - ELM driver updates mtd: nand: omap: add support for BCH16_ECC - GPMC driver updates ...
This commit is contained in:
		
						commit
						e413a19a8e
					
				
					 54 changed files with 3761 additions and 1620 deletions
				
			
		
							
								
								
									
										35
									
								
								Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					* Freescale Quad Serial Peripheral Interface(QuadSPI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Required properties:
 | 
				
			||||||
 | 
					  - compatible : Should be "fsl,vf610-qspi"
 | 
				
			||||||
 | 
					  - reg : the first contains the register location and length,
 | 
				
			||||||
 | 
					          the second contains the memory mapping address and length
 | 
				
			||||||
 | 
					  - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
 | 
				
			||||||
 | 
					  - interrupts : Should contain the interrupt for the device
 | 
				
			||||||
 | 
					  - clocks : The clocks needed by the QuadSPI controller
 | 
				
			||||||
 | 
					  - clock-names : the name of the clocks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Optional properties:
 | 
				
			||||||
 | 
					  - fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
 | 
				
			||||||
 | 
					                              Each bus can be connected with two NOR flashes.
 | 
				
			||||||
 | 
								      Most of the time, each bus only has one NOR flash
 | 
				
			||||||
 | 
								      connected, this is the default case.
 | 
				
			||||||
 | 
								      But if there are two NOR flashes connected to the
 | 
				
			||||||
 | 
								      bus, you should enable this property.
 | 
				
			||||||
 | 
								      (Please check the board's schematic.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qspi0: quadspi@40044000 {
 | 
				
			||||||
 | 
						compatible = "fsl,vf610-qspi";
 | 
				
			||||||
 | 
						reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
 | 
				
			||||||
 | 
						reg-names = "QuadSPI", "QuadSPI-memory";
 | 
				
			||||||
 | 
						interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
 | 
				
			||||||
 | 
						clocks = <&clks VF610_CLK_QSPI0_EN>,
 | 
				
			||||||
 | 
							<&clks VF610_CLK_QSPI0>;
 | 
				
			||||||
 | 
						clock-names = "qspi_en", "qspi";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flash0: s25fl128s@0 {
 | 
				
			||||||
 | 
							....
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,8 @@ Optional properties:
 | 
				
			||||||
		"ham1"		1-bit Hamming ecc code
 | 
							"ham1"		1-bit Hamming ecc code
 | 
				
			||||||
		"bch4"		4-bit BCH ecc code
 | 
							"bch4"		4-bit BCH ecc code
 | 
				
			||||||
		"bch8"		8-bit BCH ecc code
 | 
							"bch8"		8-bit BCH ecc code
 | 
				
			||||||
 | 
							"bch16"		16-bit BCH ECC code
 | 
				
			||||||
 | 
							Refer below "How to select correct ECC scheme for your device ?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - ti,nand-xfer-type:		A string setting the data transfer type. One of:
 | 
					 - ti,nand-xfer-type:		A string setting the data transfer type. One of:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,3 +92,46 @@ Example for an AM33xx board:
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					How to select correct ECC scheme for your device ?
 | 
				
			||||||
 | 
					--------------------------------------------------
 | 
				
			||||||
 | 
					Higher ECC scheme usually means better protection against bit-flips and
 | 
				
			||||||
 | 
					increased system lifetime. However, selection of ECC scheme is dependent
 | 
				
			||||||
 | 
					on various other factors also like;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(1) support of built in hardware engines.
 | 
				
			||||||
 | 
						Some legacy OMAP SoC do not have ELM harware engine, so those SoC cannot
 | 
				
			||||||
 | 
						support ecc-schemes with hardware error-correction (BCHx_HW). However
 | 
				
			||||||
 | 
						such SoC can use ecc-schemes with software library for error-correction
 | 
				
			||||||
 | 
						(BCHx_HW_DETECTION_SW). The error correction capability with software
 | 
				
			||||||
 | 
						library remains equivalent to their hardware counter-part, but there is
 | 
				
			||||||
 | 
						slight CPU penalty when too many bit-flips are detected during reads.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(2) Device parameters like OOBSIZE.
 | 
				
			||||||
 | 
						Other factor which governs the selection of ecc-scheme is oob-size.
 | 
				
			||||||
 | 
						Higher ECC schemes require more OOB/Spare area to store ECC syndrome,
 | 
				
			||||||
 | 
						so the device should have enough free bytes available its OOB/Spare
 | 
				
			||||||
 | 
						area to accomodate ECC for entire page. In general following expression
 | 
				
			||||||
 | 
						helps in determining if given device can accomodate ECC syndrome:
 | 
				
			||||||
 | 
						"2 + (PAGESIZE / 512) * ECC_BYTES" >= OOBSIZE"
 | 
				
			||||||
 | 
						where
 | 
				
			||||||
 | 
							OOBSIZE		number of bytes in OOB/spare area
 | 
				
			||||||
 | 
							PAGESIZE	number of bytes in main-area of device page
 | 
				
			||||||
 | 
							ECC_BYTES	number of ECC bytes generated to protect
 | 
				
			||||||
 | 
							                512 bytes of data, which is:
 | 
				
			||||||
 | 
									'3' for HAM1_xx ecc schemes
 | 
				
			||||||
 | 
									'7' for BCH4_xx ecc schemes
 | 
				
			||||||
 | 
									'14' for BCH8_xx ecc schemes
 | 
				
			||||||
 | 
									'26' for BCH16_xx ecc schemes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Example(a): For a device with PAGESIZE = 2048 and OOBSIZE = 64 and
 | 
				
			||||||
 | 
							trying to use BCH16 (ECC_BYTES=26) ecc-scheme.
 | 
				
			||||||
 | 
							Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B
 | 
				
			||||||
 | 
							which is greater than capacity of NAND device (OOBSIZE=64)
 | 
				
			||||||
 | 
							Hence, BCH16 cannot be supported on given device. But it can
 | 
				
			||||||
 | 
							probably use lower ecc-schemes like BCH8.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Example(b): For a device with PAGESIZE = 2048 and OOBSIZE = 128 and
 | 
				
			||||||
 | 
							trying to use BCH16 (ECC_BYTES=26) ecc-scheme.
 | 
				
			||||||
 | 
							Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B
 | 
				
			||||||
 | 
							which can be accomodate in the OOB/Spare area of this device
 | 
				
			||||||
 | 
							(OOBSIZE=128). So this device can use BCH16 ecc-scheme.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,8 @@ Required properties:
 | 
				
			||||||
  representing partitions.
 | 
					  representing partitions.
 | 
				
			||||||
- compatible : Should be the manufacturer and the name of the chip. Bear in mind
 | 
					- compatible : Should be the manufacturer and the name of the chip. Bear in mind
 | 
				
			||||||
               the DT binding is not Linux-only, but in case of Linux, see the
 | 
					               the DT binding is not Linux-only, but in case of Linux, see the
 | 
				
			||||||
               "m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of
 | 
					               "spi_nor_ids" table in drivers/mtd/spi-nor/spi-nor.c for the list
 | 
				
			||||||
               supported chips.
 | 
					               of supported chips.
 | 
				
			||||||
- reg : Chip-Select number
 | 
					- reg : Chip-Select number
 | 
				
			||||||
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
 | 
					- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,14 @@ Optional properties:
 | 
				
			||||||
 - num-cs:			Number of chipselect lines to usw
 | 
					 - num-cs:			Number of chipselect lines to usw
 | 
				
			||||||
 - nand-on-flash-bbt: 		boolean to enable on flash bbt option if
 | 
					 - nand-on-flash-bbt: 		boolean to enable on flash bbt option if
 | 
				
			||||||
				not present false
 | 
									not present false
 | 
				
			||||||
 | 
					 - nand-ecc-strength:           number of bits to correct per ECC step
 | 
				
			||||||
 | 
					 - nand-ecc-step-size:          number of data bytes covered by a single ECC step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following ECC strength and step size are currently supported:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 - nand-ecc-strength = <1>, nand-ecc-step-size = <512>
 | 
				
			||||||
 | 
					 - nand-ecc-strength = <4>, nand-ecc-step-size = <512>
 | 
				
			||||||
 | 
					 - nand-ecc-strength = <8>, nand-ecc-step-size = <512>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										62
									
								
								Documentation/mtd/spi-nor.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								Documentation/mtd/spi-nor.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					                          SPI NOR framework
 | 
				
			||||||
 | 
					               ============================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Part I - Why do we need this framework?
 | 
				
			||||||
 | 
					---------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
 | 
				
			||||||
 | 
					controller operates agnostic of the specific device attached. However, some
 | 
				
			||||||
 | 
					controllers (such as Freescale's QuadSPI controller) cannot easily handle
 | 
				
			||||||
 | 
					arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In particular, Freescale's QuadSPI controller must know the NOR commands to
 | 
				
			||||||
 | 
					find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
 | 
				
			||||||
 | 
					opcodes, addresses, or data payloads; a SPI controller simply knows to send or
 | 
				
			||||||
 | 
					receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
 | 
				
			||||||
 | 
					which the controller driver is aware of the opcodes, addressing, and other
 | 
				
			||||||
 | 
					details of the SPI NOR protocol.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Part II - How does the framework work?
 | 
				
			||||||
 | 
					--------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This framework just adds a new layer between the MTD and the SPI bus driver.
 | 
				
			||||||
 | 
					With this new layer, the SPI NOR controller driver does not depend on the
 | 
				
			||||||
 | 
					m25p80 code anymore.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Before this framework, the layer is like:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                   MTD
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
					                  m25p80
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
						       SPI bus driver
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
						        SPI NOR chip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   After this framework, the layer is like:
 | 
				
			||||||
 | 
					                   MTD
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
					              SPI NOR framework
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
					                  m25p80
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
						       SPI bus driver
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
						       SPI NOR chip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  With the SPI NOR controller driver (Freescale QuadSPI), it looks like:
 | 
				
			||||||
 | 
					                   MTD
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
					              SPI NOR framework
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
					                fsl-quadSPI
 | 
				
			||||||
 | 
					         ------------------------
 | 
				
			||||||
 | 
						       SPI NOR chip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Part III - How can drivers use the framework?
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The main API is spi_nor_scan(). Before you call the hook, a driver should
 | 
				
			||||||
 | 
					initialize the necessary fields for spi_nor{}. Please see
 | 
				
			||||||
 | 
					drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to fsl-quadspi.c
 | 
				
			||||||
 | 
					when you want to write a new driver for a SPI NOR controller.
 | 
				
			||||||
| 
						 | 
					@ -68,6 +68,9 @@
 | 
				
			||||||
#define	GPMC_ECC_BCH_RESULT_1	0x244	/* not available on OMAP2 */
 | 
					#define	GPMC_ECC_BCH_RESULT_1	0x244	/* not available on OMAP2 */
 | 
				
			||||||
#define	GPMC_ECC_BCH_RESULT_2	0x248	/* not available on OMAP2 */
 | 
					#define	GPMC_ECC_BCH_RESULT_2	0x248	/* not available on OMAP2 */
 | 
				
			||||||
#define	GPMC_ECC_BCH_RESULT_3	0x24c	/* not available on OMAP2 */
 | 
					#define	GPMC_ECC_BCH_RESULT_3	0x24c	/* not available on OMAP2 */
 | 
				
			||||||
 | 
					#define	GPMC_ECC_BCH_RESULT_4	0x300	/* not available on OMAP2 */
 | 
				
			||||||
 | 
					#define	GPMC_ECC_BCH_RESULT_5	0x304	/* not available on OMAP2 */
 | 
				
			||||||
 | 
					#define	GPMC_ECC_BCH_RESULT_6	0x308	/* not available on OMAP2 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* GPMC ECC control settings */
 | 
					/* GPMC ECC control settings */
 | 
				
			||||||
#define GPMC_ECC_CTRL_ECCCLEAR		0x100
 | 
					#define GPMC_ECC_CTRL_ECCCLEAR		0x100
 | 
				
			||||||
| 
						 | 
					@ -677,6 +680,12 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
 | 
				
			||||||
					   GPMC_BCH_SIZE * i;
 | 
										   GPMC_BCH_SIZE * i;
 | 
				
			||||||
		reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
 | 
							reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
 | 
				
			||||||
					   GPMC_BCH_SIZE * i;
 | 
										   GPMC_BCH_SIZE * i;
 | 
				
			||||||
 | 
							reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 +
 | 
				
			||||||
 | 
										   i * GPMC_BCH_SIZE;
 | 
				
			||||||
 | 
							reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 +
 | 
				
			||||||
 | 
										   i * GPMC_BCH_SIZE;
 | 
				
			||||||
 | 
							reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
 | 
				
			||||||
 | 
										   i * GPMC_BCH_SIZE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1412,6 +1421,12 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			gpmc_nand_data->ecc_opt =
 | 
								gpmc_nand_data->ecc_opt =
 | 
				
			||||||
				OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
 | 
									OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
 | 
				
			||||||
 | 
						else if (!strcmp(s, "bch16"))
 | 
				
			||||||
 | 
							if (gpmc_nand_data->elm_of_node)
 | 
				
			||||||
 | 
								gpmc_nand_data->ecc_opt =
 | 
				
			||||||
 | 
									OMAP_ECC_BCH16_CODE_HW;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								pr_err("%s: BCH16 requires ELM support\n", __func__);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
 | 
							pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source "drivers/mtd/lpddr/Kconfig"
 | 
					source "drivers/mtd/lpddr/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source "drivers/mtd/spi-nor/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source "drivers/mtd/ubi/Kconfig"
 | 
					source "drivers/mtd/ubi/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
endif # MTD
 | 
					endif # MTD
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,4 +32,5 @@ inftl-objs		:= inftlcore.o inftlmount.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
obj-y		+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
 | 
					obj-y		+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
 | 
				
			||||||
obj-$(CONFIG_MTD_UBI)		+= ubi/
 | 
					obj-$(CONFIG_MTD_UBI)		+= ubi/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,33 +169,33 @@ config MTD_OTP
 | 
				
			||||||
	  in the programming of OTP bits will waste them.
 | 
						  in the programming of OTP bits will waste them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_CFI_INTELEXT
 | 
					config MTD_CFI_INTELEXT
 | 
				
			||||||
	tristate "Support for Intel/Sharp flash chips"
 | 
						tristate "Support for CFI command set 0001 (Intel/Sharp chips)"
 | 
				
			||||||
	depends on MTD_GEN_PROBE
 | 
						depends on MTD_GEN_PROBE
 | 
				
			||||||
	select MTD_CFI_UTIL
 | 
						select MTD_CFI_UTIL
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  The Common Flash Interface defines a number of different command
 | 
						  The Common Flash Interface defines a number of different command
 | 
				
			||||||
	  sets which a CFI-compliant chip may claim to implement. This code
 | 
						  sets which a CFI-compliant chip may claim to implement. This code
 | 
				
			||||||
	  provides support for one of those command sets, used on Intel
 | 
						  provides support for command set 0001, used on Intel StrataFlash
 | 
				
			||||||
	  StrataFlash and other parts.
 | 
						  and other parts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_CFI_AMDSTD
 | 
					config MTD_CFI_AMDSTD
 | 
				
			||||||
	tristate "Support for AMD/Fujitsu/Spansion flash chips"
 | 
						tristate "Support for CFI command set 0002 (AMD/Fujitsu/Spansion chips)"
 | 
				
			||||||
	depends on MTD_GEN_PROBE
 | 
						depends on MTD_GEN_PROBE
 | 
				
			||||||
	select MTD_CFI_UTIL
 | 
						select MTD_CFI_UTIL
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  The Common Flash Interface defines a number of different command
 | 
						  The Common Flash Interface defines a number of different command
 | 
				
			||||||
	  sets which a CFI-compliant chip may claim to implement. This code
 | 
						  sets which a CFI-compliant chip may claim to implement. This code
 | 
				
			||||||
	  provides support for one of those command sets, used on chips
 | 
						  provides support for command set 0002, used on chips including
 | 
				
			||||||
	  including the AMD Am29LV320.
 | 
						  the AMD Am29LV320.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_CFI_STAA
 | 
					config MTD_CFI_STAA
 | 
				
			||||||
	tristate "Support for ST (Advanced Architecture) flash chips"
 | 
						tristate "Support for CFI command set 0020 (ST (Advanced Architecture) chips)"
 | 
				
			||||||
	depends on MTD_GEN_PROBE
 | 
						depends on MTD_GEN_PROBE
 | 
				
			||||||
	select MTD_CFI_UTIL
 | 
						select MTD_CFI_UTIL
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  The Common Flash Interface defines a number of different command
 | 
						  The Common Flash Interface defines a number of different command
 | 
				
			||||||
	  sets which a CFI-compliant chip may claim to implement. This code
 | 
						  sets which a CFI-compliant chip may claim to implement. This code
 | 
				
			||||||
	  provides support for one of those command sets.
 | 
						  provides support for command set 0020.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_CFI_UTIL
 | 
					config MTD_CFI_UTIL
 | 
				
			||||||
	tristate
 | 
						tristate
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_M25P80
 | 
					config MTD_M25P80
 | 
				
			||||||
	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
 | 
						tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
 | 
				
			||||||
	depends on SPI_MASTER
 | 
						depends on SPI_MASTER && MTD_SPI_NOR
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  This enables access to most modern SPI flash chips, used for
 | 
						  This enables access to most modern SPI flash chips, used for
 | 
				
			||||||
	  program and data storage.   Series supported include Atmel AT26DF,
 | 
						  program and data storage.   Series supported include Atmel AT26DF,
 | 
				
			||||||
| 
						 | 
					@ -212,7 +212,7 @@ config MTD_DOCG3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_ST_SPI_FSM
 | 
					config MTD_ST_SPI_FSM
 | 
				
			||||||
	tristate "ST Microelectronics SPI FSM Serial Flash Controller"
 | 
						tristate "ST Microelectronics SPI FSM Serial Flash Controller"
 | 
				
			||||||
	depends on ARM || SH
 | 
						depends on ARCH_STI
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  This provides an MTD device driver for the ST Microelectronics
 | 
						  This provides an MTD device driver for the ST Microelectronics
 | 
				
			||||||
	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
 | 
						  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,6 +213,28 @@ static void elm_load_syndrome(struct elm_info *info,
 | 
				
			||||||
				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
 | 
									val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
 | 
				
			||||||
				elm_write_reg(info, offset, val);
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
								case BCH16_ECC:
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[22]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[18]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[14]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[10]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[6]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[2]);
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									offset += 4;
 | 
				
			||||||
 | 
									val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16;
 | 
				
			||||||
 | 
									elm_write_reg(info, offset, val);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				pr_err("invalid config bch_type\n");
 | 
									pr_err("invalid config bch_type\n");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -418,6 +440,7 @@ static int elm_remove(struct platform_device *pdev)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * elm_context_save
 | 
					 * elm_context_save
 | 
				
			||||||
 * saves ELM configurations to preserve them across Hardware powered-down
 | 
					 * saves ELM configurations to preserve them across Hardware powered-down
 | 
				
			||||||
| 
						 | 
					@ -435,6 +458,13 @@ static int elm_context_save(struct elm_info *info)
 | 
				
			||||||
	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 | 
						for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 | 
				
			||||||
		offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 | 
							offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 | 
				
			||||||
		switch (bch_type) {
 | 
							switch (bch_type) {
 | 
				
			||||||
 | 
							case BCH16_ECC:
 | 
				
			||||||
 | 
								regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
 | 
				
			||||||
 | 
										ELM_SYNDROME_FRAGMENT_6 + offset);
 | 
				
			||||||
 | 
								regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,
 | 
				
			||||||
 | 
										ELM_SYNDROME_FRAGMENT_5 + offset);
 | 
				
			||||||
 | 
								regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
 | 
				
			||||||
 | 
										ELM_SYNDROME_FRAGMENT_4 + offset);
 | 
				
			||||||
		case BCH8_ECC:
 | 
							case BCH8_ECC:
 | 
				
			||||||
			regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
 | 
								regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
 | 
				
			||||||
					ELM_SYNDROME_FRAGMENT_3 + offset);
 | 
										ELM_SYNDROME_FRAGMENT_3 + offset);
 | 
				
			||||||
| 
						 | 
					@ -473,6 +503,13 @@ static int elm_context_restore(struct elm_info *info)
 | 
				
			||||||
	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 | 
						for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 | 
				
			||||||
		offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 | 
							offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 | 
				
			||||||
		switch (bch_type) {
 | 
							switch (bch_type) {
 | 
				
			||||||
 | 
							case BCH16_ECC:
 | 
				
			||||||
 | 
								elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
 | 
				
			||||||
 | 
										regs->elm_syndrome_fragment_6[i]);
 | 
				
			||||||
 | 
								elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,
 | 
				
			||||||
 | 
										regs->elm_syndrome_fragment_5[i]);
 | 
				
			||||||
 | 
								elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
 | 
				
			||||||
 | 
										regs->elm_syndrome_fragment_4[i]);
 | 
				
			||||||
		case BCH8_ECC:
 | 
							case BCH8_ECC:
 | 
				
			||||||
			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
 | 
								elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
 | 
				
			||||||
					regs->elm_syndrome_fragment_3[i]);
 | 
										regs->elm_syndrome_fragment_3[i]);
 | 
				
			||||||
| 
						 | 
					@ -509,6 +546,7 @@ static int elm_resume(struct device *dev)
 | 
				
			||||||
	elm_context_restore(info);
 | 
						elm_context_restore(info);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
 | 
					static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -13,43 +13,23 @@
 | 
				
			||||||
#define _MTD_SERIAL_FLASH_CMDS_H
 | 
					#define _MTD_SERIAL_FLASH_CMDS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Generic Flash Commands/OPCODEs */
 | 
					/* Generic Flash Commands/OPCODEs */
 | 
				
			||||||
#define FLASH_CMD_WREN		0x06
 | 
					#define SPINOR_OP_RDSR2		0x35
 | 
				
			||||||
#define FLASH_CMD_WRDI		0x04
 | 
					#define SPINOR_OP_WRVCR		0x81
 | 
				
			||||||
#define FLASH_CMD_RDID		0x9f
 | 
					#define SPINOR_OP_RDVCR		0x85
 | 
				
			||||||
#define FLASH_CMD_RDSR		0x05
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDSR2		0x35
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRSR		0x01
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE_4K		0x20
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE_32K	0x52
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE		0xd8
 | 
					 | 
				
			||||||
#define FLASH_CMD_CHIPERASE	0xc7
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRVCR		0x81
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDVCR		0x85
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
 | 
					/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
 | 
				
			||||||
#define FLASH_CMD_READ		0x03	/* READ */
 | 
					#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
 | 
				
			||||||
#define FLASH_CMD_READ_FAST	0x0b	/* FAST READ */
 | 
					#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
 | 
				
			||||||
#define FLASH_CMD_READ_1_1_2	0x3b	/* DUAL OUTPUT READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_2_2	0xbb	/* DUAL I/O READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_1_4	0x6b	/* QUAD OUTPUT READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_4_4	0xeb	/* QUAD I/O READ */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FLASH_CMD_WRITE		0x02	/* PAGE PROGRAM */
 | 
					#define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
 | 
				
			||||||
#define FLASH_CMD_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 | 
					#define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 | 
				
			||||||
#define FLASH_CMD_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 | 
					#define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 | 
				
			||||||
#define FLASH_CMD_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 | 
					#define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 | 
				
			||||||
#define FLASH_CMD_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 | 
					#define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 | 
				
			||||||
 | 
					 | 
				
			||||||
#define FLASH_CMD_EN4B_ADDR	0xb7	/* Enter 4-byte address mode */
 | 
					 | 
				
			||||||
#define FLASH_CMD_EX4B_ADDR	0xe9	/* Exit 4-byte address mode */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* READ commands with 32-bit addressing */
 | 
					/* READ commands with 32-bit addressing */
 | 
				
			||||||
#define FLASH_CMD_READ4		0x13
 | 
					#define SPINOR_OP_READ4_1_2_2	0xbc
 | 
				
			||||||
#define FLASH_CMD_READ4_FAST	0x0c
 | 
					#define SPINOR_OP_READ4_1_4_4	0xec
 | 
				
			||||||
#define FLASH_CMD_READ4_1_1_2	0x3c
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_2_2	0xbc
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_1_4	0x6c
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_4_4	0xec
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Configuration flags */
 | 
					/* Configuration flags */
 | 
				
			||||||
#define FLASH_FLAG_SINGLE	0x000000ff
 | 
					#define FLASH_FLAG_SINGLE	0x000000ff
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -280,14 +280,11 @@ __setup("slram=", mtd_slram_setup);
 | 
				
			||||||
static int __init init_slram(void)
 | 
					static int __init init_slram(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *devname;
 | 
						char *devname;
 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MODULE
 | 
					#ifndef MODULE
 | 
				
			||||||
	char *devstart;
 | 
						char *devstart;
 | 
				
			||||||
	char *devlength;
 | 
						char *devlength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	i = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!map) {
 | 
						if (!map) {
 | 
				
			||||||
		E("slram: not enough parameters.\n");
 | 
							E("slram: not enough parameters.\n");
 | 
				
			||||||
		return(-EINVAL);
 | 
							return(-EINVAL);
 | 
				
			||||||
| 
						 | 
					@ -314,6 +311,7 @@ static int __init init_slram(void)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	int count;
 | 
						int count;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
 | 
						for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
 | 
				
			||||||
			count++) {
 | 
								count++) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@
 | 
				
			||||||
#include <linux/mfd/syscon.h>
 | 
					#include <linux/mfd/syscon.h>
 | 
				
			||||||
#include <linux/mtd/mtd.h>
 | 
					#include <linux/mtd/mtd.h>
 | 
				
			||||||
#include <linux/mtd/partitions.h>
 | 
					#include <linux/mtd/partitions.h>
 | 
				
			||||||
 | 
					#include <linux/mtd/spi-nor.h>
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <linux/delay.h>
 | 
					#include <linux/delay.h>
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
| 
						 | 
					@ -201,44 +202,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define STFSM_MAX_WAIT_SEQ_MS  1000     /* FSM execution time */
 | 
					#define STFSM_MAX_WAIT_SEQ_MS  1000     /* FSM execution time */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Flash Commands */
 | 
					 | 
				
			||||||
#define FLASH_CMD_WREN         0x06
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRDI         0x04
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDID         0x9f
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDSR         0x05
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDSR2                0x35
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRSR         0x01
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE_4K                0x20
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE_32K       0x52
 | 
					 | 
				
			||||||
#define FLASH_CMD_SE           0xd8
 | 
					 | 
				
			||||||
#define FLASH_CMD_CHIPERASE    0xc7
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRVCR                0x81
 | 
					 | 
				
			||||||
#define FLASH_CMD_RDVCR                0x85
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ         0x03    /* READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_FAST    0x0b    /* FAST READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_1_2   0x3b    /* DUAL OUTPUT READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_2_2   0xbb    /* DUAL I/O READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_1_4   0x6b    /* QUAD OUTPUT READ */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ_1_4_4   0xeb    /* QUAD I/O READ */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRITE                0x02    /* PAGE PROGRAM */
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
 | 
					 | 
				
			||||||
#define FLASH_CMD_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define FLASH_CMD_EN4B_ADDR    0xb7    /* Enter 4-byte address mode */
 | 
					 | 
				
			||||||
#define FLASH_CMD_EX4B_ADDR    0xe9    /* Exit 4-byte address mode */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4                0x13
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_FAST   0x0c
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_1_2  0x3c
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_2_2  0xbc
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_1_4  0x6c
 | 
					 | 
				
			||||||
#define FLASH_CMD_READ4_1_4_4  0xec
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* S25FLxxxS commands */
 | 
					/* S25FLxxxS commands */
 | 
				
			||||||
#define S25FL_CMD_WRITE4_1_1_4 0x34
 | 
					#define S25FL_CMD_WRITE4_1_1_4 0x34
 | 
				
			||||||
#define S25FL_CMD_SE4          0xdc
 | 
					#define S25FL_CMD_SE4          0xdc
 | 
				
			||||||
| 
						 | 
					@ -246,7 +209,7 @@
 | 
				
			||||||
#define S25FL_CMD_DYBWR                0xe1
 | 
					#define S25FL_CMD_DYBWR                0xe1
 | 
				
			||||||
#define S25FL_CMD_DYBRD                0xe0
 | 
					#define S25FL_CMD_DYBRD                0xe0
 | 
				
			||||||
#define S25FL_CMD_WRITE4       0x12    /* Note, opcode clashes with
 | 
					#define S25FL_CMD_WRITE4       0x12    /* Note, opcode clashes with
 | 
				
			||||||
					* 'FLASH_CMD_WRITE_1_4_4'
 | 
										* 'SPINOR_OP_WRITE_1_4_4'
 | 
				
			||||||
					* as found on N25Qxxx devices! */
 | 
										* as found on N25Qxxx devices! */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Status register */
 | 
					/* Status register */
 | 
				
			||||||
| 
						 | 
					@ -261,6 +224,12 @@
 | 
				
			||||||
#define S25FL_STATUS_E_ERR     0x20
 | 
					#define S25FL_STATUS_E_ERR     0x20
 | 
				
			||||||
#define S25FL_STATUS_P_ERR     0x40
 | 
					#define S25FL_STATUS_P_ERR     0x40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define N25Q_CMD_WRVCR         0x81
 | 
				
			||||||
 | 
					#define N25Q_CMD_RDVCR         0x85
 | 
				
			||||||
 | 
					#define N25Q_CMD_RDVECR        0x65
 | 
				
			||||||
 | 
					#define N25Q_CMD_RDNVCR        0xb5
 | 
				
			||||||
 | 
					#define N25Q_CMD_WRNVCR        0xb1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FLASH_PAGESIZE         256			/* In Bytes    */
 | 
					#define FLASH_PAGESIZE         256			/* In Bytes    */
 | 
				
			||||||
#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)	/* In uint32_t */
 | 
					#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)	/* In uint32_t */
 | 
				
			||||||
#define FLASH_MAX_BUSY_WAIT    (300 * HZ)	/* Maximum 'CHIPERASE' time */
 | 
					#define FLASH_MAX_BUSY_WAIT    (300 * HZ)	/* Maximum 'CHIPERASE' time */
 | 
				
			||||||
| 
						 | 
					@ -270,7 +239,6 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define CFG_READ_TOGGLE_32BIT_ADDR     0x00000001
 | 
					#define CFG_READ_TOGGLE_32BIT_ADDR     0x00000001
 | 
				
			||||||
#define CFG_WRITE_TOGGLE_32BIT_ADDR    0x00000002
 | 
					#define CFG_WRITE_TOGGLE_32BIT_ADDR    0x00000002
 | 
				
			||||||
#define CFG_WRITE_EX_32BIT_ADDR_DELAY  0x00000004
 | 
					 | 
				
			||||||
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
 | 
					#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
 | 
				
			||||||
#define CFG_S25FL_CHECK_ERROR_FLAGS    0x00000010
 | 
					#define CFG_S25FL_CHECK_ERROR_FLAGS    0x00000010
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,7 +297,7 @@ struct flash_info {
 | 
				
			||||||
	u32             jedec_id;
 | 
						u32             jedec_id;
 | 
				
			||||||
	u16             ext_id;
 | 
						u16             ext_id;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * The size listed here is what works with FLASH_CMD_SE, which isn't
 | 
						 * The size listed here is what works with SPINOR_OP_SE, which isn't
 | 
				
			||||||
	 * necessarily called a "sector" by the vendor.
 | 
						 * necessarily called a "sector" by the vendor.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	unsigned        sector_size;
 | 
						unsigned        sector_size;
 | 
				
			||||||
| 
						 | 
					@ -369,17 +337,26 @@ static struct flash_info flash_types[] = {
 | 
				
			||||||
	{ "m25px32", 0x207116, 0,  64 * 1024,  64, M25PX_FLAG, 75, NULL },
 | 
						{ "m25px32", 0x207116, 0,  64 * 1024,  64, M25PX_FLAG, 75, NULL },
 | 
				
			||||||
	{ "m25px64", 0x207117, 0,  64 * 1024, 128, M25PX_FLAG, 75, NULL },
 | 
						{ "m25px64", 0x207117, 0,  64 * 1024, 128, M25PX_FLAG, 75, NULL },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Macronix MX25xxx
 | 
				
			||||||
 | 
						 *     - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices
 | 
				
			||||||
 | 
						 *       where operating frequency must be reduced.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
#define MX25_FLAG (FLASH_FLAG_READ_WRITE       |	\
 | 
					#define MX25_FLAG (FLASH_FLAG_READ_WRITE       |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_FAST         |	\
 | 
							   FLASH_FLAG_READ_FAST         |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_1_1_2        |	\
 | 
							   FLASH_FLAG_READ_1_1_2        |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_1_2_2        |	\
 | 
							   FLASH_FLAG_READ_1_2_2        |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_1_1_4        |	\
 | 
							   FLASH_FLAG_READ_1_1_4        |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_1_4_4        |	\
 | 
					 | 
				
			||||||
		   FLASH_FLAG_SE_4K             |	\
 | 
							   FLASH_FLAG_SE_4K             |	\
 | 
				
			||||||
		   FLASH_FLAG_SE_32K)
 | 
							   FLASH_FLAG_SE_32K)
 | 
				
			||||||
 | 
						{ "mx25l3255e",  0xc29e16, 0, 64 * 1024, 64,
 | 
				
			||||||
 | 
						  (MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86,
 | 
				
			||||||
 | 
						  stfsm_mx25_config},
 | 
				
			||||||
	{ "mx25l25635e", 0xc22019, 0, 64*1024, 512,
 | 
						{ "mx25l25635e", 0xc22019, 0, 64*1024, 512,
 | 
				
			||||||
	  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
 | 
						  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
 | 
				
			||||||
	  stfsm_mx25_config },
 | 
						  stfsm_mx25_config },
 | 
				
			||||||
 | 
						{ "mx25l25655e", 0xc22619, 0, 64*1024, 512,
 | 
				
			||||||
 | 
						  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
 | 
				
			||||||
 | 
						  stfsm_mx25_config},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define N25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\
 | 
					#define N25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\
 | 
				
			||||||
		   FLASH_FLAG_READ_FAST         |	\
 | 
							   FLASH_FLAG_READ_FAST         |	\
 | 
				
			||||||
| 
						 | 
					@ -407,6 +384,8 @@ static struct flash_info flash_types[] = {
 | 
				
			||||||
			FLASH_FLAG_READ_1_4_4   |	\
 | 
								FLASH_FLAG_READ_1_4_4   |	\
 | 
				
			||||||
			FLASH_FLAG_WRITE_1_1_4  |	\
 | 
								FLASH_FLAG_WRITE_1_1_4  |	\
 | 
				
			||||||
			FLASH_FLAG_READ_FAST)
 | 
								FLASH_FLAG_READ_FAST)
 | 
				
			||||||
 | 
						{ "s25fl032p",  0x010215, 0x4d00,  64 * 1024,  64, S25FLXXXP_FLAG, 80,
 | 
				
			||||||
 | 
						  stfsm_s25fl_config},
 | 
				
			||||||
	{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, S25FLXXXP_FLAG, 80,
 | 
						{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, S25FLXXXP_FLAG, 80,
 | 
				
			||||||
	  stfsm_s25fl_config },
 | 
						  stfsm_s25fl_config },
 | 
				
			||||||
	{ "s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, S25FLXXXP_FLAG, 80,
 | 
						{ "s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, S25FLXXXP_FLAG, 80,
 | 
				
			||||||
| 
						 | 
					@ -473,22 +452,22 @@ static struct flash_info flash_types[] = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Default READ configurations, in order of preference */
 | 
					/* Default READ configurations, in order of preference */
 | 
				
			||||||
static struct seq_rw_config default_read_configs[] = {
 | 
					static struct seq_rw_config default_read_configs[] = {
 | 
				
			||||||
	{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4,	0, 4, 4, 0x00, 2, 4},
 | 
						{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 2, 4},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4,	0, 1, 4, 0x00, 4, 0},
 | 
						{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 4, 0},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2,	0, 2, 2, 0x00, 4, 0},
 | 
						{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 4, 0},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_FAST,	FLASH_CMD_READ_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ,		0, 1, 1, 0x00, 0, 0},
 | 
						{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,		0, 1, 1, 0x00, 0, 0},
 | 
				
			||||||
	{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
						{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Default WRITE configurations */
 | 
					/* Default WRITE configurations */
 | 
				
			||||||
static struct seq_rw_config default_write_configs[] = {
 | 
					static struct seq_rw_config default_write_configs[] = {
 | 
				
			||||||
	{FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
 | 
						{FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
 | 
				
			||||||
	{FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
 | 
						{FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
 | 
				
			||||||
	{FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
 | 
						{FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
 | 
				
			||||||
	{FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
 | 
						{FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
 | 
				
			||||||
	{FLASH_FLAG_READ_WRITE,  FLASH_CMD_WRITE,       1, 1, 1, 0x00, 0, 0},
 | 
						{FLASH_FLAG_READ_WRITE,  SPINOR_OP_WRITE,       1, 1, 1, 0x00, 0, 0},
 | 
				
			||||||
	{0x00,			 0,			0, 0, 0, 0x00, 0, 0},
 | 
						{0x00,			 0,			0, 0, 0, 0x00, 0, 0},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = {
 | 
				
			||||||
 * cycles.
 | 
					 * cycles.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct seq_rw_config n25q_read3_configs[] = {
 | 
					static struct seq_rw_config n25q_read3_configs[] = {
 | 
				
			||||||
	{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4,	0, 4, 4, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4,	0, 1, 4, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2,	0, 2, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_FAST,	FLASH_CMD_READ_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ,	        0, 1, 1, 0x00, 0, 0},
 | 
						{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,	        0, 1, 1, 0x00, 0, 0},
 | 
				
			||||||
	{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
						{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -526,12 +505,12 @@ static struct seq_rw_config n25q_read3_configs[] = {
 | 
				
			||||||
 *	- 'FAST' variants configured for 8 dummy cycles (see note above.)
 | 
					 *	- 'FAST' variants configured for 8 dummy cycles (see note above.)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct seq_rw_config n25q_read4_configs[] = {
 | 
					static struct seq_rw_config n25q_read4_configs[] = {
 | 
				
			||||||
	{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_FAST,	FLASH_CMD_READ4_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ4_FAST,	0, 1, 1, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4,	0, 1, 1, 0x00, 0, 0},
 | 
						{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,	0, 1, 1, 0x00, 0, 0},
 | 
				
			||||||
	{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
						{0x00,			0,			0, 0, 0, 0x00, 0, 0},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -544,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
			   SEQ_OPC_CYCLES(8) |
 | 
								   SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			   SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
 | 
								   SEQ_OPC_OPCODE(SPINOR_OP_EN4B) |
 | 
				
			||||||
			   SEQ_OPC_CSDEASSERT);
 | 
								   SEQ_OPC_CSDEASSERT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq->seq[0] = STFSM_INST_CMD1;
 | 
						seq->seq[0] = STFSM_INST_CMD1;
 | 
				
			||||||
| 
						 | 
					@ -572,12 +551,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
 | 
				
			||||||
 * entering a state that is incompatible with the SPIBoot Controller.
 | 
					 * entering a state that is incompatible with the SPIBoot Controller.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
 | 
					static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
 | 
				
			||||||
	{FLASH_FLAG_READ_1_4_4,  FLASH_CMD_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
 | 
						{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_4,  FLASH_CMD_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_2_2,  FLASH_CMD_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
 | 
						{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
 | 
				
			||||||
	{FLASH_FLAG_READ_1_1_2,  FLASH_CMD_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_FAST,   FLASH_CMD_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
 | 
						{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
 | 
				
			||||||
	{FLASH_FLAG_READ_WRITE,  FLASH_CMD_READ4,        0, 1, 1, 0x00, 0, 0},
 | 
						{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
 | 
				
			||||||
	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
 | 
						{0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -590,13 +569,13 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * [W25Qxxx] Configuration
 | 
					 * [W25Qxxx] Configuration
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define W25Q_STATUS_QE			(0x1 << 9)
 | 
					#define W25Q_STATUS_QE			(0x1 << 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct stfsm_seq stfsm_seq_read_jedec = {
 | 
					static struct stfsm_seq stfsm_seq_read_jedec = {
 | 
				
			||||||
	.data_size = TRANSFER_SIZE(8),
 | 
						.data_size = TRANSFER_SIZE(8),
 | 
				
			||||||
	.seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						.seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
		       SEQ_OPC_CYCLES(8) |
 | 
							       SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
 | 
							       SEQ_OPC_OPCODE(SPINOR_OP_RDID)),
 | 
				
			||||||
	.seq = {
 | 
						.seq = {
 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
							STFSM_INST_CMD1,
 | 
				
			||||||
		STFSM_INST_DATA_READ,
 | 
							STFSM_INST_DATA_READ,
 | 
				
			||||||
| 
						 | 
					@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = {
 | 
				
			||||||
	.data_size = TRANSFER_SIZE(4),
 | 
						.data_size = TRANSFER_SIZE(4),
 | 
				
			||||||
	.seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						.seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
		       SEQ_OPC_CYCLES(8) |
 | 
							       SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
 | 
							       SEQ_OPC_OPCODE(SPINOR_OP_RDSR)),
 | 
				
			||||||
	.seq = {
 | 
						.seq = {
 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
							STFSM_INST_CMD1,
 | 
				
			||||||
		STFSM_INST_DATA_READ,
 | 
							STFSM_INST_DATA_READ,
 | 
				
			||||||
| 
						 | 
					@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
 | 
				
			||||||
	/* 'addr_cfg' configured during initialisation */
 | 
						/* 'addr_cfg' configured during initialisation */
 | 
				
			||||||
	.seq_opc = {
 | 
						.seq_opc = {
 | 
				
			||||||
		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		 SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
 | 
							 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		 SEQ_OPC_OPCODE(FLASH_CMD_SE)),
 | 
							 SEQ_OPC_OPCODE(SPINOR_OP_SE)),
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	.seq = {
 | 
						.seq = {
 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
							STFSM_INST_CMD1,
 | 
				
			||||||
| 
						 | 
					@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
 | 
				
			||||||
static struct stfsm_seq stfsm_seq_erase_chip = {
 | 
					static struct stfsm_seq stfsm_seq_erase_chip = {
 | 
				
			||||||
	.seq_opc = {
 | 
						.seq_opc = {
 | 
				
			||||||
		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		 SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
 | 
							 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		 SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
 | 
							 SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	.seq = {
 | 
						.seq = {
 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
							STFSM_INST_CMD1,
 | 
				
			||||||
| 
						 | 
					@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct stfsm_seq stfsm_seq_write_status = {
 | 
					static struct stfsm_seq stfsm_seq_write_status = {
 | 
				
			||||||
	.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
						.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
 | 
							       SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
	.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
						.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
 | 
							       SEQ_OPC_OPCODE(SPINOR_OP_WRSR)),
 | 
				
			||||||
	.seq = {
 | 
					 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
					 | 
				
			||||||
		STFSM_INST_CMD2,
 | 
					 | 
				
			||||||
		STFSM_INST_STA_WR1,
 | 
					 | 
				
			||||||
		STFSM_INST_STOP,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	.seq_cfg = (SEQ_CFG_PADS_1 |
 | 
					 | 
				
			||||||
		    SEQ_CFG_READNOTWRITE |
 | 
					 | 
				
			||||||
		    SEQ_CFG_CSDEASSERT |
 | 
					 | 
				
			||||||
		    SEQ_CFG_STARTSEQ),
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct stfsm_seq stfsm_seq_wrvcr = {
 | 
					 | 
				
			||||||
	.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
					 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
 | 
					 | 
				
			||||||
	.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
					 | 
				
			||||||
		       SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
 | 
					 | 
				
			||||||
	.seq = {
 | 
						.seq = {
 | 
				
			||||||
		STFSM_INST_CMD1,
 | 
							STFSM_INST_CMD1,
 | 
				
			||||||
		STFSM_INST_CMD2,
 | 
							STFSM_INST_CMD2,
 | 
				
			||||||
| 
						 | 
					@ -704,9 +666,9 @@ static struct stfsm_seq stfsm_seq_wrvcr = {
 | 
				
			||||||
static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
 | 
					static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
						seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			   SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
 | 
								   SEQ_OPC_OPCODE(SPINOR_OP_EN4B));
 | 
				
			||||||
	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
						seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			   SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
 | 
								   SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
 | 
				
			||||||
			   SEQ_OPC_CSDEASSERT);
 | 
								   SEQ_OPC_CSDEASSERT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq->seq[0] = STFSM_INST_CMD2;
 | 
						seq->seq[0] = STFSM_INST_CMD2;
 | 
				
			||||||
| 
						 | 
					@ -793,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
 | 
						dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
 | 
						BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (remaining) {
 | 
						while (remaining) {
 | 
				
			||||||
		for (;;) {
 | 
							for (;;) {
 | 
				
			||||||
| 
						 | 
					@ -817,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
 | 
						dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
 | 
						BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
 | 
						writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -827,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
 | 
				
			||||||
static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
 | 
					static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
 | 
						struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
 | 
				
			||||||
	uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
 | 
						uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
			   SEQ_OPC_CYCLES(8) |
 | 
								   SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
| 
						 | 
					@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
 | 
				
			||||||
	/* Use RDRS1 */
 | 
						/* Use RDRS1 */
 | 
				
			||||||
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
			   SEQ_OPC_CYCLES(8) |
 | 
								   SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			   SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
 | 
								   SEQ_OPC_OPCODE(SPINOR_OP_RDSR));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Load read_status sequence */
 | 
						/* Load read_status sequence */
 | 
				
			||||||
	stfsm_load_seq(fsm, seq);
 | 
						stfsm_load_seq(fsm, seq);
 | 
				
			||||||
| 
						 | 
					@ -889,59 +851,56 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
 | 
					static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
 | 
				
			||||||
			   uint8_t *status)
 | 
								     uint8_t *data, int bytes)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
 | 
						struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
 | 
				
			||||||
	uint32_t tmp;
 | 
						uint32_t tmp;
 | 
				
			||||||
 | 
						uint8_t *t = (uint8_t *)&tmp;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_dbg(fsm->dev, "reading STA[%s]\n",
 | 
						dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n",
 | 
				
			||||||
		(cmd == FLASH_CMD_RDSR) ? "1" : "2");
 | 
							cmd, bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
 | 
						BUG_ON(bytes != 1 && bytes != 2);
 | 
				
			||||||
			   SEQ_OPC_CYCLES(8) |
 | 
					
 | 
				
			||||||
 | 
						seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			   SEQ_OPC_OPCODE(cmd)),
 | 
								   SEQ_OPC_OPCODE(cmd)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stfsm_load_seq(fsm, seq);
 | 
						stfsm_load_seq(fsm, seq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stfsm_read_fifo(fsm, &tmp, 4);
 | 
						stfsm_read_fifo(fsm, &tmp, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*status = (uint8_t)(tmp >> 24);
 | 
						for (i = 0; i < bytes; i++)
 | 
				
			||||||
 | 
							data[i] = t[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stfsm_wait_seq(fsm);
 | 
						stfsm_wait_seq(fsm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
 | 
					static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd,
 | 
				
			||||||
			       int sta_bytes)
 | 
								    uint16_t data, int bytes, int wait_busy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stfsm_seq *seq = &stfsm_seq_write_status;
 | 
						struct stfsm_seq *seq = &stfsm_seq_write_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
 | 
						dev_dbg(fsm->dev,
 | 
				
			||||||
		(sta_bytes == 1) ? "1" : "1+2", status);
 | 
							"write 'status' register [0x%02x], %d byte(s), 0x%04x\n"
 | 
				
			||||||
 | 
							" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
 | 
						BUG_ON(bytes != 1 && bytes != 2);
 | 
				
			||||||
	seq->seq[2] = (sta_bytes == 1) ?
 | 
					
 | 
				
			||||||
		STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
 | 
						seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
 | 
								   SEQ_OPC_OPCODE(cmd));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT;
 | 
				
			||||||
 | 
						seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stfsm_load_seq(fsm, seq);
 | 
						stfsm_load_seq(fsm, seq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stfsm_wait_seq(fsm);
 | 
						stfsm_wait_seq(fsm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						if (wait_busy)
 | 
				
			||||||
};
 | 
							stfsm_wait_busy(fsm);
 | 
				
			||||||
 | 
					 | 
				
			||||||
static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct stfsm_seq *seq = &stfsm_seq_wrvcr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stfsm_load_seq(fsm, seq);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stfsm_wait_seq(fsm);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm,
 | 
				
			||||||
	if (cfg->write)
 | 
						if (cfg->write)
 | 
				
			||||||
		seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
 | 
							seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
				     SEQ_OPC_CYCLES(8) |
 | 
									     SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
				     SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
 | 
									     SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
 | 
				
			||||||
				     SEQ_OPC_CSDEASSERT);
 | 
									     SEQ_OPC_CSDEASSERT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Address configuration (24 or 32-bit addresses) */
 | 
						/* Address configuration (24 or 32-bit addresses) */
 | 
				
			||||||
| 
						 | 
					@ -1149,31 +1108,36 @@ static int stfsm_mx25_config(struct stfsm *fsm)
 | 
				
			||||||
		stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
 | 
							stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		soc_reset = stfsm_can_handle_soc_reset(fsm);
 | 
							soc_reset = stfsm_can_handle_soc_reset(fsm);
 | 
				
			||||||
		if (soc_reset || !fsm->booted_from_spi) {
 | 
							if (soc_reset || !fsm->booted_from_spi)
 | 
				
			||||||
			/* If we can handle SoC resets, we enable 32-bit address
 | 
								/* If we can handle SoC resets, we enable 32-bit address
 | 
				
			||||||
			 * mode pervasively */
 | 
								 * mode pervasively */
 | 
				
			||||||
			stfsm_enter_32bit_addr(fsm, 1);
 | 
								stfsm_enter_32bit_addr(fsm, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else {
 | 
							else
 | 
				
			||||||
			/* Else, enable/disable 32-bit addressing before/after
 | 
								/* Else, enable/disable 32-bit addressing before/after
 | 
				
			||||||
			 * each operation */
 | 
								 * each operation */
 | 
				
			||||||
			fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
 | 
								fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
 | 
				
			||||||
					      CFG_WRITE_TOGGLE_32BIT_ADDR |
 | 
										      CFG_WRITE_TOGGLE_32BIT_ADDR |
 | 
				
			||||||
					      CFG_ERASESEC_TOGGLE_32BIT_ADDR);
 | 
										      CFG_ERASESEC_TOGGLE_32BIT_ADDR);
 | 
				
			||||||
			/* It seems a small delay is required after exiting
 | 
					 | 
				
			||||||
			 * 32-bit mode following a write operation.  The issue
 | 
					 | 
				
			||||||
			 * is under investigation.
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* For QUAD mode, set 'QE' STATUS bit */
 | 
						/* Check status of 'QE' bit, update if required. */
 | 
				
			||||||
 | 
						stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1);
 | 
				
			||||||
	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
						data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
				
			||||||
	if (data_pads == 4) {
 | 
						if (data_pads == 4) {
 | 
				
			||||||
		stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
 | 
							if (!(sta & MX25_STATUS_QE)) {
 | 
				
			||||||
 | 
								/* Set 'QE' */
 | 
				
			||||||
			sta |= MX25_STATUS_QE;
 | 
								sta |= MX25_STATUS_QE;
 | 
				
			||||||
		stfsm_write_status(fsm, sta, 1);
 | 
					
 | 
				
			||||||
 | 
								stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (sta & MX25_STATUS_QE) {
 | 
				
			||||||
 | 
								/* Clear 'QE' */
 | 
				
			||||||
 | 
								sta &= ~MX25_STATUS_QE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
 | 
						vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
 | 
				
			||||||
	       N25Q_VCR_WRAP_CONT);
 | 
						       N25Q_VCR_WRAP_CONT);
 | 
				
			||||||
	stfsm_wrvcr(fsm, vcr);
 | 
						stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1297,7 +1261,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stfsm_seq seq = {
 | 
						struct stfsm_seq seq = {
 | 
				
			||||||
		.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			       SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
 | 
								       SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
 | 
				
			||||||
			       SEQ_OPC_CSDEASSERT),
 | 
								       SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
		.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
							.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			       SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
 | 
								       SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
 | 
				
			||||||
| 
						 | 
					@ -1337,7 +1301,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
 | 
				
			||||||
			       SEQ_OPC_CSDEASSERT),
 | 
								       SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
		.seq_opc[1] = (SEQ_OPC_PADS_1 |
 | 
							.seq_opc[1] = (SEQ_OPC_PADS_1 |
 | 
				
			||||||
			       SEQ_OPC_CYCLES(8) |
 | 
								       SEQ_OPC_CYCLES(8) |
 | 
				
			||||||
			       SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
 | 
								       SEQ_OPC_OPCODE(SPINOR_OP_WRDI) |
 | 
				
			||||||
			       SEQ_OPC_CSDEASSERT),
 | 
								       SEQ_OPC_CSDEASSERT),
 | 
				
			||||||
		.seq = {
 | 
							.seq = {
 | 
				
			||||||
			STFSM_INST_CMD1,
 | 
								STFSM_INST_CMD1,
 | 
				
			||||||
| 
						 | 
					@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
 | 
				
			||||||
	uint32_t offs;
 | 
						uint32_t offs;
 | 
				
			||||||
	uint16_t sta_wr;
 | 
						uint16_t sta_wr;
 | 
				
			||||||
	uint8_t sr1, cr1, dyb;
 | 
						uint8_t sr1, cr1, dyb;
 | 
				
			||||||
 | 
						int update_sr = 0;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & FLASH_FLAG_32BIT_ADDR) {
 | 
						if (flags & FLASH_FLAG_32BIT_ADDR) {
 | 
				
			||||||
| 
						 | 
					@ -1414,34 +1379,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check status of 'QE' bit */
 | 
						/* Check status of 'QE' bit, update if required. */
 | 
				
			||||||
 | 
						stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
 | 
				
			||||||
	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
						data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
				
			||||||
	stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
 | 
					 | 
				
			||||||
	if (data_pads == 4) {
 | 
						if (data_pads == 4) {
 | 
				
			||||||
		if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
 | 
							if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
 | 
				
			||||||
			/* Set 'QE' */
 | 
								/* Set 'QE' */
 | 
				
			||||||
			cr1 |= STFSM_S25FL_CONFIG_QE;
 | 
								cr1 |= STFSM_S25FL_CONFIG_QE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
 | 
								update_sr = 1;
 | 
				
			||||||
			sta_wr = ((uint16_t)cr1  << 8) | sr1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			stfsm_write_status(fsm, sta_wr, 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			stfsm_wait_busy(fsm);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
 | 
							if (cr1 & STFSM_S25FL_CONFIG_QE) {
 | 
				
			||||||
			/* Clear 'QE' */
 | 
								/* Clear 'QE' */
 | 
				
			||||||
			cr1 &= ~STFSM_S25FL_CONFIG_QE;
 | 
								cr1 &= ~STFSM_S25FL_CONFIG_QE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
 | 
								update_sr = 1;
 | 
				
			||||||
			sta_wr = ((uint16_t)cr1  << 8) | sr1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			stfsm_write_status(fsm, sta_wr, 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			stfsm_wait_busy(fsm);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (update_sr) {
 | 
				
			||||||
 | 
							stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
 | 
				
			||||||
 | 
							sta_wr = ((uint16_t)cr1  << 8) | sr1;
 | 
				
			||||||
 | 
							stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -1456,27 +1415,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
 | 
				
			||||||
static int stfsm_w25q_config(struct stfsm *fsm)
 | 
					static int stfsm_w25q_config(struct stfsm *fsm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32_t data_pads;
 | 
						uint32_t data_pads;
 | 
				
			||||||
	uint16_t sta_wr;
 | 
						uint8_t sr1, sr2;
 | 
				
			||||||
	uint8_t sta1, sta2;
 | 
						uint16_t sr_wr;
 | 
				
			||||||
 | 
						int update_sr = 0;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = stfsm_prepare_rwe_seqs_default(fsm);
 | 
						ret = stfsm_prepare_rwe_seqs_default(fsm);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If using QUAD mode, set QE STATUS bit */
 | 
						/* Check status of 'QE' bit, update if required. */
 | 
				
			||||||
 | 
						stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
 | 
				
			||||||
	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
						data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
 | 
				
			||||||
	if (data_pads == 4) {
 | 
						if (data_pads == 4) {
 | 
				
			||||||
		stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
 | 
							if (!(sr2 & W25Q_STATUS_QE)) {
 | 
				
			||||||
		stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
 | 
								/* Set 'QE' */
 | 
				
			||||||
 | 
								sr2 |= W25Q_STATUS_QE;
 | 
				
			||||||
		sta_wr = ((uint16_t)sta2 << 8) | sta1;
 | 
								update_sr = 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		sta_wr |= W25Q_STATUS_QE;
 | 
						} else {
 | 
				
			||||||
 | 
							if (sr2 & W25Q_STATUS_QE) {
 | 
				
			||||||
		stfsm_write_status(fsm, sta_wr, 2);
 | 
								/* Clear 'QE' */
 | 
				
			||||||
 | 
								sr2 &= ~W25Q_STATUS_QE;
 | 
				
			||||||
		stfsm_wait_busy(fsm);
 | 
								update_sr = 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (update_sr) {
 | 
				
			||||||
 | 
							/* Write status register */
 | 
				
			||||||
 | 
							stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
 | 
				
			||||||
 | 
							sr_wr = ((uint16_t)sr2 << 8) | sr1;
 | 
				
			||||||
 | 
							stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -1506,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
 | 
				
			||||||
	read_mask = (data_pads << 2) - 1;
 | 
						read_mask = (data_pads << 2) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Handle non-aligned buf */
 | 
						/* Handle non-aligned buf */
 | 
				
			||||||
	p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
 | 
						p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Handle non-aligned size */
 | 
						/* Handle non-aligned size */
 | 
				
			||||||
	size_ub = (size + read_mask) & ~read_mask;
 | 
						size_ub = (size + read_mask) & ~read_mask;
 | 
				
			||||||
| 
						 | 
					@ -1528,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Handle non-aligned buf */
 | 
						/* Handle non-aligned buf */
 | 
				
			||||||
	if ((uint32_t)buf & 0x3)
 | 
						if ((uintptr_t)buf & 0x3)
 | 
				
			||||||
		memcpy(buf, page_buf, size);
 | 
							memcpy(buf, page_buf, size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Wait for sequence to finish */
 | 
						/* Wait for sequence to finish */
 | 
				
			||||||
| 
						 | 
					@ -1570,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
 | 
				
			||||||
	write_mask = (data_pads << 2) - 1;
 | 
						write_mask = (data_pads << 2) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Handle non-aligned buf */
 | 
						/* Handle non-aligned buf */
 | 
				
			||||||
	if ((uint32_t)buf & 0x3) {
 | 
						if ((uintptr_t)buf & 0x3) {
 | 
				
			||||||
		memcpy(page_buf, buf, size);
 | 
							memcpy(page_buf, buf, size);
 | 
				
			||||||
		p = (uint8_t *)page_buf;
 | 
							p = (uint8_t *)page_buf;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
 | 
				
			||||||
		stfsm_s25fl_clear_status_reg(fsm);
 | 
							stfsm_s25fl_clear_status_reg(fsm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Exit 32-bit address mode, if required */
 | 
						/* Exit 32-bit address mode, if required */
 | 
				
			||||||
	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
 | 
						if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
 | 
				
			||||||
		stfsm_enter_32bit_addr(fsm, 0);
 | 
							stfsm_enter_32bit_addr(fsm, 0);
 | 
				
			||||||
		if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
 | 
					 | 
				
			||||||
			udelay(1);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (len) {
 | 
						while (len) {
 | 
				
			||||||
		/* Write up to page boundary */
 | 
							/* Write up to page boundary */
 | 
				
			||||||
		bytes = min(FLASH_PAGESIZE - page_offs, len);
 | 
							bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = stfsm_write(fsm, b, bytes, to);
 | 
							ret = stfsm_write(fsm, b, bytes, to);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
| 
						 | 
					@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm)
 | 
				
			||||||
	       fsm->base + SPI_CONFIGDATA);
 | 
						       fsm->base + SPI_CONFIGDATA);
 | 
				
			||||||
	writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
 | 
						writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Set the FSM 'WAIT' delay to the minimum workable value.  Note, for
 | 
				
			||||||
 | 
						 * our purposes, the WAIT instruction is used purely to achieve
 | 
				
			||||||
 | 
						 * "sequence validity" rather than actually implement a delay.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Clear FIFO, just in case */
 | 
						/* Clear FIFO, just in case */
 | 
				
			||||||
	stfsm_clear_fifo(fsm);
 | 
						stfsm_clear_fifo(fsm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2086,7 +2058,7 @@ static int stfsm_remove(struct platform_device *pdev)
 | 
				
			||||||
	return mtd_device_unregister(&fsm->mtd);
 | 
						return mtd_device_unregister(&fsm->mtd);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct of_device_id stfsm_match[] = {
 | 
					static const struct of_device_id stfsm_match[] = {
 | 
				
			||||||
	{ .compatible = "st,spi-fsm", },
 | 
						{ .compatible = "st,spi-fsm", },
 | 
				
			||||||
	{},
 | 
						{},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
menu "LPDDR flash memory drivers"
 | 
					menu "LPDDR & LPDDR2 PCM memory drivers"
 | 
				
			||||||
	depends on MTD!=n
 | 
						depends on MTD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_LPDDR
 | 
					config MTD_LPDDR
 | 
				
			||||||
	tristate "Support for LPDDR flash chips"
 | 
						tristate "Support for LPDDR flash chips"
 | 
				
			||||||
| 
						 | 
					@ -17,4 +17,13 @@ config MTD_QINFO_PROBE
 | 
				
			||||||
	    Window QINFO interface, permits software to be used for entire
 | 
						    Window QINFO interface, permits software to be used for entire
 | 
				
			||||||
	    families of devices. This serves similar purpose of CFI on legacy
 | 
						    families of devices. This serves similar purpose of CFI on legacy
 | 
				
			||||||
	    Flash products
 | 
						    Flash products
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config MTD_LPDDR2_NVM
 | 
				
			||||||
 | 
						# ARM dependency is only for writel_relaxed()
 | 
				
			||||||
 | 
						depends on MTD && ARM
 | 
				
			||||||
 | 
						tristate "Support for LPDDR2-NVM flash chips"
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  This option enables support of PCM memories with a LPDDR2-NVM
 | 
				
			||||||
 | 
						  (Low power double data rate 2) interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
endmenu
 | 
					endmenu
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,3 +4,4 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
obj-$(CONFIG_MTD_QINFO_PROBE)	+= qinfo_probe.o
 | 
					obj-$(CONFIG_MTD_QINFO_PROBE)	+= qinfo_probe.o
 | 
				
			||||||
obj-$(CONFIG_MTD_LPDDR)	+= lpddr_cmds.o
 | 
					obj-$(CONFIG_MTD_LPDDR)	+= lpddr_cmds.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_MTD_LPDDR2_NVM) += lpddr2_nvm.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										507
									
								
								drivers/mtd/lpddr/lpddr2_nvm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								drivers/mtd/lpddr/lpddr2_nvm.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,507 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LPDDR2-NVM MTD driver. This module provides read, write, erase, lock/unlock
 | 
				
			||||||
 | 
					 * support for LPDDR2-NVM PCM memories
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2012 Micron Technology, Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Vincenzo Aliberti <vincenzo.aliberti@gmail.com>
 | 
				
			||||||
 | 
					 * Domenico Manna <domenico.manna@gmail.com>
 | 
				
			||||||
 | 
					 * Many thanks to Andrea Vigilante for initial enabling
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
 | 
					 * as published by the Free Software Foundation; either version 2
 | 
				
			||||||
 | 
					 * of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/init.h>
 | 
				
			||||||
 | 
					#include <linux/io.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/mtd/map.h>
 | 
				
			||||||
 | 
					#include <linux/mtd/mtd.h>
 | 
				
			||||||
 | 
					#include <linux/mtd/partitions.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/platform_device.h>
 | 
				
			||||||
 | 
					#include <linux/ioport.h>
 | 
				
			||||||
 | 
					#include <linux/err.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Parameters */
 | 
				
			||||||
 | 
					#define ERASE_BLOCKSIZE			(0x00020000/2)	/* in Word */
 | 
				
			||||||
 | 
					#define WRITE_BUFFSIZE			(0x00000400/2)	/* in Word */
 | 
				
			||||||
 | 
					#define OW_BASE_ADDRESS			0x00000000	/* OW offset */
 | 
				
			||||||
 | 
					#define BUS_WIDTH			0x00000020	/* x32 devices */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* PFOW symbols address offset */
 | 
				
			||||||
 | 
					#define PFOW_QUERY_STRING_P		(0x0000/2)	/* in Word */
 | 
				
			||||||
 | 
					#define PFOW_QUERY_STRING_F		(0x0002/2)	/* in Word */
 | 
				
			||||||
 | 
					#define PFOW_QUERY_STRING_O		(0x0004/2)	/* in Word */
 | 
				
			||||||
 | 
					#define PFOW_QUERY_STRING_W		(0x0006/2)	/* in Word */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* OW registers address */
 | 
				
			||||||
 | 
					#define CMD_CODE_OFS			(0x0080/2)	/* in Word */
 | 
				
			||||||
 | 
					#define CMD_DATA_OFS			(0x0084/2)	/* in Word */
 | 
				
			||||||
 | 
					#define CMD_ADD_L_OFS			(0x0088/2)	/* in Word */
 | 
				
			||||||
 | 
					#define CMD_ADD_H_OFS			(0x008A/2)	/* in Word */
 | 
				
			||||||
 | 
					#define MPR_L_OFS			(0x0090/2)	/* in Word */
 | 
				
			||||||
 | 
					#define MPR_H_OFS			(0x0092/2)	/* in Word */
 | 
				
			||||||
 | 
					#define CMD_EXEC_OFS			(0x00C0/2)	/* in Word */
 | 
				
			||||||
 | 
					#define STATUS_REG_OFS			(0x00CC/2)	/* in Word */
 | 
				
			||||||
 | 
					#define PRG_BUFFER_OFS			(0x0010/2)	/* in Word */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Datamask */
 | 
				
			||||||
 | 
					#define MR_CFGMASK			0x8000
 | 
				
			||||||
 | 
					#define SR_OK_DATAMASK			0x0080
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* LPDDR2-NVM Commands */
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_LOCK			0x0061
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_UNLOCK		0x0062
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_SW_PROGRAM		0x0041
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_SW_OVERWRITE		0x0042
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_BUF_PROGRAM		0x00E9
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_BUF_OVERWRITE	0x00EA
 | 
				
			||||||
 | 
					#define LPDDR2_NVM_ERASE		0x0020
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* LPDDR2-NVM Registers offset */
 | 
				
			||||||
 | 
					#define LPDDR2_MODE_REG_DATA		0x0040
 | 
				
			||||||
 | 
					#define LPDDR2_MODE_REG_CFG		0x0050
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Internal Type Definitions
 | 
				
			||||||
 | 
					 * pcm_int_data contains memory controller details:
 | 
				
			||||||
 | 
					 * @reg_data : LPDDR2_MODE_REG_DATA register address after remapping
 | 
				
			||||||
 | 
					 * @reg_cfg  : LPDDR2_MODE_REG_CFG register address after remapping
 | 
				
			||||||
 | 
					 * &bus_width: memory bus-width (eg: x16 2 Bytes, x32 4 Bytes)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct pcm_int_data {
 | 
				
			||||||
 | 
						void __iomem *ctl_regs;
 | 
				
			||||||
 | 
						int bus_width;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DEFINE_MUTEX(lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Build a map_word starting from an u_long
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline map_word build_map_word(u_long myword)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						map_word val = { {0} };
 | 
				
			||||||
 | 
						val.x[0] = myword;
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Build Mode Register Configuration DataMask based on device bus-width
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline u_int build_mr_cfgmask(u_int bus_width)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u_int val = MR_CFGMASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bus_width == 0x0004)		/* x32 device */
 | 
				
			||||||
 | 
							val = val << 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Build Status Register OK DataMask based on device bus-width
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline u_int build_sr_ok_datamask(u_int bus_width)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u_int val = SR_OK_DATAMASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bus_width == 0x0004)		/* x32 device */
 | 
				
			||||||
 | 
							val = (val << 16)+val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Evaluates Overlay Window Control Registers address
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline u_long ow_reg_add(struct map_info *map, u_long offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u_long val = 0;
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data = map->fldrv_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val = map->pfow_base + offset*pcm_data->bus_width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Enable lpddr2-nvm Overlay Window
 | 
				
			||||||
 | 
					 * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers
 | 
				
			||||||
 | 
					 * used by device commands as well as uservisible resources like Device Status
 | 
				
			||||||
 | 
					 * Register, Device ID, etc
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void ow_enable(struct map_info *map)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data = map->fldrv_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
 | 
				
			||||||
 | 
							pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
 | 
				
			||||||
 | 
						writel_relaxed(0x01, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Disable lpddr2-nvm Overlay Window
 | 
				
			||||||
 | 
					 * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers
 | 
				
			||||||
 | 
					 * used by device commands as well as uservisible resources like Device Status
 | 
				
			||||||
 | 
					 * Register, Device ID, etc
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void ow_disable(struct map_info *map)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data = map->fldrv_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
 | 
				
			||||||
 | 
							pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
 | 
				
			||||||
 | 
						writel_relaxed(0x02, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Execute lpddr2-nvm operations
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_do_op(struct map_info *map, u_long cmd_code,
 | 
				
			||||||
 | 
						u_long cmd_data, u_long cmd_add, u_long cmd_mpr, u_char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						map_word add_l = { {0} }, add_h = { {0} }, mpr_l = { {0} },
 | 
				
			||||||
 | 
							mpr_h = { {0} }, data_l = { {0} }, cmd = { {0} },
 | 
				
			||||||
 | 
							exec_cmd = { {0} }, sr;
 | 
				
			||||||
 | 
						map_word data_h = { {0} };	/* only for 2x x16 devices stacked */
 | 
				
			||||||
 | 
						u_long i, status_reg, prg_buff_ofs;
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data = map->fldrv_priv;
 | 
				
			||||||
 | 
						u_int sr_ok_datamask = build_sr_ok_datamask(pcm_data->bus_width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Builds low and high words for OW Control Registers */
 | 
				
			||||||
 | 
						add_l.x[0]	= cmd_add & 0x0000FFFF;
 | 
				
			||||||
 | 
						add_h.x[0]	= (cmd_add >> 16) & 0x0000FFFF;
 | 
				
			||||||
 | 
						mpr_l.x[0]	= cmd_mpr & 0x0000FFFF;
 | 
				
			||||||
 | 
						mpr_h.x[0]	= (cmd_mpr >> 16) & 0x0000FFFF;
 | 
				
			||||||
 | 
						cmd.x[0]	= cmd_code & 0x0000FFFF;
 | 
				
			||||||
 | 
						exec_cmd.x[0]	= 0x0001;
 | 
				
			||||||
 | 
						data_l.x[0]	= cmd_data & 0x0000FFFF;
 | 
				
			||||||
 | 
						data_h.x[0]	= (cmd_data >> 16) & 0x0000FFFF; /* only for 2x x16 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set Overlay Window Control Registers */
 | 
				
			||||||
 | 
						map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS));
 | 
				
			||||||
 | 
						map_write(map, data_l, ow_reg_add(map, CMD_DATA_OFS));
 | 
				
			||||||
 | 
						map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS));
 | 
				
			||||||
 | 
						map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS));
 | 
				
			||||||
 | 
						map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS));
 | 
				
			||||||
 | 
						map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS));
 | 
				
			||||||
 | 
						if (pcm_data->bus_width == 0x0004) {	/* 2x16 devices stacked */
 | 
				
			||||||
 | 
							map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS) + 2);
 | 
				
			||||||
 | 
							map_write(map, data_h, ow_reg_add(map, CMD_DATA_OFS) + 2);
 | 
				
			||||||
 | 
							map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS) + 2);
 | 
				
			||||||
 | 
							map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS) + 2);
 | 
				
			||||||
 | 
							map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS) + 2);
 | 
				
			||||||
 | 
							map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS) + 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Fill Program Buffer */
 | 
				
			||||||
 | 
						if ((cmd_code == LPDDR2_NVM_BUF_PROGRAM) ||
 | 
				
			||||||
 | 
							(cmd_code == LPDDR2_NVM_BUF_OVERWRITE)) {
 | 
				
			||||||
 | 
							prg_buff_ofs = (map_read(map,
 | 
				
			||||||
 | 
								ow_reg_add(map, PRG_BUFFER_OFS))).x[0];
 | 
				
			||||||
 | 
							for (i = 0; i < cmd_mpr; i++) {
 | 
				
			||||||
 | 
								map_write(map, build_map_word(buf[i]), map->pfow_base +
 | 
				
			||||||
 | 
								prg_buff_ofs + i);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Command Execute */
 | 
				
			||||||
 | 
						map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS));
 | 
				
			||||||
 | 
						if (pcm_data->bus_width == 0x0004)	/* 2x16 devices stacked */
 | 
				
			||||||
 | 
							map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS) + 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Status Register Check */
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							sr = map_read(map, ow_reg_add(map, STATUS_REG_OFS));
 | 
				
			||||||
 | 
							status_reg = sr.x[0];
 | 
				
			||||||
 | 
							if (pcm_data->bus_width == 0x0004) {/* 2x16 devices stacked */
 | 
				
			||||||
 | 
								sr = map_read(map, ow_reg_add(map,
 | 
				
			||||||
 | 
									STATUS_REG_OFS) + 2);
 | 
				
			||||||
 | 
								status_reg += sr.x[0] << 16;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} while ((status_reg & sr_ok_datamask) != sr_ok_datamask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (((status_reg & sr_ok_datamask) == sr_ok_datamask) ? 0 : -EIO);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Execute lpddr2-nvm operations @ block level
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_do_block_op(struct mtd_info *mtd, loff_t start_add,
 | 
				
			||||||
 | 
						uint64_t len, u_char block_op)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct map_info *map = mtd->priv;
 | 
				
			||||||
 | 
						u_long add, end_add;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ow_enable(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add = start_add;
 | 
				
			||||||
 | 
						end_add = add + len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							ret = lpddr2_nvm_do_op(map, block_op, 0x00, add, add, NULL);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							add += mtd->erasesize;
 | 
				
			||||||
 | 
						} while (add < end_add);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						ow_disable(map);
 | 
				
			||||||
 | 
						mutex_unlock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * verify presence of PFOW string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_pfow_present(struct map_info *map)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						map_word pfow_val[4];
 | 
				
			||||||
 | 
						unsigned int found = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ow_enable(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Load string from array */
 | 
				
			||||||
 | 
						pfow_val[0] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_P));
 | 
				
			||||||
 | 
						pfow_val[1] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_F));
 | 
				
			||||||
 | 
						pfow_val[2] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_O));
 | 
				
			||||||
 | 
						pfow_val[3] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_W));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Verify the string loaded vs expected */
 | 
				
			||||||
 | 
						if (!map_word_equal(map, build_map_word('P'), pfow_val[0]))
 | 
				
			||||||
 | 
							found = 0;
 | 
				
			||||||
 | 
						if (!map_word_equal(map, build_map_word('F'), pfow_val[1]))
 | 
				
			||||||
 | 
							found = 0;
 | 
				
			||||||
 | 
						if (!map_word_equal(map, build_map_word('O'), pfow_val[2]))
 | 
				
			||||||
 | 
							found = 0;
 | 
				
			||||||
 | 
						if (!map_word_equal(map, build_map_word('W'), pfow_val[3]))
 | 
				
			||||||
 | 
							found = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ow_disable(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_unlock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return found;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver read method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_read(struct mtd_info *mtd, loff_t start_add,
 | 
				
			||||||
 | 
									size_t len, size_t *retlen, u_char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct map_info *map = mtd->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*retlen = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map_copy_from(map, buf, start_add, *retlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_unlock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver write method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_write(struct mtd_info *mtd, loff_t start_add,
 | 
				
			||||||
 | 
									size_t len, size_t *retlen, const u_char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct map_info *map = mtd->priv;
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data = map->fldrv_priv;
 | 
				
			||||||
 | 
						u_long add, current_len, tot_len, target_len, my_data;
 | 
				
			||||||
 | 
						u_char *write_buf = (u_char *)buf;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ow_enable(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set start value for the variables */
 | 
				
			||||||
 | 
						add = start_add;
 | 
				
			||||||
 | 
						target_len = len;
 | 
				
			||||||
 | 
						tot_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (tot_len < target_len) {
 | 
				
			||||||
 | 
							if (!(IS_ALIGNED(add, mtd->writesize))) { /* do sw program */
 | 
				
			||||||
 | 
								my_data = write_buf[tot_len];
 | 
				
			||||||
 | 
								my_data += (write_buf[tot_len+1]) << 8;
 | 
				
			||||||
 | 
								if (pcm_data->bus_width == 0x0004) {/* 2x16 devices */
 | 
				
			||||||
 | 
									my_data += (write_buf[tot_len+2]) << 16;
 | 
				
			||||||
 | 
									my_data += (write_buf[tot_len+3]) << 24;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_SW_OVERWRITE,
 | 
				
			||||||
 | 
									my_data, add, 0x00, NULL);
 | 
				
			||||||
 | 
								if (ret)
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								add += pcm_data->bus_width;
 | 
				
			||||||
 | 
								tot_len += pcm_data->bus_width;
 | 
				
			||||||
 | 
							} else {		/* do buffer program */
 | 
				
			||||||
 | 
								current_len = min(target_len - tot_len,
 | 
				
			||||||
 | 
									(u_long) mtd->writesize);
 | 
				
			||||||
 | 
								ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_BUF_OVERWRITE,
 | 
				
			||||||
 | 
									0x00, add, current_len, write_buf + tot_len);
 | 
				
			||||||
 | 
								if (ret)
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								add += current_len;
 | 
				
			||||||
 | 
								tot_len += current_len;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						*retlen = tot_len;
 | 
				
			||||||
 | 
						ow_disable(map);
 | 
				
			||||||
 | 
						mutex_unlock(&lpdd2_nvm_mutex);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver erase method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
 | 
				
			||||||
 | 
							LPDDR2_NVM_ERASE);
 | 
				
			||||||
 | 
						if (!ret) {
 | 
				
			||||||
 | 
							instr->state = MTD_ERASE_DONE;
 | 
				
			||||||
 | 
							mtd_erase_callback(instr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver unlock method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_unlock(struct mtd_info *mtd, loff_t start_add,
 | 
				
			||||||
 | 
						uint64_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_UNLOCK);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver lock method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_lock(struct mtd_info *mtd, loff_t start_add,
 | 
				
			||||||
 | 
						uint64_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_LOCK);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver probe method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_probe(struct platform_device *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct map_info *map;
 | 
				
			||||||
 | 
						struct mtd_info *mtd;
 | 
				
			||||||
 | 
						struct resource *add_range;
 | 
				
			||||||
 | 
						struct resource *control_regs;
 | 
				
			||||||
 | 
						struct pcm_int_data *pcm_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Allocate memory control_regs data structures */
 | 
				
			||||||
 | 
						pcm_data = devm_kzalloc(&pdev->dev, sizeof(*pcm_data), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!pcm_data)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pcm_data->bus_width = BUS_WIDTH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Allocate memory for map_info & mtd_info data structures */
 | 
				
			||||||
 | 
						map = devm_kzalloc(&pdev->dev, sizeof(*map), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!map)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mtd = devm_kzalloc(&pdev->dev, sizeof(*mtd), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!mtd)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* lpddr2_nvm address range */
 | 
				
			||||||
 | 
						add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Populate map_info data structure */
 | 
				
			||||||
 | 
						*map = (struct map_info) {
 | 
				
			||||||
 | 
							.virt		= devm_ioremap_resource(&pdev->dev, add_range),
 | 
				
			||||||
 | 
							.name		= pdev->dev.init_name,
 | 
				
			||||||
 | 
							.phys		= add_range->start,
 | 
				
			||||||
 | 
							.size		= resource_size(add_range),
 | 
				
			||||||
 | 
							.bankwidth	= pcm_data->bus_width / 2,
 | 
				
			||||||
 | 
							.pfow_base	= OW_BASE_ADDRESS,
 | 
				
			||||||
 | 
							.fldrv_priv	= pcm_data,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						if (IS_ERR(map->virt))
 | 
				
			||||||
 | 
							return PTR_ERR(map->virt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						simple_map_init(map);	/* fill with default methods */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						control_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 | 
				
			||||||
 | 
						pcm_data->ctl_regs = devm_ioremap_resource(&pdev->dev, control_regs);
 | 
				
			||||||
 | 
						if (IS_ERR(pcm_data->ctl_regs))
 | 
				
			||||||
 | 
							return PTR_ERR(pcm_data->ctl_regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Populate mtd_info data structure */
 | 
				
			||||||
 | 
						*mtd = (struct mtd_info) {
 | 
				
			||||||
 | 
							.name		= pdev->dev.init_name,
 | 
				
			||||||
 | 
							.type		= MTD_RAM,
 | 
				
			||||||
 | 
							.priv		= map,
 | 
				
			||||||
 | 
							.size		= resource_size(add_range),
 | 
				
			||||||
 | 
							.erasesize	= ERASE_BLOCKSIZE * pcm_data->bus_width,
 | 
				
			||||||
 | 
							.writesize	= 1,
 | 
				
			||||||
 | 
							.writebufsize	= WRITE_BUFFSIZE * pcm_data->bus_width,
 | 
				
			||||||
 | 
							.flags		= (MTD_CAP_NVRAM | MTD_POWERUP_LOCK),
 | 
				
			||||||
 | 
							._read		= lpddr2_nvm_read,
 | 
				
			||||||
 | 
							._write		= lpddr2_nvm_write,
 | 
				
			||||||
 | 
							._erase		= lpddr2_nvm_erase,
 | 
				
			||||||
 | 
							._unlock	= lpddr2_nvm_unlock,
 | 
				
			||||||
 | 
							._lock		= lpddr2_nvm_lock,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Verify the presence of the device looking for PFOW string */
 | 
				
			||||||
 | 
						if (!lpddr2_nvm_pfow_present(map)) {
 | 
				
			||||||
 | 
							pr_err("device not recognized\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* Parse partitions and register the MTD device */
 | 
				
			||||||
 | 
						return mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * lpddr2_nvm driver remove method
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int lpddr2_nvm_remove(struct platform_device *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mtd_device_unregister(dev_get_drvdata(&pdev->dev));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Initialize platform_driver data structure for lpddr2_nvm */
 | 
				
			||||||
 | 
					static struct platform_driver lpddr2_nvm_drv = {
 | 
				
			||||||
 | 
						.driver		= {
 | 
				
			||||||
 | 
							.name	= "lpddr2_nvm",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						.probe		= lpddr2_nvm_probe,
 | 
				
			||||||
 | 
						.remove		= lpddr2_nvm_remove,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module_platform_driver(lpddr2_nvm_drv);
 | 
				
			||||||
 | 
					MODULE_LICENSE("GPL");
 | 
				
			||||||
 | 
					MODULE_AUTHOR("Vincenzo Aliberti <vincenzo.aliberti@gmail.com>");
 | 
				
			||||||
 | 
					MODULE_DESCRIPTION("MTD driver for LPDDR2-NVM PCM memories");
 | 
				
			||||||
| 
						 | 
					@ -108,7 +108,7 @@ config MTD_SUN_UFLASH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_SC520CDP
 | 
					config MTD_SC520CDP
 | 
				
			||||||
	tristate "CFI Flash device mapped on AMD SC520 CDP"
 | 
						tristate "CFI Flash device mapped on AMD SC520 CDP"
 | 
				
			||||||
	depends on X86 && MTD_CFI
 | 
						depends on (MELAN || COMPILE_TEST) && MTD_CFI
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  The SC520 CDP board has two banks of CFI-compliant chips and one
 | 
						  The SC520 CDP board has two banks of CFI-compliant chips and one
 | 
				
			||||||
	  Dual-in-line JEDEC chip. This 'mapping' driver supports that
 | 
						  Dual-in-line JEDEC chip. This 'mapping' driver supports that
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ config MTD_SC520CDP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MTD_NETSC520
 | 
					config MTD_NETSC520
 | 
				
			||||||
	tristate "CFI Flash device mapped on AMD NetSc520"
 | 
						tristate "CFI Flash device mapped on AMD NetSc520"
 | 
				
			||||||
	depends on X86 && MTD_CFI
 | 
						depends on (MELAN || COMPILE_TEST) && MTD_CFI
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  This enables access routines for the flash chips on the AMD NetSc520
 | 
						  This enables access routines for the flash chips on the AMD NetSc520
 | 
				
			||||||
	  demonstration board. If you have one of these boards and would like
 | 
						  demonstration board. If you have one of these boards and would like
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,7 +183,7 @@ static const struct sc520_par_table par_table[NUM_FLASH_BANKS] =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sc520cdp_setup_par(void)
 | 
					static void sc520cdp_setup_par(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	volatile unsigned long __iomem *mmcr;
 | 
						unsigned long __iomem *mmcr;
 | 
				
			||||||
	unsigned long mmcr_val;
 | 
						unsigned long mmcr_val;
 | 
				
			||||||
	int i, j;
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -203,11 +203,11 @@ static void sc520cdp_setup_par(void)
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	for(i = 0; i < NUM_FLASH_BANKS; i++) {		/* for each par_table entry  */
 | 
						for(i = 0; i < NUM_FLASH_BANKS; i++) {		/* for each par_table entry  */
 | 
				
			||||||
		for(j = 0; j < NUM_SC520_PAR; j++) {	/* for each PAR register     */
 | 
							for(j = 0; j < NUM_SC520_PAR; j++) {	/* for each PAR register     */
 | 
				
			||||||
			mmcr_val = mmcr[SC520_PAR(j)];
 | 
								mmcr_val = readl(&mmcr[SC520_PAR(j)]);
 | 
				
			||||||
			/* if target device field matches, reprogram the PAR */
 | 
								/* if target device field matches, reprogram the PAR */
 | 
				
			||||||
			if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
 | 
								if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				mmcr[SC520_PAR(j)] = par_table[i].new_par;
 | 
									writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,28 +33,6 @@ struct map_info soleng_flash_map = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
 | 
					static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MTD_SUPERH_RESERVE
 | 
					 | 
				
			||||||
static struct mtd_partition superh_se_partitions[] = {
 | 
					 | 
				
			||||||
	/* Reserved for boot code, read-only */
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.name = "flash_boot",
 | 
					 | 
				
			||||||
		.offset = 0x00000000,
 | 
					 | 
				
			||||||
		.size = CONFIG_MTD_SUPERH_RESERVE,
 | 
					 | 
				
			||||||
		.mask_flags = MTD_WRITEABLE,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	/* All else is writable (e.g. JFFS) */
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		.name = "Flash FS",
 | 
					 | 
				
			||||||
		.offset = MTDPART_OFS_NXTBLK,
 | 
					 | 
				
			||||||
		.size = MTDPART_SIZ_FULL,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
#define NUM_PARTITIONS ARRAY_SIZE(superh_se_partitions)
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define superh_se_partitions NULL
 | 
					 | 
				
			||||||
#define NUM_PARTITIONS 0
 | 
					 | 
				
			||||||
#endif /* CONFIG_MTD_SUPERH_RESERVE */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int __init init_soleng_maps(void)
 | 
					static int __init init_soleng_maps(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* First probe at offset 0 */
 | 
						/* First probe at offset 0 */
 | 
				
			||||||
| 
						 | 
					@ -92,8 +70,7 @@ static int __init init_soleng_maps(void)
 | 
				
			||||||
		mtd_device_register(eprom_mtd, NULL, 0);
 | 
							mtd_device_register(eprom_mtd, NULL, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mtd_device_parse_register(flash_mtd, probes, NULL,
 | 
						mtd_device_parse_register(flash_mtd, probes, NULL, NULL, 0);
 | 
				
			||||||
				  superh_se_partitions, NUM_PARTITIONS);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,6 +87,9 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
 | 
				
			||||||
	if (req->cmd_type != REQ_TYPE_FS)
 | 
						if (req->cmd_type != REQ_TYPE_FS)
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->cmd_flags & REQ_FLUSH)
 | 
				
			||||||
 | 
							return tr->flush(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
 | 
						if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
 | 
				
			||||||
	    get_capacity(req->rq_disk))
 | 
						    get_capacity(req->rq_disk))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
| 
						 | 
					@ -407,6 +410,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
 | 
				
			||||||
	if (!new->rq)
 | 
						if (!new->rq)
 | 
				
			||||||
		goto error3;
 | 
							goto error3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tr->flush)
 | 
				
			||||||
 | 
							blk_queue_flush(new->rq, REQ_FLUSH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	new->rq->queuedata = new;
 | 
						new->rq->queuedata = new;
 | 
				
			||||||
	blk_queue_logical_block_size(new->rq, tr->blksize);
 | 
						blk_queue_logical_block_size(new->rq, tr->blksize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -568,13 +568,18 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mtd_write_req req;
 | 
						struct mtd_write_req req;
 | 
				
			||||||
	struct mtd_oob_ops ops;
 | 
						struct mtd_oob_ops ops;
 | 
				
			||||||
	void __user *usr_data, *usr_oob;
 | 
						const void __user *usr_data, *usr_oob;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (copy_from_user(&req, argp, sizeof(req)) ||
 | 
						if (copy_from_user(&req, argp, sizeof(req)))
 | 
				
			||||||
			!access_ok(VERIFY_READ, req.usr_data, req.len) ||
 | 
					 | 
				
			||||||
			!access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
 | 
					 | 
				
			||||||
		return -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						usr_data = (const void __user *)(uintptr_t)req.usr_data;
 | 
				
			||||||
 | 
						usr_oob = (const void __user *)(uintptr_t)req.usr_oob;
 | 
				
			||||||
 | 
						if (!access_ok(VERIFY_READ, usr_data, req.len) ||
 | 
				
			||||||
 | 
						    !access_ok(VERIFY_READ, usr_oob, req.ooblen))
 | 
				
			||||||
 | 
							return -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!mtd->_write_oob)
 | 
						if (!mtd->_write_oob)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -583,10 +588,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
 | 
				
			||||||
	ops.ooblen = (size_t)req.ooblen;
 | 
						ops.ooblen = (size_t)req.ooblen;
 | 
				
			||||||
	ops.ooboffs = 0;
 | 
						ops.ooboffs = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usr_data = (void __user *)(uintptr_t)req.usr_data;
 | 
						if (usr_data) {
 | 
				
			||||||
	usr_oob = (void __user *)(uintptr_t)req.usr_oob;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req.usr_data) {
 | 
					 | 
				
			||||||
		ops.datbuf = memdup_user(usr_data, ops.len);
 | 
							ops.datbuf = memdup_user(usr_data, ops.len);
 | 
				
			||||||
		if (IS_ERR(ops.datbuf))
 | 
							if (IS_ERR(ops.datbuf))
 | 
				
			||||||
			return PTR_ERR(ops.datbuf);
 | 
								return PTR_ERR(ops.datbuf);
 | 
				
			||||||
| 
						 | 
					@ -594,7 +596,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
 | 
				
			||||||
		ops.datbuf = NULL;
 | 
							ops.datbuf = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (req.usr_oob) {
 | 
						if (usr_oob) {
 | 
				
			||||||
		ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
 | 
							ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
 | 
				
			||||||
		if (IS_ERR(ops.oobbuf)) {
 | 
							if (IS_ERR(ops.oobbuf)) {
 | 
				
			||||||
			kfree(ops.datbuf);
 | 
								kfree(ops.datbuf);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -679,9 +679,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
 | 
				
			||||||
	peripheral_free_list(bfin_nfc_pin_req);
 | 
						peripheral_free_list(bfin_nfc_pin_req);
 | 
				
			||||||
	bf5xx_nand_dma_remove(info);
 | 
						bf5xx_nand_dma_remove(info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* free the common resources */
 | 
					 | 
				
			||||||
	kfree(info);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -742,10 +739,10 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
 | 
				
			||||||
		return -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	info = kzalloc(sizeof(*info), GFP_KERNEL);
 | 
						info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 | 
				
			||||||
	if (info == NULL) {
 | 
						if (info == NULL) {
 | 
				
			||||||
		err = -ENOMEM;
 | 
							err = -ENOMEM;
 | 
				
			||||||
		goto out_err_kzalloc;
 | 
							goto out_err;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	platform_set_drvdata(pdev, info);
 | 
						platform_set_drvdata(pdev, info);
 | 
				
			||||||
| 
						 | 
					@ -790,7 +787,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
 | 
				
			||||||
	/* initialise the hardware */
 | 
						/* initialise the hardware */
 | 
				
			||||||
	err = bf5xx_nand_hw_init(info);
 | 
						err = bf5xx_nand_hw_init(info);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto out_err_hw_init;
 | 
							goto out_err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* setup hardware ECC data struct */
 | 
						/* setup hardware ECC data struct */
 | 
				
			||||||
	if (hardware_ecc) {
 | 
						if (hardware_ecc) {
 | 
				
			||||||
| 
						 | 
					@ -827,9 +824,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_err_nand_scan:
 | 
					out_err_nand_scan:
 | 
				
			||||||
	bf5xx_nand_dma_remove(info);
 | 
						bf5xx_nand_dma_remove(info);
 | 
				
			||||||
out_err_hw_init:
 | 
					out_err:
 | 
				
			||||||
	kfree(info);
 | 
					 | 
				
			||||||
out_err_kzalloc:
 | 
					 | 
				
			||||||
	peripheral_free_list(bfin_nfc_pin_req);
 | 
						peripheral_free_list(bfin_nfc_pin_req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1233,7 +1233,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
 | 
				
			||||||
	return status;
 | 
						return status;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void denali_erase(struct mtd_info *mtd, int page)
 | 
					static int denali_erase(struct mtd_info *mtd, int page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct denali_nand_info *denali = mtd_to_denali(mtd);
 | 
						struct denali_nand_info *denali = mtd_to_denali(mtd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1250,8 +1250,7 @@ static void denali_erase(struct mtd_info *mtd, int page)
 | 
				
			||||||
	irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
 | 
						irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
 | 
				
			||||||
					INTR_STATUS__ERASE_FAIL);
 | 
										INTR_STATUS__ERASE_FAIL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	denali->status = (irq_status & INTR_STATUS__ERASE_FAIL) ?
 | 
						return (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS;
 | 
				
			||||||
						NAND_STATUS_FAIL : PASS;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 | 
					static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 | 
				
			||||||
| 
						 | 
					@ -1584,7 +1583,7 @@ int denali_init(struct denali_nand_info *denali)
 | 
				
			||||||
	denali->nand.ecc.write_page_raw = denali_write_page_raw;
 | 
						denali->nand.ecc.write_page_raw = denali_write_page_raw;
 | 
				
			||||||
	denali->nand.ecc.read_oob = denali_read_oob;
 | 
						denali->nand.ecc.read_oob = denali_read_oob;
 | 
				
			||||||
	denali->nand.ecc.write_oob = denali_write_oob;
 | 
						denali->nand.ecc.write_oob = denali_write_oob;
 | 
				
			||||||
	denali->nand.erase_cmd = denali_erase;
 | 
						denali->nand.erase = denali_erase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nand_scan_tail(&denali->mtd)) {
 | 
						if (nand_scan_tail(&denali->mtd)) {
 | 
				
			||||||
		ret = -ENXIO;
 | 
							ret = -ENXIO;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -872,7 +872,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void docg4_erase_block(struct mtd_info *mtd, int page)
 | 
					static int docg4_erase_block(struct mtd_info *mtd, int page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *nand = mtd->priv;
 | 
						struct nand_chip *nand = mtd->priv;
 | 
				
			||||||
	struct docg4_priv *doc = nand->priv;
 | 
						struct docg4_priv *doc = nand->priv;
 | 
				
			||||||
| 
						 | 
					@ -916,6 +916,8 @@ static void docg4_erase_block(struct mtd_info *mtd, int page)
 | 
				
			||||||
	write_nop(docptr);
 | 
						write_nop(docptr);
 | 
				
			||||||
	poll_status(doc);
 | 
						poll_status(doc);
 | 
				
			||||||
	write_nop(docptr);
 | 
						write_nop(docptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nand->waitfunc(mtd, nand);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
 | 
					static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
 | 
				
			||||||
| 
						 | 
					@ -1236,7 +1238,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
 | 
				
			||||||
	nand->block_markbad = docg4_block_markbad;
 | 
						nand->block_markbad = docg4_block_markbad;
 | 
				
			||||||
	nand->read_buf = docg4_read_buf;
 | 
						nand->read_buf = docg4_read_buf;
 | 
				
			||||||
	nand->write_buf = docg4_write_buf16;
 | 
						nand->write_buf = docg4_write_buf16;
 | 
				
			||||||
	nand->erase_cmd = docg4_erase_block;
 | 
						nand->erase = docg4_erase_block;
 | 
				
			||||||
	nand->ecc.read_page = docg4_read_page;
 | 
						nand->ecc.read_page = docg4_read_page;
 | 
				
			||||||
	nand->ecc.write_page = docg4_write_page;
 | 
						nand->ecc.write_page = docg4_write_page;
 | 
				
			||||||
	nand->ecc.read_page_raw = docg4_read_page_raw;
 | 
						nand->ecc.read_page_raw = docg4_read_page_raw;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -723,6 +723,19 @@ static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ECC will be calculated automatically, and errors will be detected in
 | 
				
			||||||
 | 
					 * waitfunc.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 | 
				
			||||||
 | 
									uint32_t offset, uint32_t data_len,
 | 
				
			||||||
 | 
									const uint8_t *buf, int oob_required)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						fsl_elbc_write_buf(mtd, buf, mtd->writesize);
 | 
				
			||||||
 | 
						fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 | 
					static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 | 
						struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 | 
				
			||||||
| 
						 | 
					@ -761,6 +774,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chip->ecc.read_page = fsl_elbc_read_page;
 | 
						chip->ecc.read_page = fsl_elbc_read_page;
 | 
				
			||||||
	chip->ecc.write_page = fsl_elbc_write_page;
 | 
						chip->ecc.write_page = fsl_elbc_write_page;
 | 
				
			||||||
 | 
						chip->ecc.write_subpage = fsl_elbc_write_subpage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If CS Base Register selects full hardware ECC then use it */
 | 
						/* If CS Base Register selects full hardware ECC then use it */
 | 
				
			||||||
	if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
 | 
						if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,7 @@ struct fsl_ifc_nand_ctrl {
 | 
				
			||||||
	struct nand_hw_control controller;
 | 
						struct nand_hw_control controller;
 | 
				
			||||||
	struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
 | 
						struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u8 __iomem *addr;	/* Address of assigned IFC buffer	*/
 | 
						void __iomem *addr;	/* Address of assigned IFC buffer	*/
 | 
				
			||||||
	unsigned int page;	/* Last page written to / read from	*/
 | 
						unsigned int page;	/* Last page written to / read from	*/
 | 
				
			||||||
	unsigned int read_bytes;/* Number of bytes read during command	*/
 | 
						unsigned int read_bytes;/* Number of bytes read during command	*/
 | 
				
			||||||
	unsigned int column;	/* Saved column from SEQIN		*/
 | 
						unsigned int column;	/* Saved column from SEQIN		*/
 | 
				
			||||||
| 
						 | 
					@ -591,6 +591,9 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 | 
				
			||||||
		 * The chip always seems to report that it is
 | 
							 * The chip always seems to report that it is
 | 
				
			||||||
		 * write-protected, even when it is not.
 | 
							 * write-protected, even when it is not.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
 | 
							if (chip->options & NAND_BUSWIDTH_16)
 | 
				
			||||||
 | 
								setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
			setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
 | 
								setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -636,7 +639,7 @@ static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 | 
				
			||||||
		len = bufsize - ifc_nand_ctrl->index;
 | 
							len = bufsize - ifc_nand_ctrl->index;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memcpy_toio(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index], buf, len);
 | 
						memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len);
 | 
				
			||||||
	ifc_nand_ctrl->index += len;
 | 
						ifc_nand_ctrl->index += len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -648,13 +651,16 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *chip = mtd->priv;
 | 
						struct nand_chip *chip = mtd->priv;
 | 
				
			||||||
	struct fsl_ifc_mtd *priv = chip->priv;
 | 
						struct fsl_ifc_mtd *priv = chip->priv;
 | 
				
			||||||
 | 
						unsigned int offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * If there are still bytes in the IFC buffer, then use the
 | 
						 * If there are still bytes in the IFC buffer, then use the
 | 
				
			||||||
	 * next byte.
 | 
						 * next byte.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes)
 | 
						if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
 | 
				
			||||||
		return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]);
 | 
							offset = ifc_nand_ctrl->index++;
 | 
				
			||||||
 | 
							return in_8(ifc_nand_ctrl->addr + offset);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
 | 
						dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
 | 
				
			||||||
	return ERR_BYTE;
 | 
						return ERR_BYTE;
 | 
				
			||||||
| 
						 | 
					@ -675,8 +681,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
 | 
				
			||||||
	 * next byte.
 | 
						 * next byte.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
 | 
						if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
 | 
				
			||||||
		data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl->
 | 
							data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
 | 
				
			||||||
			       addr[ifc_nand_ctrl->index]);
 | 
					 | 
				
			||||||
		ifc_nand_ctrl->index += 2;
 | 
							ifc_nand_ctrl->index += 2;
 | 
				
			||||||
		return (uint8_t) data;
 | 
							return (uint8_t) data;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -701,7 +706,7 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	avail = min((unsigned int)len,
 | 
						avail = min((unsigned int)len,
 | 
				
			||||||
			ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
 | 
								ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
 | 
				
			||||||
	memcpy_fromio(buf, &ifc_nand_ctrl->addr[ifc_nand_ctrl->index], avail);
 | 
						memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail);
 | 
				
			||||||
	ifc_nand_ctrl->index += avail;
 | 
						ifc_nand_ctrl->index += avail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len > avail)
 | 
						if (len > avail)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@
 | 
				
			||||||
#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0		11
 | 
					#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0		11
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0	(0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0	(0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x)				\
 | 
				
			||||||
	(GPMI_IS_MX6Q(x)					\
 | 
						(GPMI_IS_MX6(x)					\
 | 
				
			||||||
		? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)	\
 | 
							? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)	\
 | 
				
			||||||
			& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0)	\
 | 
								& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0)	\
 | 
				
			||||||
		: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0)		\
 | 
							: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0)		\
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14			\
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14			\
 | 
				
			||||||
				(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
 | 
									(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT0_GF(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT0_GF(v, x)				\
 | 
				
			||||||
	((GPMI_IS_MX6Q(x) && ((v) == 14))			\
 | 
						((GPMI_IS_MX6(x) && ((v) == 14))			\
 | 
				
			||||||
		? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)	\
 | 
							? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)	\
 | 
				
			||||||
			& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14)	\
 | 
								& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14)	\
 | 
				
			||||||
		: 0						\
 | 
							: 0						\
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE	\
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE	\
 | 
				
			||||||
			(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
 | 
								(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x)				\
 | 
				
			||||||
	(GPMI_IS_MX6Q(x)						\
 | 
						(GPMI_IS_MX6(x)						\
 | 
				
			||||||
		? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)	\
 | 
							? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)	\
 | 
				
			||||||
		: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)		\
 | 
							: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)		\
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					@ -96,7 +96,7 @@
 | 
				
			||||||
#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN		11
 | 
					#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN		11
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN	(0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN	(0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x)				\
 | 
				
			||||||
	(GPMI_IS_MX6Q(x)					\
 | 
						(GPMI_IS_MX6(x)					\
 | 
				
			||||||
		? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)	\
 | 
							? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)	\
 | 
				
			||||||
			& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN)	\
 | 
								& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN)	\
 | 
				
			||||||
		: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN)		\
 | 
							: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN)		\
 | 
				
			||||||
| 
						 | 
					@ -107,7 +107,7 @@
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14			\
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14			\
 | 
				
			||||||
				(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
 | 
									(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT1_GF(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT1_GF(v, x)				\
 | 
				
			||||||
	((GPMI_IS_MX6Q(x) && ((v) == 14))			\
 | 
						((GPMI_IS_MX6(x) && ((v) == 14))			\
 | 
				
			||||||
		? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)	\
 | 
							? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)	\
 | 
				
			||||||
			& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14)	\
 | 
								& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14)	\
 | 
				
			||||||
		: 0						\
 | 
							: 0						\
 | 
				
			||||||
| 
						 | 
					@ -119,7 +119,7 @@
 | 
				
			||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE	\
 | 
					#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE	\
 | 
				
			||||||
			(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
 | 
								(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
 | 
				
			||||||
#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x)				\
 | 
					#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x)				\
 | 
				
			||||||
	(GPMI_IS_MX6Q(x)						\
 | 
						(GPMI_IS_MX6(x)						\
 | 
				
			||||||
		? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)	\
 | 
							? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)	\
 | 
				
			||||||
		: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)		\
 | 
							: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)		\
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -861,7 +861,7 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
 | 
				
			||||||
	struct resources *r = &this->resources;
 | 
						struct resources *r = &this->resources;
 | 
				
			||||||
	unsigned long rate = clk_get_rate(r->clock[0]);
 | 
						unsigned long rate = clk_get_rate(r->clock[0]);
 | 
				
			||||||
	int mode = this->timing_mode;
 | 
						int mode = this->timing_mode;
 | 
				
			||||||
	int dll_threshold = 16; /* in ns */
 | 
						int dll_threshold = this->devdata->max_chain_delay;
 | 
				
			||||||
	unsigned long delay;
 | 
						unsigned long delay;
 | 
				
			||||||
	unsigned long clk_period;
 | 
						unsigned long clk_period;
 | 
				
			||||||
	int t_rea;
 | 
						int t_rea;
 | 
				
			||||||
| 
						 | 
					@ -886,9 +886,6 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
 | 
				
			||||||
	/* [3] for GPMI_HW_GPMI_CTRL1 */
 | 
						/* [3] for GPMI_HW_GPMI_CTRL1 */
 | 
				
			||||||
	hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
 | 
						hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (GPMI_IS_MX6Q(this))
 | 
					 | 
				
			||||||
		dll_threshold = 12;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Enlarge 10 times for the numerator and denominator in {3}.
 | 
						 * Enlarge 10 times for the numerator and denominator in {3}.
 | 
				
			||||||
	 * This make us to get more accurate result.
 | 
						 * This make us to get more accurate result.
 | 
				
			||||||
| 
						 | 
					@ -974,7 +971,7 @@ int gpmi_extra_init(struct gpmi_nand_data *this)
 | 
				
			||||||
	struct nand_chip *chip = &this->nand;
 | 
						struct nand_chip *chip = &this->nand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Enable the asynchronous EDO feature. */
 | 
						/* Enable the asynchronous EDO feature. */
 | 
				
			||||||
	if (GPMI_IS_MX6Q(this) && chip->onfi_version) {
 | 
						if (GPMI_IS_MX6(this) && chip->onfi_version) {
 | 
				
			||||||
		int mode = onfi_get_async_timing_mode(chip);
 | 
							int mode = onfi_get_async_timing_mode(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* We only support the timing mode 4 and mode 5. */
 | 
							/* We only support the timing mode 4 and mode 5. */
 | 
				
			||||||
| 
						 | 
					@ -1096,12 +1093,12 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
 | 
				
			||||||
	if (GPMI_IS_MX23(this)) {
 | 
						if (GPMI_IS_MX23(this)) {
 | 
				
			||||||
		mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
 | 
							mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
 | 
				
			||||||
		reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
 | 
							reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
 | 
				
			||||||
	} else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) {
 | 
						} else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * In the imx6, all the ready/busy pins are bound
 | 
							 * In the imx6, all the ready/busy pins are bound
 | 
				
			||||||
		 * together. So we only need to check chip 0.
 | 
							 * together. So we only need to check chip 0.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (GPMI_IS_MX6Q(this))
 | 
							if (GPMI_IS_MX6(this))
 | 
				
			||||||
			chip = 0;
 | 
								chip = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* MX28 shares the same R/B register as MX6Q. */
 | 
							/* MX28 shares the same R/B register as MX6Q. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,30 @@ static struct nand_ecclayout gpmi_hw_ecclayout = {
 | 
				
			||||||
	.oobfree = { {.offset = 0, .length = 0} }
 | 
						.oobfree = { {.offset = 0, .length = 0} }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct gpmi_devdata gpmi_devdata_imx23 = {
 | 
				
			||||||
 | 
						.type = IS_MX23,
 | 
				
			||||||
 | 
						.bch_max_ecc_strength = 20,
 | 
				
			||||||
 | 
						.max_chain_delay = 16,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct gpmi_devdata gpmi_devdata_imx28 = {
 | 
				
			||||||
 | 
						.type = IS_MX28,
 | 
				
			||||||
 | 
						.bch_max_ecc_strength = 20,
 | 
				
			||||||
 | 
						.max_chain_delay = 16,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct gpmi_devdata gpmi_devdata_imx6q = {
 | 
				
			||||||
 | 
						.type = IS_MX6Q,
 | 
				
			||||||
 | 
						.bch_max_ecc_strength = 40,
 | 
				
			||||||
 | 
						.max_chain_delay = 12,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct gpmi_devdata gpmi_devdata_imx6sx = {
 | 
				
			||||||
 | 
						.type = IS_MX6SX,
 | 
				
			||||||
 | 
						.bch_max_ecc_strength = 62,
 | 
				
			||||||
 | 
						.max_chain_delay = 12,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static irqreturn_t bch_irq(int irq, void *cookie)
 | 
					static irqreturn_t bch_irq(int irq, void *cookie)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpmi_nand_data *this = cookie;
 | 
						struct gpmi_nand_data *this = cookie;
 | 
				
			||||||
| 
						 | 
					@ -102,14 +126,8 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
 | 
				
			||||||
		/* The mx23/mx28 only support the GF13. */
 | 
							/* The mx23/mx28 only support the GF13. */
 | 
				
			||||||
		if (geo->gf_len == 14)
 | 
							if (geo->gf_len == 14)
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (geo->ecc_strength > MXS_ECC_STRENGTH_MAX)
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
	} else if (GPMI_IS_MX6Q(this)) {
 | 
					 | 
				
			||||||
		if (geo->ecc_strength > MX6_ECC_STRENGTH_MAX)
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true;
 | 
						return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -270,8 +288,7 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
 | 
				
			||||||
			"We can not support this nand chip."
 | 
								"We can not support this nand chip."
 | 
				
			||||||
			" Its required ecc strength(%d) is beyond our"
 | 
								" Its required ecc strength(%d) is beyond our"
 | 
				
			||||||
			" capability(%d).\n", geo->ecc_strength,
 | 
								" capability(%d).\n", geo->ecc_strength,
 | 
				
			||||||
			(GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX
 | 
								this->devdata->bch_max_ecc_strength);
 | 
				
			||||||
					: MXS_ECC_STRENGTH_MAX));
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -572,7 +589,7 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Get extra clocks */
 | 
						/* Get extra clocks */
 | 
				
			||||||
	if (GPMI_IS_MX6Q(this))
 | 
						if (GPMI_IS_MX6(this))
 | 
				
			||||||
		extra_clks = extra_clks_for_mx6q;
 | 
							extra_clks = extra_clks_for_mx6q;
 | 
				
			||||||
	if (!extra_clks)
 | 
						if (!extra_clks)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -590,9 +607,9 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
 | 
				
			||||||
		r->clock[i] = clk;
 | 
							r->clock[i] = clk;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (GPMI_IS_MX6Q(this))
 | 
						if (GPMI_IS_MX6(this))
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Set the default value for the gpmi clock in mx6q:
 | 
							 * Set the default value for the gpmi clock.
 | 
				
			||||||
		 *
 | 
							 *
 | 
				
			||||||
		 * If you want to use the ONFI nand which is in the
 | 
							 * If you want to use the ONFI nand which is in the
 | 
				
			||||||
		 * Synchronous Mode, you should change the clock as you need.
 | 
							 * Synchronous Mode, you should change the clock as you need.
 | 
				
			||||||
| 
						 | 
					@ -1655,7 +1672,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
 | 
				
			||||||
	 *  (1) the chip is imx6, and
 | 
						 *  (1) the chip is imx6, and
 | 
				
			||||||
	 *  (2) the size of the ECC parity is byte aligned.
 | 
						 *  (2) the size of the ECC parity is byte aligned.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (GPMI_IS_MX6Q(this) &&
 | 
						if (GPMI_IS_MX6(this) &&
 | 
				
			||||||
		((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
 | 
							((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
 | 
				
			||||||
		ecc->read_subpage = gpmi_ecc_read_subpage;
 | 
							ecc->read_subpage = gpmi_ecc_read_subpage;
 | 
				
			||||||
		chip->options |= NAND_SUBPAGE_READ;
 | 
							chip->options |= NAND_SUBPAGE_READ;
 | 
				
			||||||
| 
						 | 
					@ -1711,7 +1728,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto err_out;
 | 
							goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = nand_scan_ident(mtd, GPMI_IS_MX6Q(this) ? 2 : 1, NULL);
 | 
						ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto err_out;
 | 
							goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1740,23 +1757,19 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct platform_device_id gpmi_ids[] = {
 | 
					 | 
				
			||||||
	{ .name = "imx23-gpmi-nand", .driver_data = IS_MX23, },
 | 
					 | 
				
			||||||
	{ .name = "imx28-gpmi-nand", .driver_data = IS_MX28, },
 | 
					 | 
				
			||||||
	{ .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, },
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct of_device_id gpmi_nand_id_table[] = {
 | 
					static const struct of_device_id gpmi_nand_id_table[] = {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		.compatible = "fsl,imx23-gpmi-nand",
 | 
							.compatible = "fsl,imx23-gpmi-nand",
 | 
				
			||||||
		.data = (void *)&gpmi_ids[IS_MX23],
 | 
							.data = (void *)&gpmi_devdata_imx23,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		.compatible = "fsl,imx28-gpmi-nand",
 | 
							.compatible = "fsl,imx28-gpmi-nand",
 | 
				
			||||||
		.data = (void *)&gpmi_ids[IS_MX28],
 | 
							.data = (void *)&gpmi_devdata_imx28,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		.compatible = "fsl,imx6q-gpmi-nand",
 | 
							.compatible = "fsl,imx6q-gpmi-nand",
 | 
				
			||||||
		.data = (void *)&gpmi_ids[IS_MX6Q],
 | 
							.data = (void *)&gpmi_devdata_imx6q,
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							.compatible = "fsl,imx6sx-gpmi-nand",
 | 
				
			||||||
 | 
							.data = (void *)&gpmi_devdata_imx6sx,
 | 
				
			||||||
	}, {}
 | 
						}, {}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
 | 
					MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
 | 
				
			||||||
| 
						 | 
					@ -1767,18 +1780,18 @@ static int gpmi_nand_probe(struct platform_device *pdev)
 | 
				
			||||||
	const struct of_device_id *of_id;
 | 
						const struct of_device_id *of_id;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!this)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
 | 
						of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
 | 
				
			||||||
	if (of_id) {
 | 
						if (of_id) {
 | 
				
			||||||
		pdev->id_entry = of_id->data;
 | 
							this->devdata = of_id->data;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		dev_err(&pdev->dev, "Failed to find the right device id.\n");
 | 
							dev_err(&pdev->dev, "Failed to find the right device id.\n");
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
 | 
					 | 
				
			||||||
	if (!this)
 | 
					 | 
				
			||||||
		return -ENOMEM;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	platform_set_drvdata(pdev, this);
 | 
						platform_set_drvdata(pdev, this);
 | 
				
			||||||
	this->pdev  = pdev;
 | 
						this->pdev  = pdev;
 | 
				
			||||||
	this->dev   = &pdev->dev;
 | 
						this->dev   = &pdev->dev;
 | 
				
			||||||
| 
						 | 
					@ -1823,7 +1836,6 @@ static struct platform_driver gpmi_nand_driver = {
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	.probe   = gpmi_nand_probe,
 | 
						.probe   = gpmi_nand_probe,
 | 
				
			||||||
	.remove  = gpmi_nand_remove,
 | 
						.remove  = gpmi_nand_remove,
 | 
				
			||||||
	.id_table = gpmi_ids,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
module_platform_driver(gpmi_nand_driver);
 | 
					module_platform_driver(gpmi_nand_driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -119,11 +119,25 @@ struct nand_timing {
 | 
				
			||||||
	int8_t  tRHOH_in_ns;
 | 
						int8_t  tRHOH_in_ns;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum gpmi_type {
 | 
				
			||||||
 | 
						IS_MX23,
 | 
				
			||||||
 | 
						IS_MX28,
 | 
				
			||||||
 | 
						IS_MX6Q,
 | 
				
			||||||
 | 
						IS_MX6SX
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gpmi_devdata {
 | 
				
			||||||
 | 
						enum gpmi_type type;
 | 
				
			||||||
 | 
						int bch_max_ecc_strength;
 | 
				
			||||||
 | 
						int max_chain_delay; /* See the async EDO mode */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gpmi_nand_data {
 | 
					struct gpmi_nand_data {
 | 
				
			||||||
	/* flags */
 | 
						/* flags */
 | 
				
			||||||
#define GPMI_ASYNC_EDO_ENABLED	(1 << 0)
 | 
					#define GPMI_ASYNC_EDO_ENABLED	(1 << 0)
 | 
				
			||||||
#define GPMI_TIMING_INIT_OK	(1 << 1)
 | 
					#define GPMI_TIMING_INIT_OK	(1 << 1)
 | 
				
			||||||
	int			flags;
 | 
						int			flags;
 | 
				
			||||||
 | 
						const struct gpmi_devdata *devdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* System Interface */
 | 
						/* System Interface */
 | 
				
			||||||
	struct device		*dev;
 | 
						struct device		*dev;
 | 
				
			||||||
| 
						 | 
					@ -281,15 +295,11 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
 | 
				
			||||||
#define STATUS_ERASED		0xff
 | 
					#define STATUS_ERASED		0xff
 | 
				
			||||||
#define STATUS_UNCORRECTABLE	0xfe
 | 
					#define STATUS_UNCORRECTABLE	0xfe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* BCH's bit correction capability. */
 | 
					/* Use the devdata to distinguish different Archs. */
 | 
				
			||||||
#define MXS_ECC_STRENGTH_MAX	20	/* mx23 and mx28 */
 | 
					#define GPMI_IS_MX23(x)		((x)->devdata->type == IS_MX23)
 | 
				
			||||||
#define MX6_ECC_STRENGTH_MAX	40
 | 
					#define GPMI_IS_MX28(x)		((x)->devdata->type == IS_MX28)
 | 
				
			||||||
 | 
					#define GPMI_IS_MX6Q(x)		((x)->devdata->type == IS_MX6Q)
 | 
				
			||||||
 | 
					#define GPMI_IS_MX6SX(x)	((x)->devdata->type == IS_MX6SX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Use the platform_id to distinguish different Archs. */
 | 
					#define GPMI_IS_MX6(x)		(GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x))
 | 
				
			||||||
#define IS_MX23			0x0
 | 
					 | 
				
			||||||
#define IS_MX28			0x1
 | 
					 | 
				
			||||||
#define IS_MX6Q			0x2
 | 
					 | 
				
			||||||
#define GPMI_IS_MX23(x)		((x)->pdev->id_entry->driver_data == IS_MX23)
 | 
					 | 
				
			||||||
#define GPMI_IS_MX28(x)		((x)->pdev->id_entry->driver_data == IS_MX28)
 | 
					 | 
				
			||||||
#define GPMI_IS_MX6Q(x)		((x)->pdev->id_entry->driver_data == IS_MX6Q)
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,7 @@
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/mm.h>
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
#include <linux/mtd/mtd.h>
 | 
					#include <linux/mtd/mtd.h>
 | 
				
			||||||
#include <linux/mtd/nand.h>
 | 
					#include <linux/mtd/nand.h>
 | 
				
			||||||
| 
						 | 
					@ -1204,8 +1205,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 | 
				
			||||||
	 * ecc.pos. Let's make sure that there are no gaps in ECC positions.
 | 
						 * ecc.pos. Let's make sure that there are no gaps in ECC positions.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	for (i = 0; i < eccfrag_len - 1; i++) {
 | 
						for (i = 0; i < eccfrag_len - 1; i++) {
 | 
				
			||||||
		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
 | 
							if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
 | 
				
			||||||
			eccpos[i + start_step * chip->ecc.bytes + 1]) {
 | 
					 | 
				
			||||||
			gaps = 1;
 | 
								gaps = 1;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1501,6 +1501,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 | 
				
			||||||
		mtd->oobavail : mtd->oobsize;
 | 
							mtd->oobavail : mtd->oobsize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint8_t *bufpoi, *oob, *buf;
 | 
						uint8_t *bufpoi, *oob, *buf;
 | 
				
			||||||
 | 
						int use_bufpoi;
 | 
				
			||||||
	unsigned int max_bitflips = 0;
 | 
						unsigned int max_bitflips = 0;
 | 
				
			||||||
	int retry_mode = 0;
 | 
						int retry_mode = 0;
 | 
				
			||||||
	bool ecc_fail = false;
 | 
						bool ecc_fail = false;
 | 
				
			||||||
| 
						 | 
					@ -1523,9 +1524,20 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 | 
				
			||||||
		bytes = min(mtd->writesize - col, readlen);
 | 
							bytes = min(mtd->writesize - col, readlen);
 | 
				
			||||||
		aligned = (bytes == mtd->writesize);
 | 
							aligned = (bytes == mtd->writesize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!aligned)
 | 
				
			||||||
 | 
								use_bufpoi = 1;
 | 
				
			||||||
 | 
							else if (chip->options & NAND_USE_BOUNCE_BUFFER)
 | 
				
			||||||
 | 
								use_bufpoi = !virt_addr_valid(buf);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								use_bufpoi = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Is the current page in the buffer? */
 | 
							/* Is the current page in the buffer? */
 | 
				
			||||||
		if (realpage != chip->pagebuf || oob) {
 | 
							if (realpage != chip->pagebuf || oob) {
 | 
				
			||||||
			bufpoi = aligned ? buf : chip->buffers->databuf;
 | 
								bufpoi = use_bufpoi ? chip->buffers->databuf : buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (use_bufpoi && aligned)
 | 
				
			||||||
 | 
									pr_debug("%s: using read bounce buffer for buf@%p\n",
 | 
				
			||||||
 | 
											 __func__, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
read_retry:
 | 
					read_retry:
 | 
				
			||||||
			chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
 | 
								chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
 | 
				
			||||||
| 
						 | 
					@ -1547,7 +1559,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 | 
				
			||||||
				ret = chip->ecc.read_page(mtd, chip, bufpoi,
 | 
									ret = chip->ecc.read_page(mtd, chip, bufpoi,
 | 
				
			||||||
							  oob_required, page);
 | 
												  oob_required, page);
 | 
				
			||||||
			if (ret < 0) {
 | 
								if (ret < 0) {
 | 
				
			||||||
				if (!aligned)
 | 
									if (use_bufpoi)
 | 
				
			||||||
					/* Invalidate page cache */
 | 
										/* Invalidate page cache */
 | 
				
			||||||
					chip->pagebuf = -1;
 | 
										chip->pagebuf = -1;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
| 
						 | 
					@ -1556,7 +1568,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 | 
				
			||||||
			max_bitflips = max_t(unsigned int, max_bitflips, ret);
 | 
								max_bitflips = max_t(unsigned int, max_bitflips, ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Transfer not aligned data */
 | 
								/* Transfer not aligned data */
 | 
				
			||||||
			if (!aligned) {
 | 
								if (use_bufpoi) {
 | 
				
			||||||
				if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
 | 
									if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
 | 
				
			||||||
				    !(mtd->ecc_stats.failed - ecc_failures) &&
 | 
									    !(mtd->ecc_stats.failed - ecc_failures) &&
 | 
				
			||||||
				    (ops->mode != MTD_OPS_RAW)) {
 | 
									    (ops->mode != MTD_OPS_RAW)) {
 | 
				
			||||||
| 
						 | 
					@ -2376,11 +2388,23 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 | 
				
			||||||
		int bytes = mtd->writesize;
 | 
							int bytes = mtd->writesize;
 | 
				
			||||||
		int cached = writelen > bytes && page != blockmask;
 | 
							int cached = writelen > bytes && page != blockmask;
 | 
				
			||||||
		uint8_t *wbuf = buf;
 | 
							uint8_t *wbuf = buf;
 | 
				
			||||||
 | 
							int use_bufpoi;
 | 
				
			||||||
 | 
							int part_pagewr = (column || writelen < (mtd->writesize - 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Partial page write? */
 | 
							if (part_pagewr)
 | 
				
			||||||
		if (unlikely(column || writelen < (mtd->writesize - 1))) {
 | 
								use_bufpoi = 1;
 | 
				
			||||||
 | 
							else if (chip->options & NAND_USE_BOUNCE_BUFFER)
 | 
				
			||||||
 | 
								use_bufpoi = !virt_addr_valid(buf);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								use_bufpoi = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Partial page write?, or need to use bounce buffer */
 | 
				
			||||||
 | 
							if (use_bufpoi) {
 | 
				
			||||||
 | 
								pr_debug("%s: using write bounce buffer for buf@%p\n",
 | 
				
			||||||
 | 
										 __func__, buf);
 | 
				
			||||||
			cached = 0;
 | 
								cached = 0;
 | 
				
			||||||
			bytes = min_t(int, bytes - column, (int) writelen);
 | 
								if (part_pagewr)
 | 
				
			||||||
 | 
									bytes = min_t(int, bytes - column, writelen);
 | 
				
			||||||
			chip->pagebuf = -1;
 | 
								chip->pagebuf = -1;
 | 
				
			||||||
			memset(chip->buffers->databuf, 0xff, mtd->writesize);
 | 
								memset(chip->buffers->databuf, 0xff, mtd->writesize);
 | 
				
			||||||
			memcpy(&chip->buffers->databuf[column], buf, bytes);
 | 
								memcpy(&chip->buffers->databuf[column], buf, bytes);
 | 
				
			||||||
| 
						 | 
					@ -2618,18 +2642,20 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * single_erase_cmd - [GENERIC] NAND standard block erase command function
 | 
					 * single_erase - [GENERIC] NAND standard block erase command function
 | 
				
			||||||
 * @mtd: MTD device structure
 | 
					 * @mtd: MTD device structure
 | 
				
			||||||
 * @page: the page address of the block which will be erased
 | 
					 * @page: the page address of the block which will be erased
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Standard erase command for NAND chips.
 | 
					 * Standard erase command for NAND chips. Returns NAND status.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void single_erase_cmd(struct mtd_info *mtd, int page)
 | 
					static int single_erase(struct mtd_info *mtd, int page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *chip = mtd->priv;
 | 
						struct nand_chip *chip = mtd->priv;
 | 
				
			||||||
	/* Send commands to erase a block */
 | 
						/* Send commands to erase a block */
 | 
				
			||||||
	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
 | 
						chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
 | 
				
			||||||
	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
 | 
						chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return chip->waitfunc(mtd, chip);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -2710,9 +2736,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 | 
				
			||||||
		    (page + pages_per_block))
 | 
							    (page + pages_per_block))
 | 
				
			||||||
			chip->pagebuf = -1;
 | 
								chip->pagebuf = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		chip->erase_cmd(mtd, page & chip->pagemask);
 | 
							status = chip->erase(mtd, page & chip->pagemask);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		status = chip->waitfunc(mtd, chip);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * See if operation failed and additional status checks are
 | 
							 * See if operation failed and additional status checks are
 | 
				
			||||||
| 
						 | 
					@ -3607,7 +3631,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chip->onfi_version = 0;
 | 
						chip->onfi_version = 0;
 | 
				
			||||||
	if (!type->name || !type->pagesize) {
 | 
						if (!type->name || !type->pagesize) {
 | 
				
			||||||
		/* Check is chip is ONFI compliant */
 | 
							/* Check if the chip is ONFI compliant */
 | 
				
			||||||
		if (nand_flash_detect_onfi(mtd, chip, &busw))
 | 
							if (nand_flash_detect_onfi(mtd, chip, &busw))
 | 
				
			||||||
			goto ident_done;
 | 
								goto ident_done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3685,7 +3709,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chip->badblockbits = 8;
 | 
						chip->badblockbits = 8;
 | 
				
			||||||
	chip->erase_cmd = single_erase_cmd;
 | 
						chip->erase = single_erase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Do not replace user supplied command function! */
 | 
						/* Do not replace user supplied command function! */
 | 
				
			||||||
	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
 | 
						if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
 | 
				
			||||||
| 
						 | 
					@ -3770,6 +3794,39 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(nand_scan_ident);
 | 
					EXPORT_SYMBOL(nand_scan_ident);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Check if the chip configuration meet the datasheet requirements.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * If our configuration corrects A bits per B bytes and the minimum
 | 
				
			||||||
 | 
					 * required correction level is X bits per Y bytes, then we must ensure
 | 
				
			||||||
 | 
					 * both of the following are true:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * (1) A / B >= X / Y
 | 
				
			||||||
 | 
					 * (2) A >= X
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Requirement (1) ensures we can correct for the required bitflip density.
 | 
				
			||||||
 | 
					 * Requirement (2) ensures we can correct even when all bitflips are clumped
 | 
				
			||||||
 | 
					 * in the same sector.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool nand_ecc_strength_good(struct mtd_info *mtd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nand_chip *chip = mtd->priv;
 | 
				
			||||||
 | 
						struct nand_ecc_ctrl *ecc = &chip->ecc;
 | 
				
			||||||
 | 
						int corr, ds_corr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ecc->size == 0 || chip->ecc_step_ds == 0)
 | 
				
			||||||
 | 
							/* Not enough information */
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * We get the number of corrected bits per page to compare
 | 
				
			||||||
 | 
						 * the correction density.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						corr = (mtd->writesize * ecc->strength) / ecc->size;
 | 
				
			||||||
 | 
						ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * nand_scan_tail - [NAND Interface] Scan for the NAND device
 | 
					 * nand_scan_tail - [NAND Interface] Scan for the NAND device
 | 
				
			||||||
| 
						 | 
					@ -3990,6 +4047,9 @@ int nand_scan_tail(struct mtd_info *mtd)
 | 
				
			||||||
		ecc->layout->oobavail += ecc->layout->oobfree[i].length;
 | 
							ecc->layout->oobavail += ecc->layout->oobfree[i].length;
 | 
				
			||||||
	mtd->oobavail = ecc->layout->oobavail;
 | 
						mtd->oobavail = ecc->layout->oobavail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* ECC sanity check: warn noisily if it's too weak */
 | 
				
			||||||
 | 
						WARN_ON(!nand_ecc_strength_good(mtd));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Set the number of read / write steps for one page depending on ECC
 | 
						 * Set the number of read / write steps for one page depending on ECC
 | 
				
			||||||
	 * mode.
 | 
						 * mode.
 | 
				
			||||||
| 
						 | 
					@ -4023,8 +4083,16 @@ int nand_scan_tail(struct mtd_info *mtd)
 | 
				
			||||||
	chip->pagebuf = -1;
 | 
						chip->pagebuf = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Large page NAND with SOFT_ECC should support subpage reads */
 | 
						/* Large page NAND with SOFT_ECC should support subpage reads */
 | 
				
			||||||
	if ((ecc->mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
 | 
						switch (ecc->mode) {
 | 
				
			||||||
 | 
						case NAND_ECC_SOFT:
 | 
				
			||||||
 | 
						case NAND_ECC_SOFT_BCH:
 | 
				
			||||||
 | 
							if (chip->page_shift > 9)
 | 
				
			||||||
			chip->options |= NAND_SUBPAGE_READ;
 | 
								chip->options |= NAND_SUBPAGE_READ;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Fill in remaining MTD driver data */
 | 
						/* Fill in remaining MTD driver data */
 | 
				
			||||||
	mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
 | 
						mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -528,7 +528,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *this = mtd->priv;
 | 
						struct nand_chip *this = mtd->priv;
 | 
				
			||||||
	int i, chips;
 | 
						int i, chips;
 | 
				
			||||||
	int bits, startblock, block, dir;
 | 
						int startblock, block, dir;
 | 
				
			||||||
	int scanlen = mtd->writesize + mtd->oobsize;
 | 
						int scanlen = mtd->writesize + mtd->oobsize;
 | 
				
			||||||
	int bbtblocks;
 | 
						int bbtblocks;
 | 
				
			||||||
	int blocktopage = this->bbt_erase_shift - this->page_shift;
 | 
						int blocktopage = this->bbt_erase_shift - this->page_shift;
 | 
				
			||||||
| 
						 | 
					@ -552,9 +552,6 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 | 
				
			||||||
		bbtblocks = mtd->size >> this->bbt_erase_shift;
 | 
							bbtblocks = mtd->size >> this->bbt_erase_shift;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Number of bits for each erase block in the bbt */
 | 
					 | 
				
			||||||
	bits = td->options & NAND_BBT_NRBITS_MSK;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < chips; i++) {
 | 
						for (i = 0; i < chips; i++) {
 | 
				
			||||||
		/* Reset version information */
 | 
							/* Reset version information */
 | 
				
			||||||
		td->version[i] = 0;
 | 
							td->version[i] = 0;
 | 
				
			||||||
| 
						 | 
					@ -1285,6 +1282,7 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
 | 
				
			||||||
int nand_default_bbt(struct mtd_info *mtd)
 | 
					int nand_default_bbt(struct mtd_info *mtd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nand_chip *this = mtd->priv;
 | 
						struct nand_chip *this = mtd->priv;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Is a flash based bad block table requested? */
 | 
						/* Is a flash based bad block table requested? */
 | 
				
			||||||
	if (this->bbt_options & NAND_BBT_USE_FLASH) {
 | 
						if (this->bbt_options & NAND_BBT_USE_FLASH) {
 | 
				
			||||||
| 
						 | 
					@ -1303,8 +1301,11 @@ int nand_default_bbt(struct mtd_info *mtd)
 | 
				
			||||||
		this->bbt_md = NULL;
 | 
							this->bbt_md = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!this->badblock_pattern)
 | 
						if (!this->badblock_pattern) {
 | 
				
			||||||
		nand_create_badblock_pattern(this);
 | 
							ret = nand_create_badblock_pattern(this);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nand_scan_bbt(mtd, this->badblock_pattern);
 | 
						return nand_scan_bbt(mtd, this->badblock_pattern);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -506,7 +506,7 @@ int __nand_correct_data(unsigned char *buf,
 | 
				
			||||||
	if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
 | 
						if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
 | 
				
			||||||
		return 1;	/* error in ECC data; no action needed */
 | 
							return 1;	/* error in ECC data; no action needed */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_err("%s: uncorrectable ECC error", __func__);
 | 
						pr_err("%s: uncorrectable ECC error\n", __func__);
 | 
				
			||||||
	return -1;
 | 
						return -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(__nand_correct_data);
 | 
					EXPORT_SYMBOL(__nand_correct_data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,6 +137,10 @@
 | 
				
			||||||
#define BADBLOCK_MARKER_LENGTH		2
 | 
					#define BADBLOCK_MARKER_LENGTH		2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
 | 
					#ifdef CONFIG_MTD_NAND_OMAP_BCH
 | 
				
			||||||
 | 
					static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
 | 
				
			||||||
 | 
									0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
 | 
				
			||||||
 | 
									0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
 | 
				
			||||||
 | 
									0x07, 0x0e};
 | 
				
			||||||
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 | 
					static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 | 
				
			||||||
	0xac, 0x6b, 0xff, 0x99, 0x7b};
 | 
						0xac, 0x6b, 0xff, 0x99, 0x7b};
 | 
				
			||||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 | 
					static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 | 
				
			||||||
| 
						 | 
					@ -1114,6 +1118,19 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
 | 
				
			||||||
			ecc_size1 = BCH_ECC_SIZE1;
 | 
								ecc_size1 = BCH_ECC_SIZE1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
 | 
							bch_type = 0x2;
 | 
				
			||||||
 | 
							nsectors = chip->ecc.steps;
 | 
				
			||||||
 | 
							if (mode == NAND_ECC_READ) {
 | 
				
			||||||
 | 
								wr_mode	  = 0x01;
 | 
				
			||||||
 | 
								ecc_size0 = 52; /* ECC bits in nibbles per sector */
 | 
				
			||||||
 | 
								ecc_size1 = 0;  /* non-ECC bits in nibbles per sector */
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								wr_mode	  = 0x01;
 | 
				
			||||||
 | 
								ecc_size0 = 0;  /* extra bits in nibbles per sector */
 | 
				
			||||||
 | 
								ecc_size1 = 52; /* OOB bits in nibbles per sector */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1162,7 +1179,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
 | 
				
			||||||
	struct gpmc_nand_regs	*gpmc_regs = &info->reg;
 | 
						struct gpmc_nand_regs	*gpmc_regs = &info->reg;
 | 
				
			||||||
	u8 *ecc_code;
 | 
						u8 *ecc_code;
 | 
				
			||||||
	unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
 | 
						unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
 | 
				
			||||||
	int i;
 | 
						u32 val;
 | 
				
			||||||
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
 | 
						nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
 | 
				
			||||||
	for (i = 0; i < nsectors; i++) {
 | 
						for (i = 0; i < nsectors; i++) {
 | 
				
			||||||
| 
						 | 
					@ -1201,6 +1219,41 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
 | 
				
			||||||
			*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
 | 
								*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
 | 
				
			||||||
			*ecc_code++ = ((bch_val1 & 0xF) << 4);
 | 
								*ecc_code++ = ((bch_val1 & 0xF) << 4);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result6[i]);
 | 
				
			||||||
 | 
								ecc_code[0]  = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[1]  = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result5[i]);
 | 
				
			||||||
 | 
								ecc_code[2]  = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[3]  = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[4]  = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[5]  = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result4[i]);
 | 
				
			||||||
 | 
								ecc_code[6]  = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[7]  = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[8]  = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[9]  = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result3[i]);
 | 
				
			||||||
 | 
								ecc_code[10] = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[11] = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[12] = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[13] = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result2[i]);
 | 
				
			||||||
 | 
								ecc_code[14] = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[15] = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[16] = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[17] = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result1[i]);
 | 
				
			||||||
 | 
								ecc_code[18] = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[19] = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[20] = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[21] = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								val = readl(gpmc_regs->gpmc_bch_result0[i]);
 | 
				
			||||||
 | 
								ecc_code[22] = ((val >> 24) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[23] = ((val >> 16) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[24] = ((val >>  8) & 0xFF);
 | 
				
			||||||
 | 
								ecc_code[25] = ((val >>  0) & 0xFF);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1210,8 +1263,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
 | 
				
			||||||
		case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
 | 
							case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
 | 
				
			||||||
			/* Add constant polynomial to remainder, so that
 | 
								/* Add constant polynomial to remainder, so that
 | 
				
			||||||
			 * ECC of blank pages results in 0x0 on reading back */
 | 
								 * ECC of blank pages results in 0x0 on reading back */
 | 
				
			||||||
			for (i = 0; i < eccbytes; i++)
 | 
								for (j = 0; j < eccbytes; j++)
 | 
				
			||||||
				ecc_calc[i] ^= bch4_polynomial[i];
 | 
									ecc_calc[j] ^= bch4_polynomial[j];
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case OMAP_ECC_BCH4_CODE_HW:
 | 
							case OMAP_ECC_BCH4_CODE_HW:
 | 
				
			||||||
			/* Set  8th ECC byte as 0x0 for ROM compatibility */
 | 
								/* Set  8th ECC byte as 0x0 for ROM compatibility */
 | 
				
			||||||
| 
						 | 
					@ -1220,13 +1273,15 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
 | 
				
			||||||
		case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
 | 
							case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
 | 
				
			||||||
			/* Add constant polynomial to remainder, so that
 | 
								/* Add constant polynomial to remainder, so that
 | 
				
			||||||
			 * ECC of blank pages results in 0x0 on reading back */
 | 
								 * ECC of blank pages results in 0x0 on reading back */
 | 
				
			||||||
			for (i = 0; i < eccbytes; i++)
 | 
								for (j = 0; j < eccbytes; j++)
 | 
				
			||||||
				ecc_calc[i] ^= bch8_polynomial[i];
 | 
									ecc_calc[j] ^= bch8_polynomial[j];
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case OMAP_ECC_BCH8_CODE_HW:
 | 
							case OMAP_ECC_BCH8_CODE_HW:
 | 
				
			||||||
			/* Set 14th ECC byte as 0x0 for ROM compatibility */
 | 
								/* Set 14th ECC byte as 0x0 for ROM compatibility */
 | 
				
			||||||
			ecc_calc[eccbytes - 1] = 0x0;
 | 
								ecc_calc[eccbytes - 1] = 0x0;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1237,6 +1292,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_MTD_NAND_OMAP_BCH
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * erased_sector_bitflips - count bit flips
 | 
					 * erased_sector_bitflips - count bit flips
 | 
				
			||||||
 * @data:	data sector buffer
 | 
					 * @data:	data sector buffer
 | 
				
			||||||
| 
						 | 
					@ -1276,7 +1332,6 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
 | 
				
			||||||
	return flip_bits;
 | 
						return flip_bits;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * omap_elm_correct_data - corrects page data area in case error reported
 | 
					 * omap_elm_correct_data - corrects page data area in case error reported
 | 
				
			||||||
 * @mtd:	MTD device structure
 | 
					 * @mtd:	MTD device structure
 | 
				
			||||||
| 
						 | 
					@ -1318,6 +1373,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 | 
				
			||||||
		actual_eccbytes = ecc->bytes - 1;
 | 
							actual_eccbytes = ecc->bytes - 1;
 | 
				
			||||||
		erased_ecc_vec = bch8_vector;
 | 
							erased_ecc_vec = bch8_vector;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
 | 
							actual_eccbytes = ecc->bytes;
 | 
				
			||||||
 | 
							erased_ecc_vec = bch16_vector;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		pr_err("invalid driver configuration\n");
 | 
							pr_err("invalid driver configuration\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -1382,7 +1441,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check if any error reported */
 | 
						/* Check if any error reported */
 | 
				
			||||||
	if (!is_error_reported)
 | 
						if (!is_error_reported)
 | 
				
			||||||
		return 0;
 | 
							return stat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Decode BCH error using ELM module */
 | 
						/* Decode BCH error using ELM module */
 | 
				
			||||||
	elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
 | 
						elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
 | 
				
			||||||
| 
						 | 
					@ -1401,6 +1460,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 | 
				
			||||||
						BCH4_BIT_PAD;
 | 
											BCH4_BIT_PAD;
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				case OMAP_ECC_BCH8_CODE_HW:
 | 
									case OMAP_ECC_BCH8_CODE_HW:
 | 
				
			||||||
 | 
									case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
					pos = err_vec[i].error_loc[j];
 | 
										pos = err_vec[i].error_loc[j];
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				default:
 | 
									default:
 | 
				
			||||||
| 
						 | 
					@ -1912,6 +1972,40 @@ static int omap_nand_probe(struct platform_device *pdev)
 | 
				
			||||||
		goto return_error;
 | 
							goto return_error;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case OMAP_ECC_BCH16_CODE_HW:
 | 
				
			||||||
 | 
					#ifdef CONFIG_MTD_NAND_OMAP_BCH
 | 
				
			||||||
 | 
							pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
 | 
				
			||||||
 | 
							nand_chip->ecc.mode		= NAND_ECC_HW;
 | 
				
			||||||
 | 
							nand_chip->ecc.size		= 512;
 | 
				
			||||||
 | 
							nand_chip->ecc.bytes		= 26;
 | 
				
			||||||
 | 
							nand_chip->ecc.strength		= 16;
 | 
				
			||||||
 | 
							nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
 | 
				
			||||||
 | 
							nand_chip->ecc.correct		= omap_elm_correct_data;
 | 
				
			||||||
 | 
							nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
 | 
				
			||||||
 | 
							nand_chip->ecc.read_page	= omap_read_page_bch;
 | 
				
			||||||
 | 
							nand_chip->ecc.write_page	= omap_write_page_bch;
 | 
				
			||||||
 | 
							/* This ECC scheme requires ELM H/W block */
 | 
				
			||||||
 | 
							err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC);
 | 
				
			||||||
 | 
							if (err < 0) {
 | 
				
			||||||
 | 
								pr_err("ELM is required for this ECC scheme\n");
 | 
				
			||||||
 | 
								goto return_error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* define ECC layout */
 | 
				
			||||||
 | 
							ecclayout->eccbytes		= nand_chip->ecc.bytes *
 | 
				
			||||||
 | 
												(mtd->writesize /
 | 
				
			||||||
 | 
												nand_chip->ecc.size);
 | 
				
			||||||
 | 
							oob_index			= BADBLOCK_MARKER_LENGTH;
 | 
				
			||||||
 | 
							for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
 | 
				
			||||||
 | 
								ecclayout->eccpos[i]	= oob_index;
 | 
				
			||||||
 | 
							/* reserved marker already included in ecclayout->eccbytes */
 | 
				
			||||||
 | 
							ecclayout->oobfree->offset	=
 | 
				
			||||||
 | 
									ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto return_error;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		pr_err("nand: error: invalid or unsupported ECC scheme\n");
 | 
							pr_err("nand: error: invalid or unsupported ECC scheme\n");
 | 
				
			||||||
		err = -EINVAL;
 | 
							err = -EINVAL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -214,7 +214,7 @@ static int orion_nand_remove(struct platform_device *pdev)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_OF
 | 
					#ifdef CONFIG_OF
 | 
				
			||||||
static struct of_device_id orion_nand_of_match_table[] = {
 | 
					static const struct of_device_id orion_nand_of_match_table[] = {
 | 
				
			||||||
	{ .compatible = "marvell,orion-nand", },
 | 
						{ .compatible = "marvell,orion-nand", },
 | 
				
			||||||
	{},
 | 
						{},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,10 +127,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* macros for registers read/write */
 | 
					/* macros for registers read/write */
 | 
				
			||||||
#define nand_writel(info, off, val)	\
 | 
					#define nand_writel(info, off, val)	\
 | 
				
			||||||
	__raw_writel((val), (info)->mmio_base + (off))
 | 
						writel_relaxed((val), (info)->mmio_base + (off))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define nand_readl(info, off)		\
 | 
					#define nand_readl(info, off)		\
 | 
				
			||||||
	__raw_readl((info)->mmio_base + (off))
 | 
						readl_relaxed((info)->mmio_base + (off))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* error code and state */
 | 
					/* error code and state */
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
| 
						 | 
					@ -337,7 +337,7 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
 | 
				
			||||||
/* convert nano-seconds to nand flash controller clock cycles */
 | 
					/* convert nano-seconds to nand flash controller clock cycles */
 | 
				
			||||||
#define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 | 
					#define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct of_device_id pxa3xx_nand_dt_ids[] = {
 | 
					static const struct of_device_id pxa3xx_nand_dt_ids[] = {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		.compatible = "marvell,pxa3xx-nand",
 | 
							.compatible = "marvell,pxa3xx-nand",
 | 
				
			||||||
		.data       = (void *)PXA3XX_NAND_VARIANT_PXA,
 | 
							.data       = (void *)PXA3XX_NAND_VARIANT_PXA,
 | 
				
			||||||
| 
						 | 
					@ -1354,7 +1354,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
 | 
				
			||||||
		ecc->mode = NAND_ECC_HW;
 | 
							ecc->mode = NAND_ECC_HW;
 | 
				
			||||||
		ecc->size = 512;
 | 
							ecc->size = 512;
 | 
				
			||||||
		ecc->strength = 1;
 | 
							ecc->strength = 1;
 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
 | 
						} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
 | 
				
			||||||
		info->chunk_size = 512;
 | 
							info->chunk_size = 512;
 | 
				
			||||||
| 
						 | 
					@ -1363,7 +1362,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
 | 
				
			||||||
		ecc->mode = NAND_ECC_HW;
 | 
							ecc->mode = NAND_ECC_HW;
 | 
				
			||||||
		ecc->size = 512;
 | 
							ecc->size = 512;
 | 
				
			||||||
		ecc->strength = 1;
 | 
							ecc->strength = 1;
 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Required ECC: 4-bit correction per 512 bytes
 | 
						 * Required ECC: 4-bit correction per 512 bytes
 | 
				
			||||||
| 
						 | 
					@ -1378,7 +1376,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
 | 
				
			||||||
		ecc->size = info->chunk_size;
 | 
							ecc->size = info->chunk_size;
 | 
				
			||||||
		ecc->layout = &ecc_layout_2KB_bch4bit;
 | 
							ecc->layout = &ecc_layout_2KB_bch4bit;
 | 
				
			||||||
		ecc->strength = 16;
 | 
							ecc->strength = 16;
 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
 | 
						} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
 | 
				
			||||||
		info->ecc_bch = 1;
 | 
							info->ecc_bch = 1;
 | 
				
			||||||
| 
						 | 
					@ -1389,7 +1386,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
 | 
				
			||||||
		ecc->size = info->chunk_size;
 | 
							ecc->size = info->chunk_size;
 | 
				
			||||||
		ecc->layout = &ecc_layout_4KB_bch4bit;
 | 
							ecc->layout = &ecc_layout_4KB_bch4bit;
 | 
				
			||||||
		ecc->strength = 16;
 | 
							ecc->strength = 16;
 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Required ECC: 8-bit correction per 512 bytes
 | 
						 * Required ECC: 8-bit correction per 512 bytes
 | 
				
			||||||
| 
						 | 
					@ -1404,8 +1400,15 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
 | 
				
			||||||
		ecc->size = info->chunk_size;
 | 
							ecc->size = info->chunk_size;
 | 
				
			||||||
		ecc->layout = &ecc_layout_4KB_bch8bit;
 | 
							ecc->layout = &ecc_layout_4KB_bch8bit;
 | 
				
			||||||
		ecc->strength = 16;
 | 
							ecc->strength = 16;
 | 
				
			||||||
		return 1;
 | 
						} else {
 | 
				
			||||||
 | 
							dev_err(&info->pdev->dev,
 | 
				
			||||||
 | 
								"ECC strength %d at page size %d is not supported\n",
 | 
				
			||||||
 | 
								strength, page_size);
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n",
 | 
				
			||||||
 | 
							 ecc->strength, ecc->size);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1516,8 +1519,13 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pdata->ecc_strength && pdata->ecc_step_size) {
 | 
				
			||||||
 | 
							ecc_strength = pdata->ecc_strength;
 | 
				
			||||||
 | 
							ecc_step = pdata->ecc_step_size;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		ecc_strength = chip->ecc_strength_ds;
 | 
							ecc_strength = chip->ecc_strength_ds;
 | 
				
			||||||
		ecc_step = chip->ecc_step_ds;
 | 
							ecc_step = chip->ecc_step_ds;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set default ECC strength requirements on non-ONFI devices */
 | 
						/* Set default ECC strength requirements on non-ONFI devices */
 | 
				
			||||||
	if (ecc_strength < 1 && ecc_step < 1) {
 | 
						if (ecc_strength < 1 && ecc_step < 1) {
 | 
				
			||||||
| 
						 | 
					@ -1527,12 +1535,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
 | 
						ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
 | 
				
			||||||
			   ecc_step, mtd->writesize);
 | 
								   ecc_step, mtd->writesize);
 | 
				
			||||||
	if (!ret) {
 | 
						if (ret)
 | 
				
			||||||
		dev_err(&info->pdev->dev,
 | 
							return ret;
 | 
				
			||||||
			"ECC strength %d at page size %d is not supported\n",
 | 
					 | 
				
			||||||
			ecc_strength, mtd->writesize);
 | 
					 | 
				
			||||||
		return -ENODEV;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* calculate addressing information */
 | 
						/* calculate addressing information */
 | 
				
			||||||
	if (mtd->writesize >= 2048)
 | 
						if (mtd->writesize >= 2048)
 | 
				
			||||||
| 
						 | 
					@ -1730,6 +1734,14 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
 | 
				
			||||||
	of_property_read_u32(np, "num-cs", &pdata->num_cs);
 | 
						of_property_read_u32(np, "num-cs", &pdata->num_cs);
 | 
				
			||||||
	pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
 | 
						pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdata->ecc_strength = of_get_nand_ecc_strength(np);
 | 
				
			||||||
 | 
						if (pdata->ecc_strength < 0)
 | 
				
			||||||
 | 
							pdata->ecc_strength = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
 | 
				
			||||||
 | 
						if (pdata->ecc_step_size < 0)
 | 
				
			||||||
 | 
							pdata->ecc_step_size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pdev->dev.platform_data = pdata;
 | 
						pdev->dev.platform_data = pdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,7 +245,7 @@ static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* write DWORD chinks - faster */
 | 
						/* write DWORD chinks - faster */
 | 
				
			||||||
	while (len) {
 | 
						while (len >= 4) {
 | 
				
			||||||
		reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
 | 
							reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
 | 
				
			||||||
		r852_write_reg_dword(dev, R852_DATALINE, reg);
 | 
							r852_write_reg_dword(dev, R852_DATALINE, reg);
 | 
				
			||||||
		buf += 4;
 | 
							buf += 4;
 | 
				
			||||||
| 
						 | 
					@ -254,8 +254,10 @@ static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* write rest */
 | 
						/* write rest */
 | 
				
			||||||
	while (len)
 | 
						while (len > 0) {
 | 
				
			||||||
		r852_write_reg(dev, R852_DATALINE, *buf++);
 | 
							r852_write_reg(dev, R852_DATALINE, *buf++);
 | 
				
			||||||
 | 
							len--;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -537,9 +537,9 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);
 | 
					static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
 | 
					static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	void __iomem *base = onenand->dma_addr;
 | 
						void __iomem *base = onenand->dma_addr;
 | 
				
			||||||
	int status;
 | 
						int status;
 | 
				
			||||||
| 
						 | 
					@ -605,7 +605,7 @@ static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
 | 
				
			||||||
	return IRQ_HANDLED;
 | 
						return IRQ_HANDLED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
 | 
					static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	void __iomem *base = onenand->dma_addr;
 | 
						void __iomem *base = onenand->dma_addr;
 | 
				
			||||||
	int status;
 | 
						int status;
 | 
				
			||||||
| 
						 | 
					@ -686,7 +686,7 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
 | 
				
			||||||
		dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
 | 
							dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
 | 
				
			||||||
		goto normal;
 | 
							goto normal;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
 | 
						err = s5pc110_dma_ops(dma_dst, dma_src,
 | 
				
			||||||
			count, S5PC110_DMA_DIR_READ);
 | 
								count, S5PC110_DMA_DIR_READ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (page_dma)
 | 
						if (page_dma)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								drivers/mtd/spi-nor/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								drivers/mtd/spi-nor/Kconfig
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					menuconfig MTD_SPI_NOR
 | 
				
			||||||
 | 
						tristate "SPI-NOR device support"
 | 
				
			||||||
 | 
						depends on MTD
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  This is the framework for the SPI NOR which can be used by the SPI
 | 
				
			||||||
 | 
						  device drivers and the SPI-NOR device driver.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if MTD_SPI_NOR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config SPI_FSL_QUADSPI
 | 
				
			||||||
 | 
						tristate "Freescale Quad SPI controller"
 | 
				
			||||||
 | 
						depends on ARCH_MXC
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  This enables support for the Quad SPI controller in master mode.
 | 
				
			||||||
 | 
						  We only connect the NOR to this controller now.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endif # MTD_SPI_NOR
 | 
				
			||||||
							
								
								
									
										2
									
								
								drivers/mtd/spi-nor/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								drivers/mtd/spi-nor/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
 | 
				
			||||||
							
								
								
									
										1009
									
								
								drivers/mtd/spi-nor/fsl-quadspi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1009
									
								
								drivers/mtd/spi-nor/fsl-quadspi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1107
									
								
								drivers/mtd/spi-nor/spi-nor.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1107
									
								
								drivers/mtd/spi-nor/spi-nor.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -69,8 +69,8 @@ static int write_eraseblock(int ebnum)
 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
	loff_t addr = ebnum * mtd->erasesize;
 | 
						loff_t addr = ebnum * mtd->erasesize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
 | 
				
			||||||
	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 | 
						for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 | 
				
			||||||
		prandom_bytes_state(&rnd_state, writebuf, use_len);
 | 
					 | 
				
			||||||
		ops.mode      = MTD_OPS_AUTO_OOB;
 | 
							ops.mode      = MTD_OPS_AUTO_OOB;
 | 
				
			||||||
		ops.len       = 0;
 | 
							ops.len       = 0;
 | 
				
			||||||
		ops.retlen    = 0;
 | 
							ops.retlen    = 0;
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ static int write_eraseblock(int ebnum)
 | 
				
			||||||
		ops.oobretlen = 0;
 | 
							ops.oobretlen = 0;
 | 
				
			||||||
		ops.ooboffs   = use_offset;
 | 
							ops.ooboffs   = use_offset;
 | 
				
			||||||
		ops.datbuf    = NULL;
 | 
							ops.datbuf    = NULL;
 | 
				
			||||||
		ops.oobbuf    = writebuf;
 | 
							ops.oobbuf    = writebuf + (use_len_max * i) + use_offset;
 | 
				
			||||||
		err = mtd_write_oob(mtd, addr, &ops);
 | 
							err = mtd_write_oob(mtd, addr, &ops);
 | 
				
			||||||
		if (err || ops.oobretlen != use_len) {
 | 
							if (err || ops.oobretlen != use_len) {
 | 
				
			||||||
			pr_err("error: writeoob failed at %#llx\n",
 | 
								pr_err("error: writeoob failed at %#llx\n",
 | 
				
			||||||
| 
						 | 
					@ -122,8 +122,8 @@ static int verify_eraseblock(int ebnum)
 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
	loff_t addr = ebnum * mtd->erasesize;
 | 
						loff_t addr = ebnum * mtd->erasesize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
 | 
				
			||||||
	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 | 
						for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 | 
				
			||||||
		prandom_bytes_state(&rnd_state, writebuf, use_len);
 | 
					 | 
				
			||||||
		ops.mode      = MTD_OPS_AUTO_OOB;
 | 
							ops.mode      = MTD_OPS_AUTO_OOB;
 | 
				
			||||||
		ops.len       = 0;
 | 
							ops.len       = 0;
 | 
				
			||||||
		ops.retlen    = 0;
 | 
							ops.retlen    = 0;
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,8 @@ static int verify_eraseblock(int ebnum)
 | 
				
			||||||
			errcnt += 1;
 | 
								errcnt += 1;
 | 
				
			||||||
			return err ? err : -1;
 | 
								return err ? err : -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (memcmp(readbuf, writebuf, use_len)) {
 | 
							if (memcmp(readbuf, writebuf + (use_len_max * i) + use_offset,
 | 
				
			||||||
 | 
								   use_len)) {
 | 
				
			||||||
			pr_err("error: verify failed at %#llx\n",
 | 
								pr_err("error: verify failed at %#llx\n",
 | 
				
			||||||
			       (long long)addr);
 | 
								       (long long)addr);
 | 
				
			||||||
			errcnt += 1;
 | 
								errcnt += 1;
 | 
				
			||||||
| 
						 | 
					@ -166,7 +167,9 @@ static int verify_eraseblock(int ebnum)
 | 
				
			||||||
				errcnt += 1;
 | 
									errcnt += 1;
 | 
				
			||||||
				return err ? err : -1;
 | 
									return err ? err : -1;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (memcmp(readbuf + use_offset, writebuf, use_len)) {
 | 
								if (memcmp(readbuf + use_offset,
 | 
				
			||||||
 | 
									   writebuf + (use_len_max * i) + use_offset,
 | 
				
			||||||
 | 
									   use_len)) {
 | 
				
			||||||
				pr_err("error: verify failed at %#llx\n",
 | 
									pr_err("error: verify failed at %#llx\n",
 | 
				
			||||||
						(long long)addr);
 | 
											(long long)addr);
 | 
				
			||||||
				errcnt += 1;
 | 
									errcnt += 1;
 | 
				
			||||||
| 
						 | 
					@ -566,8 +569,8 @@ static int __init mtd_oobtest_init(void)
 | 
				
			||||||
		if (bbt[i] || bbt[i + 1])
 | 
							if (bbt[i] || bbt[i + 1])
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		addr = (i + 1) * mtd->erasesize - mtd->writesize;
 | 
							addr = (i + 1) * mtd->erasesize - mtd->writesize;
 | 
				
			||||||
 | 
							prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
 | 
				
			||||||
		for (pg = 0; pg < cnt; ++pg) {
 | 
							for (pg = 0; pg < cnt; ++pg) {
 | 
				
			||||||
			prandom_bytes_state(&rnd_state, writebuf, sz);
 | 
					 | 
				
			||||||
			ops.mode      = MTD_OPS_AUTO_OOB;
 | 
								ops.mode      = MTD_OPS_AUTO_OOB;
 | 
				
			||||||
			ops.len       = 0;
 | 
								ops.len       = 0;
 | 
				
			||||||
			ops.retlen    = 0;
 | 
								ops.retlen    = 0;
 | 
				
			||||||
| 
						 | 
					@ -575,7 +578,7 @@ static int __init mtd_oobtest_init(void)
 | 
				
			||||||
			ops.oobretlen = 0;
 | 
								ops.oobretlen = 0;
 | 
				
			||||||
			ops.ooboffs   = 0;
 | 
								ops.ooboffs   = 0;
 | 
				
			||||||
			ops.datbuf    = NULL;
 | 
								ops.datbuf    = NULL;
 | 
				
			||||||
			ops.oobbuf    = writebuf;
 | 
								ops.oobbuf    = writebuf + pg * sz;
 | 
				
			||||||
			err = mtd_write_oob(mtd, addr, &ops);
 | 
								err = mtd_write_oob(mtd, addr, &ops);
 | 
				
			||||||
			if (err)
 | 
								if (err)
 | 
				
			||||||
				goto out;
 | 
									goto out;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,6 +175,11 @@ typedef enum {
 | 
				
			||||||
#define NAND_OWN_BUFFERS	0x00020000
 | 
					#define NAND_OWN_BUFFERS	0x00020000
 | 
				
			||||||
/* Chip may not exist, so silence any errors in scan */
 | 
					/* Chip may not exist, so silence any errors in scan */
 | 
				
			||||||
#define NAND_SCAN_SILENT_NODEV	0x00040000
 | 
					#define NAND_SCAN_SILENT_NODEV	0x00040000
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This option could be defined by controller drivers to protect against
 | 
				
			||||||
 | 
					 * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define NAND_USE_BOUNCE_BUFFER	0x00080000
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Autodetect nand buswidth with readid/onfi.
 | 
					 * Autodetect nand buswidth with readid/onfi.
 | 
				
			||||||
 * This suppose the driver will configure the hardware in 8 bits mode
 | 
					 * This suppose the driver will configure the hardware in 8 bits mode
 | 
				
			||||||
| 
						 | 
					@ -552,8 +557,7 @@ struct nand_buffers {
 | 
				
			||||||
 * @ecc:		[BOARDSPECIFIC] ECC control structure
 | 
					 * @ecc:		[BOARDSPECIFIC] ECC control structure
 | 
				
			||||||
 * @buffers:		buffer structure for read/write
 | 
					 * @buffers:		buffer structure for read/write
 | 
				
			||||||
 * @hwcontrol:		platform-specific hardware control structure
 | 
					 * @hwcontrol:		platform-specific hardware control structure
 | 
				
			||||||
 * @erase_cmd:		[INTERN] erase command write function, selectable due
 | 
					 * @erase:		[REPLACEABLE] erase function
 | 
				
			||||||
 *			to AND support.
 | 
					 | 
				
			||||||
 * @scan_bbt:		[REPLACEABLE] function to scan bad block table
 | 
					 * @scan_bbt:		[REPLACEABLE] function to scan bad block table
 | 
				
			||||||
 * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transferring
 | 
					 * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transferring
 | 
				
			||||||
 *			data from array to read regs (tR).
 | 
					 *			data from array to read regs (tR).
 | 
				
			||||||
| 
						 | 
					@ -637,7 +641,7 @@ struct nand_chip {
 | 
				
			||||||
	void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
 | 
						void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
 | 
				
			||||||
			int page_addr);
 | 
								int page_addr);
 | 
				
			||||||
	int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
 | 
						int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
 | 
				
			||||||
	void (*erase_cmd)(struct mtd_info *mtd, int page);
 | 
						int (*erase)(struct mtd_info *mtd, int page);
 | 
				
			||||||
	int (*scan_bbt)(struct mtd_info *mtd);
 | 
						int (*scan_bbt)(struct mtd_info *mtd);
 | 
				
			||||||
	int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
 | 
						int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
 | 
				
			||||||
			int status, int page);
 | 
								int status, int page);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,9 +101,6 @@ static inline void send_pfow_command(struct map_info *map,
 | 
				
			||||||
				unsigned long len, map_word *datum)
 | 
									unsigned long len, map_word *datum)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int bits_per_chip = map_bankwidth(map) * 8;
 | 
						int bits_per_chip = map_bankwidth(map) * 8;
 | 
				
			||||||
	int chipnum;
 | 
					 | 
				
			||||||
	struct lpddr_private *lpddr = map->fldrv_priv;
 | 
					 | 
				
			||||||
	chipnum = adr >> lpddr->chipshift;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	map_write(map, CMD(cmd_code), map->pfow_base + PFOW_COMMAND_CODE);
 | 
						map_write(map, CMD(cmd_code), map->pfow_base + PFOW_COMMAND_CODE);
 | 
				
			||||||
	map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)),
 | 
						map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										214
									
								
								include/linux/mtd/spi-nor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								include/linux/mtd/spi-nor.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,214 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2014 Freescale Semiconductor, Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __LINUX_MTD_SPI_NOR_H
 | 
				
			||||||
 | 
					#define __LINUX_MTD_SPI_NOR_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Note on opcode nomenclature: some opcodes have a format like
 | 
				
			||||||
 | 
					 * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
 | 
				
			||||||
 | 
					 * of I/O lines used for the opcode, address, and data (respectively). The
 | 
				
			||||||
 | 
					 * FUNCTION has an optional suffix of '4', to represent an opcode which
 | 
				
			||||||
 | 
					 * requires a 4-byte (32-bit) address.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Flash opcodes. */
 | 
				
			||||||
 | 
					#define SPINOR_OP_WREN		0x06	/* Write enable */
 | 
				
			||||||
 | 
					#define SPINOR_OP_RDSR		0x05	/* Read status register */
 | 
				
			||||||
 | 
					#define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual SPI) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad SPI) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_PP		0x02	/* Page program (up to 256 bytes) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_BE_4K		0x20	/* Erase 4KiB block */
 | 
				
			||||||
 | 
					#define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
 | 
				
			||||||
 | 
					#define SPINOR_OP_BE_32K	0x52	/* Erase 32KiB block */
 | 
				
			||||||
 | 
					#define SPINOR_OP_CHIP_ERASE	0xc7	/* Erase whole flash chip */
 | 
				
			||||||
 | 
					#define SPINOR_OP_SE		0xd8	/* Sector erase (usually 64KiB) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_RDID		0x9f	/* Read JEDEC ID */
 | 
				
			||||||
 | 
					#define SPINOR_OP_RDCR		0x35	/* Read configuration register */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ4		0x13	/* Read data bytes (low frequency) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ4_FAST	0x0c	/* Read data bytes (high frequency) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ4_1_1_2	0x3c	/* Read data bytes (Dual SPI) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad SPI) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
 | 
				
			||||||
 | 
					#define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Used for SST flashes only. */
 | 
				
			||||||
 | 
					#define SPINOR_OP_BP		0x02	/* Byte program */
 | 
				
			||||||
 | 
					#define SPINOR_OP_WRDI		0x04	/* Write disable */
 | 
				
			||||||
 | 
					#define SPINOR_OP_AAI_WP	0xad	/* Auto address increment word program */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Used for Macronix and Winbond flashes. */
 | 
				
			||||||
 | 
					#define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 | 
				
			||||||
 | 
					#define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Used for Spansion flashes only. */
 | 
				
			||||||
 | 
					#define SPINOR_OP_BRWR		0x17	/* Bank register write */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Status Register bits. */
 | 
				
			||||||
 | 
					#define SR_WIP			1	/* Write in progress */
 | 
				
			||||||
 | 
					#define SR_WEL			2	/* Write enable latch */
 | 
				
			||||||
 | 
					/* meaning of other SR_* bits may differ between vendors */
 | 
				
			||||||
 | 
					#define SR_BP0			4	/* Block protect 0 */
 | 
				
			||||||
 | 
					#define SR_BP1			8	/* Block protect 1 */
 | 
				
			||||||
 | 
					#define SR_BP2			0x10	/* Block protect 2 */
 | 
				
			||||||
 | 
					#define SR_SRWD			0x80	/* SR write protect */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SR_QUAD_EN_MX		0x40	/* Macronix Quad I/O */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Configuration Register bits. */
 | 
				
			||||||
 | 
					#define CR_QUAD_EN_SPAN		0x2	/* Spansion Quad I/O */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum read_mode {
 | 
				
			||||||
 | 
						SPI_NOR_NORMAL = 0,
 | 
				
			||||||
 | 
						SPI_NOR_FAST,
 | 
				
			||||||
 | 
						SPI_NOR_DUAL,
 | 
				
			||||||
 | 
						SPI_NOR_QUAD,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
 | 
				
			||||||
 | 
					 * @wren:		command for "Write Enable", or 0x00 for not required
 | 
				
			||||||
 | 
					 * @cmd:		command for operation
 | 
				
			||||||
 | 
					 * @cmd_pins:		number of pins to send @cmd (1, 2, 4)
 | 
				
			||||||
 | 
					 * @addr:		address for operation
 | 
				
			||||||
 | 
					 * @addr_pins:		number of pins to send @addr (1, 2, 4)
 | 
				
			||||||
 | 
					 * @addr_width:		number of address bytes
 | 
				
			||||||
 | 
					 *			(3,4, or 0 for address not required)
 | 
				
			||||||
 | 
					 * @mode:		mode data
 | 
				
			||||||
 | 
					 * @mode_pins:		number of pins to send @mode (1, 2, 4)
 | 
				
			||||||
 | 
					 * @mode_cycles:	number of mode cycles (0 for mode not required)
 | 
				
			||||||
 | 
					 * @dummy_cycles:	number of dummy cycles (0 for dummy not required)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spi_nor_xfer_cfg {
 | 
				
			||||||
 | 
						u8		wren;
 | 
				
			||||||
 | 
						u8		cmd;
 | 
				
			||||||
 | 
						u8		cmd_pins;
 | 
				
			||||||
 | 
						u32		addr;
 | 
				
			||||||
 | 
						u8		addr_pins;
 | 
				
			||||||
 | 
						u8		addr_width;
 | 
				
			||||||
 | 
						u8		mode;
 | 
				
			||||||
 | 
						u8		mode_pins;
 | 
				
			||||||
 | 
						u8		mode_cycles;
 | 
				
			||||||
 | 
						u8		dummy_cycles;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SPI_NOR_MAX_CMD_SIZE	8
 | 
				
			||||||
 | 
					enum spi_nor_ops {
 | 
				
			||||||
 | 
						SPI_NOR_OPS_READ = 0,
 | 
				
			||||||
 | 
						SPI_NOR_OPS_WRITE,
 | 
				
			||||||
 | 
						SPI_NOR_OPS_ERASE,
 | 
				
			||||||
 | 
						SPI_NOR_OPS_LOCK,
 | 
				
			||||||
 | 
						SPI_NOR_OPS_UNLOCK,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct spi_nor - Structure for defining a the SPI NOR layer
 | 
				
			||||||
 | 
					 * @mtd:		point to a mtd_info structure
 | 
				
			||||||
 | 
					 * @lock:		the lock for the read/write/erase/lock/unlock operations
 | 
				
			||||||
 | 
					 * @dev:		point to a spi device, or a spi nor controller device.
 | 
				
			||||||
 | 
					 * @page_size:		the page size of the SPI NOR
 | 
				
			||||||
 | 
					 * @addr_width:		number of address bytes
 | 
				
			||||||
 | 
					 * @erase_opcode:	the opcode for erasing a sector
 | 
				
			||||||
 | 
					 * @read_opcode:	the read opcode
 | 
				
			||||||
 | 
					 * @read_dummy:		the dummy needed by the read operation
 | 
				
			||||||
 | 
					 * @program_opcode:	the program opcode
 | 
				
			||||||
 | 
					 * @flash_read:		the mode of the read
 | 
				
			||||||
 | 
					 * @sst_write_second:	used by the SST write operation
 | 
				
			||||||
 | 
					 * @cfg:		used by the read_xfer/write_xfer
 | 
				
			||||||
 | 
					 * @cmd_buf:		used by the write_reg
 | 
				
			||||||
 | 
					 * @prepare:		[OPTIONAL] do some preparations for the
 | 
				
			||||||
 | 
					 *			read/write/erase/lock/unlock operations
 | 
				
			||||||
 | 
					 * @unprepare:		[OPTIONAL] do some post work after the
 | 
				
			||||||
 | 
					 *			read/write/erase/lock/unlock operations
 | 
				
			||||||
 | 
					 * @read_xfer:		[OPTIONAL] the read fundamental primitive
 | 
				
			||||||
 | 
					 * @write_xfer:		[OPTIONAL] the writefundamental primitive
 | 
				
			||||||
 | 
					 * @read_reg:		[DRIVER-SPECIFIC] read out the register
 | 
				
			||||||
 | 
					 * @write_reg:		[DRIVER-SPECIFIC] write data to the register
 | 
				
			||||||
 | 
					 * @read_id:		[REPLACEABLE] read out the ID data, and find
 | 
				
			||||||
 | 
					 *			the proper spi_device_id
 | 
				
			||||||
 | 
					 * @wait_till_ready:	[REPLACEABLE] wait till the NOR becomes ready
 | 
				
			||||||
 | 
					 * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR
 | 
				
			||||||
 | 
					 * @write:		[DRIVER-SPECIFIC] write data to the SPI NOR
 | 
				
			||||||
 | 
					 * @erase:		[DRIVER-SPECIFIC] erase a sector of the SPI NOR
 | 
				
			||||||
 | 
					 *			at the offset @offs
 | 
				
			||||||
 | 
					 * @priv:		the private data
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spi_nor {
 | 
				
			||||||
 | 
						struct mtd_info		*mtd;
 | 
				
			||||||
 | 
						struct mutex		lock;
 | 
				
			||||||
 | 
						struct device		*dev;
 | 
				
			||||||
 | 
						u32			page_size;
 | 
				
			||||||
 | 
						u8			addr_width;
 | 
				
			||||||
 | 
						u8			erase_opcode;
 | 
				
			||||||
 | 
						u8			read_opcode;
 | 
				
			||||||
 | 
						u8			read_dummy;
 | 
				
			||||||
 | 
						u8			program_opcode;
 | 
				
			||||||
 | 
						enum read_mode		flash_read;
 | 
				
			||||||
 | 
						bool			sst_write_second;
 | 
				
			||||||
 | 
						struct spi_nor_xfer_cfg	cfg;
 | 
				
			||||||
 | 
						u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
 | 
				
			||||||
 | 
						void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
 | 
				
			||||||
 | 
						int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
 | 
				
			||||||
 | 
								 u8 *buf, size_t len);
 | 
				
			||||||
 | 
						int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
 | 
				
			||||||
 | 
								  u8 *buf, size_t len);
 | 
				
			||||||
 | 
						int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
 | 
				
			||||||
 | 
						int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
 | 
				
			||||||
 | 
								int write_enable);
 | 
				
			||||||
 | 
						const struct spi_device_id *(*read_id)(struct spi_nor *nor);
 | 
				
			||||||
 | 
						int (*wait_till_ready)(struct spi_nor *nor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int (*read)(struct spi_nor *nor, loff_t from,
 | 
				
			||||||
 | 
								size_t len, size_t *retlen, u_char *read_buf);
 | 
				
			||||||
 | 
						void (*write)(struct spi_nor *nor, loff_t to,
 | 
				
			||||||
 | 
								size_t len, size_t *retlen, const u_char *write_buf);
 | 
				
			||||||
 | 
						int (*erase)(struct spi_nor *nor, loff_t offs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void *priv;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * spi_nor_scan() - scan the SPI NOR
 | 
				
			||||||
 | 
					 * @nor:	the spi_nor structure
 | 
				
			||||||
 | 
					 * @id:		the spi_device_id provided by the driver
 | 
				
			||||||
 | 
					 * @mode:	the read mode supported by the driver
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The drivers can use this fuction to scan the SPI NOR.
 | 
				
			||||||
 | 
					 * In the scanning, it will try to get all the necessary information to
 | 
				
			||||||
 | 
					 * fill the mtd_info{} and the spi_nor{}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The board may assigns a spi_device_id with @id which be used to compared with
 | 
				
			||||||
 | 
					 * the spi_device_id detected by the scanning.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 for success, others for failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
 | 
				
			||||||
 | 
								enum read_mode mode);
 | 
				
			||||||
 | 
					extern const struct spi_device_id spi_nor_ids[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * spi_nor_match_id() - find the spi_device_id by the name
 | 
				
			||||||
 | 
					 * @name:	the name of the spi_device_id
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The drivers use this function to find the spi_device_id
 | 
				
			||||||
 | 
					 * specified by the @name.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: returns the right spi_device_id pointer on success,
 | 
				
			||||||
 | 
					 *         and returns NULL on failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const struct spi_device_id *spi_nor_match_id(char *name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@
 | 
				
			||||||
enum bch_ecc {
 | 
					enum bch_ecc {
 | 
				
			||||||
	BCH4_ECC = 0,
 | 
						BCH4_ECC = 0,
 | 
				
			||||||
	BCH8_ECC,
 | 
						BCH8_ECC,
 | 
				
			||||||
 | 
						BCH16_ECC,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* ELM support 8 error syndrome process */
 | 
					/* ELM support 8 error syndrome process */
 | 
				
			||||||
| 
						 | 
					@ -38,7 +39,7 @@ struct elm_errorvec {
 | 
				
			||||||
	bool error_reported;
 | 
						bool error_reported;
 | 
				
			||||||
	bool error_uncorrectable;
 | 
						bool error_uncorrectable;
 | 
				
			||||||
	int error_count;
 | 
						int error_count;
 | 
				
			||||||
	int error_loc[ERROR_VECTOR_MAX];
 | 
						int error_loc[16];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
 | 
					void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,8 @@ enum omap_ecc {
 | 
				
			||||||
	OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
 | 
						OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
 | 
				
			||||||
	/* 8-bit  ECC calculation by GPMC, Error detection by ELM */
 | 
						/* 8-bit  ECC calculation by GPMC, Error detection by ELM */
 | 
				
			||||||
	OMAP_ECC_BCH8_CODE_HW,
 | 
						OMAP_ECC_BCH8_CODE_HW,
 | 
				
			||||||
 | 
						/* 16-bit ECC calculation by GPMC, Error detection by ELM */
 | 
				
			||||||
 | 
						OMAP_ECC_BCH16_CODE_HW,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gpmc_nand_regs {
 | 
					struct gpmc_nand_regs {
 | 
				
			||||||
| 
						 | 
					@ -50,6 +52,9 @@ struct gpmc_nand_regs {
 | 
				
			||||||
	void __iomem	*gpmc_bch_result1[GPMC_BCH_NUM_REMAINDER];
 | 
						void __iomem	*gpmc_bch_result1[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
	void __iomem	*gpmc_bch_result2[GPMC_BCH_NUM_REMAINDER];
 | 
						void __iomem	*gpmc_bch_result2[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
	void __iomem	*gpmc_bch_result3[GPMC_BCH_NUM_REMAINDER];
 | 
						void __iomem	*gpmc_bch_result3[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
 | 
						void __iomem	*gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
 | 
						void __iomem	*gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
 | 
						void __iomem	*gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct omap_nand_platform_data {
 | 
					struct omap_nand_platform_data {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data {
 | 
				
			||||||
	/* use an flash-based bad block table */
 | 
						/* use an flash-based bad block table */
 | 
				
			||||||
	bool	flash_bbt;
 | 
						bool	flash_bbt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* requested ECC strength and ECC step size */
 | 
				
			||||||
 | 
						int ecc_strength, ecc_step_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const struct mtd_partition		*parts[NUM_CHIP_SELECT];
 | 
						const struct mtd_partition		*parts[NUM_CHIP_SELECT];
 | 
				
			||||||
	unsigned int				nr_parts[NUM_CHIP_SELECT];
 | 
						unsigned int				nr_parts[NUM_CHIP_SELECT];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,6 +109,7 @@ struct mtd_write_req {
 | 
				
			||||||
#define MTD_CAP_RAM		(MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
 | 
					#define MTD_CAP_RAM		(MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
 | 
				
			||||||
#define MTD_CAP_NORFLASH	(MTD_WRITEABLE | MTD_BIT_WRITEABLE)
 | 
					#define MTD_CAP_NORFLASH	(MTD_WRITEABLE | MTD_BIT_WRITEABLE)
 | 
				
			||||||
#define MTD_CAP_NANDFLASH	(MTD_WRITEABLE)
 | 
					#define MTD_CAP_NANDFLASH	(MTD_WRITEABLE)
 | 
				
			||||||
 | 
					#define MTD_CAP_NVRAM		(MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */
 | 
					/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */
 | 
				
			||||||
#define MTD_NANDECC_OFF		0	// Switch off ECC (Not recommended)
 | 
					#define MTD_NANDECC_OFF		0	// Switch off ECC (Not recommended)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue