162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005, Intec Automation Inc. 462306a36Sopenharmony_ci * Copyright (C) 2014, Freescale Semiconductor, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/mtd/spi-nor.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "core.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */ 1562306a36Sopenharmony_ci#define USE_CLSR BIT(0) 1662306a36Sopenharmony_ci#define USE_CLPEF BIT(1) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ 1962306a36Sopenharmony_ci#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */ 2062306a36Sopenharmony_ci#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ 2162306a36Sopenharmony_ci#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ 2262306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_VREG 0x00800000 2362306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_STR1 0x0 2462306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_STR1V \ 2562306a36Sopenharmony_ci (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_STR1) 2662306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR1 0x2 2762306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */ 2862306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR2 0x3 2962306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR2V \ 3062306a36Sopenharmony_ci (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR2) 3162306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK GENMASK(3, 0) 3262306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb 3362306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7) 3462306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR3 0x4 3562306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */ 3662306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5 0x6 3762306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6) 3862306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1) 3962306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0) 4062306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN \ 4162306a36Sopenharmony_ci (SPINOR_REG_CYPRESS_CFR5_BIT6 | SPINOR_REG_CYPRESS_CFR5_DDR | \ 4262306a36Sopenharmony_ci SPINOR_REG_CYPRESS_CFR5_OPI) 4362306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS SPINOR_REG_CYPRESS_CFR5_BIT6 4462306a36Sopenharmony_ci#define SPINOR_OP_CYPRESS_RD_FAST 0xee 4562306a36Sopenharmony_ci#define SPINOR_REG_CYPRESS_ARCFN 0x00000006 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Cypress SPI NOR flash operations. */ 4862306a36Sopenharmony_ci#define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ 4962306a36Sopenharmony_ci SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 0), \ 5062306a36Sopenharmony_ci SPI_MEM_OP_ADDR(naddr, addr, 0), \ 5162306a36Sopenharmony_ci SPI_MEM_OP_NO_DUMMY, \ 5262306a36Sopenharmony_ci SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, ndummy, buf) \ 5562306a36Sopenharmony_ci SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), \ 5662306a36Sopenharmony_ci SPI_MEM_OP_ADDR(naddr, addr, 0), \ 5762306a36Sopenharmony_ci SPI_MEM_OP_DUMMY(ndummy, 0), \ 5862306a36Sopenharmony_ci SPI_MEM_OP_DATA_IN(1, buf, 0)) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define SPANSION_OP(opcode) \ 6162306a36Sopenharmony_ci SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ 6262306a36Sopenharmony_ci SPI_MEM_OP_NO_ADDR, \ 6362306a36Sopenharmony_ci SPI_MEM_OP_NO_DUMMY, \ 6462306a36Sopenharmony_ci SPI_MEM_OP_NO_DATA) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/** 6762306a36Sopenharmony_ci * struct spansion_nor_params - Spansion private parameters. 6862306a36Sopenharmony_ci * @clsr: Clear Status Register or Clear Program and Erase Failure Flag 6962306a36Sopenharmony_ci * opcode. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistruct spansion_nor_params { 7262306a36Sopenharmony_ci u8 clsr; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * spansion_nor_clear_sr() - Clear the Status Register. 7762306a36Sopenharmony_ci * @nor: pointer to 'struct spi_nor'. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic void spansion_nor_clear_sr(struct spi_nor *nor) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci const struct spansion_nor_params *priv_params = nor->params->priv; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (nor->spimem) { 8562306a36Sopenharmony_ci struct spi_mem_op op = SPANSION_OP(priv_params->clsr); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = spi_mem_exec_op(nor->spimem, &op); 9062306a36Sopenharmony_ci } else { 9162306a36Sopenharmony_ci ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, 9262306a36Sopenharmony_ci NULL, 0); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (ret) 9662306a36Sopenharmony_ci dev_dbg(nor->dev, "error %d clearing SR\n", ret); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 10262306a36Sopenharmony_ci struct spi_mem_op op = 10362306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(params->addr_mode_nbytes, addr, 10462306a36Sopenharmony_ci 0, nor->bouncebuf); 10562306a36Sopenharmony_ci int ret; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { 10862306a36Sopenharmony_ci op.dummy.nbytes = params->rdsr_dummy; 10962306a36Sopenharmony_ci op.data.nbytes = 2; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { 11762306a36Sopenharmony_ci if (nor->bouncebuf[0] & SR_E_ERR) 11862306a36Sopenharmony_ci dev_err(nor->dev, "Erase Error occurred\n"); 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci dev_err(nor->dev, "Programming Error occurred\n"); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spansion_nor_clear_sr(nor); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = spi_nor_write_disable(nor); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return -EIO; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return !(nor->bouncebuf[0] & SR_WIP); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci/** 13462306a36Sopenharmony_ci * cypress_nor_sr_ready_and_clear() - Query the Status Register of each die by 13562306a36Sopenharmony_ci * using Read Any Register command to see if the whole flash is ready for new 13662306a36Sopenharmony_ci * commands and clear it if there are any errors. 13762306a36Sopenharmony_ci * @nor: pointer to 'struct spi_nor'. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Return: 1 if ready, 0 if not ready, -errno on errors. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic int cypress_nor_sr_ready_and_clear(struct spi_nor *nor) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 14462306a36Sopenharmony_ci u64 addr; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci u8 i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (i = 0; i < params->n_dice; i++) { 14962306a36Sopenharmony_ci addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_STR1; 15062306a36Sopenharmony_ci ret = cypress_nor_sr_ready_and_clear_reg(nor, addr); 15162306a36Sopenharmony_ci if (ret < 0) 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci else if (ret == 0) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 1; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int cypress_nor_set_memlat(struct spi_nor *nor, u64 addr) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct spi_mem_op op; 16362306a36Sopenharmony_ci u8 *buf = nor->bouncebuf; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci op = (struct spi_mem_op) 16862306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, buf); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Use 24 dummy cycles for memory array reads. */ 17562306a36Sopenharmony_ci *buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK; 17662306a36Sopenharmony_ci *buf |= FIELD_PREP(SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK, 17762306a36Sopenharmony_ci SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24); 17862306a36Sopenharmony_ci op = (struct spi_mem_op) 17962306a36Sopenharmony_ci CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, buf); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci nor->read_dummy = 24; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int cypress_nor_set_octal_dtr_bits(struct spi_nor *nor, u64 addr) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct spi_mem_op op; 19362306a36Sopenharmony_ci u8 *buf = nor->bouncebuf; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Set the octal and DTR enable bits. */ 19662306a36Sopenharmony_ci buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; 19762306a36Sopenharmony_ci op = (struct spi_mem_op) 19862306a36Sopenharmony_ci CYPRESS_NOR_WR_ANY_REG_OP(nor->params->addr_mode_nbytes, 19962306a36Sopenharmony_ci addr, 1, buf); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int cypress_nor_octal_dtr_en(struct spi_nor *nor) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci const struct spi_nor_flash_parameter *params = nor->params; 20762306a36Sopenharmony_ci u8 *buf = nor->bouncebuf; 20862306a36Sopenharmony_ci u64 addr; 20962306a36Sopenharmony_ci int i, ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for (i = 0; i < params->n_dice; i++) { 21262306a36Sopenharmony_ci addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR2; 21362306a36Sopenharmony_ci ret = cypress_nor_set_memlat(nor, addr); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; 21862306a36Sopenharmony_ci ret = cypress_nor_set_octal_dtr_bits(nor, addr); 21962306a36Sopenharmony_ci if (ret) 22062306a36Sopenharmony_ci return ret; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Read flash ID to make sure the switch was successful. */ 22462306a36Sopenharmony_ci ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf, 22562306a36Sopenharmony_ci SNOR_PROTO_8_8_8_DTR); 22662306a36Sopenharmony_ci if (ret) { 22762306a36Sopenharmony_ci dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (memcmp(buf, nor->info->id, nor->info->id_len)) 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int cypress_nor_set_single_spi_bits(struct spi_nor *nor, u64 addr) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct spi_mem_op op; 24062306a36Sopenharmony_ci u8 *buf = nor->bouncebuf; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* 24362306a36Sopenharmony_ci * The register is 1-byte wide, but 1-byte transactions are not allowed 24462306a36Sopenharmony_ci * in 8D-8D-8D mode. Since there is no register at the next location, 24562306a36Sopenharmony_ci * just initialize the value to 0 and let the transaction go on. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS; 24862306a36Sopenharmony_ci buf[1] = 0; 24962306a36Sopenharmony_ci op = (struct spi_mem_op) 25062306a36Sopenharmony_ci CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, addr, 2, buf); 25162306a36Sopenharmony_ci return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int cypress_nor_octal_dtr_dis(struct spi_nor *nor) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci const struct spi_nor_flash_parameter *params = nor->params; 25762306a36Sopenharmony_ci u8 *buf = nor->bouncebuf; 25862306a36Sopenharmony_ci u64 addr; 25962306a36Sopenharmony_ci int i, ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < params->n_dice; i++) { 26262306a36Sopenharmony_ci addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; 26362306a36Sopenharmony_ci ret = cypress_nor_set_single_spi_bits(nor, addr); 26462306a36Sopenharmony_ci if (ret) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Read flash ID to make sure the switch was successful. */ 26962306a36Sopenharmony_ci ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); 27062306a36Sopenharmony_ci if (ret) { 27162306a36Sopenharmony_ci dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (memcmp(buf, nor->info->id, nor->info->id_len)) 27662306a36Sopenharmony_ci return -EINVAL; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int cypress_nor_quad_enable_volatile_reg(struct spi_nor *nor, u64 addr) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct spi_mem_op op; 28462306a36Sopenharmony_ci u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; 28562306a36Sopenharmony_ci u8 cfr1v_written; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci op = (struct spi_mem_op) 28962306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, 29062306a36Sopenharmony_ci nor->bouncebuf); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1_QUAD_EN) 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Update the Quad Enable bit. */ 30062306a36Sopenharmony_ci nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1_QUAD_EN; 30162306a36Sopenharmony_ci op = (struct spi_mem_op) 30262306a36Sopenharmony_ci CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, 30362306a36Sopenharmony_ci nor->bouncebuf); 30462306a36Sopenharmony_ci ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci cfr1v_written = nor->bouncebuf[0]; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Read back and check it. */ 31162306a36Sopenharmony_ci op = (struct spi_mem_op) 31262306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, 31362306a36Sopenharmony_ci nor->bouncebuf); 31462306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 31562306a36Sopenharmony_ci if (ret) 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (nor->bouncebuf[0] != cfr1v_written) { 31962306a36Sopenharmony_ci dev_err(nor->dev, "CFR1: Read back test failed\n"); 32062306a36Sopenharmony_ci return -EIO; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/** 32762306a36Sopenharmony_ci * cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile 32862306a36Sopenharmony_ci * register. 32962306a36Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * It is recommended to update volatile registers in the field application due 33262306a36Sopenharmony_ci * to a risk of the non-volatile registers corruption by power interrupt. This 33362306a36Sopenharmony_ci * function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable 33462306a36Sopenharmony_ci * bit in the CFR1 non-volatile in advance (typically by a Flash programmer 33562306a36Sopenharmony_ci * before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is 33662306a36Sopenharmony_ci * also set during Flash power-up. 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_cistatic int cypress_nor_quad_enable_volatile(struct spi_nor *nor) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 34362306a36Sopenharmony_ci u64 addr; 34462306a36Sopenharmony_ci u8 i; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci for (i = 0; i < params->n_dice; i++) { 34862306a36Sopenharmony_ci addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR1; 34962306a36Sopenharmony_ci ret = cypress_nor_quad_enable_volatile_reg(nor, addr); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/** 35862306a36Sopenharmony_ci * cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode 35962306a36Sopenharmony_ci * (3 or 4-byte) by querying status 36062306a36Sopenharmony_ci * register 1 (SR1). 36162306a36Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 36262306a36Sopenharmony_ci * @addr_mode: ponter to a buffer where we return the determined 36362306a36Sopenharmony_ci * address mode. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * This function tries to determine current address mode by comparing SR1 value 36662306a36Sopenharmony_ci * from RDSR1(no address), RDAR(3-byte address), and RDAR(4-byte address). 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistatic int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, 37162306a36Sopenharmony_ci u8 *addr_mode) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct spi_mem_op op = 37462306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_STR1V, 0, 37562306a36Sopenharmony_ci nor->bouncebuf); 37662306a36Sopenharmony_ci bool is3byte, is4byte; 37762306a36Sopenharmony_ci int ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]); 38062306a36Sopenharmony_ci if (ret) 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 38462306a36Sopenharmony_ci if (ret) 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci op = (struct spi_mem_op) 39062306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0, 39162306a36Sopenharmony_ci nor->bouncebuf); 39262306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (is3byte == is4byte) 39962306a36Sopenharmony_ci return -EIO; 40062306a36Sopenharmony_ci if (is3byte) 40162306a36Sopenharmony_ci *addr_mode = 3; 40262306a36Sopenharmony_ci else 40362306a36Sopenharmony_ci *addr_mode = 4; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/** 40962306a36Sopenharmony_ci * cypress_nor_set_addr_mode_nbytes() - Set the number of address bytes mode of 41062306a36Sopenharmony_ci * current address mode. 41162306a36Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 41262306a36Sopenharmony_ci * 41362306a36Sopenharmony_ci * Determine current address mode by reading SR1 with different methods, then 41462306a36Sopenharmony_ci * query CFR2V[7] to confirm. If determination is failed, force enter to 4-byte 41562306a36Sopenharmony_ci * address mode. 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_cistatic int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct spi_mem_op op; 42262306a36Sopenharmony_ci u8 addr_mode; 42362306a36Sopenharmony_ci int ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Read SR1 by RDSR1 and RDAR(3- AND 4-byte addr). Use write enable 42762306a36Sopenharmony_ci * that sets bit-1 in SR1. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci ret = spi_nor_write_enable(nor); 43062306a36Sopenharmony_ci if (ret) 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci ret = cypress_nor_determine_addr_mode_by_sr1(nor, &addr_mode); 43362306a36Sopenharmony_ci if (ret) { 43462306a36Sopenharmony_ci ret = spi_nor_set_4byte_addr_mode(nor, true); 43562306a36Sopenharmony_ci if (ret) 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci return spi_nor_write_disable(nor); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci ret = spi_nor_write_disable(nor); 44062306a36Sopenharmony_ci if (ret) 44162306a36Sopenharmony_ci return ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * Query CFR2V and make sure no contradiction between determined address 44562306a36Sopenharmony_ci * mode and CFR2V[7]. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci op = (struct spi_mem_op) 44862306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(addr_mode, SPINOR_REG_CYPRESS_CFR2V, 44962306a36Sopenharmony_ci 0, nor->bouncebuf); 45062306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 45162306a36Sopenharmony_ci if (ret) 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR2_ADRBYT) { 45562306a36Sopenharmony_ci if (addr_mode != 4) 45662306a36Sopenharmony_ci return spi_nor_set_4byte_addr_mode(nor, true); 45762306a36Sopenharmony_ci } else { 45862306a36Sopenharmony_ci if (addr_mode != 3) 45962306a36Sopenharmony_ci return spi_nor_set_4byte_addr_mode(nor, true); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci nor->params->addr_nbytes = addr_mode; 46362306a36Sopenharmony_ci nor->params->addr_mode_nbytes = addr_mode; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/** 46962306a36Sopenharmony_ci * cypress_nor_get_page_size() - Get flash page size configuration. 47062306a36Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 47162306a36Sopenharmony_ci * 47262306a36Sopenharmony_ci * The BFPT table advertises a 512B or 256B page size depending on part but the 47362306a36Sopenharmony_ci * page size is actually configurable (with the default being 256B). Read from 47462306a36Sopenharmony_ci * CFR3V[4] and set the correct size. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_cistatic int cypress_nor_get_page_size(struct spi_nor *nor) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct spi_mem_op op = 48162306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, 48262306a36Sopenharmony_ci 0, 0, nor->bouncebuf); 48362306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 48462306a36Sopenharmony_ci int ret; 48562306a36Sopenharmony_ci u8 i; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * Use the minimum common page size configuration. Programming 256-byte 48962306a36Sopenharmony_ci * under 512-byte page size configuration is safe. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci params->page_size = 256; 49262306a36Sopenharmony_ci for (i = 0; i < params->n_dice; i++) { 49362306a36Sopenharmony_ci op.addr.val = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR3; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 49662306a36Sopenharmony_ci if (ret) 49762306a36Sopenharmony_ci return ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!(nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ)) 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci params->page_size = 512; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void cypress_nor_ecc_init(struct spi_nor *nor) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * Programming is supported only in 16-byte ECC data unit granularity. 51262306a36Sopenharmony_ci * Byte-programming, bit-walking, or multiple program operations to the 51362306a36Sopenharmony_ci * same ECC data unit without an erase are not allowed. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci nor->params->writesize = 16; 51662306a36Sopenharmony_ci nor->flags |= SNOR_F_ECC; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int 52062306a36Sopenharmony_cis25fs256t_post_bfpt_fixup(struct spi_nor *nor, 52162306a36Sopenharmony_ci const struct sfdp_parameter_header *bfpt_header, 52262306a36Sopenharmony_ci const struct sfdp_bfpt *bfpt) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct spi_mem_op op; 52562306a36Sopenharmony_ci int ret; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ret = cypress_nor_set_addr_mode_nbytes(nor); 52862306a36Sopenharmony_ci if (ret) 52962306a36Sopenharmony_ci return ret; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Read Architecture Configuration Register (ARCFN) */ 53262306a36Sopenharmony_ci op = (struct spi_mem_op) 53362306a36Sopenharmony_ci CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, 53462306a36Sopenharmony_ci SPINOR_REG_CYPRESS_ARCFN, 1, 53562306a36Sopenharmony_ci nor->bouncebuf); 53662306a36Sopenharmony_ci ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); 53762306a36Sopenharmony_ci if (ret) 53862306a36Sopenharmony_ci return ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* ARCFN value must be 0 if uniform sector is selected */ 54162306a36Sopenharmony_ci if (nor->bouncebuf[0]) 54262306a36Sopenharmony_ci return -ENODEV; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * S25FS256T does not define the SCCR map, but we would like to use the 55362306a36Sopenharmony_ci * same code base for both single and multi chip package devices, thus 55462306a36Sopenharmony_ci * set the vreg_offset and n_dice to be able to do so. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci params->vreg_offset = devm_kmalloc(nor->dev, sizeof(u32), GFP_KERNEL); 55762306a36Sopenharmony_ci if (!params->vreg_offset) 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci params->vreg_offset[0] = SPINOR_REG_CYPRESS_VREG; 56162306a36Sopenharmony_ci params->n_dice = 1; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* PP_1_1_4_4B is supported but missing in 4BAIT. */ 56462306a36Sopenharmony_ci params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; 56562306a36Sopenharmony_ci spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], 56662306a36Sopenharmony_ci SPINOR_OP_PP_1_1_4_4B, 56762306a36Sopenharmony_ci SNOR_PROTO_1_1_4); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return cypress_nor_get_page_size(nor); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int s25fs256t_late_init(struct spi_nor *nor) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci cypress_nor_ecc_init(nor); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic struct spi_nor_fixups s25fs256t_fixups = { 58062306a36Sopenharmony_ci .post_bfpt = s25fs256t_post_bfpt_fixup, 58162306a36Sopenharmony_ci .post_sfdp = s25fs256t_post_sfdp_fixup, 58262306a36Sopenharmony_ci .late_init = s25fs256t_late_init, 58362306a36Sopenharmony_ci}; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int 58662306a36Sopenharmony_cis25hx_t_post_bfpt_fixup(struct spi_nor *nor, 58762306a36Sopenharmony_ci const struct sfdp_parameter_header *bfpt_header, 58862306a36Sopenharmony_ci const struct sfdp_bfpt *bfpt) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci int ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = cypress_nor_set_addr_mode_nbytes(nor); 59362306a36Sopenharmony_ci if (ret) 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Replace Quad Enable with volatile version */ 59762306a36Sopenharmony_ci nor->params->quad_enable = cypress_nor_quad_enable_volatile; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 60562306a36Sopenharmony_ci struct spi_nor_erase_type *erase_type = params->erase_map.erase_type; 60662306a36Sopenharmony_ci unsigned int i; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!params->n_dice || !params->vreg_offset) { 60962306a36Sopenharmony_ci dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", 61062306a36Sopenharmony_ci __func__); 61162306a36Sopenharmony_ci return -EOPNOTSUPP; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ 61562306a36Sopenharmony_ci if (params->size == SZ_256M) 61662306a36Sopenharmony_ci params->n_dice = 2; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * In some parts, 3byte erase opcodes are advertised by 4BAIT. 62062306a36Sopenharmony_ci * Convert them to 4byte erase opcodes. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { 62362306a36Sopenharmony_ci switch (erase_type[i].opcode) { 62462306a36Sopenharmony_ci case SPINOR_OP_SE: 62562306a36Sopenharmony_ci erase_type[i].opcode = SPINOR_OP_SE_4B; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci case SPINOR_OP_BE_4K: 62862306a36Sopenharmony_ci erase_type[i].opcode = SPINOR_OP_BE_4K_4B; 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci default: 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return cypress_nor_get_page_size(nor); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int s25hx_t_late_init(struct spi_nor *nor) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Fast Read 4B requires mode cycles */ 64362306a36Sopenharmony_ci params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; 64462306a36Sopenharmony_ci params->ready = cypress_nor_sr_ready_and_clear; 64562306a36Sopenharmony_ci cypress_nor_ecc_init(nor); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic struct spi_nor_fixups s25hx_t_fixups = { 65162306a36Sopenharmony_ci .post_bfpt = s25hx_t_post_bfpt_fixup, 65262306a36Sopenharmony_ci .post_sfdp = s25hx_t_post_sfdp_fixup, 65362306a36Sopenharmony_ci .late_init = s25hx_t_late_init, 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/** 65762306a36Sopenharmony_ci * cypress_nor_set_octal_dtr() - Enable or disable octal DTR on Cypress flashes. 65862306a36Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 65962306a36Sopenharmony_ci * @enable: whether to enable or disable Octal DTR 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci * This also sets the memory access latency cycles to 24 to allow the flash to 66262306a36Sopenharmony_ci * run at up to 200MHz. 66362306a36Sopenharmony_ci * 66462306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_cistatic int cypress_nor_set_octal_dtr(struct spi_nor *nor, bool enable) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci return enable ? cypress_nor_octal_dtr_en(nor) : 66962306a36Sopenharmony_ci cypress_nor_octal_dtr_dis(nor); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!params->n_dice || !params->vreg_offset) { 67762306a36Sopenharmony_ci dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", 67862306a36Sopenharmony_ci __func__); 67962306a36Sopenharmony_ci return -EOPNOTSUPP; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ 68362306a36Sopenharmony_ci if (params->size == SZ_256M) 68462306a36Sopenharmony_ci params->n_dice = 2; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* 68762306a36Sopenharmony_ci * On older versions of the flash the xSPI Profile 1.0 table has the 68862306a36Sopenharmony_ci * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci if (params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) 69162306a36Sopenharmony_ci params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = 69262306a36Sopenharmony_ci SPINOR_OP_CYPRESS_RD_FAST; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* This flash is also missing the 4-byte Page Program opcode bit. */ 69562306a36Sopenharmony_ci spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], 69662306a36Sopenharmony_ci SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * Since xSPI Page Program opcode is backward compatible with 69962306a36Sopenharmony_ci * Legacy SPI, use Legacy SPI opcode there as well. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], 70262306a36Sopenharmony_ci SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * The xSPI Profile 1.0 table advertises the number of additional 70662306a36Sopenharmony_ci * address bytes needed for Read Status Register command as 0 but the 70762306a36Sopenharmony_ci * actual value for that is 4. 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci params->rdsr_addr_nbytes = 4; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return cypress_nor_get_page_size(nor); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int s28hx_t_post_bfpt_fixup(struct spi_nor *nor, 71562306a36Sopenharmony_ci const struct sfdp_parameter_header *bfpt_header, 71662306a36Sopenharmony_ci const struct sfdp_bfpt *bfpt) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci return cypress_nor_set_addr_mode_nbytes(nor); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int s28hx_t_late_init(struct spi_nor *nor) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci params->set_octal_dtr = cypress_nor_set_octal_dtr; 72662306a36Sopenharmony_ci params->ready = cypress_nor_sr_ready_and_clear; 72762306a36Sopenharmony_ci cypress_nor_ecc_init(nor); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic const struct spi_nor_fixups s28hx_t_fixups = { 73362306a36Sopenharmony_ci .post_sfdp = s28hx_t_post_sfdp_fixup, 73462306a36Sopenharmony_ci .post_bfpt = s28hx_t_post_bfpt_fixup, 73562306a36Sopenharmony_ci .late_init = s28hx_t_late_init, 73662306a36Sopenharmony_ci}; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int 73962306a36Sopenharmony_cis25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor, 74062306a36Sopenharmony_ci const struct sfdp_parameter_header *bfpt_header, 74162306a36Sopenharmony_ci const struct sfdp_bfpt *bfpt) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci /* 74462306a36Sopenharmony_ci * The S25FS-S chip family reports 512-byte pages in BFPT but 74562306a36Sopenharmony_ci * in reality the write buffer still wraps at the safe default 74662306a36Sopenharmony_ci * of 256 bytes. Overwrite the page size advertised by BFPT 74762306a36Sopenharmony_ci * to get the writes working. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci nor->params->page_size = 256; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic const struct spi_nor_fixups s25fs_s_nor_fixups = { 75562306a36Sopenharmony_ci .post_bfpt = s25fs_s_nor_post_bfpt_fixups, 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic const struct flash_info spansion_nor_parts[] = { 75962306a36Sopenharmony_ci /* Spansion/Cypress -- single (large) sector size only, at least 76062306a36Sopenharmony_ci * for the chips listed here (without boot sectors). 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64) 76362306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, 76462306a36Sopenharmony_ci { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128) 76562306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, 76662306a36Sopenharmony_ci { "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64) 76762306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 76862306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 76962306a36Sopenharmony_ci }, 77062306a36Sopenharmony_ci { "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256) 77162306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 77262306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 77362306a36Sopenharmony_ci }, 77462306a36Sopenharmony_ci { "s25fl256s0", INFO6(0x010219, 0x4d0080, 256 * 1024, 128) 77562306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | 77662306a36Sopenharmony_ci SPI_NOR_QUAD_READ) 77762306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 77862306a36Sopenharmony_ci }, 77962306a36Sopenharmony_ci { "s25fl256s1", INFO6(0x010219, 0x4d0180, 64 * 1024, 512) 78062306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 78162306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 78262306a36Sopenharmony_ci }, 78362306a36Sopenharmony_ci { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256) 78462306a36Sopenharmony_ci FLAGS(SPI_NOR_HAS_LOCK) 78562306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 78662306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 78762306a36Sopenharmony_ci }, 78862306a36Sopenharmony_ci { "s25fs128s1", INFO6(0x012018, 0x4d0181, 64 * 1024, 256) 78962306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 79062306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 79162306a36Sopenharmony_ci .fixups = &s25fs_s_nor_fixups, }, 79262306a36Sopenharmony_ci { "s25fs256s0", INFO6(0x010219, 0x4d0081, 256 * 1024, 128) 79362306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 79462306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 79562306a36Sopenharmony_ci }, 79662306a36Sopenharmony_ci { "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512) 79762306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 79862306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 79962306a36Sopenharmony_ci }, 80062306a36Sopenharmony_ci { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256) 80162306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 80262306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 80362306a36Sopenharmony_ci .fixups = &s25fs_s_nor_fixups, }, 80462306a36Sopenharmony_ci { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64) }, 80562306a36Sopenharmony_ci { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256) }, 80662306a36Sopenharmony_ci { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64) 80762306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 80862306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 80962306a36Sopenharmony_ci }, 81062306a36Sopenharmony_ci { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256) 81162306a36Sopenharmony_ci NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 81262306a36Sopenharmony_ci MFR_FLAGS(USE_CLSR) 81362306a36Sopenharmony_ci }, 81462306a36Sopenharmony_ci { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8) }, 81562306a36Sopenharmony_ci { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16) }, 81662306a36Sopenharmony_ci { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32) }, 81762306a36Sopenharmony_ci { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64) }, 81862306a36Sopenharmony_ci { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128) }, 81962306a36Sopenharmony_ci { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8) 82062306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 82162306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 82262306a36Sopenharmony_ci { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16) 82362306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 82462306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 82562306a36Sopenharmony_ci { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32) 82662306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 82762306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 82862306a36Sopenharmony_ci { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128) 82962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 83062306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 83162306a36Sopenharmony_ci { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32) 83262306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | 83362306a36Sopenharmony_ci SPI_NOR_QUAD_READ) }, 83462306a36Sopenharmony_ci { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64) 83562306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) }, 83662306a36Sopenharmony_ci { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128) 83762306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K) }, 83862306a36Sopenharmony_ci { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8) 83962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, 84062306a36Sopenharmony_ci { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16) 84162306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, 84262306a36Sopenharmony_ci { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128) 84362306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 84462306a36Sopenharmony_ci FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, 84562306a36Sopenharmony_ci { "s25fl128l", INFO(0x016018, 0, 64 * 1024, 256) 84662306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 84762306a36Sopenharmony_ci FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, 84862306a36Sopenharmony_ci { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512) 84962306a36Sopenharmony_ci NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) 85062306a36Sopenharmony_ci FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, 85162306a36Sopenharmony_ci { "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0) 85262306a36Sopenharmony_ci PARSE_SFDP 85362306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 85462306a36Sopenharmony_ci .fixups = &s25fs256t_fixups }, 85562306a36Sopenharmony_ci { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 0, 0) 85662306a36Sopenharmony_ci PARSE_SFDP 85762306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 85862306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 85962306a36Sopenharmony_ci { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 0, 0) 86062306a36Sopenharmony_ci PARSE_SFDP 86162306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 86262306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 86362306a36Sopenharmony_ci { "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0) 86462306a36Sopenharmony_ci PARSE_SFDP 86562306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 86662306a36Sopenharmony_ci FLAGS(NO_CHIP_ERASE) 86762306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 86862306a36Sopenharmony_ci { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 0, 0) 86962306a36Sopenharmony_ci PARSE_SFDP 87062306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 87162306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 87262306a36Sopenharmony_ci { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 0, 0) 87362306a36Sopenharmony_ci PARSE_SFDP 87462306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 87562306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 87662306a36Sopenharmony_ci { "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0) 87762306a36Sopenharmony_ci PARSE_SFDP 87862306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 87962306a36Sopenharmony_ci FLAGS(NO_CHIP_ERASE) 88062306a36Sopenharmony_ci .fixups = &s25hx_t_fixups }, 88162306a36Sopenharmony_ci { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) 88262306a36Sopenharmony_ci FLAGS(SPI_NOR_NO_ERASE) }, 88362306a36Sopenharmony_ci { "s28hl512t", INFO(0x345a1a, 0, 0, 0) 88462306a36Sopenharmony_ci PARSE_SFDP 88562306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 88662306a36Sopenharmony_ci .fixups = &s28hx_t_fixups, 88762306a36Sopenharmony_ci }, 88862306a36Sopenharmony_ci { "s28hl01gt", INFO(0x345a1b, 0, 0, 0) 88962306a36Sopenharmony_ci PARSE_SFDP 89062306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 89162306a36Sopenharmony_ci .fixups = &s28hx_t_fixups, 89262306a36Sopenharmony_ci }, 89362306a36Sopenharmony_ci { "s28hs512t", INFO(0x345b1a, 0, 0, 0) 89462306a36Sopenharmony_ci PARSE_SFDP 89562306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 89662306a36Sopenharmony_ci .fixups = &s28hx_t_fixups, 89762306a36Sopenharmony_ci }, 89862306a36Sopenharmony_ci { "s28hs01gt", INFO(0x345b1b, 0, 0, 0) 89962306a36Sopenharmony_ci PARSE_SFDP 90062306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 90162306a36Sopenharmony_ci .fixups = &s28hx_t_fixups, 90262306a36Sopenharmony_ci }, 90362306a36Sopenharmony_ci { "s28hs02gt", INFO(0x345b1c, 0, 0, 0) 90462306a36Sopenharmony_ci PARSE_SFDP 90562306a36Sopenharmony_ci MFR_FLAGS(USE_CLPEF) 90662306a36Sopenharmony_ci .fixups = &s28hx_t_fixups, 90762306a36Sopenharmony_ci }, 90862306a36Sopenharmony_ci}; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/** 91162306a36Sopenharmony_ci * spansion_nor_sr_ready_and_clear() - Query the Status Register to see if the 91262306a36Sopenharmony_ci * flash is ready for new commands and clear it if there are any errors. 91362306a36Sopenharmony_ci * @nor: pointer to 'struct spi_nor'. 91462306a36Sopenharmony_ci * 91562306a36Sopenharmony_ci * Return: 1 if ready, 0 if not ready, -errno on errors. 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_cistatic int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci int ret; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ret = spi_nor_read_sr(nor, nor->bouncebuf); 92262306a36Sopenharmony_ci if (ret) 92362306a36Sopenharmony_ci return ret; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { 92662306a36Sopenharmony_ci if (nor->bouncebuf[0] & SR_E_ERR) 92762306a36Sopenharmony_ci dev_err(nor->dev, "Erase Error occurred\n"); 92862306a36Sopenharmony_ci else 92962306a36Sopenharmony_ci dev_err(nor->dev, "Programming Error occurred\n"); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci spansion_nor_clear_sr(nor); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * WEL bit remains set to one when an erase or page program 93562306a36Sopenharmony_ci * error occurs. Issue a Write Disable command to protect 93662306a36Sopenharmony_ci * against inadvertent writes that can possibly corrupt the 93762306a36Sopenharmony_ci * contents of the memory. 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci ret = spi_nor_write_disable(nor); 94062306a36Sopenharmony_ci if (ret) 94162306a36Sopenharmony_ci return ret; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return -EIO; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return !(nor->bouncebuf[0] & SR_WIP); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic int spansion_nor_late_init(struct spi_nor *nor) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct spi_nor_flash_parameter *params = nor->params; 95262306a36Sopenharmony_ci struct spansion_nor_params *priv_params; 95362306a36Sopenharmony_ci u8 mfr_flags = nor->info->mfr_flags; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (params->size > SZ_16M) { 95662306a36Sopenharmony_ci nor->flags |= SNOR_F_4B_OPCODES; 95762306a36Sopenharmony_ci /* No small sector erase for 4-byte command set */ 95862306a36Sopenharmony_ci nor->erase_opcode = SPINOR_OP_SE; 95962306a36Sopenharmony_ci nor->mtd.erasesize = nor->info->sector_size; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (mfr_flags & (USE_CLSR | USE_CLPEF)) { 96362306a36Sopenharmony_ci priv_params = devm_kmalloc(nor->dev, sizeof(*priv_params), 96462306a36Sopenharmony_ci GFP_KERNEL); 96562306a36Sopenharmony_ci if (!priv_params) 96662306a36Sopenharmony_ci return -ENOMEM; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (mfr_flags & USE_CLSR) 96962306a36Sopenharmony_ci priv_params->clsr = SPINOR_OP_CLSR; 97062306a36Sopenharmony_ci else if (mfr_flags & USE_CLPEF) 97162306a36Sopenharmony_ci priv_params->clsr = SPINOR_OP_CLPEF; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci params->priv = priv_params; 97462306a36Sopenharmony_ci params->ready = spansion_nor_sr_ready_and_clear; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic const struct spi_nor_fixups spansion_nor_fixups = { 98162306a36Sopenharmony_ci .late_init = spansion_nor_late_init, 98262306a36Sopenharmony_ci}; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ciconst struct spi_nor_manufacturer spi_nor_spansion = { 98562306a36Sopenharmony_ci .name = "spansion", 98662306a36Sopenharmony_ci .parts = spansion_nor_parts, 98762306a36Sopenharmony_ci .nparts = ARRAY_SIZE(spansion_nor_parts), 98862306a36Sopenharmony_ci .fixups = &spansion_nor_fixups, 98962306a36Sopenharmony_ci}; 990