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/mtd/spi-nor.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "core.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define XILINX_OP_SE 0x50 /* Sector erase */ 1262306a36Sopenharmony_ci#define XILINX_OP_PP 0x82 /* Page program */ 1362306a36Sopenharmony_ci#define XILINX_OP_RDSR 0xd7 /* Read status register */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ 1662306a36Sopenharmony_ci#define XSR_RDY BIT(7) /* Ready */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define XILINX_RDSR_OP(buf) \ 1962306a36Sopenharmony_ci SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0), \ 2062306a36Sopenharmony_ci SPI_MEM_OP_NO_ADDR, \ 2162306a36Sopenharmony_ci SPI_MEM_OP_NO_DUMMY, \ 2262306a36Sopenharmony_ci SPI_MEM_OP_DATA_IN(1, buf, 0)) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ 2562306a36Sopenharmony_ci .id = { \ 2662306a36Sopenharmony_ci ((_jedec_id) >> 16) & 0xff, \ 2762306a36Sopenharmony_ci ((_jedec_id) >> 8) & 0xff, \ 2862306a36Sopenharmony_ci (_jedec_id) & 0xff \ 2962306a36Sopenharmony_ci }, \ 3062306a36Sopenharmony_ci .id_len = 3, \ 3162306a36Sopenharmony_ci .sector_size = (8 * (_page_size)), \ 3262306a36Sopenharmony_ci .n_sectors = (_n_sectors), \ 3362306a36Sopenharmony_ci .page_size = (_page_size), \ 3462306a36Sopenharmony_ci .n_banks = 1, \ 3562306a36Sopenharmony_ci .addr_nbytes = 3, \ 3662306a36Sopenharmony_ci .flags = SPI_NOR_NO_FR 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Xilinx S3AN share MFR with Atmel SPI NOR */ 3962306a36Sopenharmony_cistatic const struct flash_info xilinx_nor_parts[] = { 4062306a36Sopenharmony_ci /* Xilinx S3AN Internal Flash */ 4162306a36Sopenharmony_ci { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, 4262306a36Sopenharmony_ci { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, 4362306a36Sopenharmony_ci { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, 4462306a36Sopenharmony_ci { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, 4562306a36Sopenharmony_ci { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * This code converts an address to the Default Address Mode, that has non 5062306a36Sopenharmony_ci * power of two page sizes. We must support this mode because it is the default 5162306a36Sopenharmony_ci * mode supported by Xilinx tools, it can access the whole flash area and 5262306a36Sopenharmony_ci * changing over to the Power-of-two mode is irreversible and corrupts the 5362306a36Sopenharmony_ci * original data. 5462306a36Sopenharmony_ci * Addr can safely be unsigned int, the biggest S3AN device is smaller than 5562306a36Sopenharmony_ci * 4 MiB. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic u32 s3an_nor_convert_addr(struct spi_nor *nor, u32 addr) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci u32 page_size = nor->params->page_size; 6062306a36Sopenharmony_ci u32 offset, page; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci offset = addr % page_size; 6362306a36Sopenharmony_ci page = addr / page_size; 6462306a36Sopenharmony_ci page <<= (page_size > 512) ? 10 : 9; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return page | offset; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * xilinx_nor_read_sr() - Read the Status Register on S3AN flashes. 7162306a36Sopenharmony_ci * @nor: pointer to 'struct spi_nor'. 7262306a36Sopenharmony_ci * @sr: pointer to a DMA-able buffer where the value of the 7362306a36Sopenharmony_ci * Status Register will be written. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (nor->spimem) { 8262306a36Sopenharmony_ci struct spi_mem_op op = XILINX_RDSR_OP(sr); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = spi_mem_exec_op(nor->spimem, &op); 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci ret = spi_nor_controller_ops_read_reg(nor, XILINX_OP_RDSR, sr, 8962306a36Sopenharmony_ci 1); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (ret) 9362306a36Sopenharmony_ci dev_dbg(nor->dev, "error %d reading SR\n", ret); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/** 9962306a36Sopenharmony_ci * xilinx_nor_sr_ready() - Query the Status Register of the S3AN flash to see 10062306a36Sopenharmony_ci * if the flash is ready for new commands. 10162306a36Sopenharmony_ci * @nor: pointer to 'struct spi_nor'. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Return: 1 if ready, 0 if not ready, -errno on errors. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic int xilinx_nor_sr_ready(struct spi_nor *nor) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = xilinx_nor_read_sr(nor, nor->bouncebuf); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return !!(nor->bouncebuf[0] & XSR_RDY); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int xilinx_nor_setup(struct spi_nor *nor, 11762306a36Sopenharmony_ci const struct spi_nor_hwcaps *hwcaps) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 page_size; 12062306a36Sopenharmony_ci int ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = xilinx_nor_read_sr(nor, nor->bouncebuf); 12362306a36Sopenharmony_ci if (ret) 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci nor->erase_opcode = XILINX_OP_SE; 12762306a36Sopenharmony_ci nor->program_opcode = XILINX_OP_PP; 12862306a36Sopenharmony_ci nor->read_opcode = SPINOR_OP_READ; 12962306a36Sopenharmony_ci nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * This flashes have a page size of 264 or 528 bytes (known as 13362306a36Sopenharmony_ci * Default addressing mode). It can be changed to a more standard 13462306a36Sopenharmony_ci * Power of two mode where the page size is 256/512. This comes 13562306a36Sopenharmony_ci * with a price: there is 3% less of space, the data is corrupted 13662306a36Sopenharmony_ci * and the page size cannot be changed back to default addressing 13762306a36Sopenharmony_ci * mode. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * The current addressing mode can be read from the XRDSR register 14062306a36Sopenharmony_ci * and should not be changed, because is a destructive operation. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci if (nor->bouncebuf[0] & XSR_PAGESIZE) { 14362306a36Sopenharmony_ci /* Flash in Power of 2 mode */ 14462306a36Sopenharmony_ci page_size = (nor->params->page_size == 264) ? 256 : 512; 14562306a36Sopenharmony_ci nor->params->page_size = page_size; 14662306a36Sopenharmony_ci nor->mtd.writebufsize = page_size; 14762306a36Sopenharmony_ci nor->params->size = 8 * page_size * nor->info->n_sectors; 14862306a36Sopenharmony_ci nor->mtd.erasesize = 8 * page_size; 14962306a36Sopenharmony_ci } else { 15062306a36Sopenharmony_ci /* Flash in Default addressing mode */ 15162306a36Sopenharmony_ci nor->params->convert_addr = s3an_nor_convert_addr; 15262306a36Sopenharmony_ci nor->mtd.erasesize = nor->info->sector_size; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int xilinx_nor_late_init(struct spi_nor *nor) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci nor->params->setup = xilinx_nor_setup; 16162306a36Sopenharmony_ci nor->params->ready = xilinx_nor_sr_ready; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct spi_nor_fixups xilinx_nor_fixups = { 16762306a36Sopenharmony_ci .late_init = xilinx_nor_late_init, 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciconst struct spi_nor_manufacturer spi_nor_xilinx = { 17162306a36Sopenharmony_ci .name = "xilinx", 17262306a36Sopenharmony_ci .parts = xilinx_nor_parts, 17362306a36Sopenharmony_ci .nparts = ARRAY_SIZE(xilinx_nor_parts), 17462306a36Sopenharmony_ci .fixups = &xilinx_nor_fixups, 17562306a36Sopenharmony_ci}; 176