mtd: sf: Add support of sst26wf* flash ICs protection ops
sst26wf flash series block protection implementation differs from other SST series, so add specific implementation flash_lock/flash_unlock/flash_is_locked functions for sst26wf flash ICs. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
This commit is contained in:
		
							parent
							
								
									40df6b3e18
								
							
						
					
					
						commit
						3d4fed87a5
					
				|  | @ -87,6 +87,19 @@ enum spi_nor_option_flags { | ||||||
| 
 | 
 | ||||||
| /* SST specific */ | /* SST specific */ | ||||||
| #ifdef CONFIG_SPI_FLASH_SST | #ifdef CONFIG_SPI_FLASH_SST | ||||||
|  | #define SST26_CMD_READ_BPR		0x72 | ||||||
|  | #define SST26_CMD_WRITE_BPR		0x42 | ||||||
|  | 
 | ||||||
|  | #define SST26_BPR_8K_NUM		4 | ||||||
|  | #define SST26_MAX_BPR_REG_LEN		(18 + 1) | ||||||
|  | #define SST26_BOUND_REG_SIZE		((32 + SST26_BPR_8K_NUM * 8) * SZ_1K) | ||||||
|  | 
 | ||||||
|  | enum lock_ctl { | ||||||
|  | 	SST26_CTL_LOCK, | ||||||
|  | 	SST26_CTL_UNLOCK, | ||||||
|  | 	SST26_CTL_CHECK | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| # define CMD_SST_BP		0x02    /* Byte Program */ | # define CMD_SST_BP		0x02    /* Byte Program */ | ||||||
| # define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */ | # define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ | ||||||
| #include <spi.h> | #include <spi.h> | ||||||
| #include <spi_flash.h> | #include <spi_flash.h> | ||||||
| #include <linux/log2.h> | #include <linux/log2.h> | ||||||
|  | #include <linux/sizes.h> | ||||||
| #include <dma.h> | #include <dma.h> | ||||||
| 
 | 
 | ||||||
| #include "sf_internal.h" | #include "sf_internal.h" | ||||||
|  | @ -541,6 +542,164 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_SPI_FLASH_SST | #ifdef CONFIG_SPI_FLASH_SST | ||||||
|  | static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl) | ||||||
|  | { | ||||||
|  | 	switch (ctl) { | ||||||
|  | 		case SST26_CTL_LOCK: | ||||||
|  | 			cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8); | ||||||
|  | 			break; | ||||||
|  | 		case SST26_CTL_UNLOCK: | ||||||
|  | 			cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8); | ||||||
|  | 			break; | ||||||
|  | 		case SST26_CTL_CHECK: | ||||||
|  | 			return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * sst26wf016/sst26wf032/sst26wf064 have next block protection: | ||||||
|  |  * 4x   - 8  KByte blocks - read & write protection bits - upper addresses | ||||||
|  |  * 1x   - 32 KByte blocks - write protection bits | ||||||
|  |  * rest - 64 KByte blocks - write protection bits | ||||||
|  |  * 1x   - 32 KByte blocks - write protection bits | ||||||
|  |  * 4x   - 8  KByte blocks - read & write protection bits - lower addresses | ||||||
|  |  * | ||||||
|  |  * We'll support only per 64k lock/unlock so lower and upper 64 KByte region | ||||||
|  |  * will be treated as single block. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Lock, unlock or check lock status of the flash region of the flash (depending | ||||||
|  |  * on the lock_ctl value) | ||||||
|  |  */ | ||||||
|  | static int sst26_lock_ctl(struct spi_flash *flash, u32 ofs, size_t len, enum lock_ctl ctl) | ||||||
|  | { | ||||||
|  | 	u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size; | ||||||
|  | 	bool lower_64k = false, upper_64k = false; | ||||||
|  | 	u8 cmd, bpr_buff[SST26_MAX_BPR_REG_LEN] = {}; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	/* Check length and offset for 64k alignment */ | ||||||
|  | 	if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	if (ofs + len > flash->size) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */ | ||||||
|  | 	if (flash->size != SZ_2M && | ||||||
|  | 	    flash->size != SZ_4M && | ||||||
|  | 	    flash->size != SZ_8M) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	bpr_size = 2 + (flash->size / SZ_64K / 8); | ||||||
|  | 
 | ||||||
|  | 	cmd = SST26_CMD_READ_BPR; | ||||||
|  | 	ret = spi_flash_read_common(flash, &cmd, 1, bpr_buff, bpr_size); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		printf("SF: fail to read block-protection register\n"); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rptr_64k = min_t(u32, ofs + len , flash->size - SST26_BOUND_REG_SIZE); | ||||||
|  | 	lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE); | ||||||
|  | 
 | ||||||
|  | 	upper_64k = ((ofs + len) > (flash->size - SST26_BOUND_REG_SIZE)); | ||||||
|  | 	lower_64k = (ofs < SST26_BOUND_REG_SIZE); | ||||||
|  | 
 | ||||||
|  | 	/* Lower bits in block-protection register are about 64k region */ | ||||||
|  | 	bpr_ptr = lptr_64k / SZ_64K - 1; | ||||||
|  | 
 | ||||||
|  | 	/* Process 64K blocks region */ | ||||||
|  | 	while (lptr_64k < rptr_64k) { | ||||||
|  | 		if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | ||||||
|  | 			return EACCES; | ||||||
|  | 
 | ||||||
|  | 		bpr_ptr++; | ||||||
|  | 		lptr_64k += SZ_64K; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* 32K and 8K region bits in BPR are after 64k region bits */ | ||||||
|  | 	bpr_ptr = (flash->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K; | ||||||
|  | 
 | ||||||
|  | 	/* Process lower 32K block region */ | ||||||
|  | 	if (lower_64k) | ||||||
|  | 		if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | ||||||
|  | 			return EACCES; | ||||||
|  | 
 | ||||||
|  | 	bpr_ptr++; | ||||||
|  | 
 | ||||||
|  | 	/* Process upper 32K block region */ | ||||||
|  | 	if (upper_64k) | ||||||
|  | 		if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | ||||||
|  | 			return EACCES; | ||||||
|  | 
 | ||||||
|  | 	bpr_ptr++; | ||||||
|  | 
 | ||||||
|  | 	/* Process lower 8K block regions */ | ||||||
|  | 	for (i = 0; i < SST26_BPR_8K_NUM; i++) { | ||||||
|  | 		if (lower_64k) | ||||||
|  | 			if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | ||||||
|  | 				return EACCES; | ||||||
|  | 
 | ||||||
|  | 		/* In 8K area BPR has both read and write protection bits */ | ||||||
|  | 		bpr_ptr += 2; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Process upper 8K block regions */ | ||||||
|  | 	for (i = 0; i < SST26_BPR_8K_NUM; i++) { | ||||||
|  | 		if (upper_64k) | ||||||
|  | 			if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) | ||||||
|  | 				return EACCES; | ||||||
|  | 
 | ||||||
|  | 		/* In 8K area BPR has both read and write protection bits */ | ||||||
|  | 		bpr_ptr += 2; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* If we check region status we don't need to write BPR back */ | ||||||
|  | 	if (ctl == SST26_CTL_CHECK) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	cmd = SST26_CMD_WRITE_BPR; | ||||||
|  | 	ret = spi_flash_write_common(flash, &cmd, 1, bpr_buff, bpr_size); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		printf("SF: fail to write block-protection register\n"); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sst26_unlock(struct spi_flash *flash, u32 ofs, size_t len) | ||||||
|  | { | ||||||
|  | 	return sst26_lock_ctl(flash, ofs, len, SST26_CTL_UNLOCK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sst26_lock(struct spi_flash *flash, u32 ofs, size_t len) | ||||||
|  | { | ||||||
|  | 	return sst26_lock_ctl(flash, ofs, len, SST26_CTL_LOCK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Returns EACCES (positive value) if region is locked, 0 if region is unlocked, | ||||||
|  |  * and negative on errors. | ||||||
|  |  */ | ||||||
|  | static int sst26_is_locked(struct spi_flash *flash, u32 ofs, size_t len) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * is_locked function is used for check before reading or erasing flash | ||||||
|  | 	 * region, so offset and length might be not 64k allighned, so adjust | ||||||
|  | 	 * them to be 64k allighned as sst26_lock_ctl works only with 64k | ||||||
|  | 	 * allighned regions. | ||||||
|  | 	 */ | ||||||
|  | 	ofs -= ofs & (SZ_64K - 1); | ||||||
|  | 	len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len; | ||||||
|  | 
 | ||||||
|  | 	return sst26_lock_ctl(flash, ofs, len, SST26_CTL_CHECK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) | static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) | ||||||
| { | { | ||||||
| 	struct spi_slave *spi = flash->spi; | 	struct spi_slave *spi = flash->spi; | ||||||
|  | @ -1033,6 +1192,15 @@ int spi_flash_scan(struct spi_flash *flash) | ||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* sst26wf series block protection implementation differs from other series */ | ||||||
|  | #if defined(CONFIG_SPI_FLASH_SST) | ||||||
|  | 	if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST && info->id[1] == 0x26) { | ||||||
|  | 		flash->flash_lock = sst26_lock; | ||||||
|  | 		flash->flash_unlock = sst26_unlock; | ||||||
|  | 		flash->flash_is_locked = sst26_is_locked; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	/* Compute the flash size */ | 	/* Compute the flash size */ | ||||||
| 	flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0; | 	flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0; | ||||||
| 	flash->page_size = info->page_size; | 	flash->page_size = info->page_size; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue