From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jagan Teki Date: Sat, 7 Nov 2015 19:16:33 +0530 Subject: [U-Boot] [PATCH v6 19/23] sf: Use MTD lock operations In-Reply-To: <1446903997-1864-1-git-send-email-jteki@openedev.com> References: <1446903997-1864-1-git-send-email-jteki@openedev.com> Message-ID: <1446903997-1864-20-git-send-email-jteki@openedev.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Since the spi-flash framework itself is using core MTD functionalities this patch uses lock operation from mtd. Fabio Estevam Signed-off-by: Jagan Teki --- drivers/mtd/spi/sf_internal.h | 9 - drivers/mtd/spi/sf_ops.c | 383 ++++++++++++++++++++++-------------------- include/spi_flash.h | 34 ++-- 3 files changed, 220 insertions(+), 206 deletions(-) diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index fda49e9..677f582 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -163,15 +163,6 @@ int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, const void *data, size_t data_len); -/* Lock stmicro spi flash region */ -int stm_lock(struct spi_flash *flash, u32 ofs, size_t len); - -/* Unlock stmicro spi flash region */ -int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len); - -/* Check if a stmicro spi flash region is completely locked */ -int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len); - /* Enable writing on the SPI flash */ static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) { diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c index 5fa6113..a536444 100644 --- a/drivers/mtd/spi/sf_ops.c +++ b/drivers/mtd/spi/sf_ops.c @@ -256,6 +256,199 @@ static int spi_flash_cmd_wait_ready(struct spi_flash *flash, return -ETIMEDOUT; } +#ifdef CONFIG_SPI_FLASH_STMICRO +static void stm_get_locked_range(struct spi_flash *flash, u8 sr, loff_t *ofs, + uint64_t *len) +{ + struct mtd_info *mtd = flash->mtd; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + int shift = ffs(mask) - 1; + int pow; + + if (!(sr & mask)) { + /* No protection */ + *ofs = 0; + *len = 0; + } else { + pow = ((sr & mask) ^ mask) >> shift; + *len = mtd->size >> pow; + *ofs = mtd->size - *len; + } +} + +/* + * Return 1 if the entire region is locked, 0 otherwise + */ +static int stm_is_locked_sr(struct spi_flash *flash, loff_t ofs, uint64_t len, + u8 sr) +{ + loff_t lock_offs; + uint64_t lock_len; + + stm_get_locked_range(flash, sr, &lock_offs, &lock_len); + + return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); +} + +/* + * Check if a region of the flash is (completely) locked. See stm_lock() for + * more info. + * + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and + * negative on errors. + */ +static int stm_is_locked(struct spi_flash *flash, loff_t ofs, uint64_t len) +{ + int status; + u8 sr; + + status = read_sr(flash, &sr); + if (status < 0) + return status; + + return stm_is_locked_sr(flash, ofs, len, sr); +} + +/* + * Lock a region of the flash. Compatible with ST Micro and similar flash. + * Supports only the block protection bits BP{0,1,2} in the status register + * (SR). Does not support these features found in newer SR bitfields: + * - TB: top/bottom protect - only handle TB=0 (top protect) + * - SEC: sector/block protect - only handle SEC=0 (block protect) + * - CMP: complement protect - only support CMP=0 (range is not complemented) + * + * Sample table portion for 8MB flash (Winbond w25q64fw): + * + * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion + * -------------------------------------------------------------------------- + * X | X | 0 | 0 | 0 | NONE | NONE + * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 + * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 + * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 + * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 + * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 + * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 + * X | X | 1 | 1 | 1 | 8 MB | ALL + * + * Returns negative on errors, 0 on success. + */ +static int stm_lock(struct spi_flash *flash, loff_t ofs, uint64_t len) +{ + struct mtd_info *mtd = flash->mtd; + u8 status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + read_sr(flash, &status_old); + + /* SPI NOR always locks to the end */ + if (ofs + len != mtd->size) { + /* Does combined region extend to end? */ + if (!stm_is_locked_sr(flash, ofs + len, mtd->size - ofs - len, + status_old)) + return -EINVAL; + len = mtd->size - ofs; + } + + /* + * Need smallest pow such that: + * + * 1 / (2^pow) <= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) + */ + pow = ilog2(mtd->size) - ilog2(len); + val = mask - (pow << shift); + if (val & ~mask) + return -EINVAL; + + /* Don't "lock" with no region! */ + if (!(val & mask)) + return -EINVAL; + + status_new = (status_old & ~mask) | val; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & mask) <= (status_old & mask)) + return -EINVAL; + + write_sr(flash, status_new); + + return 0; +} + +/* + * Unlock a region of the flash. See stm_lock() for more info + * + * Returns negative on errors, 0 on success. + */ +static int stm_unlock(struct spi_flash *flash, loff_t ofs, uint64_t len) +{ + struct mtd_info *mtd = flash->mtd; + uint8_t status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + read_sr(flash, &status_old); + + /* Cannot unlock; would unlock larger region than requested */ + if (stm_is_locked_sr(flash, status_old, ofs - mtd->erasesize, + mtd->erasesize)) + return -EINVAL; + /* + * Need largest pow such that: + * + * 1 / (2^pow) >= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) + */ + pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); + if (ofs + len == mtd->size) { + val = 0; /* fully unlocked */ + } else { + val = mask - (pow << shift); + /* Some power-of-two sizes are not supported */ + if (val & ~mask) + return -EINVAL; + } + + status_new = (status_old & ~mask) | val; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & mask) >= (status_old & mask)) + return -EINVAL; + + write_sr(flash, status_new); + + return 0; +} +#endif /* CONFIG_SPI_FLASH_STMICRO */ + +static int spi_flash_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_flash *flash = mtd->priv; + + return flash->flash_lock(flash, ofs, len); +} + +static int spi_flash_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_flash *flash = mtd->priv; + + return flash->flash_unlock(flash, ofs, len); +} + +static int spi_flash_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_flash *flash = mtd->priv; + + return flash->flash_is_locked(flash, ofs, len); +} + int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, const void *buf, size_t buf_len) { @@ -308,7 +501,7 @@ static int spi_flash_cmd_erase_ops(struct mtd_info *mtd, offset = instr->addr; len = instr->len; - if (flash->flash_is_locked(flash, offset, len) > 0) { + if (mtd->_is_locked(mtd, offset, len) > 0) { printf("offset 0x%x is protected and cannot be erased\n", offset); return -EINVAL; } @@ -360,8 +553,8 @@ static int spi_flash_cmd_write_ops(struct mtd_info *mtd, loff_t offset, page_size = flash->page_size; - if (flash->flash_is_locked(flash, offset, len) > 0) { - printf("offset 0x%x is protected and cannot be written\n", offset); + if (mtd->_is_locked(mtd, offset, len) > 0) { + printf("offset 0x%llx is protected and cannot be written\n", offset); return -EINVAL; } @@ -631,176 +824,6 @@ static int sst_write_bp(struct mtd_info *mtd, loff_t offset, size_t len, } #endif -#ifdef CONFIG_SPI_FLASH_STMICRO -static void stm_get_locked_range(struct spi_flash *flash, u8 sr, loff_t *ofs, - u32 *len) -{ - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - int shift = ffs(mask) - 1; - int pow; - - if (!(sr & mask)) { - /* No protection */ - *ofs = 0; - *len = 0; - } else { - pow = ((sr & mask) ^ mask) >> shift; - *len = flash->size >> pow; - *ofs = flash->size - *len; - } -} - -/* - * Return 1 if the entire region is locked, 0 otherwise - */ -static int stm_is_locked_sr(struct spi_flash *flash, u32 ofs, u32 len, - u8 sr) -{ - loff_t lock_offs; - u32 lock_len; - - stm_get_locked_range(flash, sr, &lock_offs, &lock_len); - - return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); -} - -/* - * Check if a region of the flash is (completely) locked. See stm_lock() for - * more info. - * - * Returns 1 if entire region is locked, 0 if any portion is unlocked, and - * negative on errors. - */ -int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len) -{ - int status; - u8 sr; - - status = spi_flash_cmd_read_status(flash, &sr); - if (status < 0) - return status; - - return stm_is_locked_sr(flash, ofs, len, sr); -} - -/* - * Lock a region of the flash. Compatible with ST Micro and similar flash. - * Supports only the block protection bits BP{0,1,2} in the status register - * (SR). Does not support these features found in newer SR bitfields: - * - TB: top/bottom protect - only handle TB=0 (top protect) - * - SEC: sector/block protect - only handle SEC=0 (block protect) - * - CMP: complement protect - only support CMP=0 (range is not complemented) - * - * Sample table portion for 8MB flash (Winbond w25q64fw): - * - * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion - * -------------------------------------------------------------------------- - * X | X | 0 | 0 | 0 | NONE | NONE - * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 - * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 - * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 - * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 - * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 - * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 - * X | X | 1 | 1 | 1 | 8 MB | ALL - * - * Returns negative on errors, 0 on success. - */ -int stm_lock(struct spi_flash *flash, u32 ofs, size_t len) -{ - u8 status_old, status_new; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 shift = ffs(mask) - 1, pow, val; - - spi_flash_cmd_read_status(flash, &status_old); - - /* SPI NOR always locks to the end */ - if (ofs + len != flash->size) { - /* Does combined region extend to end? */ - if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len, - status_old)) - return -EINVAL; - len = flash->size - ofs; - } - - /* - * Need smallest pow such that: - * - * 1 / (2^pow) <= (len / size) - * - * so (assuming power-of-2 size) we do: - * - * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) - */ - pow = ilog2(flash->size) - ilog2(len); - val = mask - (pow << shift); - if (val & ~mask) - return -EINVAL; - - /* Don't "lock" with no region! */ - if (!(val & mask)) - return -EINVAL; - - status_new = (status_old & ~mask) | val; - - /* Only modify protection if it will not unlock other areas */ - if ((status_new & mask) <= (status_old & mask)) - return -EINVAL; - - spi_flash_cmd_write_status(flash, status_new); - - return 0; -} - -/* - * Unlock a region of the flash. See stm_lock() for more info - * - * Returns negative on errors, 0 on success. - */ -int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len) -{ - uint8_t status_old, status_new; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 shift = ffs(mask) - 1, pow, val; - - spi_flash_cmd_read_status(flash, &status_old); - - /* Cannot unlock; would unlock larger region than requested */ - if (stm_is_locked_sr(flash, status_old, ofs - flash->erase_size, - flash->erase_size)) - return -EINVAL; - /* - * Need largest pow such that: - * - * 1 / (2^pow) >= (len / size) - * - * so (assuming power-of-2 size) we do: - * - * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) - */ - pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len)); - if (ofs + len == flash->size) { - val = 0; /* fully unlocked */ - } else { - val = mask - (pow << shift); - /* Some power-of-two sizes are not supported */ - if (val & ~mask) - return -EINVAL; - } - - status_new = (status_old & ~mask) | val; - - /* Only modify protection if it will not lock other areas */ - if ((status_new & mask) >= (status_old & mask)) - return -EINVAL; - - spi_flash_cmd_write_status(flash, status_new); - - return 0; -} -#endif /* CONFIG_SPI_FLASH_STMICRO */ - - #ifdef CONFIG_SPI_FLASH_MACRONIX static int spi_flash_set_qeb_mxic(struct spi_flash *flash) { @@ -970,17 +993,19 @@ int spi_flash_scan(struct spi_flash *flash) mtd->_erase = spi_flash_cmd_erase_ops; mtd->_read = spi_flash_cmd_read_ops; - /* lock hooks are flash specific - assign them based on idcode0 */ - switch (idcode[0]) { + /* NOR protection support for STmicro/Micron chips and similar */ #ifdef CONFIG_SPI_FLASH_STMICRO - case SPI_FLASH_CFI_MFR_STMICRO: + if ((idcode[0]) == SPI_FLASH_CFI_MFR_STMICRO) { flash->flash_lock = stm_lock; flash->flash_unlock = stm_unlock; flash->flash_is_locked = stm_is_locked; + } #endif - break; - default: - debug("SF: Lock ops not supported for %02x flash\n", idcode[0]); + + if (flash->flash_lock && flash->flash_unlock && flash->flash_is_locked) { + mtd->_lock = spi_flash_lock; + mtd->_unlock = spi_flash_unlock; + mtd->_is_locked = spi_flash_is_locked; } /* Compute the flash size */ diff --git a/include/spi_flash.h b/include/spi_flash.h index 5bb56b4..06951b4 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -55,9 +55,10 @@ struct spi_slave; * @dummy_byte: Dummy cycles for read operation. * @memory_map: Address of read-only SPI flash access * @priv: the private data - * @flash_lock: lock a region of the SPI Flash - * @flash_unlock: unlock a region of the SPI Flash - * @flash_is_locked: check if a region of the SPI Flash is completely locked + * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR + * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR + * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is + * completely locked * return 0 - Success, 1 - Failure */ struct spi_flash { @@ -86,9 +87,9 @@ struct spi_flash { void *memory_map; void *priv; - int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len); - int (*flash_unlock)(struct spi_flash *flash, u32 ofs, size_t len); - int (*flash_is_locked)(struct spi_flash *flash, u32 ofs, size_t len); + int (*flash_lock)(struct spi_flash *flash, loff_t ofs, uint64_t len); + int (*flash_unlock)(struct spi_flash *flash, loff_t ofs, uint64_t len); + int (*flash_is_locked)(struct spi_flash *flash, loff_t ofs, uint64_t len); }; static inline int spi_flash_read(struct spi_flash *flash, u32 offset, @@ -116,6 +117,15 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset, return mtd_erase(flash->mtd, &instr); } +static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len, + bool prot) +{ + if (prot) + return mtd_lock(flash->mtd, ofs, len); + else + return mtd_unlock(flash->mtd, ofs, len); +} + #ifdef CONFIG_DM_SPI_FLASH int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, unsigned int max_hz, unsigned int spi_mode, @@ -157,18 +167,6 @@ void spi_flash_free(struct spi_flash *flash); #endif -static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len, - bool prot) -{ - if (!flash->flash_lock) - return -EOPNOTSUPP; - - if (prot) - return flash->flash_lock(flash, ofs, len); - else - return flash->flash_unlock(flash, ofs, len); -} - void spi_boot(void) __noreturn; void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst); -- 1.9.1