diff options
| author | Linus Torvalds <[email protected]> | 2015-04-22 12:00:44 -0700 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2015-04-22 12:00:44 -0700 | 
| commit | a62d016cece2fce1d5e4eedf36b17f03a7a5c78e (patch) | |
| tree | ac37b4835be5f4fe0e04611fdb40c85abb98ec78 /drivers/mtd/nand/mxc_nand.c | |
| parent | 7c034dfd58bbc056280262887acf5b7a98944d0a (diff) | |
| parent | 3e550d2396d9deef77328237ed992e19dcfefca5 (diff) | |
Merge tag 'for-linus-20150422' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris:
 "Common MTD:
   - Add Kconfig option for keeping both the 'master' and 'partition'
     MTDs registered as devices.  This would really make a better
     default if we could do it over, as it allows a lot more flexibility
     in (1) determining the flash topology of the system from user-space
     and (2) adding temporary partitions at runtime (ioctl(BLKPG)).
     Unfortunately, this would possibly cause user-space breakage, as it
     will cause renumbering of the /dev/mtdX devices.  We'll see if we
     can change this in the future, as there have already been a few
     people looking for this feature, and I know others have just been
     working around our current limitations instead of fixing them this
     way.
   - Along with the previous change, add some additional information to
     sysfs, so user-space can read the offset of each partition within
     its master device
  SPI NOR:
   - add new device tree compatible binding to represent the
     mostly-compatible class of SPI NOR flash which can be detected by
     their extended JEDEC ID bytes, cutting down the duplication of our
     ID tables
   - misc.  new IDs
  Various other miscellaneous fixes and changes"
* tag 'for-linus-20150422' of git://git.infradead.org/linux-mtd: (53 commits)
  mtd: spi-nor: Add support for Macronix mx25u6435f serial flash
  mtd: spi-nor: Add support for Winbond w25q64dw serial flash
  mtd: spi-nor: add support for the Winbond W25X05 flash
  mtd: spi-nor: support en25s64 device
  mtd: m25p80: bind to "nor-jedec" ID, for auto-detection
  Documentation: devicetree: m25p80: add "nor-jedec" binding
  mtd: Make MTD tests cancelable
  mtd: mtd_oobtest: Fix bitflip_limit usage in test case 3
  mtd: docg3: remove invalid __exit annotations
  mtd: fsl_ifc_nand: use msecs_to_jiffies for time conversion
  mtd: atmel_nand: don't map the ROM table if no pmecc table offset in DT
  mtd: atmel_nand: add a definition for the oob reserved bytes
  mtd: part: Remove partition overlap checks
  mtd: part: Add sysfs variable for offset of partition
  mtd: part: Create the master device node when partitioned
  mtd: ts5500_flash: Fix typo in MODULE_DESCRIPTION in ts5500_flash.c
  mtd: denali: Disable sub-page writes in Denali NAND driver
  mtd: pxa3xx_nand: cleanup wait_for_completion handling
  mtd: nand: gpmi: Check for scan_bbt() error
  mtd: nand: gpmi: fixup return type of wait_for_completion_timeout
  ...
Diffstat (limited to 'drivers/mtd/nand/mxc_nand.c')
| -rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 146 | 
1 files changed, 93 insertions, 53 deletions
| diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index a8f550fec35e..372e0e38f59b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -386,26 +386,51 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)  /* This function polls the NANDFC to wait for the basic operation to   * complete by checking the INT bit of config2 register.   */ -static void wait_op_done(struct mxc_nand_host *host, int useirq) +static int wait_op_done(struct mxc_nand_host *host, int useirq)  { -	int max_retries = 8000; +	int ret = 0; + +	/* +	 * If operation is already complete, don't bother to setup an irq or a +	 * loop. +	 */ +	if (host->devtype_data->check_int(host)) +		return 0;  	if (useirq) { -		if (!host->devtype_data->check_int(host)) { -			reinit_completion(&host->op_completion); -			irq_control(host, 1); -			wait_for_completion(&host->op_completion); +		unsigned long timeout; + +		reinit_completion(&host->op_completion); + +		irq_control(host, 1); + +		timeout = wait_for_completion_timeout(&host->op_completion, HZ); +		if (!timeout && !host->devtype_data->check_int(host)) { +			dev_dbg(host->dev, "timeout waiting for irq\n"); +			ret = -ETIMEDOUT;  		}  	} else { -		while (max_retries-- > 0) { -			if (host->devtype_data->check_int(host)) -				break; +		int max_retries = 8000; +		int done; +		do {  			udelay(1); + +			done = host->devtype_data->check_int(host); +			if (done) +				break; + +		} while (--max_retries); + +		if (!done) { +			dev_dbg(host->dev, "timeout polling for completion\n"); +			ret = -ETIMEDOUT;  		} -		if (max_retries < 0) -			pr_debug("%s: INT not set\n", __func__);  	} + +	WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); + +	return ret;  }  static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) @@ -527,30 +552,17 @@ static void send_page_v1(struct mtd_info *mtd, unsigned int ops)  static void send_read_id_v3(struct mxc_nand_host *host)  { -	struct nand_chip *this = &host->nand; -  	/* Read ID into main buffer */  	writel(NFC_ID, NFC_V3_LAUNCH);  	wait_op_done(host, true);  	memcpy32_fromio(host->data_buf, host->main_area0, 16); - -	if (this->options & NAND_BUSWIDTH_16) { -		/* compress the ID info */ -		host->data_buf[1] = host->data_buf[2]; -		host->data_buf[2] = host->data_buf[4]; -		host->data_buf[3] = host->data_buf[6]; -		host->data_buf[4] = host->data_buf[8]; -		host->data_buf[5] = host->data_buf[10]; -	}  }  /* Request the NANDFC to perform a read of the NAND device ID. */  static void send_read_id_v1_v2(struct mxc_nand_host *host)  { -	struct nand_chip *this = &host->nand; -  	/* NANDFC buffer 0 is used for device ID output */  	writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); @@ -560,15 +572,6 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)  	wait_op_done(host, true);  	memcpy32_fromio(host->data_buf, host->main_area0, 16); - -	if (this->options & NAND_BUSWIDTH_16) { -		/* compress the ID info */ -		host->data_buf[1] = host->data_buf[2]; -		host->data_buf[2] = host->data_buf[4]; -		host->data_buf[3] = host->data_buf[6]; -		host->data_buf[4] = host->data_buf[8]; -		host->data_buf[5] = host->data_buf[10]; -	}  }  static uint16_t get_dev_status_v3(struct mxc_nand_host *host) @@ -694,9 +697,17 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)  	if (host->status_request)  		return host->devtype_data->get_dev_status(host) & 0xFF; -	ret = *(uint8_t *)(host->data_buf + host->buf_start); -	host->buf_start++; +	if (nand_chip->options & NAND_BUSWIDTH_16) { +		/* only take the lower byte of each word */ +		ret = *(uint16_t *)(host->data_buf + host->buf_start); + +		host->buf_start += 2; +	} else { +		ret = *(uint8_t *)(host->data_buf + host->buf_start); +		host->buf_start++; +	} +	pr_debug("%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start);  	return ret;  } @@ -825,6 +836,12 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom)  	}  } +/* + * MXC NANDFC can only perform full page+spare or spare-only read/write.  When + * the upper layers perform a read/write buf operation, the saved column address + * is used to index into the full page. So usually this function is called with + * column == 0 (unless no column cycle is needed indicated by column == -1) + */  static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)  {  	struct nand_chip *nand_chip = mtd->priv; @@ -832,16 +849,13 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)  	/* Write out column address, if necessary */  	if (column != -1) { -		/* -		 * MXC NANDFC can only perform full page+spare or -		 * spare-only read/write.  When the upper layers -		 * perform a read/write buf operation, the saved column -		  * address is used to index into the full page. -		 */ -		host->devtype_data->send_addr(host, 0, page_addr == -1); +		host->devtype_data->send_addr(host, column & 0xff, +					      page_addr == -1);  		if (mtd->writesize > 512)  			/* another col addr cycle for 2k page */ -			host->devtype_data->send_addr(host, 0, false); +			host->devtype_data->send_addr(host, +						      (column >> 8) & 0xff, +						      false);  	}  	/* Write out page address, if necessary */ @@ -903,7 +917,7 @@ static void preset_v1(struct mtd_info *mtd)  	struct mxc_nand_host *host = nand_chip->priv;  	uint16_t config1 = 0; -	if (nand_chip->ecc.mode == NAND_ECC_HW) +	if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize)  		config1 |= NFC_V1_V2_CONFIG1_ECC_EN;  	if (!host->devtype_data->irqpending_quirk) @@ -931,9 +945,6 @@ static void preset_v2(struct mtd_info *mtd)  	struct mxc_nand_host *host = nand_chip->priv;  	uint16_t config1 = 0; -	if (nand_chip->ecc.mode == NAND_ECC_HW) -		config1 |= NFC_V1_V2_CONFIG1_ECC_EN; -  	config1 |= NFC_V2_CONFIG1_FP_INT;  	if (!host->devtype_data->irqpending_quirk) @@ -942,6 +953,9 @@ static void preset_v2(struct mtd_info *mtd)  	if (mtd->writesize) {  		uint16_t pages_per_block = mtd->erasesize / mtd->writesize; +		if (nand_chip->ecc.mode == NAND_ECC_HW) +			config1 |= NFC_V1_V2_CONFIG1_ECC_EN; +  		host->eccsize = get_eccsize(mtd);  		if (host->eccsize == 4)  			config1 |= NFC_V2_CONFIG1_ECC_MODE_4; @@ -999,9 +1013,6 @@ static void preset_v3(struct mtd_info *mtd)  		NFC_V3_CONFIG2_INT_MSK |  		NFC_V3_CONFIG2_NUM_ADDR_PHASE0; -	if (chip->ecc.mode == NAND_ECC_HW) -		config2 |= NFC_V3_CONFIG2_ECC_EN; -  	addr_phases = fls(chip->pagemask) >> 3;  	if (mtd->writesize == 2048) { @@ -1016,6 +1027,9 @@ static void preset_v3(struct mtd_info *mtd)  	}  	if (mtd->writesize) { +		if (chip->ecc.mode == NAND_ECC_HW) +			config2 |= NFC_V3_CONFIG2_ECC_EN; +  		config2 |= NFC_V3_CONFIG2_PPB(  				ffs(mtd->erasesize / mtd->writesize) - 6,  				host->devtype_data->ppb_shift); @@ -1066,6 +1080,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,  		host->status_request = true;  		host->devtype_data->send_cmd(host, command, true); +		WARN_ONCE(column != -1 || page_addr != -1, +			  "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", +			  command, column, page_addr);  		mxc_do_addr_cycle(mtd, column, page_addr);  		break; @@ -1079,7 +1096,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,  		command = NAND_CMD_READ0; /* only READ0 is valid */  		host->devtype_data->send_cmd(host, command, false); -		mxc_do_addr_cycle(mtd, column, page_addr); +		WARN_ONCE(column < 0, +			  "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", +			  command, column, page_addr); +		mxc_do_addr_cycle(mtd, 0, page_addr);  		if (mtd->writesize > 512)  			host->devtype_data->send_cmd(host, @@ -1100,7 +1120,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,  		host->buf_start = column;  		host->devtype_data->send_cmd(host, command, false); -		mxc_do_addr_cycle(mtd, column, page_addr); +		WARN_ONCE(column < -1, +			  "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", +			  command, column, page_addr); +		mxc_do_addr_cycle(mtd, 0, page_addr);  		break;  	case NAND_CMD_PAGEPROG: @@ -1108,6 +1131,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,  		copy_spare(mtd, false);  		host->devtype_data->send_page(mtd, NFC_INPUT);  		host->devtype_data->send_cmd(host, command, true); +		WARN_ONCE(column != -1 || page_addr != -1, +			  "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", +			  command, column, page_addr);  		mxc_do_addr_cycle(mtd, column, page_addr);  		break; @@ -1115,15 +1141,29 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,  		host->devtype_data->send_cmd(host, command, true);  		mxc_do_addr_cycle(mtd, column, page_addr);  		host->devtype_data->send_read_id(host); -		host->buf_start = column; +		host->buf_start = 0;  		break;  	case NAND_CMD_ERASE1:  	case NAND_CMD_ERASE2:  		host->devtype_data->send_cmd(host, command, false); +		WARN_ONCE(column != -1, +			  "Unexpected column value (cmd=%u, col=%d)\n", +			  command, column);  		mxc_do_addr_cycle(mtd, column, page_addr);  		break; +	case NAND_CMD_PARAM: +		host->devtype_data->send_cmd(host, command, false); +		mxc_do_addr_cycle(mtd, column, page_addr); +		host->devtype_data->send_page(mtd, NFC_OUTPUT); +		memcpy32_fromio(host->data_buf, host->main_area0, 512); +		host->buf_start = 0; +		break; +	default: +		WARN_ONCE(1, "Unimplemented command (cmd=%u)\n", +			  command); +		break;  	}  } |