2020-03-13 19:42:47 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2005 , Intec Automation Inc .
* Copyright ( C ) 2014 , Freescale Semiconductor , Inc .
*/
# include <linux/mtd/spi-nor.h>
# include "core.h"
2021-12-07 16:02:45 +02:00
/* SST flash_info mfr_flag. Used to specify SST byte programming. */
# define SST_WRITE BIT(0)
2021-01-21 13:05:46 +02:00
# define SST26VF_CR_BPNV BIT(3)
2022-02-23 14:43:39 +01:00
static int sst26vf_nor_lock ( struct spi_nor * nor , loff_t ofs , uint64_t len )
2021-01-21 13:05:46 +02:00
{
return - EOPNOTSUPP ;
}
2022-02-23 14:43:39 +01:00
static int sst26vf_nor_unlock ( struct spi_nor * nor , loff_t ofs , uint64_t len )
2021-01-21 13:05:46 +02:00
{
int ret ;
/* We only support unlocking the entire flash array. */
if ( ofs ! = 0 | | len ! = nor - > params - > size )
return - EINVAL ;
ret = spi_nor_read_cr ( nor , nor - > bouncebuf ) ;
if ( ret )
return ret ;
if ( ! ( nor - > bouncebuf [ 0 ] & SST26VF_CR_BPNV ) ) {
dev_dbg ( nor - > dev , " Any block has been permanently locked \n " ) ;
return - EINVAL ;
}
return spi_nor_global_block_unlock ( nor ) ;
}
2022-02-23 14:43:39 +01:00
static int sst26vf_nor_is_locked ( struct spi_nor * nor , loff_t ofs , uint64_t len )
2021-01-21 13:05:46 +02:00
{
return - EOPNOTSUPP ;
}
2022-02-23 14:43:39 +01:00
static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = {
. lock = sst26vf_nor_lock ,
. unlock = sst26vf_nor_unlock ,
. is_locked = sst26vf_nor_is_locked ,
2021-01-21 13:05:46 +02:00
} ;
2023-07-26 10:52:47 +03:00
static int sst26vf_nor_late_init ( struct spi_nor * nor )
2021-01-21 13:05:46 +02:00
{
2022-02-23 14:43:39 +01:00
nor - > params - > locking_ops = & sst26vf_nor_locking_ops ;
2023-07-26 10:52:47 +03:00
return 0 ;
2021-01-21 13:05:46 +02:00
}
2022-02-23 14:43:39 +01:00
static const struct spi_nor_fixups sst26vf_nor_fixups = {
. late_init = sst26vf_nor_late_init ,
2021-01-21 13:05:46 +02:00
} ;
2022-02-23 14:43:39 +01:00
static const struct flash_info sst_nor_parts [ ] = {
2023-09-08 12:16:45 +02:00
{
2023-09-08 12:16:56 +02:00
. id = SNOR_ID ( 0x62 , 0x16 , 0x12 ) ,
. name = " sst25wf020a " ,
. size = SZ_256K ,
. flags = SPI_NOR_HAS_LOCK ,
2023-09-08 12:16:45 +02:00
. no_sfdp_flags = SECT_4K ,
} , {
2023-09-08 12:16:56 +02:00
. id = SNOR_ID ( 0x62 , 0x16 , 0x13 ) ,
. name = " sst25wf040b " ,
. size = SZ_512K ,
. flags = SPI_NOR_HAS_LOCK ,
2023-09-08 12:16:45 +02:00
. no_sfdp_flags = SECT_4K ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x01 ) ,
. name = " sst25wf512 " ,
. size = SZ_64K ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x02 ) ,
. name = " sst25wf010 " ,
. size = SZ_128K ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x03 ) ,
. name = " sst25wf020 " ,
. size = SZ_256K ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x04 ) ,
. name = " sst25wf040 " ,
. size = SZ_512K ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x05 ) ,
. name = " sst25wf080 " ,
. size = SZ_1M ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
2023-09-08 12:16:56 +02:00
. id = SNOR_ID ( 0xbf , 0x25 , 0x41 ) ,
. name = " sst25vf016b " ,
2023-09-08 12:16:45 +02:00
. size = SZ_2M ,
2023-09-08 12:16:56 +02:00
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x4a ) ,
. name = " sst25vf032b " ,
. size = SZ_4M ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x4b ) ,
. name = " sst25vf064c " ,
. size = SZ_8M ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x8d ) ,
. name = " sst25vf040b " ,
. size = SZ_512K ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
} , {
. id = SNOR_ID ( 0xbf , 0x25 , 0x8e ) ,
. name = " sst25vf080b " ,
. size = SZ_1M ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K ,
. mfr_flags = SST_WRITE ,
2023-09-08 12:16:45 +02:00
} , {
. id = SNOR_ID ( 0xbf , 0x26 , 0x41 ) ,
. name = " sst26vf016b " ,
. size = SZ_2M ,
. no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ ,
} , {
. id = SNOR_ID ( 0xbf , 0x26 , 0x42 ) ,
. name = " sst26vf032b " ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. fixups = & sst26vf_nor_fixups ,
} , {
. id = SNOR_ID ( 0xbf , 0x26 , 0x43 ) ,
. name = " sst26vf064b " ,
. size = SZ_8M ,
. flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE ,
. no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ ,
. fixups = & sst26vf_nor_fixups ,
2023-09-08 12:16:56 +02:00
} , {
. id = SNOR_ID ( 0xbf , 0x26 , 0x51 ) ,
. name = " sst26wf016b " ,
. size = SZ_2M ,
. no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ ,
2023-09-08 12:16:45 +02:00
}
2020-03-13 19:42:47 +00:00
} ;
2022-02-23 14:43:39 +01:00
static int sst_nor_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
2020-03-13 19:42:47 +00:00
{
struct spi_nor * nor = mtd_to_spi_nor ( mtd ) ;
size_t actual = 0 ;
int ret ;
dev_dbg ( nor - > dev , " to 0x%08x, len %zd \n " , ( u32 ) to , len ) ;
2023-03-28 17:41:00 +02:00
ret = spi_nor_prep_and_lock ( nor ) ;
2020-03-13 19:42:47 +00:00
if ( ret )
return ret ;
ret = spi_nor_write_enable ( nor ) ;
if ( ret )
goto out ;
nor - > sst_write_second = false ;
/* Start write from odd address. */
if ( to % 2 ) {
nor - > program_opcode = SPINOR_OP_BP ;
/* write one byte. */
ret = spi_nor_write_data ( nor , to , 1 , buf ) ;
if ( ret < 0 )
goto out ;
WARN ( ret ! = 1 , " While writing 1 byte written %i bytes \n " , ret ) ;
ret = spi_nor_wait_till_ready ( nor ) ;
if ( ret )
goto out ;
to + + ;
actual + + ;
}
/* Write out most of the data here. */
for ( ; actual < len - 1 ; actual + = 2 ) {
nor - > program_opcode = SPINOR_OP_AAI_WP ;
/* write two bytes. */
ret = spi_nor_write_data ( nor , to , 2 , buf + actual ) ;
if ( ret < 0 )
goto out ;
WARN ( ret ! = 2 , " While writing 2 bytes written %i bytes \n " , ret ) ;
ret = spi_nor_wait_till_ready ( nor ) ;
if ( ret )
goto out ;
to + = 2 ;
nor - > sst_write_second = true ;
}
nor - > sst_write_second = false ;
ret = spi_nor_write_disable ( nor ) ;
if ( ret )
goto out ;
ret = spi_nor_wait_till_ready ( nor ) ;
if ( ret )
goto out ;
/* Write out trailing byte if it exists. */
if ( actual ! = len ) {
ret = spi_nor_write_enable ( nor ) ;
if ( ret )
goto out ;
nor - > program_opcode = SPINOR_OP_BP ;
ret = spi_nor_write_data ( nor , to , 1 , buf + actual ) ;
if ( ret < 0 )
goto out ;
WARN ( ret ! = 1 , " While writing 1 byte written %i bytes \n " , ret ) ;
ret = spi_nor_wait_till_ready ( nor ) ;
if ( ret )
goto out ;
actual + = 1 ;
ret = spi_nor_write_disable ( nor ) ;
}
out :
* retlen + = actual ;
spi_nor_unlock_and_unprep ( nor ) ;
return ret ;
}
2023-07-26 10:52:47 +03:00
static int sst_nor_late_init ( struct spi_nor * nor )
2020-03-13 19:42:47 +00:00
{
2021-12-07 16:02:45 +02:00
if ( nor - > info - > mfr_flags & SST_WRITE )
2022-02-23 14:43:39 +01:00
nor - > mtd . _write = sst_nor_write ;
2023-07-26 10:52:47 +03:00
return 0 ;
2020-03-13 19:42:47 +00:00
}
2022-02-23 14:43:39 +01:00
static const struct spi_nor_fixups sst_nor_fixups = {
. late_init = sst_nor_late_init ,
2020-03-13 19:42:47 +00:00
} ;
const struct spi_nor_manufacturer spi_nor_sst = {
. name = " sst " ,
2022-02-23 14:43:39 +01:00
. parts = sst_nor_parts ,
. nparts = ARRAY_SIZE ( sst_nor_parts ) ,
. fixups = & sst_nor_fixups ,
2020-03-13 19:42:47 +00:00
} ;