Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2005, Intec Automation Inc.
0004  * Copyright (C) 2014, Freescale Semiconductor, Inc.
0005  */
0006 
0007 #include <linux/mtd/spi-nor.h>
0008 
0009 #include "core.h"
0010 
0011 #define WINBOND_NOR_OP_RDEAR    0xc8    /* Read Extended Address Register */
0012 #define WINBOND_NOR_OP_WREAR    0xc5    /* Write Extended Address Register */
0013 
0014 #define WINBOND_NOR_WREAR_OP(buf)                   \
0015     SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0),     \
0016            SPI_MEM_OP_NO_ADDR,                  \
0017            SPI_MEM_OP_NO_DUMMY,                 \
0018            SPI_MEM_OP_DATA_OUT(1, buf, 0))
0019 
0020 static int
0021 w25q256_post_bfpt_fixups(struct spi_nor *nor,
0022              const struct sfdp_parameter_header *bfpt_header,
0023              const struct sfdp_bfpt *bfpt)
0024 {
0025     /*
0026      * W25Q256JV supports 4B opcodes but W25Q256FV does not.
0027      * Unfortunately, Winbond has re-used the same JEDEC ID for both
0028      * variants which prevents us from defining a new entry in the parts
0029      * table.
0030      * To differentiate between W25Q256JV and W25Q256FV check SFDP header
0031      * version: only JV has JESD216A compliant structure (version 5).
0032      */
0033     if (bfpt_header->major == SFDP_JESD216_MAJOR &&
0034         bfpt_header->minor == SFDP_JESD216A_MINOR)
0035         nor->flags |= SNOR_F_4B_OPCODES;
0036 
0037     return 0;
0038 }
0039 
0040 static const struct spi_nor_fixups w25q256_fixups = {
0041     .post_bfpt = w25q256_post_bfpt_fixups,
0042 };
0043 
0044 static const struct flash_info winbond_nor_parts[] = {
0045     /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
0046     { "w25x05", INFO(0xef3010, 0, 64 * 1024,  1)
0047         NO_SFDP_FLAGS(SECT_4K) },
0048     { "w25x10", INFO(0xef3011, 0, 64 * 1024,  2)
0049         NO_SFDP_FLAGS(SECT_4K) },
0050     { "w25x20", INFO(0xef3012, 0, 64 * 1024,  4)
0051         NO_SFDP_FLAGS(SECT_4K) },
0052     { "w25x40", INFO(0xef3013, 0, 64 * 1024,  8)
0053         NO_SFDP_FLAGS(SECT_4K) },
0054     { "w25x80", INFO(0xef3014, 0, 64 * 1024,  16)
0055         NO_SFDP_FLAGS(SECT_4K) },
0056     { "w25x16", INFO(0xef3015, 0, 64 * 1024,  32)
0057         NO_SFDP_FLAGS(SECT_4K) },
0058     { "w25q16dw", INFO(0xef6015, 0, 64 * 1024,  32)
0059         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0060         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0061                   SPI_NOR_QUAD_READ) },
0062     { "w25x32", INFO(0xef3016, 0, 64 * 1024,  64)
0063         NO_SFDP_FLAGS(SECT_4K) },
0064     { "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024,  32)
0065         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0066         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0067                   SPI_NOR_QUAD_READ) },
0068     { "w25q20cl", INFO(0xef4012, 0, 64 * 1024,  4)
0069         NO_SFDP_FLAGS(SECT_4K) },
0070     { "w25q20bw", INFO(0xef5012, 0, 64 * 1024,  4)
0071         NO_SFDP_FLAGS(SECT_4K) },
0072     { "w25q20ew", INFO(0xef6012, 0, 64 * 1024,  4)
0073         NO_SFDP_FLAGS(SECT_4K) },
0074     { "w25q32", INFO(0xef4016, 0, 64 * 1024,  64)
0075         NO_SFDP_FLAGS(SECT_4K) },
0076     { "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64)
0077         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0078         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0079         OTP_INFO(256, 3, 0x1000, 0x1000) },
0080     { "w25q32jv", INFO(0xef7016, 0, 64 * 1024,  64)
0081         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0082         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0083                   SPI_NOR_QUAD_READ) },
0084     { "w25q32jwm", INFO(0xef8016, 0, 64 * 1024,  64)
0085         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0086         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0087         OTP_INFO(256, 3, 0x1000, 0x1000) },
0088     { "w25q64jwm", INFO(0xef8017, 0, 64 * 1024, 128)
0089         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0090         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0091                   SPI_NOR_QUAD_READ) },
0092     { "w25q128jwm", INFO(0xef8018, 0, 64 * 1024, 256)
0093         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0094         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0095                   SPI_NOR_QUAD_READ) },
0096     { "w25q256jwm", INFO(0xef8019, 0, 64 * 1024, 512)
0097         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0098         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0099                   SPI_NOR_QUAD_READ) },
0100     { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128)
0101         NO_SFDP_FLAGS(SECT_4K) },
0102     { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128)
0103         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0104                   SPI_NOR_QUAD_READ) },
0105     { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128)
0106         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0107         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0108                   SPI_NOR_QUAD_READ) },
0109     { "w25q64jvm", INFO(0xef7017, 0, 64 * 1024, 128)
0110         NO_SFDP_FLAGS(SECT_4K) },
0111     { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256)
0112         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0113         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0114                   SPI_NOR_QUAD_READ) },
0115     { "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256)
0116         FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
0117         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0118                   SPI_NOR_QUAD_READ) },
0119     { "w25q80", INFO(0xef5014, 0, 64 * 1024,  16)
0120         NO_SFDP_FLAGS(SECT_4K) },
0121     { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16)
0122         NO_SFDP_FLAGS(SECT_4K) },
0123     { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256)
0124         NO_SFDP_FLAGS(SECT_4K) },
0125     { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512)
0126         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
0127         .fixups = &w25q256_fixups },
0128     { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512)
0129         PARSE_SFDP },
0130     { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512)
0131         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0132                   SPI_NOR_QUAD_READ) },
0133     { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
0134         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
0135                   SPI_NOR_DUAL_READ) },
0136     { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
0137         PARSE_SFDP
0138         OTP_INFO(256, 3, 0x1000, 0x1000) },
0139     { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024)
0140         NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
0141                   SPI_NOR_QUAD_READ) },
0142 };
0143 
0144 /**
0145  * winbond_nor_write_ear() - Write Extended Address Register.
0146  * @nor:    pointer to 'struct spi_nor'.
0147  * @ear:    value to write to the Extended Address Register.
0148  *
0149  * Return: 0 on success, -errno otherwise.
0150  */
0151 static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
0152 {
0153     int ret;
0154 
0155     nor->bouncebuf[0] = ear;
0156 
0157     if (nor->spimem) {
0158         struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
0159 
0160         spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
0161 
0162         ret = spi_mem_exec_op(nor->spimem, &op);
0163     } else {
0164         ret = spi_nor_controller_ops_write_reg(nor,
0165                                WINBOND_NOR_OP_WREAR,
0166                                nor->bouncebuf, 1);
0167     }
0168 
0169     if (ret)
0170         dev_dbg(nor->dev, "error %d writing EAR\n", ret);
0171 
0172     return ret;
0173 }
0174 
0175 /**
0176  * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
0177  * flashes.
0178  * @nor:    pointer to 'struct spi_nor'.
0179  * @enable: true to enter the 4-byte address mode, false to exit the 4-byte
0180  *      address mode.
0181  *
0182  * Return: 0 on success, -errno otherwise.
0183  */
0184 static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
0185 {
0186     int ret;
0187 
0188     ret = spi_nor_set_4byte_addr_mode(nor, enable);
0189     if (ret || enable)
0190         return ret;
0191 
0192     /*
0193      * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
0194      * Register to be set to 1, so all 3-byte-address reads come from the
0195      * second 16M. We must clear the register to enable normal behavior.
0196      */
0197     ret = spi_nor_write_enable(nor);
0198     if (ret)
0199         return ret;
0200 
0201     ret = winbond_nor_write_ear(nor, 0);
0202     if (ret)
0203         return ret;
0204 
0205     return spi_nor_write_disable(nor);
0206 }
0207 
0208 static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
0209     .read = spi_nor_otp_read_secr,
0210     .write = spi_nor_otp_write_secr,
0211     .erase = spi_nor_otp_erase_secr,
0212     .lock = spi_nor_otp_lock_sr2,
0213     .is_locked = spi_nor_otp_is_locked_sr2,
0214 };
0215 
0216 static void winbond_nor_default_init(struct spi_nor *nor)
0217 {
0218     nor->params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
0219 }
0220 
0221 static void winbond_nor_late_init(struct spi_nor *nor)
0222 {
0223     if (nor->params->otp.org->n_regions)
0224         nor->params->otp.ops = &winbond_nor_otp_ops;
0225 }
0226 
0227 static const struct spi_nor_fixups winbond_nor_fixups = {
0228     .default_init = winbond_nor_default_init,
0229     .late_init = winbond_nor_late_init,
0230 };
0231 
0232 const struct spi_nor_manufacturer spi_nor_winbond = {
0233     .name = "winbond",
0234     .parts = winbond_nor_parts,
0235     .nparts = ARRAY_SIZE(winbond_nor_parts),
0236     .fixups = &winbond_nor_fixups,
0237 };